-
-
Notifications
You must be signed in to change notification settings - Fork 9k
在JsapiResult中添加prepayId字段并提供静态工厂方法实现解耦 #3798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,7 +58,7 @@ public class WxPayUnifiedOrderV3Result implements Serializable { | |
| /** | ||
| * <pre> | ||
| * 字段名:二维码链接(NATIVE支付 会返回) | ||
| * 变量名:h5_url | ||
| * 变量名:code_url | ||
| * 是否必填:是 | ||
| * 类型:string[1,512] | ||
| * 描述: | ||
|
|
@@ -81,6 +81,19 @@ public static class JsapiResult implements Serializable { | |
| private String packageValue; | ||
| private String signType; | ||
| private String paySign; | ||
| /** | ||
| * <pre> | ||
| * 字段名:预支付交易会话标识 | ||
| * 变量名:prepay_id | ||
| * 是否必填:否(用户可选存储) | ||
| * 类型:string[1,64] | ||
| * 描述: | ||
| * 预支付交易会话标识。用于后续接口调用中使用,该值有效期为2小时 | ||
| * 此字段用于支持用户存储prepay_id,以便复用和重新生成支付签名 | ||
| * 示例值:wx201410272009395522657a690389285100 | ||
| * </pre> | ||
| */ | ||
| private String prepayId; | ||
|
|
||
| private String getSignStr() { | ||
| return String.format("%s\n%s\n%s\n%s\n", appId, timeStamp, nonceStr, packageValue); | ||
|
|
@@ -113,6 +126,7 @@ public <T> T getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, Pri | |
| JsapiResult jsapiResult = new JsapiResult(); | ||
| jsapiResult.setAppId(appId).setTimeStamp(timestamp) | ||
| .setPackageValue("prepay_id=" + this.prepayId).setNonceStr(nonceStr) | ||
| .setPrepayId(this.prepayId) | ||
| //签名类型,默认为RSA,仅支持RSA。 | ||
| .setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey)); | ||
| return (T) jsapiResult; | ||
|
|
@@ -132,4 +146,79 @@ public <T> T getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, Pri | |
| throw new WxRuntimeException("不支持的支付类型"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * <pre> | ||
| * 根据已有的prepay_id生成JSAPI支付所需的参数对象(解耦版本) | ||
| * 应用场景: | ||
| * 1. 用户已经通过createPartnerOrderV3或unifiedPartnerOrderV3获取了prepay_id | ||
| * 2. 用户希望存储prepay_id用于后续复用 | ||
| * 3. 支付失败后,使用存储的prepay_id重新生成支付签名信息 | ||
| * | ||
| * 使用示例: | ||
| * // 步骤1:创建订单并获取prepay_id | ||
| * WxPayUnifiedOrderV3Result result = wxPayService.unifiedPartnerOrderV3(TradeTypeEnum.JSAPI, request); | ||
| * String prepayId = result.getPrepayId(); | ||
| * // 存储prepayId到数据库... | ||
| * | ||
| * // 步骤2:需要支付时,使用存储的prepay_id生成支付信息 | ||
| * WxPayUnifiedOrderV3Result.JsapiResult payInfo = WxPayUnifiedOrderV3Result.getJsapiPayInfo( | ||
| * prepayId, appId, wxPayService.getConfig().getPrivateKey() | ||
| * ); | ||
| * </pre> | ||
| * | ||
| * @param prepayId 预支付交易会话标识 | ||
| * @param appId 应用ID | ||
| * @param privateKey 商户私钥,用于签名 | ||
| * @return JSAPI支付所需的参数对象 | ||
| */ | ||
| 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; | ||
| } | ||
|
Comment on lines
+175
to
+223
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| package com.github.binarywang.wxpay.bean.result; | ||
|
|
||
| import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; | ||
| import com.github.binarywang.wxpay.v3.util.SignUtils; | ||
| import org.testng.Assert; | ||
| import org.testng.annotations.Test; | ||
|
|
||
| import java.security.KeyPair; | ||
| import java.security.KeyPairGenerator; | ||
| import java.security.PrivateKey; | ||
|
|
||
| /** | ||
| * <pre> | ||
| * WxPayUnifiedOrderV3Result 测试类 | ||
| * 主要测试prepayId字段和静态工厂方法的解耦功能 | ||
| * </pre> | ||
| * | ||
| * @author copilot | ||
| */ | ||
| public class WxPayUnifiedOrderV3ResultTest { | ||
|
|
||
| /** | ||
| * 生成测试用的RSA密钥对 | ||
| */ | ||
| private KeyPair generateKeyPair() throws Exception { | ||
| KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); | ||
| keyPairGenerator.initialize(2048); | ||
| return keyPairGenerator.generateKeyPair(); | ||
| } | ||
|
|
||
| /** | ||
| * 测试JsapiResult中的prepayId字段是否正确设置 | ||
| */ | ||
| @Test | ||
| public void testJsapiResultWithPrepayId() throws Exception { | ||
| // 准备测试数据 | ||
| String testPrepayId = "wx201410272009395522657a690389285100"; | ||
| String testAppId = "wx8888888888888888"; | ||
| KeyPair keyPair = generateKeyPair(); | ||
| PrivateKey privateKey = keyPair.getPrivate(); | ||
|
|
||
| // 创建WxPayUnifiedOrderV3Result对象 | ||
| WxPayUnifiedOrderV3Result result = new WxPayUnifiedOrderV3Result(); | ||
| result.setPrepayId(testPrepayId); | ||
|
|
||
| // 调用getPayInfo生成JsapiResult | ||
| WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = | ||
| result.getPayInfo(TradeTypeEnum.JSAPI, testAppId, null, privateKey); | ||
|
|
||
| // 验证prepayId字段是否正确设置 | ||
| Assert.assertNotNull(jsapiResult.getPrepayId(), "prepayId不应为null"); | ||
| Assert.assertEquals(jsapiResult.getPrepayId(), testPrepayId, "prepayId应该与设置的值相同"); | ||
|
|
||
| // 验证其他字段 | ||
| Assert.assertEquals(jsapiResult.getAppId(), testAppId); | ||
| Assert.assertNotNull(jsapiResult.getTimeStamp()); | ||
| Assert.assertNotNull(jsapiResult.getNonceStr()); | ||
| Assert.assertEquals(jsapiResult.getPackageValue(), "prepay_id=" + testPrepayId); | ||
| Assert.assertEquals(jsapiResult.getSignType(), "RSA"); | ||
| Assert.assertNotNull(jsapiResult.getPaySign()); | ||
| } | ||
|
|
||
| /** | ||
| * 测试使用静态工厂方法生成JsapiResult(解耦场景) | ||
| */ | ||
| @Test | ||
| public void testGetJsapiPayInfoStaticMethod() throws Exception { | ||
| // 准备测试数据 | ||
| String testPrepayId = "wx201410272009395522657a690389285100"; | ||
| String testAppId = "wx8888888888888888"; | ||
| KeyPair keyPair = generateKeyPair(); | ||
| PrivateKey privateKey = keyPair.getPrivate(); | ||
|
|
||
| // 使用静态工厂方法生成JsapiResult | ||
| WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = | ||
| WxPayUnifiedOrderV3Result.getJsapiPayInfo(testPrepayId, testAppId, privateKey); | ||
|
|
||
| // 验证prepayId字段 | ||
| Assert.assertNotNull(jsapiResult.getPrepayId(), "prepayId不应为null"); | ||
| Assert.assertEquals(jsapiResult.getPrepayId(), testPrepayId, "prepayId应该与输入的值相同"); | ||
|
|
||
| // 验证其他字段 | ||
| Assert.assertEquals(jsapiResult.getAppId(), testAppId); | ||
| Assert.assertNotNull(jsapiResult.getTimeStamp()); | ||
| Assert.assertNotNull(jsapiResult.getNonceStr()); | ||
| Assert.assertEquals(jsapiResult.getPackageValue(), "prepay_id=" + testPrepayId); | ||
| Assert.assertEquals(jsapiResult.getSignType(), "RSA"); | ||
| Assert.assertNotNull(jsapiResult.getPaySign()); | ||
| } | ||
|
|
||
| /** | ||
| * 测试使用静态工厂方法生成AppResult(解耦场景) | ||
| */ | ||
| @Test | ||
| public void testGetAppPayInfoStaticMethod() throws Exception { | ||
| // 准备测试数据 | ||
| String testPrepayId = "wx201410272009395522657a690389285100"; | ||
| String testAppId = "wx8888888888888888"; | ||
| String testMchId = "1900000109"; | ||
| KeyPair keyPair = generateKeyPair(); | ||
| PrivateKey privateKey = keyPair.getPrivate(); | ||
|
|
||
| // 使用静态工厂方法生成AppResult | ||
| WxPayUnifiedOrderV3Result.AppResult appResult = | ||
| WxPayUnifiedOrderV3Result.getAppPayInfo(testPrepayId, testAppId, testMchId, privateKey); | ||
|
|
||
| // 验证prepayId字段 | ||
| Assert.assertNotNull(appResult.getPrepayId(), "prepayId不应为null"); | ||
| Assert.assertEquals(appResult.getPrepayId(), testPrepayId, "prepayId应该与输入的值相同"); | ||
|
|
||
| // 验证其他字段 | ||
| Assert.assertEquals(appResult.getAppid(), testAppId); | ||
| Assert.assertEquals(appResult.getPartnerId(), testMchId); | ||
| Assert.assertNotNull(appResult.getTimestamp()); | ||
| Assert.assertNotNull(appResult.getNoncestr()); | ||
| Assert.assertEquals(appResult.getPackageValue(), "Sign=WXPay"); | ||
| Assert.assertNotNull(appResult.getSign()); | ||
| } | ||
|
|
||
| /** | ||
| * 测试解耦场景:先获取prepayId,后续再生成支付信息 | ||
| */ | ||
| @Test | ||
| public void testDecoupledScenario() throws Exception { | ||
| // 模拟场景:先创建订单获取prepayId | ||
| String testPrepayId = "wx201410272009395522657a690389285100"; | ||
| String testAppId = "wx8888888888888888"; | ||
| KeyPair keyPair = generateKeyPair(); | ||
| PrivateKey privateKey = keyPair.getPrivate(); | ||
|
|
||
| // 步骤1:模拟从创建订单接口获取prepayId | ||
| WxPayUnifiedOrderV3Result orderResult = new WxPayUnifiedOrderV3Result(); | ||
| orderResult.setPrepayId(testPrepayId); | ||
|
|
||
| // 获取prepayId用于存储 | ||
| String storedPrepayId = orderResult.getPrepayId(); | ||
| Assert.assertEquals(storedPrepayId, testPrepayId); | ||
|
|
||
| // 步骤2:后续支付失败时,使用存储的prepayId重新生成支付信息 | ||
| WxPayUnifiedOrderV3Result.JsapiResult newPayInfo = | ||
| WxPayUnifiedOrderV3Result.getJsapiPayInfo(storedPrepayId, testAppId, privateKey); | ||
|
|
||
| // 验证重新生成的支付信息 | ||
| Assert.assertEquals(newPayInfo.getPrepayId(), storedPrepayId); | ||
| Assert.assertEquals(newPayInfo.getPackageValue(), "prepay_id=" + storedPrepayId); | ||
| Assert.assertNotNull(newPayInfo.getPaySign()); | ||
| } | ||
|
|
||
| /** | ||
| * 测试多次生成支付信息,签名应该不同(因为timestamp和nonceStr每次都不同) | ||
| */ | ||
| @Test | ||
| public void testMultipleGenerationsHaveDifferentSignatures() throws Exception { | ||
| String testPrepayId = "wx201410272009395522657a690389285100"; | ||
| String testAppId = "wx8888888888888888"; | ||
| KeyPair keyPair = generateKeyPair(); | ||
| PrivateKey privateKey = keyPair.getPrivate(); | ||
|
|
||
| // 生成第一次支付信息 | ||
| WxPayUnifiedOrderV3Result.JsapiResult result1 = | ||
| WxPayUnifiedOrderV3Result.getJsapiPayInfo(testPrepayId, testAppId, privateKey); | ||
|
|
||
| // 等待一秒确保timestamp不同 | ||
| Thread.sleep(1000); | ||
|
|
||
| // 生成第二次支付信息 | ||
| WxPayUnifiedOrderV3Result.JsapiResult result2 = | ||
| WxPayUnifiedOrderV3Result.getJsapiPayInfo(testPrepayId, testAppId, privateKey); | ||
|
|
||
| // prepayId应该相同 | ||
| Assert.assertEquals(result1.getPrepayId(), result2.getPrepayId()); | ||
|
|
||
| // 但是timestamp、nonceStr和签名应该不同 | ||
| Assert.assertNotEquals(result1.getTimeStamp(), result2.getTimeStamp(), "timestamp应该不同"); | ||
| Assert.assertNotEquals(result1.getNonceStr(), result2.getNonceStr(), "nonceStr应该不同"); | ||
| Assert.assertNotEquals(result1.getPaySign(), result2.getPaySign(), "签名应该不同"); | ||
| } | ||
|
|
||
| /** | ||
| * 测试AppResult中的prepayId字段 | ||
| */ | ||
| @Test | ||
| public void testAppResultWithPrepayId() throws Exception { | ||
| String testPrepayId = "wx201410272009395522657a690389285100"; | ||
| String testAppId = "wx8888888888888888"; | ||
| String testMchId = "1900000109"; | ||
| KeyPair keyPair = generateKeyPair(); | ||
| PrivateKey privateKey = keyPair.getPrivate(); | ||
|
|
||
| WxPayUnifiedOrderV3Result result = new WxPayUnifiedOrderV3Result(); | ||
| result.setPrepayId(testPrepayId); | ||
|
|
||
| // 调用getPayInfo生成AppResult | ||
| WxPayUnifiedOrderV3Result.AppResult appResult = | ||
| result.getPayInfo(TradeTypeEnum.APP, testAppId, testMchId, privateKey); | ||
|
|
||
| // 验证prepayId字段 | ||
| Assert.assertNotNull(appResult.getPrepayId(), "prepayId不应为null"); | ||
| Assert.assertEquals(appResult.getPrepayId(), testPrepayId, "prepayId应该与设置的值相同"); | ||
| } | ||
| } |
There was a problem hiding this comment.
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 或生成无效的签名数据。
建议在方法开始处添加参数验证,例如:
或者使用 Objects.requireNonNull() 进行验证。