Skip to content
Merged
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
@@ -1,5 +1,6 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.bean.xpay.WxMaXPayTeamInfo;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
Expand Down Expand Up @@ -313,6 +314,110 @@ public class WxMaMessage implements Serializable {
@XStreamAlias("settlement_time")
private Long settlementTime;

// xpay_refund_notify 退款推送字段

/**
* 微信退款单号.
* xpay_refund_notify
Comment on lines +317 to +321
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

新增 xpay_refund_notify 字段后,建议在现有 WxMaMessageTest 中补充对应的 fromXml/fromJson 解析用例(含 TeamInfo 嵌套对象),以确保字段名大小写、类型(Integer/Long)及反序列化行为在后续重构时不会回归。当前 PR 仅新增字段定义,缺少针对该事件的覆盖。

Copilot uses AI. Check for mistakes.
*/
@SerializedName("WxRefundId")
@XStreamAlias("WxRefundId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String wxRefundId;

/**
* 商户退款单号.
* xpay_refund_notify
*/
@SerializedName("MchRefundId")
@XStreamAlias("MchRefundId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String mchRefundId;

/**
* 退款单对应支付单的微信单号.
* xpay_refund_notify
*/
@SerializedName("WxOrderId")
@XStreamAlias("WxOrderId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String wxOrderId;

/**
* 退款单对应支付单的商户单号.
* xpay_refund_notify
*/
@SerializedName("MchOrderId")
@XStreamAlias("MchOrderId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String mchOrderId;

/**
* 退款金额,单位分.
* xpay_refund_notify
*/
@SerializedName("RefundFee")
@XStreamAlias("RefundFee")
private Integer refundFee;

/**
* 退款结果,0为成功,非0为失败.
* xpay_refund_notify
*/
@SerializedName("RetCode")
@XStreamAlias("RetCode")
private Integer retCode;

/**
* 退款结果详情,失败时为退款失败的原因.
* xpay_refund_notify
*/
@SerializedName("RetMsg")
@XStreamAlias("RetMsg")
@XStreamConverter(value = XStreamCDataConverter.class)
private String retMsg;

/**
* 开始退款时间,秒级时间戳.
* xpay_refund_notify
*/
@SerializedName("RefundStartTimestamp")
@XStreamAlias("RefundStartTimestamp")
private Long refundStartTimestamp;

/**
* 结束退款时间,秒级时间戳.
* xpay_refund_notify
*/
@SerializedName("RefundSuccTimestamp")
@XStreamAlias("RefundSuccTimestamp")
private Long refundSuccTimestamp;

/**
* 退款单的微信支付单号.
* xpay_refund_notify
*/
@SerializedName("WxpayRefundTransactionId")
@XStreamAlias("WxpayRefundTransactionId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String wxpayRefundTransactionId;

/**
* 重试次数,从0开始,重试间隔为2 4 8 16...最多15次.
* xpay_refund_notify
*/
@SerializedName("RetryTimes")
@XStreamAlias("RetryTimes")
private Integer retryTimes;

/**
* 拼团信息.
* xpay_goods_deliver_notify, xpay_refund_notify
*/
@SerializedName("TeamInfo")
@XStreamAlias("TeamInfo")
private WxMaXPayTeamInfo teamInfo;

/**
* 不要直接使用这个字段,
* 这个字段只是为了适配 SubscribeMsgPopupEvent SubscribeMsgChangeEvent SubscribeMsgSentEvent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cn.binarywang.wx.miniapp.bean.xpay;

import com.google.gson.annotations.SerializedName;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
* 虚拟支付拼团信息.
* 用于 xpay_goods_deliver_notify、xpay_refund_notify 等推送事件
*/
@Data
@NoArgsConstructor
@XStreamAlias("TeamInfo")
public class WxMaXPayTeamInfo implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 活动id.
*/
@SerializedName("ActivityId")
@XStreamAlias("ActivityId")
private String activityId;

/**
* 团id.
*/
@SerializedName("TeamId")
@XStreamAlias("TeamId")
private String teamId;

/**
* 团类型.
* 1-支付全部,拼成退款
*/
@SerializedName("TeamType")
@XStreamAlias("TeamType")
private Integer teamType;

/**
* 0-创团 1-参团.
*/
@SerializedName("TeamAction")
@XStreamAlias("TeamAction")
private Integer teamAction;
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ public static final class XPayOrderStatus {
public static final class XPayNotifyEvent {
public static String COIN_PAY = "xpay_coin_pay_notify";
public static String GOODS_DELIVER = "xpay_goods_deliver_notify";
public static String REFUND = "xpay_refund_notify";

}
@UtilityClass
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.bean.xpay.WxMaXPayTeamInfo;
import me.chanjar.weixin.common.api.WxConsts;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -294,38 +295,97 @@ public void testFromXmlForAllFieldsMap() {
}

/**
* 自定义交易组件付款通知事件测试用例
* msgType等于event且event等于WxConsts.EventType.OPEN_PRODUCT_ORDER_PAY
* 虚拟支付退款推送事件 xpay_refund_notify 测试用例(XML格式,含TeamInfo)
*/
@Test
public void testFromXmlForOpenProductOrderPayEvent(){
String xml = "<xml> \n" +
" <ToUserName>gh_abcdefg</ToUserName> \n" +
" <FromUserName>oABCD</FromUserName> \n" +
" <CreateTime>1642658087</CreateTime> \n" +
" <MsgType>event</MsgType> \n" +
" <Event>open_product_order_pay</Event>\n" +
" <order_info>\n" +
" <out_order_id>123456</out_order_id>\n" +
" <order_id>1234567</order_id>\n" +
" <transaction_id>42000000123123</transaction_id>\n" +
" <pay_time>2021-12-30 22:31:00</pay_time>\n" +
" <amount>10</amount>\n" +
" <sp_openid>oNMZ-5C0SPGHUiKsTwnOXpSHzFvw</sp_openid>\n" +
" </order_info>\n" +
public void testXPayRefundNotifyFromXml() {
String xml = "<xml>\n" +
" <ToUserName><![CDATA[gh_abcdefg]]></ToUserName>\n" +
" <FromUserName><![CDATA[oABCDEFG]]></FromUserName>\n" +
" <CreateTime>1700000000</CreateTime>\n" +
" <MsgType><![CDATA[event]]></MsgType>\n" +
" <Event><![CDATA[xpay_refund_notify]]></Event>\n" +
" <OpenId><![CDATA[oABCDEFG]]></OpenId>\n" +
" <WxRefundId><![CDATA[wx_refund_123]]></WxRefundId>\n" +
" <MchRefundId><![CDATA[mch_refund_456]]></MchRefundId>\n" +
" <WxOrderId><![CDATA[wx_order_789]]></WxOrderId>\n" +
" <MchOrderId><![CDATA[mch_order_101]]></MchOrderId>\n" +
" <RefundFee>100</RefundFee>\n" +
" <RetCode>0</RetCode>\n" +
" <RetMsg><![CDATA[success]]></RetMsg>\n" +
" <RefundStartTimestamp>1700000000</RefundStartTimestamp>\n" +
" <RefundSuccTimestamp>1700000010</RefundSuccTimestamp>\n" +
" <WxpayRefundTransactionId><![CDATA[wxpay_refund_tx_202]]></WxpayRefundTransactionId>\n" +
" <RetryTimes>0</RetryTimes>\n" +
" <TeamInfo>\n" +
" <ActivityId><![CDATA[act_001]]></ActivityId>\n" +
" <TeamId><![CDATA[team_002]]></TeamId>\n" +
" <TeamType>1</TeamType>\n" +
" <TeamAction>0</TeamAction>\n" +
" </TeamInfo>\n" +
"</xml>";
WxMaMessage wxMessage = WxMaMessage.fromXml(xml);
assertThat(wxMessage.getMsgType()).isEqualTo("event");
assertThat(wxMessage.getEvent()).isEqualTo(WxConsts.EventType.OPEN_PRODUCT_ORDER_PAY);
Map<String, Object> allFieldsMap = wxMessage.getAllFieldsMap();
Map<String, Object> orderInfo = (Map<String, Object>) allFieldsMap.get("order_info");
assertThat(orderInfo).isNotEmpty();
assertThat(orderInfo)
.containsEntry("out_order_id","123456")
.containsEntry("order_id","1234567")
.containsEntry("transaction_id","42000000123123")
.containsEntry("pay_time","2021-12-30 22:31:00")
.containsEntry("amount","10")
.containsEntry("sp_openid","oNMZ-5C0SPGHUiKsTwnOXpSHzFvw");

WxMaMessage msg = WxMaMessage.fromXml(xml);
checkXPayRefundNotifyMessage(msg);
}

/**
* 虚拟支付退款推送事件 xpay_refund_notify 测试用例(JSON格式,含TeamInfo)
*/
@Test
public void testXPayRefundNotifyFromJson() {
String json = "{\n" +
" \"ToUserName\": \"gh_abcdefg\",\n" +
" \"FromUserName\": \"oABCDEFG\",\n" +
" \"CreateTime\": 1700000000,\n" +
" \"MsgType\": \"event\",\n" +
" \"Event\": \"xpay_refund_notify\",\n" +
" \"OpenId\": \"oABCDEFG\",\n" +
" \"WxRefundId\": \"wx_refund_123\",\n" +
" \"MchRefundId\": \"mch_refund_456\",\n" +
" \"WxOrderId\": \"wx_order_789\",\n" +
" \"MchOrderId\": \"mch_order_101\",\n" +
" \"RefundFee\": 100,\n" +
" \"RetCode\": 0,\n" +
" \"RetMsg\": \"success\",\n" +
" \"RefundStartTimestamp\": 1700000000,\n" +
" \"RefundSuccTimestamp\": 1700000010,\n" +
" \"WxpayRefundTransactionId\": \"wxpay_refund_tx_202\",\n" +
" \"RetryTimes\": 0,\n" +
" \"TeamInfo\": {\n" +
" \"ActivityId\": \"act_001\",\n" +
" \"TeamId\": \"team_002\",\n" +
" \"TeamType\": 1,\n" +
" \"TeamAction\": 0\n" +
" }\n" +
"}";

WxMaMessage msg = WxMaMessage.fromJson(json);
checkXPayRefundNotifyMessage(msg);
}

private void checkXPayRefundNotifyMessage(WxMaMessage msg) {
assertEquals(msg.getToUser(), "gh_abcdefg");
assertEquals(msg.getFromUser(), "oABCDEFG");
assertEquals(msg.getCreateTime(), new Integer(1700000000));
assertEquals(msg.getMsgType(), WxConsts.XmlMsgType.EVENT);
assertEquals(msg.getEvent(), "xpay_refund_notify");
assertEquals(msg.getWxRefundId(), "wx_refund_123");
assertEquals(msg.getMchRefundId(), "mch_refund_456");
assertEquals(msg.getWxOrderId(), "wx_order_789");
assertEquals(msg.getMchOrderId(), "mch_order_101");
assertEquals(msg.getRefundFee(), new Integer(100));
assertEquals(msg.getRetCode(), new Integer(0));
assertEquals(msg.getRetMsg(), "success");
assertEquals(msg.getRefundStartTimestamp(), new Long(1700000000L));
assertEquals(msg.getRefundSuccTimestamp(), new Long(1700000010L));
assertEquals(msg.getWxpayRefundTransactionId(), "wxpay_refund_tx_202");
assertEquals(msg.getRetryTimes(), new Integer(0));
WxMaXPayTeamInfo teamInfo = msg.getTeamInfo();
assertNotNull(teamInfo);
assertEquals(teamInfo.getActivityId(), "act_001");
assertEquals(teamInfo.getTeamId(), "team_002");
assertEquals(teamInfo.getTeamType(), new Integer(1));
assertEquals(teamInfo.getTeamAction(), new Integer(0));
}
}