|
|
@ -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); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|