Browse Source

feat(payment): 添加微信支付 Native支付方式

- 新增 Native 支付处理逻辑,支持扫码支付
- 重构原有 JSAPI 支付逻辑,提高代码可读性和可维护性
-增加支付配置诊断功能,方便排查支付问题
- 优化错误处理和日志记录,提高系统稳定性
pan
科技小王子 1 month ago
parent
commit
d0791bc85d
  1. 334
      src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java

334
src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java

@ -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);
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 HashMap<String, String> orderInfo = new HashMap<>();
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());
// 根据支付类型选择不同的处理逻辑
if (order.getPayType().equals(102)) {
// Native支付处理
return createNativePayOrder(order, payment);
} else {
// JSAPI支付处理(原有逻辑)
return createJsapiPayOrder(order, payment);
}
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,19 +198,160 @@ 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
@ -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);
}
}
}

Loading…
Cancel
Save