1
0
mirror of synced 2025-12-18 13:58:11 +08:00

Compare commits

..

7 Commits

14 changed files with 461 additions and 325 deletions

View File

@@ -241,4 +241,16 @@ public class OrderProductInfo implements Serializable {
@JsonProperty("national_subsidy_merchant_discounted_price")
private Integer nationalSubsidyMerchantDiscountedPrice;
/**
* 订单内商品维度活动商家补贴,即参与平台补贴活动时商家通过活动报名价优惠的部分,单位为分
*/
@JsonProperty("platform_activity_merchant_discounted_price")
private Integer platformActivityMerchantDiscountedPrice;
/**
* 订单内商品维度平台券优惠金额,单位为分
*/
@JsonProperty("cash_coupon_discounted_price")
private Integer cashCouponDiscountedPrice;
}

View File

@@ -465,32 +465,40 @@ public class WxConsts {
/**
* 名称审核事件
*/
public static final String WXA_NICKNAME_AUDIT = "wxa_nickname_audit" ;
public static final String WXA_NICKNAME_AUDIT = "wxa_nickname_audit";
/**
*小程序违规记录事件
*/
public static final String WXA_ILLEGAL_RECORD= "wxa_illegal_record";
* 小程序违规记录事件
*/
public static final String WXA_ILLEGAL_RECORD = "wxa_illegal_record";
/**
*小程序申诉记录推送
*/
public static final String WXA_APPEAL_RECORD= "wxa_appeal_record";
* 小程序申诉记录推送
*/
public static final String WXA_APPEAL_RECORD = "wxa_appeal_record";
/**
* 隐私权限审核结果推送
*/
public static final String WXA_PRIVACY_APPLY= "wxa_privacy_apply";
public static final String WXA_PRIVACY_APPLY = "wxa_privacy_apply";
/**
* 类目审核结果事件推送
*/
public static final String WXA_CATEGORY_AUDIT= "wxa_category_audit";
public static final String WXA_CATEGORY_AUDIT = "wxa_category_audit";
/**
* 小程序微信认证支付成功事件
*/
public static final String WX_VERIFY_PAY_SUCC= "wx_verify_pay_succ";
public static final String WX_VERIFY_PAY_SUCC = "wx_verify_pay_succ";
/**
* 小程序微信认证派单事件
*/
public static final String WX_VERIFY_DISPATCH= "wx_verify_dispatch";
}
public static final String WX_VERIFY_DISPATCH = "wx_verify_dispatch";
/**
* 提醒需要上传发货信息事件曾经发过货的小程序订单超过48小时未发货时
*/
public static final String TRADE_MANAGE_REMIND_SHIPPING = "trade_manage_remind_shipping";
/**
* 订单完成发货时、订单结算时
*/
public static final String TRADE_MANAGE_ORDER_SETTLEMENT = "trade_manage_order_settlement";
}
/**
* 上传多媒体(临时素材)文件的类型.

View File

@@ -40,6 +40,9 @@ public class WxMaKefuMessage implements Serializable {
@SerializedName("miniprogrampage")
private KfMaPage maPage;
@SerializedName("aimsgcontext")
private AiMsgContext aiMsgContext;
@Data
@AllArgsConstructor
@NoArgsConstructor
@@ -90,6 +93,16 @@ public class WxMaKefuMessage implements Serializable {
private String thumbMediaId;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class AiMsgContext implements Serializable {
private static final long serialVersionUID = 1L;
@SerializedName("msgid")
private String msgId;
}
/**
* 获得文本消息builder.
*/

View File

@@ -257,6 +257,62 @@ public class WxMaMessage implements Serializable {
*/
private String context;
/**
* 微信支付订单号
*/
@XStreamAlias("transaction_id")
private String transactionId;
/**
* 商户号
*/
@XStreamAlias("merchant_id")
private String merchantId;
/**
* 子商户号
*/
@XStreamAlias("sub_merchant_id")
private String subMerchantId;
/**
* 商户订单号
*/
@XStreamAlias("merchant_trade_no")
private String merchantTradeNo;
/**
* 支付成功时间,秒级时间戳
*/
@XStreamAlias("pay_time")
private Long payTime;
/**
* 消息文本内容
*/
@XStreamAlias("msg")
private String msg;
/**
* 发货时间,秒级时间戳
*/
@XStreamAlias("shipped_time")
private Long shippedTime;
/**
* 预计结算时间,秒级时间戳。发货时推送才有该字段
*/
@XStreamAlias("estimated_settlement_time")
private Long estimatedSettlementTime;
/**
* 确认收货方式1. 手动确认收货2. 自动确认收货。结算时推送才有该字段
*/
@XStreamAlias("confirm_receive_method")
private Integer confirmReceiveMethod;
/**
* 确认收货时间,秒级时间戳。结算时推送才有该字段
*/
@XStreamAlias("confirm_receive_time")
private Long confirmReceiveTime;
/**
* 订单结算时间,秒级时间戳。结算时推送才有该字段
*/
@XStreamAlias("settlement_time")
private Long settlementTime;
/**
* 不要直接使用这个字段,
* 这个字段只是为了适配 SubscribeMsgPopupEvent SubscribeMsgChangeEvent SubscribeMsgSentEvent

View File

@@ -34,7 +34,7 @@ public class WxMaExpressOrderCargo implements Serializable {
* 描述: 单位是千克(kg)
* </pre>
*/
private Integer weight;
private Double weight;
/**
* 包裹长度
@@ -44,7 +44,7 @@ public class WxMaExpressOrderCargo implements Serializable {
* </pre>
*/
@SerializedName("space_x")
private Integer spaceLength;
private Double spaceLength;
/**
* 包裹宽度
@@ -54,7 +54,7 @@ public class WxMaExpressOrderCargo implements Serializable {
* </pre>
*/
@SerializedName("space_y")
private Integer spaceWidth;
private Double spaceWidth;
/**
* 包裹高度
@@ -64,7 +64,7 @@ public class WxMaExpressOrderCargo implements Serializable {
* </pre>
*/
@SerializedName("space_z")
private Integer spaceHeight;
private Double spaceHeight;
/**
* 包裹中商品详情列表

View File

@@ -8,6 +8,7 @@ import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
public class BaseBuilder<T> {
protected String msgType;
protected String toUser;
protected String aiMsgContextMsgId;
@SuppressWarnings("unchecked")
public T toUser(String toUser) {
@@ -15,6 +16,12 @@ public class BaseBuilder<T> {
return (T) this;
}
@SuppressWarnings("unchecked")
public T aiMsgContextMsgId(String msgId) {
this.aiMsgContextMsgId = msgId;
return (T) this;
}
/**
* 构造器方法.
*/
@@ -22,6 +29,9 @@ public class BaseBuilder<T> {
WxMaKefuMessage m = new WxMaKefuMessage();
m.setMsgType(this.msgType);
m.setToUser(this.toUser);
if (this.aiMsgContextMsgId != null) {
m.setAiMsgContext(new WxMaKefuMessage.AiMsgContext(this.aiMsgContextMsgId));
}
return m;
}
}

View File

@@ -116,10 +116,10 @@ public class WxMaExpressServiceImplTest {
goodsCount ++;
}
cargo.setCount(detailList.size());
cargo.setWeight(5);
cargo.setSpaceHeight(10);
cargo.setSpaceLength(10);
cargo.setSpaceWidth(10);
cargo.setWeight(1.2);
cargo.setSpaceHeight(10.0);
cargo.setSpaceLength(20.0);
cargo.setSpaceWidth(15.0);
cargo.setDetailList(detailList);

View File

@@ -66,4 +66,14 @@ public class WxMaKefuMessageTest {
"\"link\":{\"title\":\"title\",\"description\":\"description\",\"url\":\"https://mp.weixin.qq.com/s?__biz=MzI0MDA2OTY5NQ==\",\"thumb_url\":\"thumbUrl\"}}");
}
public void testTextBuilderWithAiMsgContext() {
WxMaKefuMessage reply = WxMaKefuMessage.newTextBuilder()
.toUser("OPENID")
.content("回复内容")
.aiMsgContextMsgId("MSG_ID_123")
.build();
assertThat(reply.toJson())
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"回复内容\"},\"aimsgcontext\":{\"msgid\":\"MSG_ID_123\"}}");
}
}

View File

@@ -23,7 +23,7 @@ import java.util.List;
@AllArgsConstructor
@Accessors(chain = true)
public class WxPayApplyment4SubCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 4104022969945059126L;
/**
* 业务申请编号
@@ -78,7 +78,7 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
@AllArgsConstructor
@Accessors(chain = true)
public static class ContactInfo implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = -9087348002744428474L;
/**
* 超级管理员类型
@@ -211,7 +211,7 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
@AllArgsConstructor
@Accessors(chain = true)
public static class SubjectInfo implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = -6651911735969445765L;
/**
* 主体类型
@@ -242,6 +242,13 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
@SerializedName("certificate_letter_copy")
private String certificateLetterCopy;
/**
* 小微辅助证明材料
* 主体类型为小微商户时,小微辅助证明材料必填
*/
@SerializedName("micro_biz_info")
private MicroBizInfo microBizInfo;
/**
* 金融机构许可证信息
*/
@@ -393,6 +400,212 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
private List<String> financeLicensePics;
}
/**
* 小微辅助证明材料
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public static class MicroBizInfo implements Serializable {
private static final long serialVersionUID = 2327302539406612422L;
/**
* 小微经营类型
* 枚举值:
* MICRO_TYPE_STORE门店场所
* MICRO_TYPE_MOBILE流动经营/便民服务
* MICRO_TYPE_ONLINE线上商品/服务交易
* 示例值MICRO_TYPE_STORE
*/
@SerializedName("micro_biz_type")
private MicroBizTypeEnum microBizType;
/**
* 【门店场所】 经营类型为“门店场所”时填写
*/
@SerializedName("micro_store_info")
private MicroStoreInfo microStoreInfo;
/**
* 【流动经营/便民服务】 经营类型为“流动经营/便民服务”时填写
*/
@SerializedName("micro_mobile_info")
private MicroMobileInfo microMobileInfo;
/**
* 【线上商品/服务交易】 经营场景为“线上商品/服务交易”时填写
*/
@SerializedName("micro_online_info")
private MicroOnlineInfo microOnlineInfo;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public static class MicroOnlineInfo implements Serializable {
private static final long serialVersionUID = -4672635122639034459L;
/**
* 【线上店铺名称】 填写商家的线上店铺名称
* 1、长度为1-50个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@SerializedName("micro_online_store")
private String microOnlineStore;
/**
* 【电商平台名称】 填写电商平台名称
* 1、长度为1-50个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@SerializedName("micro_ec_name")
private String microEcName;
/**
* 【店铺二维码】
* 1、店铺二维码或店铺链接二选一必填
* 2、若为电商小程序可上传店铺页面的小程序二维码
* 3、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@SerializedName("micro_qrcode")
private String microQrcode;
/**
* 【店铺链接】
* 1、店铺二维码或店铺链接二选一必填
* 2、请填写店铺主页链接需符合网站规范。
*/
@SerializedName("micro_link")
private String microLink;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public static class MicroMobileInfo implements Serializable {
private static final long serialVersionUID = -4672635122639034460L;
/**
* 【经营/服务名称】 请填写经营/服务名称
* 1、长度为1-50个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@SerializedName("micro_mobile_name")
private String microMobileName;
/**
* 【经营/服务所在地省市】 请填写经营/服务所在地省市编码
*/
@SerializedName("micro_mobile_city")
private String microMobileCity;
/**
* 【经营/服务所在地(不含省市)】 填写“无"
*/
@SerializedName("micro_mobile_address")
private String microMobileAddress;
/**
* 【经营/服务现场照片】
* 1、提交流动经营现场照片如摊位场景应提交摊位全景照片+商品照片。
* 2、可上传多张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@SerializedName("micro_mobile_pics")
private List<String> microMobilePics;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public static class MicroStoreInfo implements Serializable {
private static final long serialVersionUID = -4672635122639034461L;
/**
* 【门店名称】
* 请填写门店名称
* 1、长度为1-50个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@SerializedName("micro_name")
private String microName;
/**
* 【门店省市编码】
* 填写门店省市编码,只能由数字组成,详细参见微信支付提供的省市对照表
*/
@SerializedName("micro_address_code")
private String microAddressCode;
/**
* 【门店地址】
* 请填写详细的经营场所信息,如有多个场所,选择一个主要场所填写即可。
* 1、长度为4-512个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@SerializedName("micro_address")
private String microAddress;
/**
* 【门店门头照片】
* 1、请上传门头正面照片要求门店招牌、门框完整、清晰、可辨识若为停车场等无固定门头照片的经营场所可上传岗亭/出入闸口。
* 2、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@SerializedName("store_entrance_pic")
private String storeEntrancePic;
/**
* 【店内环境照片】
* 1、请上传门店内部环境照片可辨识经营内容。若为停车场等无固定门头的经营场所可上传停车场内部照片。
* 2、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@SerializedName("micro_indoor_copy")
private String microIndoorCopy;
/**
* 【门店经度】 数字或小数,商户自定义字段
*/
@SerializedName("store_longitude")
private String storeLongitude;
/**
* 【门店纬度】 纬度,商户自定义字段
*/
@SerializedName("store_latitude")
private String storeLatitude;
}
}
@Data
@Builder
@NoArgsConstructor
@@ -603,7 +816,7 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
@AllArgsConstructor
@Accessors(chain = true)
public static class BusinessInfo implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = -8605049544105644011L;
/**
* 商户简称
@@ -876,7 +1089,7 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
@AllArgsConstructor
@Accessors(chain = true)
public static class SettlementInfo implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = -5025743467243760522L;
/**
* 入驻结算规则ID
@@ -937,7 +1150,7 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
@AllArgsConstructor
@Accessors(chain = true)
public static class BankAccountInfo implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = -5853122395888860086L;
/**
* 账户类型
@@ -995,7 +1208,7 @@ public class WxPayApplyment4SubCreateRequest implements Serializable {
@AllArgsConstructor
@Accessors(chain = true)
public static class AdditionInfo implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = -7526912529114022379L;
/**
* 法人开户承诺函

View File

@@ -58,7 +58,7 @@ public class WxPayUnifiedOrderV3Result implements Serializable {
/**
* <pre>
* 字段名二维码链接NATIVE支付 会返回)
* 变量名:code_url
* 变量名:h5_url
* 是否必填:是
* 类型string[1,512]
* 描述:
@@ -81,19 +81,6 @@ public class WxPayUnifiedOrderV3Result 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);
@@ -126,7 +113,6 @@ public class WxPayUnifiedOrderV3Result implements Serializable {
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;
@@ -146,79 +132,4 @@ public class WxPayUnifiedOrderV3Result implements Serializable {
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;
}
}

View File

@@ -21,7 +21,7 @@ public interface BusinessOperationTransferService {
* 发起运营工具商家转账
*
* 请求方式POSTHTTPS
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills
*
* 文档地址:<a href="https://pay.weixin.qq.com/doc/v3/merchant/4012711988">运营工具-商家转账API</a>
* </pre>
@@ -37,7 +37,7 @@ public interface BusinessOperationTransferService {
* 查询运营工具转账结果
*
* 请求方式GETHTTPS
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}
*
* 文档地址:<a href="https://pay.weixin.qq.com/doc/v3/merchant/4012711988">运营工具-商家转账API</a>
* </pre>
@@ -53,7 +53,7 @@ public interface BusinessOperationTransferService {
* 通过商户单号查询运营工具转账结果
*
* 请求方式GETHTTPS
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}
*
* 文档地址:<a href="https://pay.weixin.qq.com/doc/v3/merchant/4012711988">运营工具-商家转账API</a>
* </pre>
@@ -69,7 +69,7 @@ public interface BusinessOperationTransferService {
* 通过微信转账单号查询运营工具转账结果
*
* 请求方式GETHTTPS
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills/transfer-bill-no/{transfer_bill_no}
* 请求地址https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/{transfer_bill_no}
*
* 文档地址:<a href="https://pay.weixin.qq.com/doc/v3/merchant/4012711988">运营工具-商家转账API</a>
* </pre>

View File

@@ -33,7 +33,7 @@ public class BusinessOperationTransferServiceImpl implements BusinessOperationTr
request.setAppid(this.wxPayService.getConfig().getAppId());
}
String url = String.format("%s/v3/fund-app/operation/mch-transfer/transfer-bills", this.wxPayService.getPayBaseUrl());
String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills", this.wxPayService.getPayBaseUrl());
// 如果传入了用户姓名需要进行RSA加密
if (StringUtils.isNotEmpty(request.getUserName())) {
@@ -58,7 +58,7 @@ public class BusinessOperationTransferServiceImpl implements BusinessOperationTr
@Override
public BusinessOperationTransferQueryResult queryOperationTransferByOutBillNo(String outBillNo) throws WxPayException {
String url = String.format("%s/v3/fund-app/operation/mch-transfer/transfer-bills/out-bill-no/%s",
String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/%s",
this.wxPayService.getPayBaseUrl(), outBillNo);
String response = wxPayService.getV3(url);
return GSON.fromJson(response, BusinessOperationTransferQueryResult.class);
@@ -66,7 +66,7 @@ public class BusinessOperationTransferServiceImpl implements BusinessOperationTr
@Override
public BusinessOperationTransferQueryResult queryOperationTransferByTransferBillNo(String transferBillNo) throws WxPayException {
String url = String.format("%s/v3/fund-app/operation/mch-transfer/transfer-bills/transfer-bill-no/%s",
String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/%s",
this.wxPayService.getPayBaseUrl(), transferBillNo);
String response = wxPayService.getV3(url);
return GSON.fromJson(response, BusinessOperationTransferQueryResult.class);

View File

@@ -0,0 +1,104 @@
package com.github.binarywang.wxpay.bean.applyment;
import java.util.Arrays;
import org.testng.annotations.Test;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class WxPayApplyment4SubCreateRequestTest {
@Test
public void testMicroBizInfoSerialization() {
// 1. Test MicroStoreInfo
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroStoreInfo storeInfo =
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroStoreInfo.builder()
.microName("门店名称")
.microAddressCode("440305")
.microAddress("详细地址")
.storeEntrancePic("media_id_1")
.microIndoorCopy("media_id_2")
.storeLongitude("113.941046")
.storeLatitude("22.546154")
.build();
// 2. Test MicroMobileInfo
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroMobileInfo mobileInfo =
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroMobileInfo.builder()
.microMobileName("流动经营名称")
.microMobileCity("440305")
.microMobileAddress("")
.microMobilePics(Arrays.asList("media_id_3", "media_id_4"))
.build();
// 3. Test MicroOnlineInfo
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroOnlineInfo onlineInfo =
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroOnlineInfo.builder()
.microOnlineStore("线上店铺名称")
.microEcName("电商平台名称")
.microQrcode("media_id_5")
.microLink("https://www.example.com")
.build();
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo microBizInfo =
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.builder()
.microStoreInfo(storeInfo)
.microMobileInfo(mobileInfo)
.microOnlineInfo(onlineInfo)
.build();
Gson gson = new GsonBuilder().create();
String json = gson.toJson(microBizInfo);
// Verify MicroStoreInfo serialization
assertTrue(json.contains("\"micro_name\":\"门店名称\""));
assertTrue(json.contains("\"micro_address_code\":\"440305\""));
assertTrue(json.contains("\"micro_address\":\"详细地址\""));
assertTrue(json.contains("\"store_entrance_pic\":\"media_id_1\""));
assertTrue(json.contains("\"micro_indoor_copy\":\"media_id_2\""));
assertTrue(json.contains("\"store_longitude\":\"113.941046\""));
assertTrue(json.contains("\"store_latitude\":\"22.546154\""));
// Verify MicroMobileInfo serialization
assertTrue(json.contains("\"micro_mobile_name\":\"流动经营名称\""));
assertTrue(json.contains("\"micro_mobile_city\":\"440305\""));
assertTrue(json.contains("\"micro_mobile_address\":\"\""));
assertTrue(json.contains("\"micro_mobile_pics\":[\"media_id_3\",\"media_id_4\"]"));
// Verify MicroOnlineInfo serialization
assertTrue(json.contains("\"micro_online_store\":\"线上店铺名称\""));
assertTrue(json.contains("\"micro_ec_name\":\"电商平台名称\""));
assertTrue(json.contains("\"micro_qrcode\":\"media_id_5\""));
assertTrue(json.contains("\"micro_link\":\"https://www.example.com\""));
// Verify deserialization
WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo deserialized =
gson.fromJson(json, WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.class);
// Verify MicroStoreInfo deserialization
assertEquals(deserialized.getMicroStoreInfo().getMicroName(), "门店名称");
assertEquals(deserialized.getMicroStoreInfo().getMicroAddressCode(), "440305");
assertEquals(deserialized.getMicroStoreInfo().getMicroAddress(), "详细地址");
assertEquals(deserialized.getMicroStoreInfo().getStoreEntrancePic(), "media_id_1");
assertEquals(deserialized.getMicroStoreInfo().getMicroIndoorCopy(), "media_id_2");
assertEquals(deserialized.getMicroStoreInfo().getStoreLongitude(), "113.941046");
assertEquals(deserialized.getMicroStoreInfo().getStoreLatitude(), "22.546154");
// Verify MicroMobileInfo deserialization
assertEquals(deserialized.getMicroMobileInfo().getMicroMobileName(), "流动经营名称");
assertEquals(deserialized.getMicroMobileInfo().getMicroMobileCity(), "440305");
assertEquals(deserialized.getMicroMobileInfo().getMicroMobileAddress(), "");
assertEquals(deserialized.getMicroMobileInfo().getMicroMobilePics().size(), 2);
assertEquals(deserialized.getMicroMobileInfo().getMicroMobilePics(), Arrays.asList("media_id_3", "media_id_4"));
// Verify MicroOnlineInfo deserialization
assertEquals(deserialized.getMicroOnlineInfo().getMicroOnlineStore(), "线上店铺名称");
assertEquals(deserialized.getMicroOnlineInfo().getMicroEcName(), "电商平台名称");
assertEquals(deserialized.getMicroOnlineInfo().getMicroQrcode(), "media_id_5");
assertEquals(deserialized.getMicroOnlineInfo().getMicroLink(), "https://www.example.com");
}
}

View File

@@ -1,201 +0,0 @@
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应该与设置的值相同");
}
}