Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 5, 2025

微信支付V3的 createPartnerOrderV3 接口返回的 JsapiResult 对象缺少 prepay_id 字段,导致用户无法存储并复用该标识,支付失败时必须重新下单。

主要修改

  • JsapiResult 中添加 prepayId 字段:使 createPartnerOrderV3 等方法返回的对象包含预支付交易会话标识
  • 添加静态工厂方法实现解耦
    • getJsapiPayInfo(String prepayId, String appId, PrivateKey privateKey) - 根据已有prepay_id生成JSAPI支付参数
    • getAppPayInfo(String prepayId, String appId, String mchId, PrivateKey privateKey) - 根据已有prepay_id生成APP支付参数

使用示例

原有方式(现在可以获取prepay_id)

WxPayUnifiedOrderV3Result.JsapiResult result = 
  wxPayService.createPartnerOrderV3(TradeTypeEnum.JSAPI, request);
String prepayId = result.getPrepayId(); // 现在可以获取并存储

解耦方式(推荐用于支付重试场景)

// 步骤1:创建订单并存储prepay_id
WxPayUnifiedOrderV3Result result = 
  wxPayService.unifiedPartnerOrderV3(TradeTypeEnum.JSAPI, request);
String prepayId = result.getPrepayId();
// 存储到数据库...

// 步骤2:支付时使用存储的prepay_id生成签名
WxPayUnifiedOrderV3Result.JsapiResult payInfo = 
  WxPayUnifiedOrderV3Result.getJsapiPayInfo(
    prepayId, appId, wxPayService.getConfig().getPrivateKey()
  );

兼容性

完全向后兼容,现有代码无需修改。

Original prompt

This section details on the original issue you should resolve

<issue_title>微信支付下单相关的接口 createOrder 怎么获取到 prepay_id,能否把 createOrder 和 getPayInfo 解耦?</issue_title>
<issue_description>付款时,总不会每次都是一次性成功,那么就需要存储 prepay_id,来复用它,

但是现在想享受签名便利的同时,且拿到 prepay_id,是没有办法做到的。

简要描述

我想在调用的是合作伙伴的的接口,我现在设计存储的时候只想存储 prepay_id ,怎么获取到?

  WxPayUnifiedOrderV3Result.JsapiResult wxPayMpOrderResult = wxPayService.createPartnerOrderV3(TradeTypeEnum.JSAPI, request);

但是 WxPayUnifiedOrderV3Result.JsapiResult 没有返回 prepay_id 字段

    public static class JsapiResult implements Serializable {
        private static final long serialVersionUID = 4465376277943307271L;
        private String appId;
        private String timeStamp;
        private String nonceStr;
        private String packageValue;
        private String signType;
        private String paySign;
}

解决预案:

能否同时满足,先从 createOrder 拿到存储 prepay_id ,再可以调用 getPayInfo 进行签名?

就是把请求和签名两个步骤解耦开。

官方文档地址

https://pay.weixin.qq.com/doc/v3/partner/4012759974
</issue_description>

Comments on the Issue (you are @copilot in this section)

Custom agent used: 全部用中文
需要用中文,包括PR标题和分析总结过程


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits December 5, 2025 08:20
Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
Copilot AI changed the title [WIP] Decouple createOrder from getPayInfo to access prepay_id 在JsapiResult中添加prepayId字段并提供静态工厂方法实现解耦 Dec 5, 2025
Copilot AI requested a review from binarywang December 5, 2025 08:30
@binarywang binarywang marked this pull request as ready for review December 20, 2025 12:08
Copilot AI review requested due to automatic review settings December 20, 2025 12:08
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

本 PR 通过在 JsapiResult 中添加 prepayId 字段并提供静态工厂方法,解决了微信支付 V3 API 中用户无法存储和复用预支付交易会话标识的问题,同时实现了订单创建与支付签名生成的解耦。

主要变更:

  • JsapiResult 类中添加 prepayId 字段,使用户能够获取并存储预支付交易会话标识
  • 新增两个静态工厂方法 getJsapiPayInfo()getAppPayInfo(),支持使用已存储的 prepay_id 重新生成支付签名
  • 修正了 code_url 字段注释中的变量名错误

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java JsapiResult 中添加 prepayId 字段;新增 getJsapiPayInfo()getAppPayInfo() 静态工厂方法用于解耦场景;修正 code_url 注释错误
weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3ResultTest.java 新增完整的测试用例,覆盖 prepayId 字段的设置、静态工厂方法的功能以及解耦场景的使用

Comment on lines +175 to +223
public static JsapiResult getJsapiPayInfo(String prepayId, String appId, PrivateKey privateKey) {
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = SignUtils.genRandomStr();
JsapiResult jsapiResult = new JsapiResult();
jsapiResult.setAppId(appId).setTimeStamp(timestamp)
.setPackageValue("prepay_id=" + prepayId).setNonceStr(nonceStr)
.setPrepayId(prepayId)
//签名类型,默认为RSA,仅支持RSA。
.setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey));
return jsapiResult;
}

/**
* <pre>
* 根据已有的prepay_id生成APP支付所需的参数对象(解耦版本)
* 应用场景:
* 1. 用户已经通过createPartnerOrderV3或unifiedPartnerOrderV3获取了prepay_id
* 2. 用户希望存储prepay_id用于后续复用
* 3. 支付失败后,使用存储的prepay_id重新生成支付签名信息
*
* 使用示例:
* // 步骤1:创建订单并获取prepay_id
* WxPayUnifiedOrderV3Result result = wxPayService.unifiedPartnerOrderV3(TradeTypeEnum.APP, request);
* String prepayId = result.getPrepayId();
* // 存储prepayId到数据库...
*
* // 步骤2:需要支付时,使用存储的prepay_id生成支付信息
* WxPayUnifiedOrderV3Result.AppResult payInfo = WxPayUnifiedOrderV3Result.getAppPayInfo(
* prepayId, appId, mchId, wxPayService.getConfig().getPrivateKey()
* );
* </pre>
*
* @param prepayId 预支付交易会话标识
* @param appId 应用ID
* @param mchId 商户号
* @param privateKey 商户私钥,用于签名
* @return APP支付所需的参数对象
*/
public static AppResult getAppPayInfo(String prepayId, String appId, String mchId, PrivateKey privateKey) {
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = SignUtils.genRandomStr();
AppResult appResult = new AppResult();
appResult.setAppid(appId).setPrepayId(prepayId).setPartnerId(mchId)
.setNoncestr(nonceStr).setTimestamp(timestamp)
//暂填写固定值Sign=WXPay
.setPackageValue("Sign=WXPay")
.setSign(SignUtils.sign(appResult.getSignStr(), privateKey));
return appResult;
}
Copy link

Copilot AI Dec 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

静态工厂方法 getJsapiPayInfogetAppPayInfo 与实例方法 getPayInfo 中的逻辑存在重复。建议将重复的签名生成逻辑提取为私有方法以提高代码可维护性。

例如,可以创建私有方法:

  • buildJsapiResult(String prepayId, String appId, PrivateKey privateKey)
  • buildAppResult(String prepayId, String appId, String mchId, PrivateKey privateKey)

然后在实例方法 getPayInfo 和静态方法中都调用这些私有方法。

Copilot uses AI. Check for mistakes.
Comment on lines +175 to +185
public static JsapiResult getJsapiPayInfo(String prepayId, String appId, PrivateKey privateKey) {
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = SignUtils.genRandomStr();
JsapiResult jsapiResult = new JsapiResult();
jsapiResult.setAppId(appId).setTimeStamp(timestamp)
.setPackageValue("prepay_id=" + prepayId).setNonceStr(nonceStr)
.setPrepayId(prepayId)
//签名类型,默认为RSA,仅支持RSA。
.setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey));
return jsapiResult;
}
Copy link

Copilot AI Dec 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

静态工厂方法缺少对输入参数的空值验证。如果传入 null 参数(如 prepayId、appId 或 privateKey),可能导致 NullPointerException 或生成无效的签名数据。

建议在方法开始处添加参数验证,例如:

if (prepayId == null || appId == null || privateKey == null) {
  throw new IllegalArgumentException("prepayId, appId 和 privateKey 不能为空");
}

或者使用 Objects.requireNonNull() 进行验证。

Copilot uses AI. Check for mistakes.
Comment on lines +213 to +223
public static AppResult getAppPayInfo(String prepayId, String appId, String mchId, PrivateKey privateKey) {
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = SignUtils.genRandomStr();
AppResult appResult = new AppResult();
appResult.setAppid(appId).setPrepayId(prepayId).setPartnerId(mchId)
.setNoncestr(nonceStr).setTimestamp(timestamp)
//暂填写固定值Sign=WXPay
.setPackageValue("Sign=WXPay")
.setSign(SignUtils.sign(appResult.getSignStr(), privateKey));
return appResult;
}
Copy link

Copilot AI Dec 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

静态工厂方法 getAppPayInfo 缺少对输入参数的空值验证。如果传入 null 参数(如 prepayId、appId、mchId 或 privateKey),可能导致 NullPointerException 或生成无效的签名数据。

建议在方法开始处添加参数验证,例如:

if (prepayId == null || appId == null || mchId == null || privateKey == null) {
  throw new IllegalArgumentException("prepayId, appId, mchId 和 privateKey 不能为空");
}

或者使用 Objects.requireNonNull() 进行验证。

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

微信支付下单相关的接口 createOrder 怎么获取到 prepay_id,能否把 createOrder 和 getPayInfo 解耦?

2 participants