From bbab5538be73bc1916df8412fd890f56fb4cf585 Mon Sep 17 00:00:00 2001 From: Mingyuan Wu Date: Mon, 22 Jul 2024 14:01:47 +0800 Subject: [PATCH 1/5] :art: Bump org.bouncycastle:bcpkix-jdk18on & bcprov-jdk18on from 1.78 to 1.78.1 --- pom.xml | 2 +- weixin-java-cp/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5cf69419f..9cb1db226 100644 --- a/pom.xml +++ b/pom.xml @@ -326,7 +326,7 @@ org.bouncycastle bcpkix-jdk18on - 1.78 + 1.78.1 diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 100f2907d..1ff701bbd 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -84,7 +84,7 @@ org.bouncycastle bcprov-jdk18on - 1.78 + 1.78.1 From 3d8e8e56a5ad69b9c9fc47322071712ab6ea17b1 Mon Sep 17 00:00:00 2001 From: 55 <38285521+lizhengwu@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:23:01 +0800 Subject: [PATCH 2/5] =?UTF-8?q?:art:=20#3337=20=E3=80=90=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E5=8F=B7=E5=B0=8F=E5=BA=97=E3=80=91=20=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E5=AD=97=E6=AE=B5=E8=A1=A5=E5=85=85=E3=80=81?= =?UTF-8?q?=E5=94=AE=E5=90=8E=E6=96=B0=E7=89=B9=E6=80=A7=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/WxChannelAfterSaleService.java | 34 +++++- .../impl/WxChannelAfterSaleServiceImpl.java | 43 +++---- .../bean/after/AfterSaleAcceptParam.java | 10 ++ .../channel/bean/after/AfterSaleInfo.java | 4 + .../channel/bean/after/AfterSaleReason.java | 33 ++++++ .../bean/after/AfterSaleReasonResponse.java | 28 +++++ .../bean/after/AfterSaleRejectParam.java | 16 ++- .../bean/after/AfterSaleRejectReason.java | 39 +++++++ .../after/AfterSaleRejectReasonResponse.java | 29 +++++ .../weixin/channel/bean/after/RefundInfo.java | 4 + .../channel/bean/order/OrderExtInfo.java | 34 +++++- .../channel/bean/order/OrderProductInfo.java | 105 ++++++++++++++---- .../channel/bean/order/OrderSettleInfo.java | 27 ++++- .../channel/bean/order/OrderSharerInfo.java | 24 +++- .../constant/WxChannelApiUrlConstants.java | 4 + .../weixin/channel/enums/AfterSaleStatus.java | 4 + .../weixin/channel/enums/OrderScene.java | 52 +++++++++ .../weixin/channel/enums/RefundReason.java | 51 +++++++++ .../WxChannelAfterSaleServiceImplTest.java | 27 ++++- 19 files changed, 507 insertions(+), 61 deletions(-) create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java index ac1e61729..418feab7a 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java @@ -4,6 +4,8 @@ import java.util.List; import me.chanjar.weixin.channel.bean.after.AfterSaleInfoResponse; import me.chanjar.weixin.channel.bean.after.AfterSaleListResponse; +import me.chanjar.weixin.channel.bean.after.AfterSaleReasonResponse; +import me.chanjar.weixin.channel.bean.after.AfterSaleRejectReasonResponse; import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; import me.chanjar.weixin.channel.bean.complaint.ComplaintOrderResponse; import me.chanjar.weixin.common.error.WxErrorException; @@ -39,26 +41,31 @@ AfterSaleListResponse listIds(Long beginCreateTime, Long endCreateTime, String n AfterSaleInfoResponse get(String afterSaleOrderId) throws WxErrorException; /** - * 同意退款 + * 同意售后 + * 文档地址 https://developers.weixin.qq.com/doc/channels/API/aftersale/acceptapply.html * * @param afterSaleOrderId 售后单号 * @param addressId 同意退货时传入地址id + * @param acceptType 1. 同意退货退款,并通知用户退货; 2. 确认收到货并退款给用户。 如果不填则将根据当前的售后单状态自动选择相应操作。对于仅退款的情况,由于只存在一种同意的场景,无需填写此字段。 * @return BaseResponse * * @throws WxErrorException 异常 */ - WxChannelBaseResponse accept(String afterSaleOrderId, String addressId) throws WxErrorException; + WxChannelBaseResponse accept(String afterSaleOrderId, String addressId, Integer acceptType) throws WxErrorException; /** * 拒绝售后 + * 文档地址 https://developers.weixin.qq.com/doc/channels/API/aftersale/rejectapply.html * * @param afterSaleOrderId 售后单号 * @param rejectReason 拒绝原因 + * @param rejectReasonType 拒绝原因枚举值 + * @see #getRejectReason() * @return BaseResponse * * @throws WxErrorException 异常 */ - WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason) throws WxErrorException; + WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason, Integer rejectReasonType) throws WxErrorException; /** * 上传退款凭证 @@ -108,4 +115,25 @@ WxChannelBaseResponse addComplaintEvidence(String complaintId, String content, L * @throws WxErrorException 异常 */ ComplaintOrderResponse getComplaint(String complaintId) throws WxErrorException; + + + /** + * 获取全量售后原因 + * 文档地址:https://developers.weixin.qq.com/doc/channels/API/aftersale/getaftersalereason.html + * + * @return 售后原因 + * + * @throws WxErrorException 异常 + */ + AfterSaleReasonResponse getAllReason() throws WxErrorException; + + /** + * 获取拒绝售后原因 + * 文档地址:https://developers.weixin.qq.com/doc/channels/API/aftersale/getrejectreason.html + * + * @return 拒绝售后原因 + * + * @throws WxErrorException 异常 + */ + AfterSaleRejectReasonResponse getRejectReason() throws WxErrorException; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java index c29ea49b3..a4be86f28 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java @@ -1,30 +1,19 @@ package me.chanjar.weixin.channel.api.impl; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_ACCEPT_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_GET_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_LIST_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_REJECT_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_UPLOAD_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.ADD_COMPLAINT_MATERIAL_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.ADD_COMPLAINT_PROOF_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.GET_COMPLAINT_ORDER_URL; - -import java.util.List; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.channel.api.WxChannelAfterSaleService; -import me.chanjar.weixin.channel.bean.after.AfterSaleAcceptParam; -import me.chanjar.weixin.channel.bean.after.AfterSaleIdParam; -import me.chanjar.weixin.channel.bean.after.AfterSaleInfoResponse; -import me.chanjar.weixin.channel.bean.after.AfterSaleListParam; -import me.chanjar.weixin.channel.bean.after.AfterSaleListResponse; -import me.chanjar.weixin.channel.bean.after.AfterSaleRejectParam; -import me.chanjar.weixin.channel.bean.after.RefundEvidenceParam; +import me.chanjar.weixin.channel.bean.after.*; import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; import me.chanjar.weixin.channel.bean.complaint.ComplaintOrderResponse; import me.chanjar.weixin.channel.bean.complaint.ComplaintParam; import me.chanjar.weixin.channel.util.ResponseUtils; import me.chanjar.weixin.common.error.WxErrorException; +import java.util.List; + +import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.*; +import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.*; + /** * 视频号小店 售后服务实现 * @@ -56,15 +45,15 @@ public AfterSaleInfoResponse get(String afterSaleOrderId) throws WxErrorExceptio } @Override - public WxChannelBaseResponse accept(String afterSaleOrderId, String addressId) throws WxErrorException { - AfterSaleAcceptParam param = new AfterSaleAcceptParam(afterSaleOrderId, addressId); + public WxChannelBaseResponse accept(String afterSaleOrderId, String addressId, Integer acceptType) throws WxErrorException { + AfterSaleAcceptParam param = new AfterSaleAcceptParam(afterSaleOrderId, addressId, acceptType); String resJson = shopService.post(AFTER_SALE_ACCEPT_URL, param); return ResponseUtils.decode(resJson, WxChannelBaseResponse.class); } @Override - public WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason) throws WxErrorException { - AfterSaleRejectParam param = new AfterSaleRejectParam(afterSaleOrderId, rejectReason); + public WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason, Integer rejectReasonType) throws WxErrorException { + AfterSaleRejectParam param = new AfterSaleRejectParam(afterSaleOrderId, rejectReason, rejectReasonType); String resJson = shopService.post(AFTER_SALE_REJECT_URL, param); return ResponseUtils.decode(resJson, WxChannelBaseResponse.class); } @@ -100,4 +89,16 @@ public ComplaintOrderResponse getComplaint(String complaintId) throws WxErrorExc String resJson = shopService.post(GET_COMPLAINT_ORDER_URL, reqJson); return ResponseUtils.decode(resJson, ComplaintOrderResponse.class); } + + @Override + public AfterSaleReasonResponse getAllReason() throws WxErrorException { + String resJson = shopService.post(AFTER_SALE_REASON_GET_URL, "{}"); + return ResponseUtils.decode(resJson, AfterSaleReasonResponse.class); + } + + @Override + public AfterSaleRejectReasonResponse getRejectReason() throws WxErrorException { + String resJson = shopService.post(AFTER_SALE_REJECT_REASON_GET_URL, "{}"); + return ResponseUtils.decode(resJson, AfterSaleRejectReasonResponse.class); + } } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java index ebc63a219..32ad9154e 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java @@ -19,6 +19,10 @@ public class AfterSaleAcceptParam extends AfterSaleIdParam { @JsonProperty("address_id") private String addressId; + /** 针对退货退款同意售后的阶段: 1. 同意退货退款,并通知用户退货; 2. 确认收到货并退款给用户。 如果不填则将根据当前的售后单状态自动选择相应操作。对于仅退款的情况,由于只存在一种同意的场景,无需填写此字段。*/ + @JsonProperty("accept_type") + private Integer acceptType; + public AfterSaleAcceptParam() { } @@ -26,4 +30,10 @@ public AfterSaleAcceptParam(String afterSaleOrderId, String addressId) { super(afterSaleOrderId); this.addressId = addressId; } + + public AfterSaleAcceptParam(String afterSaleOrderId, String addressId, Integer acceptType) { + super(afterSaleOrderId); + this.addressId = addressId; + this.acceptType = acceptType; + } } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java index b0d668b30..3a9d390c9 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java @@ -82,4 +82,8 @@ public class AfterSaleInfo implements Serializable { /** 纠纷id,该字段可用于获取纠纷信息 */ @JsonProperty("complaint_id") private String complaintId; + + /** 仅在待商家审核退款退货申请或收货期间返回,表示操作剩余时间(秒数)*/ + @JsonProperty("deadline") + private Long deadline; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java new file mode 100644 index 000000000..7c66eff18 --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.channel.bean.after; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 全量售后原因 + * + * @author lizhengwu + * @date 2024/7/24 + */ +@Data +@NoArgsConstructor +public class AfterSaleReason implements Serializable { + + private static final long serialVersionUID = -3674527884494606230L; + + /** + * 售后原因枚举 + */ + @JsonProperty("reason") + private Integer reason; + + /** + * 售后原因说明 + */ + @JsonProperty("reason_text") + private String reasonText; + +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java new file mode 100644 index 000000000..7372dea1f --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.channel.bean.after; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; + +import java.util.List; + +/** + * 售后原因 + * + * + * @author lizhengwu + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode +public class AfterSaleReasonResponse extends WxChannelBaseResponse { + + + private static final long serialVersionUID = -580378623915041396L; + + @JsonProperty("reason_list") + private List reasonList; + +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java index 080665ac0..cbde459fe 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java @@ -15,10 +15,18 @@ public class AfterSaleRejectParam extends AfterSaleIdParam { private static final long serialVersionUID = -7507483859864253314L; - /** 拒绝原因 */ + /** + * 拒绝原因 + */ @JsonProperty("reject_reason") private String rejectReason; + /** + * 拒绝原因枚举值 + */ + @JsonProperty("reject_reason_type") + private Integer rejectReasonType; + public AfterSaleRejectParam() { } @@ -26,4 +34,10 @@ public AfterSaleRejectParam(String afterSaleOrderId, String rejectReason) { super(afterSaleOrderId); this.rejectReason = rejectReason; } + + public AfterSaleRejectParam(String afterSaleOrderId, String rejectReason, Integer rejectReasonType) { + super(afterSaleOrderId); + this.rejectReason = rejectReason; + this.rejectReasonType = rejectReasonType; + } } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java new file mode 100644 index 000000000..51c88ae22 --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java @@ -0,0 +1,39 @@ +package me.chanjar.weixin.channel.bean.after; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 拒绝售后原因 + * + * @author lizhengwu + * @date 2024/7/24 + */ +@Data +@NoArgsConstructor +public class AfterSaleRejectReason implements Serializable { + + private static final long serialVersionUID = -3672834150982780L; + + /** + * 售后拒绝原因枚举 + */ + @JsonProperty("reject_reason_type") + private Integer rejectReasonType; + + /** + * 售后拒绝原因说明 + */ + @JsonProperty("reject_reason_type_text") + private String rejectReasonTypeText; + + /** + * 售后拒绝原因默认描述 + */ + @JsonProperty("reject_reason") + private String rejectReason; + +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java new file mode 100644 index 000000000..7b50691d0 --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.channel.bean.after; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; + +import java.util.List; + +/** + * 售后原因 + * + * @author lizhengwu + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode +public class AfterSaleRejectReasonResponse extends WxChannelBaseResponse { + + private static final long serialVersionUID = -7946679037747710613L; + + /** + * 售后原因列表 + */ + @JsonProperty("reject_reason_list") + private List rejectReasonList; + +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java index 9837b72b2..73aedf99c 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java @@ -18,4 +18,8 @@ public class RefundInfo implements Serializable { /** 退款金额(分) */ @JsonProperty("amount") private Integer amount; + + /** 标明售后单退款直接原因, 枚举值详情请参考 {@link me.chanjar.weixin.channel.enums.RefundReason} */ + @JsonProperty("refund_reason") + private Integer refundReason; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java index 3338d1a42..a846311c6 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java @@ -1,10 +1,11 @@ package me.chanjar.weixin.channel.bean.order; import com.fasterxml.jackson.annotation.JsonProperty; -import java.io.Serializable; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + /** * 订单备注信息 * @@ -15,12 +16,39 @@ public class OrderExtInfo implements Serializable { private static final long serialVersionUID = 4568097877621455429L; - /** 用户备注 */ + /** + * 用户备注 + */ @JsonProperty("customer_notes") private String customerNotes; - /** 商家备注 */ + /** + * 商家备注 + */ @JsonProperty("merchant_notes") private String merchantNotes; + /** + * 确认收货时间,包括用户主动确认收货和超时自动确认收货 + */ + @JsonProperty("confirm_receipt_time") + private Long confirmReceiptTime; + + /** + * 视频号id + */ + @JsonProperty("finder_id") + private String finderId; + + /** + * 直播id + */ + @JsonProperty("live_id") + private String liveId; + + /** + * 下单场景,枚举值见 {@link me.chanjar.weixin.channel.enums.OrderScene} + */ + @JsonProperty("order_scene") + private Integer orderScene; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java index acef8cc4f..e7edeb891 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java @@ -1,8 +1,10 @@ package me.chanjar.weixin.channel.bean.order; import com.fasterxml.jackson.annotation.JsonProperty; + import java.io.Serializable; import java.util.List; + import lombok.Data; import lombok.NoArgsConstructor; import me.chanjar.weixin.channel.bean.base.AttrInfo; @@ -17,96 +19,153 @@ public class OrderProductInfo implements Serializable { private static final long serialVersionUID = -2193536732955185928L; - /** 商品spu id */ + /** + * 商品spu id + */ @JsonProperty("product_id") private String productId; - /** sku_id */ + /** + * sku_id + */ @JsonProperty("sku_id") private String skuId; - /** sku小图 */ + /** + * sku小图 + */ @JsonProperty("thumb_img") private String thumbImg; - /** sku数量 */ + /** + * sku数量 + */ @JsonProperty("sku_cnt") private Integer skuCnt; - /** 售卖价格(单位:分) */ + /** + * 售卖价格(单位:分) + */ @JsonProperty("sale_price") private Integer salePrice; - /** 商品标题 */ + /** + * 商品标题 + */ @JsonProperty("title") private String title; - /** 正在售后/退款流程中的 sku 数量 */ + /** + * 正在售后/退款流程中的 sku 数量 + */ @JsonProperty("on_aftersale_sku_cnt") private Integer onAfterSaleSkuCnt; - /** 完成售后/退款的 sku 数量 */ + /** + * 完成售后/退款的 sku 数量 + */ @JsonProperty("finish_aftersale_sku_cnt") private Integer finishAfterSaleSkuCnt; - /** 商品编码 */ + /** + * 商品编码 + */ @JsonProperty("sku_code") private String skuCode; - /** 市场价格(单位:分) */ + /** + * 市场价格(单位:分) + */ @JsonProperty("market_price") private Integer marketPrice; - /** sku属性 */ + /** + * sku属性 + */ @JsonProperty("sku_attrs") private List skuAttrs; - /** sku实付价格 */ + /** + * sku实付价格 + */ @JsonProperty("real_price") private Integer realPrice; - /** 商品外部spu id */ + /** + * 商品外部spu id + */ @JsonProperty("out_product_id") private String outProductId; - /** 商品外部sku id */ + /** + * 商品外部sku id + */ @JsonProperty("out_sku_id") private String outSkuId; - /** 是否有优惠金额,非必填,默认为false */ + /** + * 是否有优惠金额,非必填,默认为false + */ @JsonProperty("is_discounted") private Boolean isDiscounted; - /** 优惠后 sku 价格,非必填,is_discounted为 true 时有值 */ + /** + * 优惠后 sku 价格,非必填,is_discounted为 true 时有值 + */ @JsonProperty("estimate_price") private Integer estimatePrice; - /** 是否修改过价格,非必填,默认为false */ + /** + * 是否修改过价格,非必填,默认为false + */ @JsonProperty("is_change_price") private Boolean changePriced; - /** 改价后 sku 价格,非必填,is_change_price为 true 时有值 */ + /** + * 改价后 sku 价格,非必填,is_change_price为 true 时有值 + */ @JsonProperty("change_price") private Integer changePrice; - /** 区域库存id */ + /** + * 区域库存id + */ @JsonProperty("out_warehouse_id") private String outWarehouseId; - /** 商品发货信息 */ + /** + * 商品发货信息 + */ @JsonProperty("sku_deliver_info") private OrderSkuDeliverInfo skuDeliverInfo; - /** 商品额外服务信息 */ + /** + * 商品额外服务信息 + */ @JsonProperty("extra_service") private OrderProductExtraService extraService; - /** 是否使用了会员积分抵扣 */ + /** + * 是否使用了会员积分抵扣 + */ @JsonProperty("use_deduction") private Boolean useDeduction; - /** 会员积分抵扣金额,单位为分 */ + /** + * 会员积分抵扣金额,单位为分 + */ @JsonProperty("deduction_price") private Integer deductionPrice; + /** + * 商品优惠券信息,具体结构请参考OrderProductCouponInfo结构体,逐步替换 order.order_detail.coupon_info + */ + @JsonProperty("order_product_coupon_info_list") + private List orderProductCouponInfoList; + + /** + * 商品发货时效,超时此时间未发货即为发货超时 + */ + @JsonProperty("delivery_deadline") + private Long deliveryDeadline; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java index c264a6289..bd3193144 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java @@ -1,7 +1,9 @@ package me.chanjar.weixin.channel.bean.order; import com.fasterxml.jackson.annotation.JsonProperty; + import java.io.Serializable; + import lombok.Data; import lombok.NoArgsConstructor; @@ -15,12 +17,33 @@ public class OrderSettleInfo implements Serializable { private static final long serialVersionUID = 2140632631448343656L; - /** 预计技术服务费(单位为分) */ + /** + * 预计技术服务费(单位为分) + */ @JsonProperty("predict_commission_fee") private Integer predictCommissionFee; - /** 实际技术服务费(单位为分)(未结算时本字段为空) */ + /** + * 实际技术服务费(单位为分)(未结算时本字段为空) + */ @JsonProperty("commission_fee") private Integer commissionFee; + /** + * 预计人气卡返佣金额,单位为分(未发起结算时本字段为空) + */ + @JsonProperty("predict_wecoin_commission") + private Integer predictWecoinCommission; + + /** + * 实际人气卡返佣金额,单位为分(未结算时本字段为空) + */ + @JsonProperty("wecoin_commission") + private Integer wecoinCommission; + + /** + * 商家结算时间 + */ + @JsonProperty("settle_time") + private Long settleTime; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java index be6646344..7ed41d2ed 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java @@ -1,7 +1,9 @@ package me.chanjar.weixin.channel.bean.order; import com.fasterxml.jackson.annotation.JsonProperty; + import java.io.Serializable; + import lombok.Data; import lombok.NoArgsConstructor; @@ -15,19 +17,33 @@ public class OrderSharerInfo implements Serializable { private static final long serialVersionUID = 7183259072254660971L; - /** 分享员openid */ + /** + * 分享员openid + */ @JsonProperty("sharer_openid") private String sharerOpenid; - /** 分享员unionid */ + /** + * 分享员unionid + */ @JsonProperty("sharer_unionid") private String sharerUnionid; - /** 分享员类型,0:普通分享员,1:店铺分享员 */ + /** + * 分享员类型,0:普通分享员,1:店铺分享员 + */ @JsonProperty("sharer_type") private Integer sharerType; - /** 分享场景 {@link me.chanjar.weixin.channel.enums.ShareScene} */ + /** + * 分享场景 {@link me.chanjar.weixin.channel.enums.ShareScene} + */ @JsonProperty("share_scene") private Integer shareScene; + + /** + * 分享员数据是否已经解析完成【1:解析完成 0:解析中】 + */ + @JsonProperty("handling_progress") + private Integer handlingProgress; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java index 4be90c370..79ff5f8f8 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java @@ -170,6 +170,10 @@ public interface AfterSale { String AFTER_SALE_REJECT_URL = "https://api.weixin.qq.com/channels/ec/aftersale/rejectapply"; /** 上传退款凭证 */ String AFTER_SALE_UPLOAD_URL = "https://api.weixin.qq.com/channels/ec/aftersale/uploadrefundcertificate"; + /** 获取全量售后原因*/ + String AFTER_SALE_REASON_GET_URL = "https://api.weixin.qq.com/channels/ec/aftersale/reason/get"; + /** 获取拒绝售后原因*/ + String AFTER_SALE_REJECT_REASON_GET_URL = "https://api.weixin.qq.com/channels/ec/aftersale/rejectreason/get"; } /** 纠纷相关接口 */ diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java index 60e77d9e5..b249c96bc 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java @@ -39,6 +39,10 @@ public enum AfterSaleStatus { MERCHANT_REFUND_RETRY_FAIL("MERCHANT_REFUND_RETRY_FAIL", "商家打款失败,客服关闭售后"), /** 售后关闭 */ MERCHANT_FAIL("MERCHANT_FAIL", "售后关闭"), + /** 待用户处理商家协商 */ + USER_WAIT_CONFIRM_UPDATE("USER_WAIT_CONFIRM_UPDATE", "待用户处理商家协商"), + /** 待用户处理商家代发起的售后申请 */ + USER_WAIT_HANDLE_MERCHANT_AFTER_SALE("USER_WAIT_HANDLE_MERCHANT_AFTER_SALE", "待用户处理商家代发起的售后申请"), ; private final String key; diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java new file mode 100644 index 000000000..c00f6a800 --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.channel.enums; + +/** + * 下单场景 + * + * @author lizhengwu + * @description + */ +public enum OrderScene { + /** + * 其他 + */ + OTHER(1, "其他"), + /** + * 直播间下单 + */ + LIVE(2, "直播间"), + /** + * 短视频 + */ + VIDEO(3, "短视频"), + /** + * 商品分享 + */ + SHARE(4, "商品分享"), + /** + * 商品橱窗主页 + */ + SHOW_CASE(5, "商品橱窗主页"), + /** + * 公众号文章商品卡片 + */ + ARTICLE_CARD(6, "公众号文章商品卡片"), + ; + + private final int key; + private final String value; + + OrderScene(int key, String value) { + this.key = key; + this.value = value; + } + + public int getKey() { + return key; + } + + public String getValue() { + return value; + } + +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java new file mode 100644 index 000000000..8a2825c28 --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.channel.enums; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 售后单退款直接原因 + * + * @author lizhengwu + */ +@JsonFormat(shape = JsonFormat.Shape.OBJECT) +public enum RefundReason { + /** 1 商家通过店铺管理页或者小助手发起退款 */ + MERCHANT_INITIATED_REFUND(1, "商家通过店铺管理页或者小助手发起退款"), + /** 2 退货退款场景,商家同意买家未上传物流单号情况下确认收货并退款,该场景限于订单无运费险 */ + MERCHANT_AGREES_NO_TRACKING_REFUND(2, "退货退款场景,商家同意买家未上传物流单号情况下确认收货并退款,该场景限于订单无运费险"), + /** 3 商家通过后台api发起退款 */ + MERCHANT_API_INITIATED_REFUND(3, "商家通过后台api发起退款"), + /** 4 未发货售后平台自动同意 */ + PRE_SHIPMENT_AUTOMATIC_REFUND(4, "未发货售后平台自动同意"), + /** 5 平台介入纠纷退款 */ + PLATFORM_INTERVENED_DISPUTE_REFUND(5, "平台介入纠纷退款"), + /** 6 特殊场景下平台强制退款 */ + PLATFORM_FORCED_REFUND(6, "特殊场景下平台强制退款"), + /** 7 退货退款场景,买家同意没有上传物流单号情况下,商家确认收货并退款,该场景限于订单包含运费险,并无法理赔 */ + BUYER_AGREES_NO_TRACKING_REFUND(7, "退货退款场景,买家同意没有上传物流单号情况下,商家确认收货并退款,该场景限于订单包含运费险,并无法理赔"), + /** 8 商家发货超时,平台退款 */ + LATE_SHIPMENT_PLATFORM_REFUND(8, "商家发货超时,平台退款"), + /** 9 商家处理买家售后申请超时,平台自动同意退款 */ + MERCHANT_OVERDUE_AUTO_REFUND(9, "商家处理买家售后申请超时,平台自动同意退款"), + /** 10 用户确认收货超时,平台退款 */ + BUYER_OVERDUE_AUTO_REFUND(10, "用户确认收货超时,平台退款"), + /** 11 商家确认收货超时,平台退款 */ + MERCHANT_OVERDUE_CONFIRMATION_REFUND(11, "商家确认收货超时,平台退款"), + ; + + private final int key; + private final String value; + + RefundReason(int key, String value) { + this.key = key; + this.value = value; + } + + public int getKey() { + return key; + } + + public String getValue() { + return value; + } +} diff --git a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java index 7e54a9eaa..81122f7a0 100644 --- a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java +++ b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java @@ -12,6 +12,8 @@ import me.chanjar.weixin.channel.api.WxChannelService; import me.chanjar.weixin.channel.bean.after.AfterSaleInfoResponse; import me.chanjar.weixin.channel.bean.after.AfterSaleListResponse; +import me.chanjar.weixin.channel.bean.after.AfterSaleReasonResponse; +import me.chanjar.weixin.channel.bean.after.AfterSaleRejectReasonResponse; import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; import me.chanjar.weixin.channel.bean.complaint.ComplaintOrderResponse; import me.chanjar.weixin.channel.test.ApiTestModule; @@ -52,8 +54,8 @@ public void testGet() throws WxErrorException { public void testAccept() throws WxErrorException { WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService(); String afterSaleOrderId = ""; - String addressId = "123"; - WxChannelBaseResponse response = afterSaleService.accept(afterSaleOrderId, addressId); + String addressId = null; + WxChannelBaseResponse response = afterSaleService.accept(afterSaleOrderId, addressId, 2); assertNotNull(response); assertTrue(response.isSuccess()); } @@ -62,8 +64,8 @@ public void testAccept() throws WxErrorException { public void testReject() throws WxErrorException { WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService(); String afterSaleOrderId = ""; - String rejectReason = "123"; - WxChannelBaseResponse response = afterSaleService.reject(afterSaleOrderId, rejectReason); + String rejectReason = null; + WxChannelBaseResponse response = afterSaleService.reject(afterSaleOrderId, rejectReason,1); assertNotNull(response); assertTrue(response.isSuccess()); } @@ -109,4 +111,21 @@ public void testGetComplaint() throws WxErrorException { assertNotNull(response); assertTrue(response.isSuccess()); } + + + @Test + public void testGetAllReason() throws WxErrorException { + WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService(); + AfterSaleReasonResponse allReason = afterSaleService.getAllReason(); + assertNotNull(allReason); + assertTrue(allReason.isSuccess()); + } + + @Test + public void testGetRejectReason() throws WxErrorException { + WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService(); + AfterSaleRejectReasonResponse rejectReason = afterSaleService.getRejectReason(); + assertNotNull(rejectReason); + assertTrue(rejectReason.isSuccess()); + } } From 26ee4c4a8f8da071379fcb5e45b4695391471c91 Mon Sep 17 00:00:00 2001 From: Sean Sun <1194458432@qq.com> Date: Tue, 30 Jul 2024 19:24:21 +0800 Subject: [PATCH 3/5] =?UTF-8?q?:new:=20#3339=20=E3=80=90=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91=E5=A2=9E=E5=8A=A0=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E7=B4=A0=E6=9D=90=E7=9A=84=E9=87=8D=E8=BD=BD?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/cp/api/WxCpMediaService.java | 26 +++++++++++++++++++ .../cp/api/impl/WxCpMediaServiceImpl.java | 24 +++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java index d0d4b661b..82f6db917 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java @@ -53,6 +53,32 @@ WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputS WxMediaUploadResult upload(String mediaType, String filename, String url) throws WxErrorException, IOException; + /** + *
+   *   上传多媒体文件.
+   * 
+ * + * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param file 文件对象, 上传的文件内容 + * @param filename 上传内容的实际文件名.例如:wework.txt + * @return wx media upload result + * @throws WxErrorException the wx error exception + */ + WxMediaUploadResult upload(String mediaType, File file, String filename) throws WxErrorException; + + /** + *
+   *   上传多媒体文件.
+   * 
+ * + * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param inputStream 上传的文件内容 + * @param filename 上传内容的实际文件名.例如:wework.txt + * @return wx media upload result + * @throws WxErrorException the wx error exception + */ + WxMediaUploadResult upload(String mediaType, InputStream inputStream, String filename) throws WxErrorException; + /** * 上传多媒体文件. * diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java index 7953d69e3..863dd7c1d 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.error.WxRuntimeException; import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.InputStreamData; @@ -16,6 +17,7 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.file.Files; import java.util.UUID; import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*; @@ -67,6 +69,28 @@ public WxMediaUploadResult upload(String mediaType, String filename, String url) } } + @Override + public WxMediaUploadResult upload(String mediaType, File file, String filename) throws WxErrorException { + if(!file.exists()){ + throw new WxRuntimeException("文件[" + file.getAbsolutePath() + "]不存在"); + } + try (InputStream inputStream = Files.newInputStream(file.toPath())) { + return this.mainService.execute(MediaInputStreamUploadRequestExecutor.create(this.mainService.getRequestHttp()) + , this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType), + new InputStreamData(inputStream, filename)); + } catch (IOException e) { + throw new WxRuntimeException(e); + } + } + + @Override + public WxMediaUploadResult upload(String mediaType, InputStream inputStream, String filename) throws WxErrorException{ + return this.mainService.execute(MediaInputStreamUploadRequestExecutor.create(this.mainService.getRequestHttp()) + , this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType), + new InputStreamData(inputStream, filename)); + } + + @Override public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), From 764a8d7228236b59a4f21175e57dabe6cde8b90a Mon Sep 17 00:00:00 2001 From: xxm Date: Tue, 30 Jul 2024 19:27:36 +0800 Subject: [PATCH 4/5] =?UTF-8?q?:new:=20=20#3340=E3=80=90=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E5=A2=9E=E5=8A=A0=E7=9B=B4=E8=BF=9E?= =?UTF-8?q?=E5=95=86=E6=88=B7=E4=BB=98=E6=AC=BE=E7=A0=81=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=92=8C=E6=92=A4=E9=94=80=E6=94=AF=E4=BB=98=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E7=9A=84V3=E7=89=88=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/request/WxPayCodepayRequest.java | 593 ++++++++++++++++ .../request/WxPayOrderReverseV3Request.java | 59 ++ .../wxpay/bean/result/WxPayCodepayResult.java | 636 ++++++++++++++++++ .../result/WxPayOrderReverseV3Result.java | 57 ++ .../wxpay/service/WxPayService.java | 60 ++ .../service/impl/BaseWxPayServiceImpl.java | 38 ++ 6 files changed, 1443 insertions(+) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java new file mode 100644 index 000000000..ecfa614a1 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java @@ -0,0 +1,593 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * V3付款码支付请求对象类 + * @author DaxPay + * @date 2024/7/29 + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class WxPayCodepayRequest implements Serializable { + + private static final long serialVersionUID = 1L; + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+   *  示例值:wxd678efh567hg6787
+   * 
+ */ + @SerializedName(value = "appid") + protected String appid; + /** + *
+   * 字段名:直连商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  直连商户的商户号,由微信支付生成并下发。
+   *  示例值:1230000109
+   * 
+ */ + @SerializedName(value = "mchid") + protected String mchid; + /** + *
+   * 字段名:商品描述
+   * 变量名:description
+   * 是否必填:是
+   * 类型:string[1,127]
+   * 描述:
+   *  商品描述
+   *  示例值:Image形象店-深圳腾大-QQ公仔
+   * 
+ */ + @SerializedName(value = "description") + protected String description; + /** + *
+   * 字段名:商户订单号
+   * 变量名:out_trade_no
+   * 是否必填:是
+   * 类型:string[6,32]
+   * 描述:
+   *  商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+   *  示例值:1217752501201407033233368018
+   * 
+ */ + @SerializedName(value = "out_trade_no") + protected String outTradeNo; + + /** + *
+   * 字段名:微信支付返回的订单号
+   * 变量名:transaction_id
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  微信分配的公众账号ID
+   *  示例值:1000320306201511078440737890
+   * 
+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+   * 字段名:附加数据
+   * 变量名:attach
+   * 是否必填:否
+   * 类型:string[1,128]
+   * 描述:
+   *  附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+   *  示例值:自定义数据
+   * 
+ */ + @SerializedName(value = "attach") + protected String attach; + + /** + *
+   * 字段名:订单优惠标记
+   * 变量名:goods_tag
+   * 是否必填:否
+   * 类型:string[1,256]
+   * 描述:
+   *  订单优惠标记
+   *  示例值:WXG
+   * 
+ */ + @SerializedName(value = "goods_tag") + private String goodsTag; + /** + *
+   * 字段名:电子发票入口开放标识
+   * 变量名:support_fapiao
+   * 是否必填:否
+   * 类型:boolean
+   * 描述:传入true时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效。
+   * 
+ */ + @SerializedName(value = "support_fapiao") + private Boolean supportFapiao; + /** + *
+   * 字段名:支付者
+   * 变量名:payer
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  支付者信息
+   * 
+ */ + @SerializedName(value = "payer") + private Payer payer; + /** + *
+   * 字段名:订单金额
+   * 变量名:amount
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  订单金额信息
+   * 
+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+   * 字段名:场景信息
+   * 变量名:scene_info
+   * 是否必填:否
+   * 类型:object
+   * 描述:
+   *  支付场景描述
+   * 
+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + + /** + *
+   * 字段名:优惠功能
+   * 变量名:promotion_detail
+   * 是否必填:否
+   * 类型:array
+   * 描述:
+   *  优惠功能,享受优惠时返回该字段。
+   * 
+ */ + @SerializedName(value = "promotion_detail") + private List promotionDetails; + + /** + *
+   * 字段名:结算信息
+   * 变量名:settle_info
+   * 是否必填:否
+   * 类型:Object
+   * 描述:结算信息
+   * 
+ */ + @SerializedName(value = "settle_info") + private SettleInfo settleInfo; + + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:总金额
+     * 变量名:total
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  订单总金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+     * 字段名:用户支付金额
+     * 变量名:payer_total
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  用户支付金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + /** + *
+     * 字段名:货币类型
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string[1,16]
+     * 描述:
+     *  CNY:人民币,境内商户号仅支持人民币。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+     * 字段名:用户支付币种
+     * 变量名:payer_currency
+     * 是否必填:否
+     * 类型:string[1,16]
+     * 描述:
+     *  用户支付币种
+     *  示例值: CNY
+     * 
+ */ + @SerializedName(value = "payer_currency") + private String payerCurrency; + } + + @Data + @NoArgsConstructor + public static class Payer implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+     * 字段名:用户标识
+     * 变量名:auth_code
+     * 是否必填:是
+     * 类型:string[32]
+     * 描述:
+     *   付款码支付授权码,即用户打开微信钱包显示的码。
+     *  示例值:130061098828009406
+     * 
+ */ + @SerializedName(value = "auth_code") + private String authCode; + } + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:商户端设备 IP
+     * 变量名:device_ip
+     * 是否必填:是
+     * 类型:string[1,45]
+     * 描述:
+     *  用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。
+     *  示例值:14.23.150.211
+     * 
+ */ + @SerializedName(value = "device_ip") + private String deviceIp; + /** + *
+     * 字段名:商户端设备号
+     * 变量名:device_id
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  商户端设备号(门店号或收银设备ID)。
+     *  示例值:013467007045764
+     * 
+ */ + @SerializedName(value = "device_id") + private String deviceId; + /** + *
+     * 字段名:商户门店信息
+     * 变量名:store_info
+     * 是否必填:否
+     * 类型:object
+     * 描述:
+     *  商户门店信息
+     * 
+ */ + @SerializedName(value = "store_info") + private StoreInfo storeInfo; + } + + /** + * 商户门店信息 + */ + @Data + @NoArgsConstructor + public static class StoreInfo implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+     * 字段名:门店编号
+     * 变量名:id
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  此参数与商家自定义编码(out_id)二选一必填。
+     *  微信支付线下场所ID,格式为纯数字。
+     *  基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+     *  指引参见:https://kf.qq.com/faq/230817neeaem2308177ZFfqM.html。
+     *  示例值:0001
+     * 
+ */ + @SerializedName(value = "id") + private String id; + /** + *
+     * 字段名:商家自定义编码
+     * 变量名:out_id
+     * 是否必填:否
+     * 类型:string[1,256]
+     * 描述:
+     *  此参数与门店(id)二选一必填。
+     * 商户系统的门店编码,支持大小写英文字母、数字,仅支持utf-8格式。
+     * 基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+     *  示例值:A1111
+     * 
+ */ + @SerializedName(value = "out_id") + private String outId; + } + + + @Data + @NoArgsConstructor + public static class SettleInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:是否指定分账
+     * 变量名:profit_sharing
+     * 是否必填:否
+     * 类型:boolean
+     * 描述:
+     *  是否指定分账
+     *  示例值:false
+     * 
+ */ + @SerializedName(value = "profit_sharing") + private Boolean profitSharing; + } + + + /** + * 优惠功能 + */ + @Data + @NoArgsConstructor + public static class PromotionDetail implements Serializable { + /** + *
+     * 字段名:券ID
+     * 变量名:coupon_id
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  券ID
+     *  示例值:109519
+     * 
+ */ + @SerializedName(value = "coupon_id") + private String couponId; + /** + *
+     * 字段名:优惠名称
+     * 变量名:name
+     * 是否必填:否
+     * 类型:string[1,64]
+     * 描述:
+     *  优惠名称
+     *  示例值:单品惠-6
+     * 
+ */ + @SerializedName(value = "name") + private String name; + /** + *
+     * 字段名:优惠范围
+     * 变量名:scope
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  GLOBAL:全场代金券
+     *  SINGLE:单品优惠
+     *  示例值:GLOBAL
+     * 
+ */ + @SerializedName(value = "scope") + private String scope; + /** + *
+     * 字段名:优惠类型
+     * 变量名:type
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  CASH:充值
+     *  NOCASH:预充值
+     *  示例值:CASH
+     * 
+ */ + @SerializedName(value = "type") + private String type; + /** + *
+     * 字段名:优惠券面额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  优惠券面额
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "amount") + private Integer amount; + /** + *
+     * 字段名:活动ID
+     * 变量名:stock_id
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  活动ID
+     *  示例值:931386
+     * 
+ */ + @SerializedName(value = "stock_id") + private String stockId; + /** + *
+     * 字段名:微信出资
+     * 变量名:wechatpay_contribute
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  微信出资,单位为分
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "wechatpay_contribute") + private Integer wechatpayContribute; + /** + *
+     * 字段名:商户出资
+     * 变量名:merchant_contribute
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  商户出资,单位为分
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "merchant_contribute") + private Integer merchantContribute; + /** + *
+     * 字段名:其他出资
+     * 变量名:other_contribute
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  其他出资,单位为分
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "other_contribute") + private Integer otherContribute; + /** + *
+     * 字段名:优惠币种
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string[1,16]
+     * 描述:
+     *  CNY:人民币,境内商户号仅支持人民币。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+     * 字段名:单品列表
+     * 变量名:goods_detail
+     * 是否必填:否
+     * 类型:array
+     * 描述:
+     *  单品列表信息
+     * 
+ */ + @SerializedName(value = "goods_detail") + private List goodsDetails; + } + + @Data + @NoArgsConstructor + public static class GoodsDetail implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:商品编码
+     * 变量名:goods_id
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  商品编码
+     *  示例值:M1006
+     * 
+ */ + @SerializedName(value = "goods_id") + private String goodsId; + /** + *
+     * 字段名:商品数量
+     * 变量名:quantity
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  用户购买的数量
+     *  示例值:1
+     * 
+ */ + @SerializedName(value = "quantity") + private Integer quantity; + /** + *
+     * 字段名:商品单价
+     * 变量名:unit_price
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  商品单价,单位为分
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + /** + *
+     * 字段名:商品优惠金额
+     * 变量名:discount_amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  商品优惠金额
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "discount_amount") + private Integer discountAmount; + /** + *
+     * 字段名:商品备注
+     * 变量名:goods_remark
+     * 是否必填:否
+     * 类型:string[1,128]
+     * 描述:
+     *  商品备注信息
+     *  示例值:商品备注信息
+     * 
+ */ + @SerializedName(value = "goods_remark") + private String goodsRemark; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java new file mode 100644 index 000000000..2505d6130 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java @@ -0,0 +1,59 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * V3撤销订单请求对象类 + * @author DaxPay + * @date 2024/7/29 + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class WxPayOrderReverseV3Request implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+   *  示例值:wxd678efh567hg6787
+   * 
+ */ + @SerializedName(value = "appid") + protected String appid; + /** + *
+   * 字段名:直连商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  直连商户的商户号,由微信支付生成并下发。
+   *  示例值:1230000109
+   * 
+ */ + @SerializedName(value = "mchid") + protected String mchid; + + /** + *
+   * 字段名:商户订单号
+   * 变量名:out_trade_no
+   * 是否必填:是
+   * 类型:string[6,32]
+   * 描述:
+   *  商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+   *  示例值:1217752501201407033233368018
+   * 
+ */ + private transient String outTradeNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java new file mode 100644 index 000000000..ad6a8be70 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java @@ -0,0 +1,636 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信V3付款码返回结果 + * @author DaxPay + * @date 2024/7/29 + */ +@Data +@Accessors(chain = true) +public class WxPayCodepayResult implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+   *  示例值:wxd678efh567hg6787
+   * 
+ */ + @SerializedName(value = "appid") + protected String appid; + /** + *
+   * 字段名:直连商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  直连商户的商户号,由微信支付生成并下发。
+   *  示例值:1230000109
+   * 
+ */ + @SerializedName(value = "mchid") + protected String mchid; + /** + *
+   * 字段名:商户订单号
+   * 变量名:out_trade_no
+   * 是否必填:是
+   * 类型:string[6,32]
+   * 描述:
+   *  商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+   *  示例值:1217752501201407033233368018
+   * 
+ */ + @SerializedName(value = "out_trade_no") + protected String outTradeNo; + + /** + *
+   * 字段名:微信支付返回的订单号
+   * 变量名:transaction_id
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  微信分配的公众账号ID
+   *  示例值:1000320306201511078440737890
+   * 
+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+   * 字段名:交易类型
+   * 变量名:trade_type
+   * 是否必填:是
+   * 类型:string[1,16]
+   * 描述:
+   *  枚举值:
+   *  NATIVE:扫码支付
+   *  JSAPI:公众号支付
+   *  APP:APP支付
+   *  MWEB:H5支付
+   *  示例值: JSAPI
+   * 
+ */ + @SerializedName(value = "trade_type") + private String tradeType; + /** + *
+   * 字段名:付款银行
+   * 变量名:bank_type
+   * 是否必填:否
+   * 类型:string(16)
+   * 描述:
+   *  银行类型,采用字符串类型的银行标识。
+   *  示例值:CMC
+   * 
+ */ + @SerializedName(value = "bank_type") + private String bankType; + /** + *
+   * 字段名:支付完成时间
+   * 变量名:success_time
+   * 是否必填:否
+   * 类型:string(64)
+   * 描述:支付完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
+   * 示例值:2018-06-08T10:34:56+08:00
+   * 
+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+   * 字段名:交易状态
+   * 变量名:trade_state
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  交易状态,枚举值:
+   *  SUCCESS:支付成功
+   *  REFUND:转入退款
+   *  NOTPAY:未支付
+   *  REVOKED:已撤销(付款码支付)
+   *  USERPAYING:用户支付中(付款码支付)
+   *  PAYERROR:支付失败(其他原因,如银行返回失败)
+   *  示例值:SUCCESS
+   * 
+ */ + @SerializedName(value = "trade_state") + private String tradeState; + /** + *
+   * 字段名:交易状态描述
+   * 变量名:trade_state_desc
+   * 是否必填:是
+   * 类型:string(256)
+   * 描述:交易状态描述
+   * 示例值:支付失败,请重新下单支付
+   * 
+ */ + @SerializedName(value = "trade_state_desc") + private String tradeStateDesc; + /** + *
+   * 字段名:附加数据
+   * 变量名:attach
+   * 是否必填:否
+   * 类型:string[1,128]
+   * 描述:
+   *  附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+   *  示例值:自定义数据
+   * 
+ */ + @SerializedName(value = "attach") + protected String attach; + + /** + *
+   * 字段名:订单优惠标记
+   * 变量名:goods_tag
+   * 是否必填:否
+   * 类型:string[1,256]
+   * 描述:
+   *  订单优惠标记
+   *  示例值:WXG
+   * 
+ */ + @SerializedName(value = "goods_tag") + private String goodsTag; + /** + *
+   * 字段名:电子发票入口开放标识
+   * 变量名:support_fapiao
+   * 是否必填:否
+   * 类型:boolean
+   * 描述:传入true时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效。
+   * 
+ */ + @SerializedName(value = "support_fapiao") + private Boolean supportFapiao; + /** + *
+   * 字段名:支付者
+   * 变量名:payer
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  支付者信息
+   * 
+ */ + @SerializedName(value = "payer") + private Payer payer; + /** + *
+   * 字段名:订单金额
+   * 变量名:amount
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  订单金额信息
+   * 
+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+   * 字段名:场景信息
+   * 变量名:scene_info
+   * 是否必填:否
+   * 类型:object
+   * 描述:
+   *  支付场景描述
+   * 
+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + + /** + *
+   * 字段名:优惠功能
+   * 变量名:promotion_detail
+   * 是否必填:否
+   * 类型:array
+   * 描述:
+   *  优惠功能,享受优惠时返回该字段。
+   * 
+ */ + @SerializedName(value = "promotion_detail") + private List promotionDetails; + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:总金额
+     * 变量名:total
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  订单总金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+     * 字段名:用户支付金额
+     * 变量名:payer_total
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  用户支付金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + /** + *
+     * 字段名:货币类型
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string[1,16]
+     * 描述:
+     *  CNY:人民币,境内商户号仅支持人民币。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+     * 字段名:用户支付币种
+     * 变量名:payer_currency
+     * 是否必填:否
+     * 类型:string[1,16]
+     * 描述:
+     *  用户支付币种
+     *  示例值: CNY
+     * 
+ */ + @SerializedName(value = "payer_currency") + private String payerCurrency; + } + + @Data + @NoArgsConstructor + public static class Payer implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+     * 字段名:用户标识
+     * 变量名:auth_code
+     * 是否必填:是
+     * 类型:string[32]
+     * 描述:
+     *   付款码支付授权码,即用户打开微信钱包显示的码。
+     *  示例值:130061098828009406
+     * 
+ */ + @SerializedName(value = "auth_code") + private String authCode; + } + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:商户端设备 IP
+     * 变量名:device_ip
+     * 是否必填:是
+     * 类型:string[1,45]
+     * 描述:
+     *  用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。
+     *  示例值:14.23.150.211
+     * 
+ */ + @SerializedName(value = "device_ip") + private String deviceIp; + /** + *
+     * 字段名:商户端设备号
+     * 变量名:device_id
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  商户端设备号(门店号或收银设备ID)。
+     *  示例值:013467007045764
+     * 
+ */ + @SerializedName(value = "device_id") + private String deviceId; + /** + *
+     * 字段名:商户门店信息
+     * 变量名:store_info
+     * 是否必填:否
+     * 类型:object
+     * 描述:
+     *  商户门店信息
+     * 
+ */ + @SerializedName(value = "store_info") + private StoreInfo storeInfo; + } + + /** + * 商户门店信息 + */ + @Data + @NoArgsConstructor + public static class StoreInfo implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+     * 字段名:门店编号
+     * 变量名:id
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  此参数与商家自定义编码(out_id)二选一必填。
+     *  微信支付线下场所ID,格式为纯数字。
+     *  基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+     *  指引参见:https://kf.qq.com/faq/230817neeaem2308177ZFfqM.html。
+     *  示例值:0001
+     * 
+ */ + @SerializedName(value = "id") + private String id; + /** + *
+     * 字段名:商家自定义编码
+     * 变量名:out_id
+     * 是否必填:否
+     * 类型:string[1,256]
+     * 描述:
+     *  此参数与门店(id)二选一必填。
+     * 商户系统的门店编码,支持大小写英文字母、数字,仅支持utf-8格式。
+     * 基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+     *  示例值:A1111
+     * 
+ */ + @SerializedName(value = "out_id") + private String outId; + } + + + @Data + @NoArgsConstructor + public static class SettleInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:是否指定分账
+     * 变量名:profit_sharing
+     * 是否必填:否
+     * 类型:boolean
+     * 描述:
+     *  是否指定分账
+     *  示例值:false
+     * 
+ */ + @SerializedName(value = "profit_sharing") + private Boolean profitSharing; + } + + + @Data + @NoArgsConstructor + public static class PromotionDetail implements Serializable { + /** + *
+     * 字段名:券ID
+     * 变量名:coupon_id
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  券ID
+     *  示例值:109519
+     * 
+ */ + @SerializedName(value = "coupon_id") + private String couponId; + /** + *
+     * 字段名:优惠名称
+     * 变量名:name
+     * 是否必填:否
+     * 类型:string[1,64]
+     * 描述:
+     *  优惠名称
+     *  示例值:单品惠-6
+     * 
+ */ + @SerializedName(value = "name") + private String name; + /** + *
+     * 字段名:优惠范围
+     * 变量名:scope
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  GLOBAL:全场代金券
+     *  SINGLE:单品优惠
+     *  示例值:GLOBAL
+     * 
+ */ + @SerializedName(value = "scope") + private String scope; + /** + *
+     * 字段名:优惠类型
+     * 变量名:type
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  CASH:充值
+     *  NOCASH:预充值
+     *  示例值:CASH
+     * 
+ */ + @SerializedName(value = "type") + private String type; + /** + *
+     * 字段名:优惠券面额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  优惠券面额
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "amount") + private Integer amount; + /** + *
+     * 字段名:活动ID
+     * 变量名:stock_id
+     * 是否必填:否
+     * 类型:string[1,32]
+     * 描述:
+     *  活动ID
+     *  示例值:931386
+     * 
+ */ + @SerializedName(value = "stock_id") + private String stockId; + /** + *
+     * 字段名:微信出资
+     * 变量名:wechatpay_contribute
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  微信出资,单位为分
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "wechatpay_contribute") + private Integer wechatpayContribute; + /** + *
+     * 字段名:商户出资
+     * 变量名:merchant_contribute
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  商户出资,单位为分
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "merchant_contribute") + private Integer merchantContribute; + /** + *
+     * 字段名:其他出资
+     * 变量名:other_contribute
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  其他出资,单位为分
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "other_contribute") + private Integer otherContribute; + /** + *
+     * 字段名:优惠币种
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string[1,16]
+     * 描述:
+     *  CNY:人民币,境内商户号仅支持人民币。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+     * 字段名:单品列表
+     * 变量名:goods_detail
+     * 是否必填:否
+     * 类型:array
+     * 描述:
+     *  单品列表信息
+     * 
+ */ + @SerializedName(value = "goods_detail") + private List goodsDetails; + } + + @Data + @NoArgsConstructor + public static class GoodsDetail implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:商品编码
+     * 变量名:goods_id
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  商品编码
+     *  示例值:M1006
+     * 
+ */ + @SerializedName(value = "goods_id") + private String goodsId; + /** + *
+     * 字段名:商品数量
+     * 变量名:quantity
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  用户购买的数量
+     *  示例值:1
+     * 
+ */ + @SerializedName(value = "quantity") + private Integer quantity; + /** + *
+     * 字段名:商品单价
+     * 变量名:unit_price
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  商品单价,单位为分
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + /** + *
+     * 字段名:商品优惠金额
+     * 变量名:discount_amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  商品优惠金额
+     *  示例值:0
+     * 
+ */ + @SerializedName(value = "discount_amount") + private Integer discountAmount; + /** + *
+     * 字段名:商品备注
+     * 变量名:goods_remark
+     * 是否必填:否
+     * 类型:string[1,128]
+     * 描述:
+     *  商品备注信息
+     *  示例值:商品备注信息
+     * 
+ */ + @SerializedName(value = "goods_remark") + private String goodsRemark; + } +} + diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java new file mode 100644 index 000000000..bcc990fac --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java @@ -0,0 +1,57 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 微信V3撤销支付订单返回结果 + * @author DaxPay + * @date 2024/7/29 + */ +@Data +@Accessors(chain = true) +public class WxPayOrderReverseV3Result implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:应用ID
+     * 变量名:appid
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+     *  示例值:wxd678efh567hg6787
+     * 
+ */ + @SerializedName(value = "appid") + protected String appid; + /** + *
+     * 字段名:直连商户号
+     * 变量名:mchid
+     * 是否必填:是
+     * 类型:string[1,32]
+     * 描述:
+     *  直连商户的商户号,由微信支付生成并下发。
+     *  示例值:1230000109
+     * 
+ */ + @SerializedName(value = "mchid") + protected String mchid; + /** + *
+     * 字段名:商户订单号
+     * 变量名:out_trade_no
+     * 是否必填:是
+     * 类型:string[6,32]
+     * 描述:
+     *  商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+     *  示例值:1217752501201407033233368018
+     * 
+ */ + @SerializedName(value = "out_trade_no") + protected String outTradeNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index b73029f4e..57c2937c6 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1275,6 +1275,25 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri */ WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayException; + /** + *
+   * 付款码支付API.
+   * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/code-pay.html
+   * 应用场景:
+   * 收银员使用扫码设备读取微信用户付款码以后,二维码或条码信息会传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
+   * 提醒1:提交支付请求后微信会同步返回支付结果。当返回结果为“系统错误”时,商户系统等待5秒后调用【查询订单API】,查询支付实际交易结果;当返回结果为“USERPAYING”时,商户系统可设置间隔时间(建议10秒)重新查询支付结果,直到支付成功或超时(建议30秒);
+   * 提醒2:在调用查询接口返回后,如果交易状况不明晰,请调用【撤销订单API】,此时如果交易失败则关闭订单,该单不能再支付成功;如果交易成功,则将扣款退回到用户账户。当撤销无返回或错误时,请再次调用。注意:请勿扣款后立即调用【撤销订单API】,建议至少15秒后再调用。撤销订单API需要双向证书。
+   * 接口地址:   https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+   * 是否需要证书:不需要。
+   * 
+ * + * @param request the request + * @return the wx codepay result + * @throws WxPayException the wx pay exception + */ + WxPayCodepayResult codepay(WxPayCodepayRequest request) throws WxPayException; + + /** *
    * 撤销订单API.
@@ -1295,6 +1314,47 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
    */
   WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException;
 
+  /**
+   * 
+   * 撤销订单API.
+   * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+   * 应用场景:
+   *  支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;
+   *  如果用户支付成功,微信支付系统会将此订单资金退还给用户。
+   *  注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。
+   *  提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
+   *  调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。
+   *  接口链接 :https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+   *  是否需要证书:请求需要双向证书。
+   * 
+ * + * @param request the request + * @return the wx pay order reverse result + * @throws WxPayException the wx pay exception + */ + WxPayOrderReverseV3Result reverseOrderV3(WxPayOrderReverseV3Request request) throws WxPayException; + + /** + *
+   * 撤销订单API.
+   * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+   * 应用场景:
+   *  支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;
+   *  如果用户支付成功,微信支付系统会将此订单资金退还给用户。
+   *  注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。
+   *  提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
+   *  调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。
+   *  接口链接 :https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+   *  是否需要证书:请求需要双向证书。
+   * 
+ * + * @param outTradeNo 商户系统内部的订单号 + * @return the wx pay order reverse result + * @throws WxPayException the wx pay exception + */ + WxPayOrderReverseV3Result reverseOrderV3(String outTradeNo) throws WxPayException; + + /** *
    *  转换短链接.
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
index 1187880cb..851040dd1 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
@@ -1130,6 +1130,19 @@ public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayEx
     return result;
   }
 
+  @Override
+  public WxPayCodepayResult codepay(WxPayCodepayRequest request) throws WxPayException {
+    if (StringUtils.isBlank(request.getAppid())) {
+      request.setAppid(this.getConfig().getAppId());
+    }
+    if (StringUtils.isBlank(request.getMchid())) {
+      request.setMchid(this.getConfig().getMchId());
+    }
+    String url = String.format("%s/v3/pay/transactions/codepay", this.getPayBaseUrl());
+    String body = this.postV3(url, GSON.toJson(request));
+    return GSON.fromJson(body, WxPayCodepayResult.class);
+  }
+
   @Override
   public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException {
     request.checkAndSign(this.getConfig());
@@ -1141,6 +1154,31 @@ public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) th
     return result;
   }
 
+
+  @Override
+  public WxPayOrderReverseV3Result reverseOrderV3(WxPayOrderReverseV3Request request) throws WxPayException {
+    if (StringUtils.isBlank(request.getAppid())) {
+      request.setAppid(this.getConfig().getAppId());
+    }
+    if (StringUtils.isBlank(request.getMchid())) {
+      request.setMchid(this.getConfig().getMchId());
+    }
+    // 拼接参数请求路径并发送
+    String url = String.format("%s/v3/pay/transactions/out-trade-no/%s/reverse", this.getPayBaseUrl(), request.getOutTradeNo());
+    String response = this.postV3(url, GSON.toJson(request));
+    return GSON.fromJson(response, WxPayOrderReverseV3Result.class);
+  }
+
+  @Override
+  public WxPayOrderReverseV3Result reverseOrderV3(String outTradeNo) throws WxPayException {
+    if (StringUtils.isBlank(outTradeNo)) {
+      throw new WxPayException("out_trade_no不能为空");
+    }
+    WxPayOrderReverseV3Request request = new WxPayOrderReverseV3Request();
+    request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
+    return this.reverseOrderV3(request);
+  }
+
   @Override
   public String shorturl(WxPayShorturlRequest request) throws WxPayException {
     request.checkAndSign(this.getConfig());

From e44f6d8b7216cad1fe5dce722bbc764896646825 Mon Sep 17 00:00:00 2001
From: zhiyuan 
Date: Tue, 20 Aug 2024 18:05:08 +0800
Subject: [PATCH 5/5] =?UTF-8?q?:art:=20#3345=20=E3=80=90=E5=B0=8F=E7=A8=8B?=
 =?UTF-8?q?=E5=BA=8F=E3=80=91=E8=8E=B7=E5=8F=96=E6=89=8B=E6=9C=BA=E5=8F=B7?=
 =?UTF-8?q?=20getPhoneNoInfo=20=E6=97=A7=E7=89=88=E6=9C=AC=E5=85=BC?=
 =?UTF-8?q?=E5=AE=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../wx/miniapp/api/WxMaUserService.java          | 16 ++++++++++++++--
 .../wx/miniapp/api/impl/WxMaUserServiceImpl.java |  6 +++++-
 .../api/impl/WxMaUserServiceImplTest.java        | 11 ++++++++++-
 3 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
index 3b2c3f74d..8c6a8ef87 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
@@ -45,7 +45,19 @@ public interface WxMaUserService {
   void setUserStorage(Map kvMap, String sessionKey, String openid) throws WxErrorException;
 
   /**
-   * 获取手机号信息,2023年8月28日起
+   * 解密用户手机号信息.
+   *
+   * @param sessionKey    会话密钥
+   * @param encryptedData 消息密文
+   * @param ivStr         加密算法的初始向量
+   * @return .
+   * @deprecated 当前(基础库2.21.2以下使用)旧版本,以上请使用替代方法 {@link #getPhoneNoInfo(String)}
+   */
+  @Deprecated
+  WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr);
+
+  /**
+   * 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起
    *
    * @param code 每个code只能使用一次,code的有效期为5min。code获取方式参考手机号快速验证组件
    * @return 用户手机号信息
@@ -55,7 +67,7 @@ public interface WxMaUserService {
   WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException;
 
   /**
-   * 获取手机号信息,2023年8月28日起
+   * 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起
    *
    * @param code 每个code只能使用一次,code的有效期为5min。code获取方式参考手机号快速验证组件
    * @return 用户手机号信息
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
index 8a921d05a..c9f5c2e33 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
@@ -57,6 +57,11 @@ public void setUserStorage(Map kvMap, String sessionKey, String
     this.service.post(url, params);
   }
 
+  @Override
+  public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) {
+    return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
+  }
+
   @Override
   public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException {
     JsonObject param = new JsonObject();
@@ -67,7 +72,6 @@ public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException {
       return WxMaGsonBuilder.create().fromJson(response.getAsJsonObject(PHONE_INFO),
         WxMaPhoneNumberInfo.class);
     }
-
     return null;
   }
 
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java
index d3cd1b7d2..7c6d61082 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java
@@ -49,7 +49,16 @@ public void testCheckUserInfo() {
 
 
   @Test
-  public void testGetPhoneNoInfo() throws WxErrorException {
+  public void testGetPhoneNoInfo() {
+    WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNoInfo("tiihtNczf5v6AKRyjwEUhQ==",
+      "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==",
+      "r7BXXKkLb8qrSNn05n0qiA==");
+    assertNotNull(phoneNoInfo);
+    System.out.println(phoneNoInfo.toString());
+  }
+
+  @Test
+  public void testGetPhoneInfo() throws WxErrorException {
     WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNumber("tiihtNczf5v6AKRyjwEUhQ==");
     assertNotNull(phoneNoInfo);
     System.out.println(phoneNoInfo.toString());