alipay 当面付扫码支付实战开发
阅读原文时间:2023年07月10日阅读:4

参考官网地址:https://opendocs.alipay.com/open/194/105072

1、当面付介绍:

当面付包括付款码支付和扫码支付两种收款方式。适用于线下实体店支付、面对面支付、自助售货机等场景。

  • 付款码支付:商家使用扫码枪或其他扫码机具扫描用户出示的付款码,来实现收款。

  • 扫码支付:商家提供收款二维码,由用户通过支付宝扫码支付,来实现收款。

2、参数准备

• APPID

• 商家私钥

• 支付宝公钥

• 支付回调地址

• 网关地址

• 加密签名算法RSA2

3、JAVA 代码实战,项目基础功能准备

3.1 创建 springBoot 工程

3.2 导入依赖

                                   org.springframework.boot              spring-boot-starter-web            ​                                  org.springframework.boot              spring-boot-starter-test              test                                  junit              junit              4.13            ​                                  org.projectlombok              lombok              true            ​                                  com.alipay.sdk              alipay-sdk-java              4.22.57.ALL            ​                                  cn.hutool              hutool-all              5.7.22            ​                                  com.google.zxing              core              3.4.1            ​      

3.3 配置yml

 # 支付宝支付参数配置
 alipay:
  app_id: 公司支付宝的APPID
  merchant_private_key: 公司支付宝商户私钥
  alipay_public_key: 公司支付宝公钥
  notify_url: alipay 异步回调地址
  return_url: alipay 同步回调地址(我们做二维码扫码是异步操作,没用,可以不配置)
  sign_type: RSA2
  charset: utf-8
  gatewayUrl: https://openapi.alipay.com/gateway.do

3.4 定义配置类

 import lombok.Data;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 ​
 /**
  * @author: yijun.wen
  * @date: 2022/3/11 5:32 下午
  * @description: alipay 配置类
  */
 @Data
 @Component
 public class AlipayConfig {
 ​
     /**
      * 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
      */
 ​
     @Value("${alipay.app_id}")
     public String app_id;
 ​
     /**
      * 商户私钥,您的PKCS8格式RSA2私钥
      */
 ​
     @Value("${alipay.merchant_private_key}")
     public String merchant_private_key;
 ​
     /**
      * 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
      */
 ​
     @Value("${alipay.alipay_public_key}")
     public String alipay_public_key;
 ​
     /**
      * 服务器异步通知页面路径 需http://格式的完整路径,不能加参数,必须外网可以正常访问
      */
     @Value("${alipay.notify_url}")
     public String notify_url;
 ​
     /**
      * 页面跳转同步通知页面路径 需http://格式的完整路径,不能加参数,必须外网可以正常访问(我们这里没用这个)
      */
 ​
     @Value("${alipay.return_url}")
     public String return_url;
 ​
     /**
      * 签名方式
      */
 ​
     @Value("${alipay.sign_type}")
     public String sign_type;
 ​
     /**
      * 字符编码格式
      */
 ​
     @Value("${alipay.charset}")
     public String charset;
 ​
     /**
      * 支付宝网关
      */
     @Value("${alipay.gatewayUrl}")
     public String gatewayUrl;
 ​
 }
 ​

3.5 二维码生成工具类

 package com.wyj.alipay.util;
 ​
 import cn.hutool.extra.qrcode.BufferedImageLuminanceSource;
 import com.google.zxing.*;
 import com.google.zxing.common.BitMatrix;
 import com.google.zxing.common.HybridBinarizer;
 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
 ​
 import javax.imageio.ImageIO;
 import java.awt.*;
 import java.awt.geom.RoundRectangle2D;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.OutputStream;
 import java.util.Hashtable;
 ​
 /**
  * @author: yijun.wen
  * @date: 2022/3/11 5:36 下午
  * @description:
  */
 public class QrCodeUtil {
 ​
     private static final String CHARSET = "utf-8";
     private static final String FORMAT_NAME = "JPG";
     // 二维码尺寸
     private static final int QRCODE_SIZE = 300;
     // LOGO宽度
     private static final int WIDTH = 90;
     // LOGO高度
     private static final int HEIGHT = 90;
 ​
     private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {
         Hashtable hints = new Hashtable();
         hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
         hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
         hints.put(EncodeHintType.MARGIN, 1);
         BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
                 hints);
         int width = bitMatrix.getWidth();
         int height = bitMatrix.getHeight();
         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
         for (int x = 0; x < width; x++) {              for (int y = 0; y < height; y++) {                  image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);             }         }          if (imgPath == null || "".equals(imgPath)) {              return image;         }          // 插入图片          insertImage(image, imgPath, needCompress);          return image;     }  ​      private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {          File file = new File(imgPath);          if (!file.exists()) {              System.err.println("" + imgPath + "   该文件不存在!");              return;         }          Image src = ImageIO.read(new File(imgPath));          int width = src.getWidth(null);          int height = src.getHeight(null);          if (needCompress) { // 压缩LOGO              if (width > WIDTH) {
                 width = WIDTH;
            }
             if (height > HEIGHT) {
                 height = HEIGHT;
            }
             Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
             BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
             Graphics g = tag.getGraphics();
             // 绘制缩小后的图
             g.drawImage(image, 0, 0, null);
             g.dispose();
             src = image;
        }
         // 插入LOGO
         Graphics2D graph = source.createGraphics();
         int x = (QRCODE_SIZE - width) / 2;
         int y = (QRCODE_SIZE - height) / 2;
         graph.drawImage(src, x, y, width, height, null);
         Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
         graph.setStroke(new BasicStroke(3f));
         graph.draw(shape);
         graph.dispose();
    }
 ​
     public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {
         BufferedImage image = createImage(content, imgPath, needCompress);
         mkdirs(destPath);
         ImageIO.write(image, FORMAT_NAME, new File(destPath));
    }
 ​ 
   public static BufferedImage encode(String content, String imgPath, boolean needCompress) throws Exception { 
       BufferedImage image = createImage(content, imgPath, needCompress); 
       return image; 
  } 
​ 
   public static void mkdirs(String destPath) { 
       File file = new File(destPath); 
       // 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常) 
       if (!file.exists() && !file.isDirectory()) { 
           file.mkdirs(); 
      } 
  } 
​ 
   public static void encode(String content, String imgPath, String destPath) throws Exception { 
       encode(content, imgPath, destPath, false); 
  } 
​ 
​ 
   public static void encode(String content, String destPath) throws Exception { 
       encode(content, null, destPath, false); 
  } 
​ 
   public static void encode(String content, String imgPath, OutputStream output, boolean needCompress) 
           throws Exception { 
       BufferedImage image = createImage(content, imgPath, needCompress); 
       ImageIO.write(image, FORMAT_NAME, output); 
  } 
​ 
   public static void encode(String content, OutputStream output) throws Exception { 
       encode(content, null, output, false); 
  } 
​ 
   public static String decode(File file) throws Exception { 
       BufferedImage image; 
       image = ImageIO.read(file); 
       if (image == null) { 
           return null; 
      } 
       BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image); 
       BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); 
       Result result; 
       Hashtable hints = new Hashtable(); 
       hints.put(DecodeHintType.CHARACTER_SET, CHARSET); 
       result = new MultiFormatReader().decode(bitmap, hints); 
       String resultStr = result.getText(); 
       return resultStr; 
  } 
​ 
   public static String decode(String path) throws Exception { 
       return decode(new File(path)); 
  } 
}

 package com.wyj.alipay.util;
 ​
 import lombok.Data;
 ​
 /**
  * @author: yijun.wen
  * @date: 2022/3/11 5:39 下午
  * @description:
  */
 @Data
 public class QrCodeResponse {
     /**
      * 返回的状态码
      */
     private String code;
 ​
     /**
      * 返回的信息
      */
     private String msg;
 ​
     /**
      * 交易的流水号
      */
     private String out_trade_no;
 ​
     /**
      * 生成二维码的内容
      */
     private String qr_code;
 }

 package com.wyj.alipay.util;
 ​
 import lombok.Data;
 ​
 /**
  * @author: yijun.wen
  * @date: 2022/3/11 5:38 下午
  * @description:
  */
 @Data
 public class QrResponse {
 ​
     private QrCodeResponse alipay_trade_precreate_response;
 ​
     private String sign;
 ​
     public QrCodeResponse getAlipay_trade_precreate_response() {
         return alipay_trade_precreate_response;
    }
 ​
     public void setAlipay_trade_precreate_response(QrCodeResponse alipay_trade_precreate_response) {
         this.alipay_trade_precreate_response = alipay_trade_precreate_response;
    }
 }

3.6 订单号生成工具类

类似雪花只试用单体项目

 package com.wyj.alipay.util;
 ​
 import java.text.SimpleDateFormat;
 import java.util.Date;
 ​
 /**
  * @author: yijun.wen
  * @date: 2022/3/14 10:23 上午
  * @description:
  */
 public class GenerateNum {
     /**
      * 全局自增数
      */
     private static int count = 0;
 ​
     /**
      * 每毫秒秒最多生成多少订单(最好是像9999这种准备进位的值)
      */
     private static final int total = 99;
 ​
     /**
      * 格式化的时间字符串
      */
     private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
 ​
     /**
      * 获取当前时间年月日时分秒毫秒字符串
      *
      * @return
      */
     private static String getNowDateStr() {
         return sdf.format(new Date());
    }
 ​
     /**
      * 记录上一次的时间,用来判断是否需要递增全局数
      */
 ​
     private static String now = null;
 ​
     /**
      * 生成一个订单号
      */
     public static String generateOrder() {
         String dataStr = getNowDateStr();
         if (dataStr.equals(now)) {
             count++;
        } else {
             count = 1;
             now = dataStr;
        }
         // 算补位
         int countInteger = String.valueOf(total).length() - String.valueOf(count).length();
         //补字符串
         String bu = "";
         for (int i = 0; i < countInteger; i++) {              bu += "0";         }  ​          bu += String.valueOf(count);          if (count >= total) {
             count = 0;
        }
         return dataStr + bu;
    }
 }

3.7 项目VODTO导入

 package com.wyj.alipay.model;
 ​
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import java.io.Serializable;
 ​
 /**
  * @author: yijun.wen
  * @date: 2022/3/11 5:52 下午
  * @description:
  */
 @Data
 @NoArgsConstructor
 public class ViewData implements Serializable {
 ​
     protected int code;
     protected V data;
     protected Object error;
 ​
 }
 ​

package com.wyj.alipay.model;

import lombok.Data;

import java.io.Serializable;

/**
* @author: yijun.wen
* @date: 2022/3/11 5:50 下午
* @description:
*/
@Data
public class PayDto implements Serializable {

private Long userId;  

private String totalAmount;  

private int payType;  

}

package com.wyj.alipay.model;

import lombok.Data;

import java.io.Serializable;

/**
* @author: yijun.wen
* @date: 2022/3/11 5:52 下午
* @description:
*/
@Data
public class PayCallbackDto implements Serializable {

private Long userId;  

private String payNumber;  

}

package com.wyj.alipay.model;

import lombok.Data;

/**
* @author: yijun.wen
* @date: 2022/3/14 10:21 上午
* @description:
*/
@Data
public class QrCodeVo {
private Long UserId;

private String payNumber;  

private String qrCode;  

}

4、 创建alipay业务接口及实现类

4.1 整体业务接口

package com.wyj.alipay.service;

import com.alipay.api.AlipayApiException;
import com.wyj.alipay.model.PayCallbackDto;
import com.wyj.alipay.model.PayDto;
import com.wyj.alipay.model.ViewData;

import javax.servlet.http.HttpServletRequest;

/**
* @author: yijun.wen
* @date: 2022/3/11 5:44 下午
* @description:
*/
public interface AlipayService {
/**
* 生成支付二维码
*
* @param payInfo
* @return
*/
ViewData alipay(PayDto payInfo);

/\*\*  
 \* 支付宝回调接口  
 \*  
 \* @param request  
 \* @return  
 \*/  
boolean alipayCallback(HttpServletRequest request);  

/\*\*  
 \* alipay 监听支付状态的接口  
 \*  
 \* @param payCallbackInfo  
 \* @return  
 \* @throws AlipayApiException  
 \*/  
ViewData alipayCallback(PayCallbackDto payCallbackInfo) throws AlipayApiException;  

}

4.2 二维码返回接口实现

package com.wyj.alipay.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.wyj.alipay.config.AlipayConfig;
import com.wyj.alipay.model.PayCallbackDto;
import com.wyj.alipay.model.PayDto;
import com.wyj.alipay.model.QrCodeVo;
import com.wyj.alipay.model.ViewData;
import com.wyj.alipay.service.IAlipayService;
import com.wyj.alipay.util.GenerateNum;
import com.wyj.alipay.util.QrCodeResponse;
import com.wyj.alipay.util.QrCodeUtil;
import com.wyj.alipay.util.QrResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FileCopyUtils;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Base64;

/**
* @author: yijun.wen
* @date: 2022/3/14 10:19 上午
* @description:
*/
@Service
@Slf4j
public class AlipayServiceImpl implements IAlipayService {

@Autowired  
private AlipayConfig alipayConfig;  

@Override  
public ViewData alipay(PayDto payInfo) {  
    ViewData<QrCodeVo> viewData = new ViewData<>();  
    // 1:支付的用户  
    Long userId = payInfo.getUserId();  
    // 2: 支付金额  
    String totalAmount = payInfo.getTotalAmount();  
    // 3: 支付的产品名称  
    String productName = "Alipay test";  
    // 4: 支付的订单编号  
    String payNumber = GenerateNum.generateOrder();  
    // 5: 支付方式  
    int payType = payInfo.getPayType();  
    // 6:支付宝携带的参数在回调中可以通过request获取 参数  
    JSONObject json = JSONUtil.createObj();  
    json.set("memberId", userId);  
    json.set("totalAmount", totalAmount);  
    json.set("productName", productName);  
    json.set("payNumber", payNumber);  
    json.set("payType", payType);  
    // 7:设置支付相关的信息  
    AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();  
    // 自定义订单号  
    model.setOutTradeNo(payNumber);  
    // 支付金额  
    model.setTotalAmount(totalAmount);  
    // 支付的产品名称  
    model.setSubject(productName);  
    // 支付的请求体参数  
    model.setBody(json.toString());  
    // 支付的超时时间  
    model.setTimeoutExpress("5m");  
    // 支付的库存 id(根据 cloudPKI 业务,这里我们用用户id )  
    model.setStoreId(userId + "");  
    // 调用 alipay 获取二维码参数  
    QrCodeResponse qrCodeResponse = qrcodePay(model);  

    try {  
        ByteArrayOutputStream output = new ByteArrayOutputStream();  
        // 自定义二维码logo todo: 可以在二维码中间可以加上公司 logo  
        //String logoPath = ResourceUtils.getFile("classpath:favicon.png").getAbsolutePath();  
        String logoPath = "";  
        // 生成二维码  
        BufferedImage buffImg = QrCodeUtil.encode(qrCodeResponse.getQr\_code(), logoPath, false);  
        ImageOutputStream imageOut = ImageIO.createImageOutputStream(output);  
        ImageIO.write(buffImg, "JPEG", imageOut);  
        imageOut.close();  
        ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());  
        byte\[\] data = FileCopyUtils.copyToByteArray(input);  
        QrCodeVo qrCodeVo = new QrCodeVo();  
        qrCodeVo.setQrCode(Base64.getEncoder().encodeToString(data));  
        qrCodeVo.setPayNumber(payNumber);  
        qrCodeVo.setUserId(userId);  
        viewData.setData(qrCodeVo);  
        return viewData;  
    } catch (Exception ex) {  
        ex.printStackTrace();  
        return viewData;  
    }  
}  

@Override  
public boolean alipayCallback(HttpServletRequest request) {  
    return false;  
}  

@Override  
public ViewData alipayCallback(PayCallbackDto payCallbackInfo) throws AlipayApiException {  
    return null;  
}  

/\*\*  
 \* 扫码运行代码  
 \* 验签通过返回QrResponse  
 \* 失败打印日志信息  
 \* 参考地址:https://opendocs.alipay.com/apis/api\_1/alipay.trade.app.pay  
 \*  
 \* @param model  
 \* @return  
 \*/  
public QrCodeResponse qrcodePay(AlipayTradePrecreateModel model) {  
    // 1: 获取阿里请求客户端  
    AlipayClient alipayClient = getAlipayClient();  
    // 2: 获取阿里请求对象  
    AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();  
    // 3:设置请求参数的集合,最大长度不限  
    request.setBizModel(model);  
    // 设置异步回调地址  
    request.setNotifyUrl(alipayConfig.getNotify\_url());  
    // 设置同步回调地址  
    request.setReturnUrl(alipayConfig.getReturn\_url());  
    AlipayTradePrecreateResponse alipayTradePrecreateResponse = null;  
    try {  
        alipayTradePrecreateResponse = alipayClient.execute(request);  
    } catch (AlipayApiException e) {  
        e.printStackTrace();  
    }  
    QrResponse qrResponse = JSON.parseObject(alipayTradePrecreateResponse.getBody(), QrResponse.class);  
    return qrResponse.getAlipay\_trade\_precreate\_response();  
}  

/\*\*  
 \* 获取AlipayClient对象  
 \*  
 \* @return  
 \*/  
private AlipayClient getAlipayClient() {  
    //获得初始化的AlipayClient  
    AlipayClient alipayClient =  
            new DefaultAlipayClient(alipayConfig.getGatewayUrl(), alipayConfig.getApp\_id(), alipayConfig.getMerchant\_private\_key(),  
                    "JSON", alipayConfig.getCharset(), alipayConfig.getAlipay\_public\_key(), alipayConfig.getSign\_type());  
    return alipayClient;  
}  

}

4.3 创建 web 接口

package com.wyj.alipay.controller;

import com.wyj.alipay.model.PayDto;
import com.wyj.alipay.model.ViewData;
import com.wyj.alipay.service.IAlipayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author: yijun.wen
* @date: 2022/3/14 10:57 上午
* @description:
*/
@RestController
@RequestMapping("/api/pay/alipay/")
public class AlipayController {
@Autowired
private IAlipayService alipayService;

/\*\*  
 \* 生成支付宝支付二维码  
 \*  
 \* @param payInfo  
 \* @return  
 \*/  
@PostMapping("/qr\_code")  
public ViewData alipay(@RequestBody PayDto payInfo) {  
    return alipayService.alipay(payInfo);  
}  

}

4.4 使用 Postman 进行接口测试

qr_Code响应值为 Base64 编码

我们可以简单写个 html 页面来测试


My alipay qr_code

4.5 测试效果

5、扫码后回调接口开发

5.1 支付回调接口实现

重写 AlipayServiceImpl alipayCallback方法

/\*\*  
 \* 支付宝回调  
 \*  
 \* @return  
 \* @throws Exception  
 \*/  
@Override  
public boolean alipayCallback(HttpServletRequest request) {  
    // 获取支付宝GET过来反馈信息  
    Map<String, String> params = new LinkedHashMap<>();  
    Map requestParams = request.getParameterMap();  
    for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {  
        String name = (String) iter.next();  
        String\[\] values = (String\[\]) requestParams.get(name);  
        String valueStr = "";  
        for (int i = 0; i < values.length; i++) {  
            valueStr = (i == values.length - 1) ? valueStr + values\[i\] : valueStr + values\[i\] + ",";  
        }  
        try {  
            params.put(name, new String(valueStr.getBytes("ISO-8859-1"), "UTF-8"));  
        } catch (UnsupportedEncodingException e) {  
            e.printStackTrace();  
        }  
    }  
    // 计算得出通知验证结果  
    log.info("1:获取支付宝回传的参数" + params);  
    try {  
        // 验签  
        //RSA2密钥验签  
        boolean checkV1 = AlipaySignature.rsaCheckV1(params, alipayConfig.alipay\_public\_key, alipayConfig.charset, alipayConfig.sign\_type);  
        log.info("验签成功");  
        if (!checkV1) {  
            log.info("验签失败接口参数:{}", params);  
            return false;  
        }  
    } catch (AlipayApiException e) {  
        e.printStackTrace();  
    }  
    // 返回公共参数  
    String extparamString = request.getParameter("extra\_common\_param");  
    log.info("2:支付宝交易返回的公共参数:{}", extparamString);  
    String tradeNo = params.get("trade\_no");  
    //交易完成  
    String body = params.get("body");  
    log.info("3:【支付宝】交易的参数信息是:{},流水号是:{}", body, tradeNo);  
    try {  
        JSONObject bodyJson = new JSONObject(body);  
        Long userId = bodyJson.getLong("userId");  
        String payType = bodyJson.getStr("payType");  
        String payNumber = bodyJson.getStr("payNumber");  
        log.info("4:【支付宝】交易的参数信息是:payType:{},payNumber:{},userId:{}", payType, payNumber, userId);  
        // todo 入库充值记录 修改库存等一系列 DB 操作  
    } catch (Exception ex) {  
        log.error("支付宝支付出现了异常,流水号是:{}", tradeNo);  
        ex.printStackTrace();  
        return false;  
    }  
    return true;  
}

5.2 创建 web 接口

下面代码拷贝到 AlipayController

检查 alipay 回调地址是否为当前接口地址

/\*\*  
 \* alipay 异步通知  
 \* 参考地址:https://opendocs.alipay.com/support/01ravg  
 \*/  
@ResponseBody  
@PostMapping("/notifyUrl")  
public String notify\_url(HttpServletRequest request) {  
    boolean result = alipayService.alipayCallback(request);  
    if (result) {  
        // alipay 规范,请不要修改或删除  
        return "success";  
    } else {  
        // 验证失败  
        return "fail";  
    }  
}

5.3 查看测试效果

注意:回调接口为 alipay 调用,必须外网能够访问

我这里打包部署到服务器上,给大家看下日志效果

重新按照步骤4生成二维码,支付宝扫码支付成功后,可见日志:

6、Alipay 支付状态查询

这个接口主要是给前端轮询调用获取支付状态使用

当然,还有一种解决方案,使用websocket,在步骤5中直接发送消息通知前端

6.1 监听支付状态的接口实现

重写 AlipayServiceImpl alipayCallback 方法

@Override  
public ViewData alipayCallback(PayCallbackDto payCallbackInfo) throws AlipayApiException {  
    ViewData<Object> viewData = new ViewData<>();  
    // 1: 获取阿里请求客户端  
    AlipayClient alipayClient = getAlipayClient();  
    // 2: 获取阿里请求对象  
    AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();  
    // 3: 设置业务参数  
    request.setBizContent(JSONUtil.toJsonStr(JSONUtil.createObj().set("out\_trade\_no", payCallbackInfo.getPayNumber())));  
    //通过alipayClient调用API,获得对应的response类  
    AlipayTradeQueryResponse response = alipayClient.execute(request);  
    String body = response.getBody();  
    JSONObject json = new JSONObject(new JSONObject(body).getStr("alipay\_trade\_query\_response"));  
    if ("10000".equals(json.getStr("code")) && "TRADE\_SUCCESS".equals(json.getStr("trade\_status"))) {  
        viewData.setData("success");  
    } else {  
        viewData.setData("fail");  
    }  
    return viewData;  
}

6.2 创建 web 接口

/\*\*  
 \* alipay 监听支付状态的接口  
 \*  
 \* @param PayCallbackInfo  
 \* @return  
 \*/  
@PostMapping("/alipaycallback")  
public ViewData alipayCallback(@RequestBody PayCallbackDto PayCallbackInfo) throws AlipayApiException {  
    return alipayService.alipayCallback(PayCallbackInfo);  
}

6.3 测试

这里前端轮询逻辑,展示二维码 5 秒后,每3秒调用一次支付查询接口,得到响应success或5分钟后结束调用

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器