feat(payment): 添加微信支付 Native支付方式
- 新增 Native 支付处理逻辑,支持扫码支付 - 重构原有 JSAPI 支付逻辑,提高代码可读性和可维护性 -增加支付配置诊断功能,方便排查支付问题 - 优化错误处理和日志记录,提高系统稳定性
This commit is contained in:
@@ -15,6 +15,7 @@ import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.shop.entity.*;
|
||||
import com.gxwebsoft.shop.service.*;
|
||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.gxwebsoft.common.system.service.PaymentService;
|
||||
import com.gxwebsoft.common.system.service.SettingService;
|
||||
@@ -28,6 +29,8 @@ import com.wechat.pay.java.core.RSAPublicKeyConfig;
|
||||
import com.wechat.pay.java.core.exception.ServiceException;
|
||||
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
||||
import com.wechat.pay.java.service.payments.jsapi.model.*;
|
||||
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
|
||||
// Native支付的类将使用完全限定名避免冲突
|
||||
import com.wechat.pay.java.service.payments.model.Transaction;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -125,6 +128,7 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
||||
|
||||
@Override
|
||||
public HashMap<String, String> createWxOrder(ShopOrder order) {
|
||||
Payment payment = null; // 声明在try块外面,这样catch块也能访问
|
||||
try {
|
||||
System.out.println("=== 开始创建微信支付订单 ===");
|
||||
System.out.println("订单号: " + order.getOrderNo());
|
||||
@@ -143,34 +147,23 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
||||
if (order.getTotalPrice() == null) {
|
||||
throw new RuntimeException("订单金额为null");
|
||||
}
|
||||
if (order.getOpenid() == null || order.getOpenid().trim().isEmpty()) {
|
||||
if(order.getPayType().equals(1)){
|
||||
throw new RuntimeException("用户OpenID为空");
|
||||
|
||||
// 根据支付类型检查OpenID
|
||||
if (order.getPayType().equals(1)) {
|
||||
// JSAPI支付需要OpenID
|
||||
if (order.getOpenid() == null || order.getOpenid().trim().isEmpty()) {
|
||||
throw new RuntimeException("JSAPI支付必须传openid");
|
||||
}
|
||||
}
|
||||
|
||||
// 后台微信支付配置信息
|
||||
final Payment payment = getPayment(order);
|
||||
payment = getPayment(order);
|
||||
System.out.println("支付配置: " + (payment != null ? "已获取" : "未获取"));
|
||||
|
||||
if (payment == null) {
|
||||
throw new RuntimeException("支付配置为null,租户ID: " + order.getTenantId());
|
||||
}
|
||||
|
||||
// 返回的订单数据
|
||||
final HashMap<String, String> orderInfo = new HashMap<>();
|
||||
// 构建service
|
||||
System.out.println("开始构建微信支付服务...");
|
||||
JsapiServiceExtension service = getWxService(order);
|
||||
System.out.println("微信支付服务构建完成");
|
||||
|
||||
// 订单金额
|
||||
BigDecimal decimal = order.getTotalPrice();
|
||||
final BigDecimal multiply = decimal.multiply(new BigDecimal(100));
|
||||
Integer money = multiply.intValue();
|
||||
|
||||
System.out.println("=== 构建支付请求参数 ===");
|
||||
|
||||
// 检查支付配置的关键字段
|
||||
if (payment.getAppId() == null || payment.getAppId().trim().isEmpty()) {
|
||||
throw new RuntimeException("支付配置中应用ID为null或空");
|
||||
@@ -179,61 +172,18 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
||||
throw new RuntimeException("支付配置中商户号为null或空");
|
||||
}
|
||||
|
||||
PrepayRequest request = new PrepayRequest();
|
||||
Amount amount = new Amount();
|
||||
amount.setTotal(money);
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
// 返回的订单数据
|
||||
final HashMap<String, String> orderInfo = new HashMap<>();
|
||||
|
||||
System.out.println("设置应用ID: " + payment.getAppId());
|
||||
request.setAppid(payment.getAppId());
|
||||
|
||||
System.out.println("设置商户号: " + payment.getMchId());
|
||||
request.setMchid(payment.getMchId());
|
||||
|
||||
// 微信支付description字段限制127字节,需要截断处理
|
||||
String description = com.gxwebsoft.common.core.utils.WechatPayUtils.processDescription(order.getComments());
|
||||
System.out.println("设置描述: " + description);
|
||||
request.setDescription(description);
|
||||
|
||||
System.out.println("设置订单号: " + order.getOrderNo());
|
||||
request.setOutTradeNo(order.getOrderNo());
|
||||
|
||||
System.out.println("设置附加数据: " + order.getTenantId().toString());
|
||||
request.setAttach(order.getTenantId().toString());
|
||||
|
||||
final Payer payer = new Payer();
|
||||
System.out.println("设置用户OpenID: " + order.getOpenid());
|
||||
payer.setOpenid(order.getOpenid());
|
||||
request.setPayer(payer);
|
||||
request.setNotifyUrl(config.getServerUrl() + "/system/wx-pay/notify/" + order.getTenantId()); // 默认回调地址
|
||||
// 测试环境
|
||||
if (active.equals("dev")) {
|
||||
amount.setTotal(1);
|
||||
request.setAmount(amount);
|
||||
request.setNotifyUrl("http://jimei-api.natapp1.cc/api/shop/wx-pay/notify/" + order.getTenantId()); // 默认回调地址
|
||||
// 根据支付类型选择不同的处理逻辑
|
||||
if (order.getPayType().equals(102)) {
|
||||
// Native支付处理
|
||||
return createNativePayOrder(order, payment);
|
||||
} else {
|
||||
// JSAPI支付处理(原有逻辑)
|
||||
return createJsapiPayOrder(order, payment);
|
||||
}
|
||||
// 后台配置的回调地址
|
||||
if (StrUtil.isNotBlank(payment.getNotifyUrl())) {
|
||||
request.setNotifyUrl(payment.getNotifyUrl().concat("/").concat(order.getTenantId().toString()));
|
||||
System.out.println("后台配置的回调地址 = " + request.getNotifyUrl());
|
||||
}
|
||||
System.out.println("=== 发起微信支付请求 ===");
|
||||
System.out.println("请求参数: " + request);
|
||||
|
||||
PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
|
||||
|
||||
System.out.println("=== 微信支付响应成功 ===");
|
||||
System.out.println("预支付ID: " + response.getPackageVal());
|
||||
|
||||
orderInfo.put("provider", "wxpay");
|
||||
orderInfo.put("timeStamp", response.getTimeStamp());
|
||||
orderInfo.put("nonceStr", response.getNonceStr());
|
||||
orderInfo.put("package", response.getPackageVal());
|
||||
orderInfo.put("signType", "RSA");
|
||||
orderInfo.put("paySign", response.getPaySign());
|
||||
orderInfo.put("orderNo", order.getOrderNo());
|
||||
return orderInfo;
|
||||
} catch (Exception e) {
|
||||
System.err.println("=== 创建微信支付订单失败 ===");
|
||||
System.err.println("错误信息: " + e.getMessage());
|
||||
@@ -248,21 +198,162 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
||||
System.err.println("4. 检查微信支付平台证书是否过期");
|
||||
System.err.println("5. 建议使用自动证书配置避免证书管理问题");
|
||||
System.err.println("当前支付配置:");
|
||||
try {
|
||||
final Payment paymentInfo = getPayment(order);
|
||||
System.err.println(" - 商户号: " + paymentInfo.getMchId());
|
||||
System.err.println(" - 序列号: " + paymentInfo.getMerchantSerialNumber());
|
||||
System.err.println(" - 环境: " + active);
|
||||
} catch (Exception ex) {
|
||||
System.err.println(" - 无法获取支付配置信息: " + ex.getMessage());
|
||||
}
|
||||
System.err.println("租户ID: " + order.getTenantId());
|
||||
System.err.println("商户号: " + (payment != null ? payment.getMchId() : "null"));
|
||||
System.err.println("应用ID: " + (payment != null ? payment.getAppId() : "null"));
|
||||
}
|
||||
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("创建支付订单失败:" + e.getMessage(), e);
|
||||
throw new RuntimeException("创建微信支付订单失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Native支付订单
|
||||
*/
|
||||
private HashMap<String, String> createNativePayOrder(ShopOrder order, Payment payment) throws Exception {
|
||||
System.out.println("=== 使用Native支付模式 ===");
|
||||
|
||||
// 构建Native支付服务
|
||||
Config wxPayConfig = getWxPayConfig(order);
|
||||
NativePayService nativeService = new NativePayService.Builder().config(wxPayConfig).build();
|
||||
|
||||
// 订单金额(转换为分)
|
||||
BigDecimal decimal = order.getTotalPrice();
|
||||
final BigDecimal multiply = decimal.multiply(new BigDecimal(100));
|
||||
Integer money = multiply.intValue();
|
||||
|
||||
// 测试环境使用1分钱
|
||||
if (active.equals("dev")) {
|
||||
money = 1;
|
||||
}
|
||||
|
||||
// 构建Native支付请求
|
||||
com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request =
|
||||
new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
|
||||
com.wechat.pay.java.service.payments.nativepay.model.Amount amount =
|
||||
new com.wechat.pay.java.service.payments.nativepay.model.Amount();
|
||||
amount.setTotal(money);
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
|
||||
request.setAppid(payment.getAppId());
|
||||
request.setMchid(payment.getMchId());
|
||||
|
||||
// 微信支付description字段限制127字节,需要截断处理
|
||||
String description = com.gxwebsoft.common.core.utils.WechatPayUtils.processDescription(order.getComments());
|
||||
request.setDescription(description);
|
||||
request.setOutTradeNo(order.getOrderNo());
|
||||
request.setAttach(order.getTenantId().toString());
|
||||
|
||||
// 设置回调地址
|
||||
String notifyUrl = config.getServerUrl() + "/system/wx-pay/notify/" + order.getTenantId();
|
||||
if (active.equals("dev")) {
|
||||
notifyUrl = "http://jimei-api.natapp1.cc/api/shop/wx-pay/notify/" + order.getTenantId();
|
||||
}
|
||||
if (StrUtil.isNotBlank(payment.getNotifyUrl())) {
|
||||
notifyUrl = payment.getNotifyUrl().concat("/").concat(order.getTenantId().toString());
|
||||
}
|
||||
request.setNotifyUrl(notifyUrl);
|
||||
|
||||
System.out.println("=== 发起Native支付请求 ===");
|
||||
System.out.println("请求参数: " + request);
|
||||
|
||||
// 调用Native支付API
|
||||
com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse response = nativeService.prepay(request);
|
||||
|
||||
System.out.println("=== Native支付响应成功 ===");
|
||||
System.out.println("二维码URL: " + response.getCodeUrl());
|
||||
|
||||
// 构建返回数据
|
||||
final HashMap<String, String> orderInfo = new HashMap<>();
|
||||
orderInfo.put("provider", "wxpay");
|
||||
orderInfo.put("codeUrl", response.getCodeUrl()); // Native支付返回二维码URL
|
||||
orderInfo.put("orderNo", order.getOrderNo());
|
||||
orderInfo.put("payType", "NATIVE");
|
||||
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建JSAPI支付订单(原有逻辑)
|
||||
*/
|
||||
private HashMap<String, String> createJsapiPayOrder(ShopOrder order, Payment payment) throws Exception {
|
||||
System.out.println("=== 使用JSAPI支付模式 ===");
|
||||
|
||||
// JSAPI支付必须有OpenID
|
||||
if (order.getOpenid() == null || order.getOpenid().trim().isEmpty()) {
|
||||
throw new RuntimeException("JSAPI支付必须传openid");
|
||||
}
|
||||
|
||||
// 构建JSAPI支付服务
|
||||
JsapiServiceExtension service = getWxService(order);
|
||||
System.out.println("微信支付服务构建完成");
|
||||
|
||||
// 订单金额
|
||||
BigDecimal decimal = order.getTotalPrice();
|
||||
final BigDecimal multiply = decimal.multiply(new BigDecimal(100));
|
||||
Integer money = multiply.intValue();
|
||||
|
||||
System.out.println("=== 构建支付请求参数 ===");
|
||||
|
||||
PrepayRequest request = new PrepayRequest();
|
||||
Amount amount = new Amount();
|
||||
amount.setTotal(money);
|
||||
amount.setCurrency("CNY");
|
||||
request.setAmount(amount);
|
||||
|
||||
System.out.println("设置应用ID: " + payment.getAppId());
|
||||
request.setAppid(payment.getAppId());
|
||||
|
||||
System.out.println("设置商户号: " + payment.getMchId());
|
||||
request.setMchid(payment.getMchId());
|
||||
|
||||
// 微信支付description字段限制127字节,需要截断处理
|
||||
String description = com.gxwebsoft.common.core.utils.WechatPayUtils.processDescription(order.getComments());
|
||||
System.out.println("设置描述: " + description);
|
||||
request.setDescription(description);
|
||||
|
||||
System.out.println("设置订单号: " + order.getOrderNo());
|
||||
request.setOutTradeNo(order.getOrderNo());
|
||||
|
||||
System.out.println("设置附加数据: " + order.getTenantId().toString());
|
||||
request.setAttach(order.getTenantId().toString());
|
||||
|
||||
final Payer payer = new Payer();
|
||||
System.out.println("设置用户OpenID: " + order.getOpenid());
|
||||
payer.setOpenid(order.getOpenid());
|
||||
request.setPayer(payer);
|
||||
request.setNotifyUrl(config.getServerUrl() + "/system/wx-pay/notify/" + order.getTenantId()); // 默认回调地址
|
||||
// 测试环境
|
||||
if (active.equals("dev")) {
|
||||
amount.setTotal(1);
|
||||
request.setAmount(amount);
|
||||
request.setNotifyUrl("http://jimei-api.natapp1.cc/api/shop/wx-pay/notify/" + order.getTenantId()); // 默认回调地址
|
||||
}
|
||||
// 后台配置的回调地址
|
||||
if (StrUtil.isNotBlank(payment.getNotifyUrl())) {
|
||||
request.setNotifyUrl(payment.getNotifyUrl().concat("/").concat(order.getTenantId().toString()));
|
||||
System.out.println("后台配置的回调地址 = " + request.getNotifyUrl());
|
||||
}
|
||||
System.out.println("=== 发起微信支付请求 ===");
|
||||
System.out.println("请求参数: " + request);
|
||||
|
||||
PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
|
||||
|
||||
System.out.println("=== 微信支付响应成功 ===");
|
||||
System.out.println("预支付ID: " + response.getPackageVal());
|
||||
|
||||
final HashMap<String, String> orderInfo = new HashMap<>();
|
||||
orderInfo.put("provider", "wxpay");
|
||||
orderInfo.put("timeStamp", response.getTimeStamp());
|
||||
orderInfo.put("nonceStr", response.getNonceStr());
|
||||
orderInfo.put("package", response.getPackageVal());
|
||||
orderInfo.put("signType", "RSA");
|
||||
orderInfo.put("paySign", response.getPaySign());
|
||||
orderInfo.put("orderNo", order.getOrderNo());
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShopOrder getByOutTradeNo(String outTradeNo) {
|
||||
return baseMapper.getByOutTradeNo(outTradeNo);
|
||||
@@ -839,5 +930,86 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Native支付的微信支付配置
|
||||
*/
|
||||
private Config getWxPayConfig(ShopOrder order) throws Exception {
|
||||
try {
|
||||
final Payment payment = getPayment(order);
|
||||
String privateKey;
|
||||
String apiclientCert = null;
|
||||
String pubKey = null;
|
||||
|
||||
// 运行配置诊断
|
||||
System.out.println("=== 微信支付配置诊断 ===");
|
||||
System.out.println("租户ID: " + order.getTenantId());
|
||||
System.out.println("商户号: " + payment.getMchId());
|
||||
System.out.println("应用ID: " + payment.getAppId());
|
||||
System.out.println("序列号: " + payment.getMerchantSerialNumber());
|
||||
System.out.println("API密钥: " + (payment.getApiKey() != null ? "已配置" : "未配置"));
|
||||
|
||||
// 证书路径构建
|
||||
String tenantCertPath = active + "/wechat/" + order.getTenantId();
|
||||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||||
String certPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
|
||||
String pubKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getWechatpayCertFile();
|
||||
|
||||
System.out.println("=== 证书路径信息 ===");
|
||||
System.out.println("私钥路径: " + privateKeyPath);
|
||||
System.out.println("证书路径: " + certPath);
|
||||
System.out.println("公钥路径: " + pubKeyPath);
|
||||
|
||||
// 获取私钥文件路径
|
||||
String actualPrivateKeyPath = certificateLoader.loadCertificatePath(privateKeyPath);
|
||||
privateKey = actualPrivateKeyPath;
|
||||
if (privateKey == null || privateKey.trim().isEmpty()) {
|
||||
throw new RuntimeException("私钥文件路径为空,路径: " + privateKeyPath);
|
||||
}
|
||||
|
||||
// 尝试获取公钥文件路径(如果配置了公钥)
|
||||
if (StrUtil.isNotBlank(payment.getPubKey()) && StrUtil.isNotBlank(payment.getPubKeyId())) {
|
||||
try {
|
||||
String actualPubKeyPath = certificateLoader.loadCertificatePath(pubKeyPath);
|
||||
pubKey = actualPubKeyPath;
|
||||
System.out.println("✅ 公钥文件路径获取成功,将使用RSA公钥配置");
|
||||
} catch (Exception e) {
|
||||
System.out.println("⚠️ 公钥文件路径获取失败,将使用自动证书配置: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 根据可用的证书类型选择配置方式
|
||||
Config config;
|
||||
if (pubKey != null && StrUtil.isNotBlank(payment.getPubKeyId())) {
|
||||
// 使用RSA公钥配置(推荐方式)
|
||||
System.out.println("🔧 使用RSA公钥配置");
|
||||
config = new RSAPublicKeyConfig.Builder()
|
||||
.merchantId(payment.getMchId())
|
||||
.privateKeyFromPath(privateKey)
|
||||
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
||||
.wechatPayPublicKeyFromPath(pubKey)
|
||||
.wechatPayPublicKeyId(payment.getPubKeyId())
|
||||
.apiV3Key(payment.getApiKey())
|
||||
.build();
|
||||
} else {
|
||||
// 使用自动证书配置
|
||||
System.out.println("🔧 使用RSA自动证书配置");
|
||||
config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(payment.getMchId())
|
||||
.privateKey(privateKey)
|
||||
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
||||
.apiV3Key(payment.getApiKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
System.out.println("✅ 微信支付配置构建成功");
|
||||
return config;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ 构建微信支付服务失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("构建微信支付服务失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user