支付宝当面付扫码支付(证书模式)
前言:自己对接时的成功代码,其中验签为支付宝技术支持提供
一、官网:
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");