支付宝当面付扫码支付下单以及回调(证书模式)

支付宝当面付扫码支付(证书模式)

前言:自己对接时的成功代码,其中验签为支付宝技术支持提供

一、官网:

1、支付宝开放平台:https://open.alipay.com

2、当面付开发者文档:https://opendoc.alipay.com/open-v3/05pf4i?pathHash=d7773c48

3、沙箱控制台环境:https://open.alipay.com/develop/sandbox/app

二、引入依赖:

<!--支付宝支付SDK-->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.39.190.ALL</version>
</dependency>

<!--生成二维码-->
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.0.0</version>
</dependency>

三、配置文件:

请将以下参数替换为你自己的实际参数

alibaba:
  pay:
  	#网关:沙箱网关(正式网关:https://openapi.alipay.com/gateway.do)
	gatewayHost: https://openapi-sandbox.dl.alipaydev.com/gateway.do
    appId: #APPID
    sellerId: #商家PID
    appPrivateKey: #应用私钥
    appCertPublicKey: #应用公钥证书的存放路径
    alipayCertPublicKey: #支付宝公钥证书存放路径
    alipayRootCert: #支付宝根证书存放路径
    notifyUrl: #回调地址(必须为https且公网可以访问的接口地址)

四、支付方法:

注意:请求支付下单成功后记得编写自己的订单逻辑

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
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.yunjianlaike.common.utils.file.QRImageUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;

/**
 * @Author: Zacc
 * @Desc: 简单支付宝支付
 * @DateTime: 2024/9/3 15:32
 */
@Component
public class AliPay {

    @Value("${alibaba.pay.gatewayHost}")
    private String gatewayHost;
    @Value("${alibaba.pay.appId}")
    private String appId;
    @Value("${alibaba.pay.appPrivateKey}")
    private String appPrivateKey;
    @Value("${alibaba.pay.appCertPublicKey}")
    private String appCertPublicKey;
    @Value("${alibaba.pay.alipayCertPublicKey}")
    private String alipayCertPublicKey;
    @Value("${alibaba.pay.alipayRootCert}")
    private String alipayRootCert;
    @Value("${alibaba.pay.notifyUrl}")
    private String notifyUrl;

    public String pay(ShrOrder order) throws AlipayApiException {
		//构造支付配置
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setAppId(appId);
        alipayConfig.setAlipayPublicCertPath(alipayCertPublicKey);
        alipayConfig.setRootCertPath(alipayRootCert);
        alipayConfig.setAppCertPath(appCertPublicKey);
        alipayConfig.setPrivateKey(appPrivateKey);
        alipayConfig.setServerUrl(gatewayHost);
        alipayConfig.setFormat("json");
        alipayConfig.setCharset("UTF8");
        alipayConfig.setSignType("RSA2");
        
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
        AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
		
        //设置支付参数
        //自己生成的订单号(必须保证同一个商家号中唯一,这里使用统一前缀+当前时间的毫秒值)
        model.setOutTradeNo("out_trade_no_"+ System.currentTimeMillis());
        model.setSubject("测试订单");
        //订单金额(精确到小数后两位,并且需要转换成字符串)
        model.setTotalAmount("10.00");
        //设置超时时间
        model.setTimeoutExpress("3m");
        request.setBizModel(model);
        //设置回调地址
        request.setNotifyUrl(notifyUrl);
        AlipayTradePrecreateResponse response = alipayClient.certificateExecute(request);
        if (response.isSuccess()) {
            //根据成功响应中的QrCode生成验证码图片Url返回给前端
            String qrCode = response.getQrCode();
            String image = QRImageUtils.getQRImage(qrCode);
            return image;
        }
        return null;
    }

}
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import java.util.Base64;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: Zacc
 * @Desc: 二维码工具类
 * @DateTime: 2024/9/3 15:32
 */
public class QRImageUtils {

    // 二维码需要使用到的颜色
    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;
    
    //你的二维码存放路径
    private static final String imgPath = "/usr/local/qrcode/";

    public static String getQRImage(String QrCodeStr) {
        try{

            // 生成的二维码名称
            String imgName = "QrCodeStr"+ System.currentTimeMillis() +".jpg";

            // 创建二维码try {
            Map<EncodeHintType, String> charcter = new HashMap<>();
            // 设置字符集
            charcter.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            // 设置二维码的四个参数   需要生成的字符串,类型设置为二维码,二维码宽度,二维码高度,字符串字符集
            BitMatrix bitMatrix = new MultiFormatWriter()
                    .encode(QrCodeStr, BarcodeFormat.QR_CODE, 500, 500, charcter);
            // 创建文件对象
            File file = new File(imgPath, imgName);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            // 二维码像素,也就是上面设置的 500
            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) ? BLACK : WHITE);
                }
            }
            // 1、第一种方式// 生成的二维码图片对象转 base64
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            // 设置图片的格式
            ImageIO.write(image, "png", stream);
            String base64 = Base64.getEncoder().encodeToString(stream.toByteArray());
            // 输出转换成功后的base64编码
            System.out.println(base64);
            // 2、第二种方式
            // 直接输出二维码文件
            ImageIO.write(image, "jpg", file);
            return imgPath + imgName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "验证码生成异常";
    }
}

五、支付异步回调:

使用官方文档上的验签方法一直验签失败,无奈只能求助支付宝技术支持,其中的验签方法为支付宝技术支持提供

package com.yunjianlaike.web.controller.share;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConstants;
import com.alipay.api.internal.util.AlipaySignature;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.yunjianlaike.share.pay.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: Zacc
 * @Desc: 支付回调
 * @DateTime: 2024/8/26 9:23
 */
@Slf4j
@RestController
public class NotifyController {

    @Value("${alibaba.pay.certPath}")
    private String certPath;

    private final ReentrantLock lock = new ReentrantLock();

    /**
     * Desc: 支付宝支付回调
     *
     * @param request
     * @param response
     * @return: void
     */
    @PostMapping("/aliNotify")
    public String aliNotify(HttpServletRequest request, HttpServletResponse response) {

        System.out.println(response);
        if (lock.tryLock()) {
            try {
                //参数拼接(此方法为支付宝技术支持提供)
                Map<String, String[]> requestParams = request.getParameterMap();
                StringBuffer buffer = new StringBuffer();
                requestParams.forEach((key, val) -> buffer.append(key).append("=").append(val[0]).append("&"));

                String resultInfo = buffer.toString();
                resultInfo = resultInfo.substring(0, resultInfo.length() - 1);

                //对待签名字符串数据通过&进行拆分
                String[] temp = resultInfo.split("&");
                LinkedHashMap<String, String> params = new LinkedHashMap<>();
                //把拆分数据放在map集合内
                for (int i = 0; i < temp.length; i++) {
                    String[] arr = temp[i].split("=", 2); //通过"="号分割成2个数据
                    String[] tempAagin = new String[arr.length]; //再开辟一个数组用来接收分割后的数据
                    for (int j = 0; j < arr.length; j++) {
                        tempAagin[j] = arr[j];
                    }
                    params.put(tempAagin[0], tempAagin[1]);
                }

                //验签方法(证书验签)
                boolean signVerified = AlipaySignature.rsaCertCheckV1(params, certPath, AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA2);
                
                if (signVerified) {
                    //商户订单号,之前生成的带用户ID的订单号
                    String outTradeNo = new String(params.get("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                    //支付宝交易号
                    String tradeNo = new String(params.get("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                    //交易状态
                    String tradeStatus = new String(params.get("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                    
                    //TODO根据交易状态进行幂等操作,避免数据重入(支付回调会每隔几秒调用)
                } else {
                    return "fail";
                }

            } catch (AlipayApiException e) {
                throw new RuntimeException(e);
            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }
        return "success";
    }
}

根据公钥证书文件获取公钥:

String alipaypublicKey = AlipaySignature.getAlipayPublicKey("/opt/module/office/alipayCertPublicKey_RSA2.crt");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值