🆕 #2022 【微信支付】增加智慧商圈相关接口和解析支付营销代金券核销回调消息的方法

* 增加解析支付营销代金券核销回调消息解析

* 增加微信支付智慧商圈接口和回调解析

Co-authored-by: 黄星 <huang.xing@aquilaflycloud.com>
This commit is contained in:
thinsstar 2021-03-09 16:12:11 +08:00 committed by GitHub
parent e46c6c1995
commit 30155b316b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1226 additions and 56 deletions

View File

@ -0,0 +1,93 @@
package com.github.binarywang.wxpay.bean.businesscircle;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 智慧商圈回调通知对象
* <pre>
* 文档地址https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/businesscircle/chapter3_1.shtml
* https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/businesscircle/chapter3_3.shtml
* </pre>
*
* @author thinsstar
*/
@NoArgsConstructor
@Data
public class BusinessCircleNotifyData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 通知ID
*/
@SerializedName("id")
private String id;
/**
* 通知创建时间
*/
@SerializedName("create_time")
private String createTime;
/**
* 通知类型
*/
@SerializedName("event_type")
private String eventType;
/**
* 通知数据类型
*/
@SerializedName("resource_type")
private String resourceType;
/**
* 回调摘要
* summary
*/
@SerializedName("summary")
private String summary;
/**
* 通知数据
*/
@SerializedName("resource")
private Resource resource;
@Data
public static class Resource implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 加密算法类型
*/
@SerializedName("algorithm")
private String algorithm;
/**
* 数据密文
*/
@SerializedName("ciphertext")
private String cipherText;
/**
* 附加数据
*/
@SerializedName("associated_data")
private String associatedData;
/**
* 随机串
*/
@SerializedName("nonce")
private String nonce;
/**
* 原始回调类型
*/
@SerializedName("original_type")
private String originalType;
}
}

View File

@ -0,0 +1,112 @@
package com.github.binarywang.wxpay.bean.businesscircle;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 商圈支付结果通知内容
* <pre>
* 文档地址https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/businesscircle/chapter3_1.shtml
* </pre>
*
* @author thinsstar
*/
@NoArgsConstructor
@Data
public class PaidResult implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 商户号
* <p>
* 微信支付分配的商户号
* 示例值1230000109
*/
@SerializedName("mchid")
private String mchid;
/**
* 商圈商户名称
* <p>
* 商圈商户名称
* 示例值微信支付
*/
@SerializedName("merchant_name")
private String merchantName;
/**
* 门店名称
* <p>
* 门店名称商圈在商圈小程序上圈店时填写的门店名称
* 示例值微信支付
*/
@SerializedName("shop_name")
private String shopName;
/**
* 门店编号
* <p>
* 门店编号商圈在商圈小程序上圈店时填写的门店编号用于跟商圈自身已有的商户识别码对齐
* 示例值123456
*/
@SerializedName("shop_number")
private String shop_number;
/**
* 小程序APPID
* <p>
* 顾客授权积分时使用的小程序的appid
* 示例值wxd678efh567hg6787
*/
@SerializedName("appid")
private String appid;
/**
* 用户标识
* <p>
* 顾客授权时使用的小程序上的openid
* 示例值oUpF8uMuAJ2pxb1Q9zNjWeS6o
*/
@SerializedName("openid")
private String openid;
/**
* 交易完成时间
* <p>
* 交易完成时间遵循rfc3339标准格式格式为YYYY-MM-DDTHH:mm:ss+TIMEZONEYYYY-MM-DD表示年月日T出现在字符串中表示time元素的开头HH:mm:ss表示时分秒毫秒TIMEZONE表示时区+08:00表示东八区时间领先UTC 8小时即北京时间例如2015-05-20T13:29:35+08:00表示北京时间2015年05月20日13点29分35秒需要增加所有跟时间有关的参数的描述
* 示例值2015-05-20T13:29:35+08:00
*/
@SerializedName("time_end")
private String timeEnd;
/**
* 金额
* <p>
* 用户实际消费金额单位
* 示例值200
*/
@SerializedName("amount")
private Integer amount;
/**
* 微信支付订单号
* <p>
* 微信支付订单号
* 示例值1234567890
*/
@SerializedName("transaction_id")
private String transactionId;
/**
* 手动提交积分标记
* <p>
* 手动提交积分标记自动提交时无该字段用于区分用户手动申请后推送的积分数据
* 示例值oUpF8uMuAJ2pxb1Q9zNjWUHsd
*/
@SerializedName("commit_tag")
private String commitTag;
}

View File

@ -0,0 +1,154 @@
package com.github.binarywang.wxpay.bean.businesscircle;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 商圈积分同步
* <pre>
* 文档地址https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/businesscircle/chapter3_2.shtml
* </pre>
*
* @author thinsstar
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PointsNotifyRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* <pre>
* 字段名商圈商户ID
* 变量名sub_mchid
* 是否必填
* 类型string[1,64]
* 描述
* 当以服务商模式管理商圈积分能力时则要带上商圈商户ID否则留空
* 示例值1234567890
* </pre>
*/
@SerializedName(value = "sub_mchid")
private String subMchid;
/**
* <pre>
* 字段名微信订单号
* 变量名transaction_id
* 是否必填
* 类型string[1,64]
* 描述
* 微信支付推送的商圈内交易通知里携带的微信订单号
* 示例值1217752501201407033233368018
* </pre>
*/
@SerializedName(value = "transaction_id")
private String transactionId;
/**
* <pre>
* 字段名小程序appid
* 变量名appid
* 是否必填
* 类型string[1,128]
* 描述
* 顾客授权积分时使用的小程序的appid
* 示例值wx1234567890abcdef
* </pre>
*/
@SerializedName(value = "appid")
private String appid;
/**
* <pre>
* 字段名用户标识
* 变量名openid
* 是否必填
* 类型string[1,64]
* 描述
* 顾客授权时使用的小程序上的openid
* 示例值oWmnN4xxxxxxxxxxe92NHIGf1xd8
* </pre>
*/
@SerializedName(value = "openid")
private String openid;
/**
* <pre>
* 字段名是否获得积分
* 变量名earn_points
* 是否必填
* 类型boolean
* 描述
* 用于标明此单是否获得积分
* true为获得积分
* false为未获得
* 示例值true
* </pre>
*/
@SerializedName(value = "earn_points")
private Boolean earnPoints;
/**
* <pre>
* 字段名订单新增积分值
* 变量名increased_points
* 是否必填
* 类型int
* 描述
* 顾客此笔交易新增的积分值
* 示例值100
* </pre>
*/
@SerializedName(value = "increased_points")
private Integer increasedPoints;
/**
* <pre>
* 字段名积分更新时间
* 变量名points_update_time
* 是否必填
* 类型string[1,32]
* 描述
* 为顾客此笔交易成功积分的时间
* 示例值2020-05-20T13:29:35.120+08:00
* </pre>
*/
@SerializedName(value = "points_update_time")
private String pointsUpdateTime;
/**
* <pre>
* 字段名未获得积分的备注信息
* 变量名no_points_remarks
* 是否必填
* 类型string[1,128]
* 描述
* 当未获得积分时提供未获得积分的原因等备注信息
* 示例值商品不参与积分活动
* </pre>
*/
@SerializedName(value = "no_points_remarks")
private String noPointsRemarks;
/**
* <pre>
* 字段名顾客积分总额
* 变量名total_points
* 是否必填
* 类型int
* 描述
* 当前顾客积分总额
* 示例值888888
* </pre>
*/
@SerializedName(value = "total_points")
private Integer totalPoints;
}

View File

@ -0,0 +1,121 @@
package com.github.binarywang.wxpay.bean.businesscircle;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 商圈退款成功通知内容
* <pre>
* 文档地址https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/businesscircle/chapter3_3.shtml
* </pre>
*
* @author thinsstar
*/
@NoArgsConstructor
@Data
public class RefundResult implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 商户号
* <p>
* 微信支付分配的商户号
* 示例值1230000109
*/
@SerializedName("mchid")
private String mchid;
/**
* 商圈商户名称
* <p>
* 商圈商户名称
* 示例值微信支付
*/
@SerializedName("merchant_name")
private String merchantName;
/**
* 门店名称
* <p>
* 门店名称商圈在商圈小程序上圈店时填写的门店名称
* 示例值微信支付
*/
@SerializedName("shop_name")
private String shopName;
/**
* 门店编号
* <p>
* 门店编号商圈在商圈小程序上圈店时填写的门店编号用于跟商圈自身已有的商户识别码对齐
* 示例值123456
*/
@SerializedName("shop_number")
private String shop_number;
/**
* 小程序APPID
* <p>
* 顾客授权积分时使用的小程序的appid
* 示例值wxd678efh567hg6787
*/
@SerializedName("appid")
private String appid;
/**
* 用户标识
* <p>
* 顾客授权时使用的小程序上的openid
* 示例值oUpF8uMuAJ2pxb1Q9zNjWeS6o
*/
@SerializedName("openid")
private String openid;
/**
* 退款完成时间
* <p>
* 退款完成时间遵循rfc3339标准格式格式为YYYY-MM-DDTHH:mm:ss+TIMEZONEYYYY-MM-DD表示年月日T出现在字符串中表示time元素的开头HH:mm:ss表示时分秒毫秒TIMEZONE表示时区+08:00表示东八区时间领先UTC 8小时即北京时间例如2015-05-20T13:29:35+08:00表示北京时间2015年05月20日13点29分35秒需要增加所有跟时间有关的参数的描述
* 示例值2015-05-20T13:29:35+08:00
*/
@SerializedName("refund_time")
private String refundTime;
/**
* 消费金额
* <p>
* 用户实际消费金额单位
* 示例值100
*/
@SerializedName("pay_amount")
private Integer payAmount;
/**
* 退款金额
* <p>
* 用户退款金额单位
* 示例值100
*/
@SerializedName("refund_amount")
private Integer refundAmount;
/**
* 微信支付订单号
* <p>
* 微信支付订单号
* 示例值1234567890
*/
@SerializedName("transaction_id")
private String transactionId;
/**
* 微信支付退款单号
* <p>
* 微信支付退款单号
* 示例值1217752501201407033233368999
*/
@SerializedName("refund_id")
private String refundId;
}

View File

@ -3,7 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
/**
* 设置消息通知地址返回结果对象
@ -12,11 +13,9 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder;
*/
@NoArgsConstructor
@Data
public class FavorCallbacksSaveResult {
public class FavorCallbacksSaveResult implements Serializable {
public static FavorCallbacksSaveResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorCallbacksSaveResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 修改时间

View File

@ -3,7 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
/**
* 发放代金券返回结果对象
@ -12,11 +13,9 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder;
*/
@NoArgsConstructor
@Data
public class FavorCouponsCreateResult {
public class FavorCouponsCreateResult implements Serializable {
public static FavorCouponsCreateResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorCouponsCreateResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 代金券id

View File

@ -3,7 +3,6 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
@ -14,11 +13,9 @@ import java.io.Serializable;
*/
@NoArgsConstructor
@Data
public class FavorCouponsGetResult {
public class FavorCouponsGetResult implements Serializable {
public static FavorCouponsGetResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorCouponsGetResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 创建批次的商户号
@ -156,6 +153,8 @@ public class FavorCouponsGetResult {
@Data
@NoArgsConstructor
public static class CutToMessage implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 可用优惠的商品最高单价
* <p>
@ -178,6 +177,8 @@ public class FavorCouponsGetResult {
@Data
@NoArgsConstructor
public static class NormalCouponInformation implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 面额
* <p>

View File

@ -3,8 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
@ -14,11 +14,9 @@ import java.util.List;
*/
@NoArgsConstructor
@Data
public class FavorCouponsQueryResult {
public class FavorCouponsQueryResult implements Serializable {
public static FavorCouponsQueryResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorCouponsQueryResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 查询结果总数

View File

@ -0,0 +1,315 @@
package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 核销事件回调内容
*
* @author thinsstar
*/
@NoArgsConstructor
@Data
public class FavorCouponsUseResult implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 创建批次的商户号
* <p>
* 批次创建方商户号
* 示例值9800064
*/
@SerializedName("stock_creator_mchid")
private String stockCreatorMchid;
/**
* 批次号
* <p>
* 微信为每个代金券批次分配的唯一id
* 示例值9865888
*/
@SerializedName("stock_id")
private String stockId;
/**
* 代金券id
* <p>
* 微信为代金券唯一分配的id
* 示例值98674556
*/
@SerializedName("coupon_id")
private String couponId;
/**
* 单品优惠特定信息
* <p>
* 单品优惠特定信息
*/
@SerializedName("singleitem_discount_off")
private SingleitemDiscountOff singleitemDiscountOff;
/**
* 减至优惠特定信息
* <p>
* 减至优惠限定字段仅减至优惠场景有返回
*/
@SerializedName("discount_to")
private DiscountTo discountTo;
/**
* 代金券名称
* <p>
* 代金券名称
* 示例值微信支付代金券
*/
@SerializedName("coupon_name")
private String couponName;
/**
* 代金券状态
* <p>
* 代金券状态
* SENDED可用
* USED已实扣
* EXPIRED已过期
* 示例值EXPIRED
*/
@SerializedName("status")
private String status;
/**
* 使用说明
* <p>
* 代金券描述说明字段
* 示例值微信支付营销
*/
@SerializedName("description")
private String description;
/**
* 领券时间
* <p>
* 领券时间遵循rfc3339标准格式格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONEYYYY-MM-DD表示年月日T出现在字符串中表示time元素的开头HH:mm:ss.sss表示时分秒毫秒TIMEZONE表示时区+08:00表示东八区时间领先UTC 8小时即北京时间例如2015-05-20T13:29:35.120+08:00表示北京时间2015年5月20日 13点29分35秒
* 示例值: 2015-05-20T13:29:35.120+08:00
*/
@SerializedName("create_time")
private String createTime;
/**
* 券类型
* <p>
* 券类型
* NORMAL满减券
* CUT_TO减至券
* 示例值CUT_TO
*/
@SerializedName("coupon_type")
private String couponType;
/**
* 是否无资金流
* <p>
* 枚举值
* true
* false
* 示例值true
*/
@SerializedName("no_cash")
private Boolean noCash;
/**
* 可用开始时间
* <p>
* 可用开始时间遵循rfc3339标准格式格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONEYYYY-MM-DD表示年月日T出现在字符串中表示time元素的开头HH:mm:ss.sss表示时分秒毫秒TIMEZONE表示时区+08:00表示东八区时间领先UTC 8小时即北京时间例如2015-05-20T13:29:35.120+08:00表示北京时间2015年5月20日 13点29分35秒
* 示例值: 2015-05-20T13:29:35.120+08:00
*/
@SerializedName("available_begin_time")
private String availableBeginTime;
/**
* 可用结束时间
* <p>
* 可用结束时间遵循rfc3339标准格式格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONEYYYY-MM-DD表示年月日T出现在字符串中表示time元素的开头HH:mm:ss.sss表示时分秒毫秒TIMEZONE表示时区+08:00表示东八区时间领先UTC 8小时即北京时间例如2015-05-20T13:29:35.120+08:00表示北京时间2015年5月20日 13点29分35秒
* 示例值: 2015-05-20T13:29:35.120+08:00
*/
@SerializedName("available_end_time")
private String availableEndTime;
/**
* 是否单品优惠
* <p>
* 枚举值
* true
* false
* 示例值true
*/
@SerializedName("singleitem")
private Boolean singleitem;
/**
* 普通满减券信息
* <p>
* 普通满减券面额门槛信息
*/
@SerializedName("normal_coupon_information")
private NormalCouponInformation normalCouponInformation;
/**
* 实扣代金券信息
* <p>
* 普通满减券面额门槛信息
*/
@SerializedName("consume_information")
private ConsumeInformation consumeInformation;
@Data
@NoArgsConstructor
public static class SingleitemDiscountOff implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 单品最高优惠价格
* <p>
* 单品最高优惠价格单位
* 示例值100
*/
@SerializedName(value = "single_price_max")
private Integer singlePriceMax;
}
@Data
@NoArgsConstructor
public static class DiscountTo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 减至后优惠单价
* <p>
* 减至后优惠单价单位
* 示例值100
*/
@SerializedName(value = "cut_to_price")
private Integer cutToPrice;
/**
* 最高价格
* <p>
* 可享受优惠的最高价格单位
* 示例值20
*/
@SerializedName(value = "max_price")
private Integer maxPrice;
}
@Data
@NoArgsConstructor
public static class NormalCouponInformation implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 面额
* <p>
* 面额单位
* 示例值100
*/
@SerializedName(value = "coupon_amount")
private Integer couponAmount;
/**
* 门槛
* <p>
* 使用券金额门槛单位
* 示例值100
*/
@SerializedName(value = "transaction_minimum")
private Integer transactionMinimum;
}
@Data
@NoArgsConstructor
public static class ConsumeInformation implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 核销时间
* <p>
* 代金券核销时间遵循rfc3339标准格式格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONEYYYY-MM-DD表示年月日T出现在字符串中表示time元素的开头HH:mm:ss.sss表示时分秒毫秒TIMEZONE表示时区+08:00表示东八区时间领先UTC 8小时即北京时间例如2015-05-20T13:29:35.120+08:00表示北京时间2015年5月20日 13点29分35秒
* 示例值2015-05-20T13:29:35.120+08:00
*/
@SerializedName(value = "consume_time")
private String consumeTime;
/**
* 核销商户号
* <p>
* 核销代金券的商户号
* 示例值9856081
*/
@SerializedName(value = "consume_mchid")
private String consumeMchid;
/**
* 核销订单号
* <p>
* 核销订单号
* 示例值2345234523
*/
@SerializedName(value = "transaction_id")
private String transactionId;
/**
* 单品信息
* <p>
* 商户下单接口传的单品信息
*/
@SerializedName("goods_detail")
private List<GoodsDetail> goodsDetail;
}
@Data
@NoArgsConstructor
public static class GoodsDetail implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 单品编码
* <p>
* 单品券创建时录入的单品编码
* 示例值a_goods1
*/
@SerializedName(value = "goods_id")
private String goodsId;
/**
* 单品数量
* <p>
* 单品数据
* 示例值7
*/
@SerializedName(value = "quantity")
private Integer quantity;
/**
* 单品单价
* <p>
* 单品单价
* 示例值1
*/
@SerializedName(value = "price")
private Integer price;
/**
* 优惠金额
* <p>
* 优惠金额
* 示例值4
*/
@SerializedName(value = "discount_amount")
private Integer discountAmount;
}
}

View File

@ -210,6 +210,8 @@ public class FavorStocksCreateRequest implements Serializable {
@Data
@NoArgsConstructor
public static class StockUseRule implements Serializable {
private static final long serialVersionUID = 1L;
/**
* <pre>
* 字段名发放总上限
@ -312,6 +314,8 @@ public class FavorStocksCreateRequest implements Serializable {
@Data
@NoArgsConstructor
public static class PatternInfo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* <pre>
* 字段名使用说明
@ -393,6 +397,8 @@ public class FavorStocksCreateRequest implements Serializable {
@Data
@NoArgsConstructor
public static class CouponUseRule implements Serializable {
private static final long serialVersionUID = 1L;
/**
* <pre>
* 字段名券生效时间
@ -666,6 +672,8 @@ public class FavorStocksCreateRequest implements Serializable {
@Data
@NoArgsConstructor
public static class FixedNormalCoupon implements Serializable {
private static final long serialVersionUID = 1L;
/**
* <pre>
* 字段名面额
@ -703,6 +711,8 @@ public class FavorStocksCreateRequest implements Serializable {
@Data
@NoArgsConstructor
public static class LimitCard implements Serializable {
private static final long serialVersionUID = 1L;
/**
* <pre>
* 字段名银行卡名称

View File

@ -3,7 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
/**
* 创建代金券批次返回结果对象
@ -12,11 +13,9 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder;
*/
@NoArgsConstructor
@Data
public class FavorStocksCreateResult {
public class FavorStocksCreateResult implements Serializable {
public static FavorStocksCreateResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorStocksCreateResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 批次号

View File

@ -3,7 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
/**
* 获取下载结果对象
@ -12,11 +13,9 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder;
*/
@NoArgsConstructor
@Data
public class FavorStocksFlowGetResult {
public class FavorStocksFlowGetResult implements Serializable {
public static FavorStocksFlowGetResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorStocksFlowGetResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 下载链接

View File

@ -3,7 +3,6 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
import java.util.List;
@ -15,11 +14,9 @@ import java.util.List;
*/
@NoArgsConstructor
@Data
public class FavorStocksGetResult {
public class FavorStocksGetResult implements Serializable {
public static FavorStocksGetResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorStocksGetResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 批次号
@ -180,6 +177,8 @@ public class FavorStocksGetResult {
@Data
@NoArgsConstructor
public static class CutToMessage implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 可用优惠的商品最高单价
* <p>
@ -202,6 +201,8 @@ public class FavorStocksGetResult {
@Data
@NoArgsConstructor
public static class StockUseRule implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 发放总上限
* <p>
@ -298,6 +299,8 @@ public class FavorStocksGetResult {
@Data
@NoArgsConstructor
public static class FixedNormalCoupon implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 面额
* <p>

View File

@ -3,8 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
@ -14,11 +14,9 @@ import java.util.List;
*/
@NoArgsConstructor
@Data
public class FavorStocksItemsGetResult {
public class FavorStocksItemsGetResult implements Serializable {
public static FavorStocksItemsGetResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorStocksItemsGetResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 可用单品编码总数

View File

@ -3,8 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
@ -14,11 +14,9 @@ import java.util.List;
*/
@NoArgsConstructor
@Data
public class FavorStocksMerchantsGetResult {
public class FavorStocksMerchantsGetResult implements Serializable {
public static FavorStocksMerchantsGetResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorStocksMerchantsGetResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 可用商户总数量

View File

@ -3,8 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
@ -14,11 +14,9 @@ import java.util.List;
*/
@NoArgsConstructor
@Data
public class FavorStocksQueryResult {
public class FavorStocksQueryResult implements Serializable {
public static FavorStocksQueryResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorStocksQueryResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 批次总数

View File

@ -3,7 +3,8 @@ package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import java.io.Serializable;
/**
* 激活代金券批次返回结果对象
@ -12,11 +13,9 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder;
*/
@NoArgsConstructor
@Data
public class FavorStocksStartResult {
public class FavorStocksStartResult implements Serializable {
public static FavorStocksStartResult fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, FavorStocksStartResult.class);
}
private static final long serialVersionUID = 1L;
/**
* 生效时间

View File

@ -0,0 +1,89 @@
package com.github.binarywang.wxpay.bean.marketing;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 核销事件回调通知对象
*
* @author thinsstar
*/
@NoArgsConstructor
@Data
public class UseNotifyData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 通知ID
*/
@SerializedName("id")
private String id;
/**
* 通知创建时间
*/
@SerializedName("create_time")
private String createTime;
/**
* 通知类型
*/
@SerializedName("event_type")
private String eventType;
/**
* 通知数据类型
*/
@SerializedName("resource_type")
private String resourceType;
/**
* 回调摘要
* summary
*/
@SerializedName("summary")
private String summary;
/**
* 通知数据
*/
@SerializedName("resource")
private Resource resource;
@Data
public static class Resource implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 加密算法类型
*/
@SerializedName("algorithm")
private String algorithm;
/**
* 数据密文
*/
@SerializedName("ciphertext")
private String cipherText;
/**
* 附加数据
*/
@SerializedName("associated_data")
private String associatedData;
/**
* 随机串
*/
@SerializedName("nonce")
private String nonce;
/**
* 原始回调类型
*/
@SerializedName("original_type")
private String originalType;
}
}

View File

@ -0,0 +1,35 @@
package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.businesscircle.BusinessCircleNotifyData;
import com.github.binarywang.wxpay.bean.businesscircle.PaidResult;
import com.github.binarywang.wxpay.bean.businesscircle.PointsNotifyRequest;
import com.github.binarywang.wxpay.bean.businesscircle.RefundResult;
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
import com.github.binarywang.wxpay.exception.WxPayException;
/**
* <pre>
* 微信支付智慧商圈API
* </pre>
*
* @author thinsstar
*/
public interface BusinessCircleService {
/**
* <pre>
* 智慧商圈接口-商圈积分同步API
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/businesscircle/chapter3_2.shtml
* 接口链接https://api.mch.weixin.qq.com/v3/businesscircle/points/notify
* </pre>
*
* @param request 请求对象
* @throws WxPayException the wx pay exception
*/
void notifyPoints(PointsNotifyRequest request) throws WxPayException;
BusinessCircleNotifyData parseNotifyData(String data, SignatureHeader header) throws WxPayException;
PaidResult decryptPaidNotifyDataResource(BusinessCircleNotifyData data) throws WxPayException;
RefundResult decryptRefundNotifyDataResource(BusinessCircleNotifyData data) throws WxPayException;
}

View File

@ -1,5 +1,6 @@
package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
import com.github.binarywang.wxpay.bean.marketing.*;
import com.github.binarywang.wxpay.exception.WxPayException;
@ -172,13 +173,39 @@ public interface MarketingFavorService {
* 接口链接https://api.mch.weixin.qq.com/v3/marketing/favor/callbacks
* </pre>
*
* @param request 批次号
* @param request 请求对象
* @return FavorCallbacksSaveResult 微信返回的结果信息
* @throws WxPayException the wx pay exception
*/
FavorCallbacksSaveResult saveFavorCallbacksV3(FavorCallbacksSaveRequest request) throws WxPayException;
/**
* <pre>
* 代金券接口-暂停代金券批次API
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/marketing/convention/chapter3_13.shtml
* 接口链接https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/{stock_id}/pause
* </pre>
*
* @param request 请求对象
* @return FavorCallbacksSaveResult 微信返回的结果信息
* @throws WxPayException the wx pay exception
*/
FavorStocksStartResult pauseFavorStocksV3(String stockId, FavorStocksSetRequest request) throws WxPayException;
/**
* <pre>
* 代金券接口-重启代金券批次API
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/marketing/convention/chapter3_14.shtml
* 接口链接https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/{stock_id}/restart
* </pre>
*
* @param request 请求对象
* @return FavorCallbacksSaveResult 微信返回的结果信息
* @throws WxPayException the wx pay exception
*/
FavorStocksStartResult restartFavorStocksV3(String stockId, FavorStocksSetRequest request) throws WxPayException;
UseNotifyData parseNotifyData(String data, SignatureHeader header) throws WxPayException;
FavorCouponsUseResult decryptNotifyDataResource(UseNotifyData data) throws WxPayException;
}

View File

@ -153,6 +153,13 @@ public interface WxPayService {
*/
EcommerceService getEcommerceService();
/**
* 获取微信支付智慧商圈服务类
*
* @return the business circle service
*/
BusinessCircleService getBusinessCircleService();
/**
* 获取微信支付通用媒体服务类
*

View File

@ -60,6 +60,7 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
private final RedpackService redpackService = new RedpackServiceImpl(this);
private final PayScoreService payScoreService = new PayScoreServiceImpl(this);
private final EcommerceService ecommerceService = new EcommerceServiceImpl(this);
private final BusinessCircleService businessCircleService = new BusinessCircleServiceImpl(this);
private final MerchantMediaService merchantMediaService = new MerchantMediaServiceImpl(this);
private final MarketingMediaService marketingMediaService = new MarketingMediaServiceImpl(this);
private final MarketingFavorService marketingFavorService = new MarketingFavorServiceImpl(this);
@ -91,6 +92,11 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
return ecommerceService;
}
@Override
public BusinessCircleService getBusinessCircleService() {
return this.businessCircleService;
}
@Override
public MerchantMediaService getMerchantMediaService() {
return this.merchantMediaService;

View File

@ -0,0 +1,89 @@
package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.businesscircle.BusinessCircleNotifyData;
import com.github.binarywang.wxpay.bean.businesscircle.PaidResult;
import com.github.binarywang.wxpay.bean.businesscircle.PointsNotifyRequest;
import com.github.binarywang.wxpay.bean.businesscircle.RefundResult;
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.BusinessCircleService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.AesUtils;
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Objects;
/**
* 微信支付-微信支付智慧商圈service
*
* @author thinsstar
*/
@Slf4j
@RequiredArgsConstructor
public class BusinessCircleServiceImpl implements BusinessCircleService {
private static final Gson GSON = new GsonBuilder().create();
private final WxPayService payService;
@Override
public void notifyPoints(PointsNotifyRequest request) throws WxPayException {
String url = String.format("%s/v3/businesscircle/points/notify", this.payService.getPayBaseUrl());
RsaCryptoUtil.encryptFields(request, this.payService.getConfig().getVerifier().getValidCertificate());
this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request));
}
/**
* 校验通知签名
*
* @param header 通知头信息
* @param data 通知数据
* @return true:校验通过 false:校验不通过
*/
private boolean verifyNotifySign(SignatureHeader header, String data) {
String beforeSign = String.format("%s%n%s%n%s%n", header.getTimeStamp(), header.getNonce(), data);
return payService.getConfig().getVerifier().verify(header.getSerialNo(),
beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned());
}
@Override
public BusinessCircleNotifyData parseNotifyData(String data, SignatureHeader header) throws WxPayException {
if (Objects.nonNull(header) && !this.verifyNotifySign(header, data)) {
throw new WxPayException("非法请求,头部信息验证失败");
}
return GSON.fromJson(data, BusinessCircleNotifyData.class);
}
@Override
public PaidResult decryptPaidNotifyDataResource(BusinessCircleNotifyData data) throws WxPayException {
BusinessCircleNotifyData.Resource resource = data.getResource();
String cipherText = resource.getCipherText();
String associatedData = resource.getAssociatedData();
String nonce = resource.getNonce();
String apiV3Key = this.payService.getConfig().getApiV3Key();
try {
return GSON.fromJson(AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key), PaidResult.class);
} catch (GeneralSecurityException | IOException e) {
throw new WxPayException("解析报文异常!", e);
}
}
@Override
public RefundResult decryptRefundNotifyDataResource(BusinessCircleNotifyData data) throws WxPayException {
BusinessCircleNotifyData.Resource resource = data.getResource();
String cipherText = resource.getCipherText();
String associatedData = resource.getAssociatedData();
String nonce = resource.getNonce();
String apiV3Key = this.payService.getConfig().getApiV3Key();
try {
return GSON.fromJson(AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key), RefundResult.class);
} catch (GeneralSecurityException | IOException e) {
throw new WxPayException("解析报文异常!", e);
}
}
}

View File

@ -1,9 +1,11 @@
package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
import com.github.binarywang.wxpay.bean.marketing.*;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.MarketingFavorService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.AesUtils;
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@ -11,6 +13,11 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Objects;
/**
* 微信支付-营销代金券接口
*
@ -161,4 +168,39 @@ public class MarketingFavorServiceImpl implements MarketingFavorService {
String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(result, FavorStocksStartResult.class);
}
/**
* 校验通知签名
*
* @param header 通知头信息
* @param data 通知数据
* @return true:校验通过 false:校验不通过
*/
private boolean verifyNotifySign(SignatureHeader header, String data) {
String beforeSign = String.format("%s%n%s%n%s%n", header.getTimeStamp(), header.getNonce(), data);
return payService.getConfig().getVerifier().verify(header.getSerialNo(),
beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned());
}
@Override
public UseNotifyData parseNotifyData(String data, SignatureHeader header) throws WxPayException {
if (Objects.nonNull(header) && !this.verifyNotifySign(header, data)) {
throw new WxPayException("非法请求,头部信息验证失败");
}
return GSON.fromJson(data, UseNotifyData.class);
}
@Override
public FavorCouponsUseResult decryptNotifyDataResource(UseNotifyData data) throws WxPayException {
UseNotifyData.Resource resource = data.getResource();
String cipherText = resource.getCipherText();
String associatedData = resource.getAssociatedData();
String nonce = resource.getNonce();
String apiV3Key = this.payService.getConfig().getApiV3Key();
try {
return GSON.fromJson(AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key), FavorCouponsUseResult.class);
} catch (GeneralSecurityException | IOException e) {
throw new WxPayException("解析报文异常!", e);
}
}
}

View File

@ -0,0 +1,79 @@
package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.businesscircle.BusinessCircleNotifyData;
import com.github.binarywang.wxpay.bean.businesscircle.PaidResult;
import com.github.binarywang.wxpay.bean.businesscircle.PointsNotifyRequest;
import com.github.binarywang.wxpay.bean.businesscircle.RefundResult;
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.testbase.ApiTestModule;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
/**
* <pre>
* 智慧商圈测试类
* </pre>
*
* @author thinsstar
*/
@Slf4j
@Test
@Guice(modules = ApiTestModule.class)
public class BusinessCircleServiceImplTest {
@Inject
private WxPayService wxPayService;
private static final Gson GSON = new GsonBuilder().create();
@Test
public void testNotifyPointsV3() throws WxPayException {
PointsNotifyRequest request = new PointsNotifyRequest();
String subMchid = "商圈商户ID";
String transactionId = "微信订单号";
String appId = "公众号id";
String openId = "微信openid";
request.setSubMchid(subMchid);
request.setTransactionId(transactionId);
request.setAppid(appId);
request.setOpenid(openId);
request.setEarnPoints(true);
request.setIncreasedPoints(10);
request.setPointsUpdateTime("2021-03-03T13:29:35.120+08:00");
wxPayService.getBusinessCircleService().notifyPoints(request);
}
@Test
public void testDecryptPaidNotifyDataResource() throws WxPayException {
SignatureHeader header = new SignatureHeader();
header.setSerialNo("Wechatpay-Serial");
header.setTimeStamp("Wechatpay-Timestamp");
header.setNonce("Wechatpay-Nonce");
header.setSigned("Wechatpay-Signature");
String data = "body";
BusinessCircleNotifyData notifyData = wxPayService.getBusinessCircleService().parseNotifyData(data, header);
PaidResult result = wxPayService.getBusinessCircleService().decryptPaidNotifyDataResource(notifyData);
log.info("result: {}", GSON.toJson(result));
}
@Test
public void testDecryptRefundNotifyDataResource() throws WxPayException {
SignatureHeader header = new SignatureHeader();
header.setSerialNo("Wechatpay-Serial");
header.setTimeStamp("Wechatpay-Timestamp");
header.setNonce("Wechatpay-Nonce");
header.setSigned("Wechatpay-Signature");
String data = "body";
BusinessCircleNotifyData notifyData = wxPayService.getBusinessCircleService().parseNotifyData(data, header);
RefundResult result = wxPayService.getBusinessCircleService().decryptRefundNotifyDataResource(notifyData);
log.info("result: {}", GSON.toJson(result));
}
}