Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class WxPayUnifiedOrderV3Result implements Serializable {
/**
* <pre>
* 字段名:二维码链接(NATIVE支付 会返回)
* 变量名:h5_url
* 变量名:code_url
* 是否必填:是
* 类型:string[1,512]
* 描述:
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
}
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应该与设置的值相同");
}
}