Skip to content

Commit 0174215

Browse files
authored
🎨 #4120 【小程序】新增个人主体虚拟支付参数支持
1 parent c1591ba commit 0174215

8 files changed

Lines changed: 367 additions & 0 deletions

File tree

weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaXPayService.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ public interface WxMaXPayService {
5151
*/
5252
WxMaXPayCancelCurrencyPayResponse cancelCurrencyPay(WxMaXPayCancelCurrencyPayRequest request, WxMaXPaySigParams sigParams) throws WxErrorException;
5353

54+
/**
55+
* 生成调起 wx.requestVirtualPayment 所需的支付参数。
56+
*
57+
* @param request 虚拟支付调起参数
58+
* @param sigParams 签名参数对象
59+
* @return 支付参数
60+
*/
61+
default WxMaXPayRequestVirtualPaymentData createRequestVirtualPaymentData(WxMaXPayRequestVirtualPaymentRequest request,
62+
WxMaXPaySigParams sigParams) {
63+
return request.createPayData(sigParams);
64+
}
65+
5466
/**
5567
* 通知发货。
5668
*

weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cn.binarywang.wx.miniapp.bean;
22

3+
import cn.binarywang.wx.miniapp.bean.xpay.WxMaXPayGoodsInfo;
34
import cn.binarywang.wx.miniapp.bean.xpay.WxMaXPayTeamInfo;
5+
import cn.binarywang.wx.miniapp.bean.xpay.WxMaXPayWeChatPayInfo;
46
import cn.binarywang.wx.miniapp.config.WxMaConfig;
57
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
68
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
@@ -213,6 +215,31 @@ public class WxMaMessage implements Serializable {
213215
@XStreamAlias("SubscribeMsgSentEvent")
214216
private WxMaSubscribeMsgEvent.SubscribeMsgSentEvent subscribeMsgSentEvent;
215217

218+
/**
219+
* 商户订单号.
220+
* xpay_goods_deliver_notify
221+
*/
222+
@SerializedName("OutTradeNo")
223+
@XStreamAlias("OutTradeNo")
224+
@XStreamConverter(value = XStreamCDataConverter.class)
225+
private String outTradeNo;
226+
227+
/**
228+
* 微信支付信息.
229+
* xpay_goods_deliver_notify
230+
*/
231+
@SerializedName("WeChatPayInfo")
232+
@XStreamAlias("WeChatPayInfo")
233+
private WxMaXPayWeChatPayInfo weChatPayInfo;
234+
235+
/**
236+
* 道具信息.
237+
* xpay_goods_deliver_notify
238+
*/
239+
@SerializedName("GoodsInfo")
240+
@XStreamAlias("GoodsInfo")
241+
private WxMaXPayGoodsInfo goodsInfo;
242+
216243
// 小程序基本信息
217244

218245
//region 小程序基本信息 infoType=notify_3rd_wxa_auth_and_icp
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package cn.binarywang.wx.miniapp.bean.xpay;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import com.thoughtworks.xstream.annotations.XStreamAlias;
5+
import com.thoughtworks.xstream.annotations.XStreamConverter;
6+
import lombok.Data;
7+
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
8+
9+
import java.io.Serializable;
10+
11+
/**
12+
* xpay_goods_deliver_notify 推送中的道具信息。
13+
*/
14+
@Data
15+
public class WxMaXPayGoodsInfo implements Serializable {
16+
private static final long serialVersionUID = 7495157056049312108L;
17+
18+
@SerializedName("ProductId")
19+
@XStreamAlias("ProductId")
20+
@XStreamConverter(value = XStreamCDataConverter.class)
21+
private String productId;
22+
23+
@SerializedName("Quantity")
24+
@XStreamAlias("Quantity")
25+
private Integer quantity;
26+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package cn.binarywang.wx.miniapp.bean.xpay;
2+
3+
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
4+
import com.google.gson.annotations.SerializedName;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NoArgsConstructor;
9+
10+
import java.io.Serializable;
11+
12+
/**
13+
* 调用 wx.requestVirtualPayment 所需的支付参数。
14+
*/
15+
@Data
16+
@Builder
17+
@NoArgsConstructor
18+
@AllArgsConstructor
19+
public class WxMaXPayRequestVirtualPaymentData implements Serializable {
20+
private static final long serialVersionUID = 7495157056049312108L;
21+
22+
/**
23+
* 支付模式,个人主体虚拟支付固定为 short_series_goods。
24+
*/
25+
@SerializedName("mode")
26+
private String mode;
27+
28+
/**
29+
* 下单签名原文,需与计算 paySig 和 signature 时使用的字符串完全一致。
30+
*/
31+
@SerializedName("signData")
32+
private String signData;
33+
34+
/**
35+
* 使用 AppKey 对 requestVirtualPayment&signData 计算得到的签名。
36+
*/
37+
@SerializedName("paySig")
38+
private String paySig;
39+
40+
/**
41+
* 使用 session_key 对 signData 计算得到的签名。
42+
*/
43+
@SerializedName("signature")
44+
private String signature;
45+
46+
public String toJson() {
47+
return WxMaGsonBuilder.create().toJson(this);
48+
}
49+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package cn.binarywang.wx.miniapp.bean.xpay;
2+
3+
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
4+
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
5+
import com.google.gson.annotations.SerializedName;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Data;
9+
import lombok.NoArgsConstructor;
10+
11+
import java.io.Serializable;
12+
13+
/**
14+
* 个人主体虚拟支付调起 wx.requestVirtualPayment 的签名原文参数。
15+
*/
16+
@Data
17+
@Builder
18+
@NoArgsConstructor
19+
@AllArgsConstructor
20+
public class WxMaXPayRequestVirtualPaymentRequest implements Serializable {
21+
private static final long serialVersionUID = 7495157056049312108L;
22+
23+
/**
24+
* 支付应用 ID。
25+
*/
26+
@SerializedName("offerId")
27+
private String offerId;
28+
29+
/**
30+
* 购买数量。
31+
*/
32+
@SerializedName("buyQuantity")
33+
private Integer buyQuantity;
34+
35+
/**
36+
* 环境,个人主体虚拟支付固定为 0。
37+
*/
38+
@SerializedName("env")
39+
private Integer env;
40+
41+
/**
42+
* 币种,个人主体虚拟支付固定为 CNY。
43+
*/
44+
@SerializedName("currencyType")
45+
private String currencyType;
46+
47+
/**
48+
* 道具 ID。
49+
*/
50+
@SerializedName("productId")
51+
private String productId;
52+
53+
/**
54+
* 道具单价,单位为分。
55+
*/
56+
@SerializedName("goodsPrice")
57+
private Integer goodsPrice;
58+
59+
/**
60+
* 商户订单号,8 到 32 位且不能以下划线开头。
61+
*/
62+
@SerializedName("outTradeNo")
63+
private String outTradeNo;
64+
65+
/**
66+
* 商户透传数据。
67+
*/
68+
@SerializedName("attach")
69+
private String attach;
70+
71+
public WxMaXPayRequestVirtualPaymentData createPayData(WxMaXPaySigParams sigParams) {
72+
final String signData = this.toJson();
73+
return WxMaXPayRequestVirtualPaymentData.builder()
74+
.mode(WxMaConstants.XPayPaymentMode.GOODS)
75+
.signData(signData)
76+
.paySig(sigParams.calcPaySig(WxMaConstants.XPayWxApiSigUri.WXAPI, signData))
77+
.signature(sigParams.calcSig(signData))
78+
.build();
79+
}
80+
81+
public String toJson() {
82+
return WxMaGsonBuilder.create().toJson(this);
83+
}
84+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package cn.binarywang.wx.miniapp.bean.xpay;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import com.thoughtworks.xstream.annotations.XStreamAlias;
5+
import com.thoughtworks.xstream.annotations.XStreamConverter;
6+
import lombok.Data;
7+
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
8+
9+
import java.io.Serializable;
10+
11+
/**
12+
* xpay_goods_deliver_notify 推送中的微信支付信息。
13+
*/
14+
@Data
15+
public class WxMaXPayWeChatPayInfo implements Serializable {
16+
private static final long serialVersionUID = 7495157056049312108L;
17+
18+
@SerializedName("MchOrderNo")
19+
@XStreamAlias("MchOrderNo")
20+
@XStreamConverter(value = XStreamCDataConverter.class)
21+
private String mchOrderNo;
22+
}

weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,77 @@ private void checkXPayRefundNotifyMessage(WxMaMessage msg) {
397397
assertEquals(teamInfo.getTeamAction(), new Integer(0));
398398
}
399399

400+
/**
401+
* 个人主体虚拟支付发货通知事件 xpay_goods_deliver_notify 测试用例(XML格式)。
402+
*/
403+
@Test
404+
public void testXPayGoodsDeliverNotifyFromXml() {
405+
String xml = "<xml>\n" +
406+
" <ToUserName><![CDATA[gh_abcdefg]]></ToUserName>\n" +
407+
" <FromUserName><![CDATA[oABCDEFG]]></FromUserName>\n" +
408+
" <CreateTime>1700000000</CreateTime>\n" +
409+
" <MsgType><![CDATA[event]]></MsgType>\n" +
410+
" <Event><![CDATA[xpay_goods_deliver_notify]]></Event>\n" +
411+
" <OpenId><![CDATA[oABCDEFG]]></OpenId>\n" +
412+
" <OutTradeNo><![CDATA[order12345]]></OutTradeNo>\n" +
413+
" <WeChatPayInfo>\n" +
414+
" <MchOrderNo><![CDATA[wx_order_123]]></MchOrderNo>\n" +
415+
" </WeChatPayInfo>\n" +
416+
" <GoodsInfo>\n" +
417+
" <ProductId><![CDATA[product_001]]></ProductId>\n" +
418+
" <Quantity>2</Quantity>\n" +
419+
" </GoodsInfo>\n" +
420+
" <RetryTimes>0</RetryTimes>\n" +
421+
"</xml>";
422+
423+
WxMaMessage msg = WxMaMessage.fromXml(xml);
424+
425+
checkXPayGoodsDeliverNotifyMessage(msg);
426+
}
427+
428+
/**
429+
* 个人主体虚拟支付发货通知事件 xpay_goods_deliver_notify 测试用例(JSON格式)。
430+
*/
431+
@Test
432+
public void testXPayGoodsDeliverNotifyFromJson() {
433+
String json = "{\n" +
434+
" \"ToUserName\": \"gh_abcdefg\",\n" +
435+
" \"FromUserName\": \"oABCDEFG\",\n" +
436+
" \"CreateTime\": 1700000000,\n" +
437+
" \"MsgType\": \"event\",\n" +
438+
" \"Event\": \"xpay_goods_deliver_notify\",\n" +
439+
" \"OpenId\": \"oABCDEFG\",\n" +
440+
" \"OutTradeNo\": \"order12345\",\n" +
441+
" \"WeChatPayInfo\": {\n" +
442+
" \"MchOrderNo\": \"wx_order_123\"\n" +
443+
" },\n" +
444+
" \"GoodsInfo\": {\n" +
445+
" \"ProductId\": \"product_001\",\n" +
446+
" \"Quantity\": 2\n" +
447+
" },\n" +
448+
" \"RetryTimes\": 0\n" +
449+
"}";
450+
451+
WxMaMessage msg = WxMaMessage.fromJson(json);
452+
checkXPayGoodsDeliverNotifyMessage(msg);
453+
}
454+
455+
private void checkXPayGoodsDeliverNotifyMessage(WxMaMessage msg) {
456+
assertEquals(msg.getToUser(), "gh_abcdefg");
457+
assertEquals(msg.getFromUser(), "oABCDEFG");
458+
assertEquals(msg.getCreateTime(), new Integer(1700000000));
459+
assertEquals(msg.getMsgType(), WxConsts.XmlMsgType.EVENT);
460+
assertEquals(msg.getEvent(), WxMaConstants.XPayNotifyEvent.GOODS_DELIVER);
461+
assertEquals(msg.getOpenId(), "oABCDEFG");
462+
assertEquals(msg.getOutTradeNo(), "order12345");
463+
assertNotNull(msg.getWeChatPayInfo());
464+
assertEquals(msg.getWeChatPayInfo().getMchOrderNo(), "wx_order_123");
465+
assertNotNull(msg.getGoodsInfo());
466+
assertEquals(msg.getGoodsInfo().getProductId(), "product_001");
467+
assertEquals(msg.getGoodsInfo().getQuantity(), new Integer(2));
468+
assertEquals(msg.getRetryTimes(), new Integer(0));
469+
}
470+
400471
/**
401472
* 虚拟支付投诉推送事件 xpay_complaint_notify 测试用例(XML格式)
402473
*/
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package cn.binarywang.wx.miniapp.bean.xpay;
2+
3+
import cn.binarywang.wx.miniapp.api.WxMaService;
4+
import cn.binarywang.wx.miniapp.api.impl.WxMaXPayServiceImpl;
5+
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
6+
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
7+
import com.google.gson.JsonObject;
8+
import org.testng.annotations.Test;
9+
10+
import static org.mockito.Mockito.mock;
11+
import static org.testng.Assert.assertEquals;
12+
13+
/**
14+
* 验证个人主体虚拟支付调起 wx.requestVirtualPayment 所需参数。
15+
*/
16+
public class WxMaXPayRequestVirtualPaymentTest {
17+
18+
@Test
19+
public void testCreatePayData() {
20+
WxMaXPayRequestVirtualPaymentRequest request = WxMaXPayRequestVirtualPaymentRequest.builder()
21+
.offerId("1450019686")
22+
.buyQuantity(1)
23+
.env(0)
24+
.currencyType(WxMaConstants.XPayCurrencyType.CNY)
25+
.productId("product_001")
26+
.goodsPrice(100)
27+
.outTradeNo("order12345")
28+
.attach("attach中文")
29+
.build();
30+
WxMaXPaySigParams sigParams = WxMaXPaySigParams.builder()
31+
.appKey("app_key_123")
32+
.sessionKey("session_key_123")
33+
.build();
34+
35+
WxMaXPayRequestVirtualPaymentData payData = request.createPayData(sigParams);
36+
37+
assertEquals(payData.getMode(), WxMaConstants.XPayPaymentMode.GOODS);
38+
assertEquals(payData.getSignData(),
39+
"{\"offerId\":\"1450019686\",\"buyQuantity\":1,\"env\":0,\"currencyType\":\"CNY\","
40+
+ "\"productId\":\"product_001\",\"goodsPrice\":100,\"outTradeNo\":\"order12345\","
41+
+ "\"attach\":\"attach中文\"}");
42+
assertEquals(payData.getPaySig(), "52b0abda3c933b0273d328b5c5102ee9ab9249309474ddcdf5e9ce0f80b23532");
43+
assertEquals(payData.getSignature(), "602f76d9cadf36c232f7f6c1faa5fe295b0f955d188049997d0df699c0619c86");
44+
45+
JsonObject jsonObject = WxMaGsonBuilder.create().fromJson(payData.toJson(), JsonObject.class);
46+
assertEquals(jsonObject.get("mode").getAsString(), WxMaConstants.XPayPaymentMode.GOODS);
47+
assertEquals(jsonObject.get("paySig").getAsString(), payData.getPaySig());
48+
assertEquals(jsonObject.get("signature").getAsString(), payData.getSignature());
49+
assertEquals(jsonObject.get("signData").getAsString(), payData.getSignData());
50+
}
51+
52+
@Test
53+
public void testCreatePayDataByService() {
54+
WxMaXPayRequestVirtualPaymentRequest request = WxMaXPayRequestVirtualPaymentRequest.builder()
55+
.offerId("1450019686")
56+
.buyQuantity(1)
57+
.env(0)
58+
.currencyType(WxMaConstants.XPayCurrencyType.CNY)
59+
.productId("product_001")
60+
.goodsPrice(100)
61+
.outTradeNo("order12345")
62+
.attach("attach中文")
63+
.build();
64+
WxMaXPaySigParams sigParams = WxMaXPaySigParams.builder()
65+
.appKey("app_key_123")
66+
.sessionKey("session_key_123")
67+
.build();
68+
69+
WxMaXPayRequestVirtualPaymentData payData = new WxMaXPayServiceImpl(mock(WxMaService.class))
70+
.createRequestVirtualPaymentData(request, sigParams);
71+
72+
assertEquals(payData.getMode(), WxMaConstants.XPayPaymentMode.GOODS);
73+
assertEquals(payData.getPaySig(), "52b0abda3c933b0273d328b5c5102ee9ab9249309474ddcdf5e9ce0f80b23532");
74+
assertEquals(payData.getSignature(), "602f76d9cadf36c232f7f6c1faa5fe295b0f955d188049997d0df699c0619c86");
75+
}
76+
}

0 commit comments

Comments
 (0)