From 0536fc448821ab16d99f0a0634b252dceadbeeae Mon Sep 17 00:00:00 2001 From: wanggeng <450292408@qq.com> Date: Sat, 12 Nov 2022 00:39:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E9=80=80=E6=AC=BE=EF=BC=8C=E9=80=80=E6=AC=BE=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=EF=BC=8C=E9=80=80=E6=AC=BE=E9=80=9A=E7=9F=A5=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/wechat/v3/PayController.java | 5 +- .../wechat/v3/RefundController.java | 47 +++ .../enums/RefundAmountFromAccountEnum.java | 30 ++ .../wechat/pay/enums/RefundChannelEnum.java | 32 ++ .../pay/enums/RefundFundsAccountEnum.java | 33 ++ .../enums/RefundPromotionDetailScopeEnum.java | 30 ++ .../enums/RefundPromotionDetailTypeEnum.java | 30 ++ .../wechat/pay/enums/RefundStatusEnum.java | 32 ++ .../pojo/v3/{PayNotice.java => Notice.java} | 2 +- .../pay/pojo/v3/jsapi/pojo/PayPlaceOrder.java | 12 + .../pay/pojo/v3/jsapi/pojo/PayPrepay.java | 12 +- .../wechat/pay/pojo/v3/nav/PayCodeUrl.java | 30 ++ .../wechat/pay/pojo/v3/refund/AmountFrom.java | 40 ++ .../pay/pojo/v3/refund/GoodsDetail.java | 90 ++++ .../wechat/pay/pojo/v3/refund/Refund.java | 132 ++++++ .../v3/refund/RefundNoticeCiphertext.java | 205 +++++++++ .../pay/pojo/v3/refund/RefundResult.java | 392 ++++++++++++++++++ .../pay/remote/v3/IOrderPayRemoteService.java | 16 + .../pay/remote/v3/IRefundsRemoteService.java | 45 ++ ....java => DefaultPayNoticeServiceImpl.java} | 2 +- .../pay/service/v3/impl/PayServiceImpl.java | 89 +--- .../v3/jsapi/impl/JsapiServiceImpl.java | 183 +------- .../pay/service/v3/nav/INativeService.java | 25 ++ .../v3/nav/impl/NativeServiceImpl.java | 52 +++ .../v3/refund/IRefundNoticeService.java | 21 + .../pay/service/v3/refund/IRefundService.java | 56 +++ .../impl/DefaultRefundNoticeServiceImpl.java | 23 + .../v3/refund/impl/RefundServiceImpl.java | 113 +++++ .../pay/utils/v3/AsyncNoticeResultUtil.java | 118 ++++++ .../wechat/pay/utils/v3/PayOrderUtil.java | 153 +++++++ 30 files changed, 1787 insertions(+), 263 deletions(-) create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/RefundController.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundAmountFromAccountEnum.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundChannelEnum.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundFundsAccountEnum.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailScopeEnum.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailTypeEnum.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundStatusEnum.java rename module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/{PayNotice.java => Notice.java} (99%) create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/nav/PayCodeUrl.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/AmountFrom.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/GoodsDetail.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/Refund.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundNoticeCiphertext.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundResult.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IRefundsRemoteService.java rename module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/{DefaultPayNoticeService.java => DefaultPayNoticeServiceImpl.java} (86%) create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/INativeService.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/impl/NativeServiceImpl.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundNoticeService.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundService.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/DefaultRefundNoticeServiceImpl.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/RefundServiceImpl.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/AsyncNoticeResultUtil.java create mode 100644 module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/PayOrderUtil.java diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/PayController.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/PayController.java index 33980d40..7dc824d1 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/PayController.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/PayController.java @@ -23,7 +23,7 @@ import java.io.IOException; * @Date: 2021/8/19 9:36 下午 * @Version: 1.0 */ -@Api(tags = ISystemConstant.API_TAGS_WECHAT_PREFIX + "微信公众号") +@Api(tags = ISystemConstant.API_TAGS_WECHAT_PREFIX + "微信支付") @RestController @RequestMapping(ISystemConstant.WECHAT_PREFIX + "/pay") public class PayController extends DefaultBaseController { @@ -38,7 +38,8 @@ public class PayController extends DefaultBaseController { @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = PayErrorResponse.class)}) @PostMapping("notice") @CheckRequestBodyAnnotation - public void notice(HttpServletRequest request, HttpServletResponse response, + public void notice(HttpServletRequest request, + HttpServletResponse response, @RequestHeader("Wechatpay-Nonce") String nonce, @RequestHeader("Wechatpay-Timestamp") String timestamp, @RequestHeader("Wechatpay-Signature") String wechatpaySignature) throws IOException { diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/RefundController.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/RefundController.java new file mode 100644 index 00000000..c499bb42 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/controller/wechat/v3/RefundController.java @@ -0,0 +1,47 @@ +package ink.wgink.module.wechat.pay.controller.wechat.v3; + +import ink.wgink.common.base.DefaultBaseController; +import ink.wgink.interfaces.consts.ISystemConstant; +import ink.wgink.module.wechat.pay.pojo.v3.PayErrorResponse; +import ink.wgink.module.wechat.pay.service.v3.refund.IRefundService; +import io.swagger.annotations.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @ClassName: RefundController + * @Description: 退款 + * @Author: wanggeng + * @Date: 2022/11/11 23:30 + * @Version: 1.0 + */ +@Api(tags = ISystemConstant.API_TAGS_WECHAT_PREFIX + "微信退款") +@RestController +@RequestMapping(ISystemConstant.WECHAT_PREFIX + "/refund") +public class RefundController extends DefaultBaseController { + + @Autowired + private IRefundService refundService; + + @ApiOperation(value = "通知退款结果", notes = "有微信支付主动发起的退款结果通知接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "Wechatpay-Signature", value = "来自微信的签名值", paramType = "header") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = PayErrorResponse.class)}) + @PostMapping("notice") + public void notice(HttpServletRequest request, + HttpServletResponse response, + @RequestHeader("Wechatpay-Nonce") String nonce, + @RequestHeader("Wechatpay-Timestamp") String timestamp, + @RequestHeader("Wechatpay-Signature") String wechatpaySignature) throws IOException { + refundService.notice(request, response, nonce, timestamp, wechatpaySignature); + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundAmountFromAccountEnum.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundAmountFromAccountEnum.java new file mode 100644 index 00000000..c24d5a6e --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundAmountFromAccountEnum.java @@ -0,0 +1,30 @@ +package ink.wgink.module.wechat.pay.enums; + +/** + * @ClassName: RefundsAmountFromAccountEnum + * @Description: 出资账户类型 + * @Author: wanggeng + * @Date: 2022/11/11 17:10 + * @Version: 1.0 + */ +public enum RefundAmountFromAccountEnum { + + AVAILABLE("AVAILABLE", "可用余额"), + UNAVAILABLE("UNAVAILABLE", "不可用余额"); + + private String value; + private String text; + + RefundAmountFromAccountEnum(String value, String text) { + this.value = value; + this.text = text; + } + + public String getValue() { + return value == null ? "" : value.trim(); + } + + public String getText() { + return text == null ? "" : text.trim(); + } +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundChannelEnum.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundChannelEnum.java new file mode 100644 index 00000000..129444c0 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundChannelEnum.java @@ -0,0 +1,32 @@ +package ink.wgink.module.wechat.pay.enums; + +/** + * @ClassName: RefundsChannelEnum + * @Description: 退款渠道枚举 + * @Author: wanggeng + * @Date: 2022/11/10 21:34 + * @Version: 1.0 + */ +public enum RefundChannelEnum { + + ORIGINAL("ORIGINAL", "原路退款"), + BALANCE("BALANCE", "退回到余额"), + OTHER_BALANCE("OTHER_BALANCE", "原账户异常退到其他余额账户"), + OTHER_BANKCARD("OTHER_BANKCARD", "原银行卡异常退到其他银行卡"); + + private String value; + private String text; + + RefundChannelEnum(String value, String text) { + this.value = value; + this.text = text; + } + + public String getValue() { + return value == null ? "" : value.trim(); + } + + public String getText() { + return text == null ? "" : text.trim(); + } +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundFundsAccountEnum.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundFundsAccountEnum.java new file mode 100644 index 00000000..06d207a5 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundFundsAccountEnum.java @@ -0,0 +1,33 @@ +package ink.wgink.module.wechat.pay.enums; + +/** + * @ClassName: FundsAccountEnum + * @Description: 资金账户 + * @Author: wanggeng + * @Date: 2022/11/11 09:41 + * @Version: 1.0 + */ +public enum RefundFundsAccountEnum { + + UNSETTLED("UNSETTLED", "未结算资金"), + AVAILABLE("AVAILABLE", "可用余额"), + UNAVAILABLE("UNAVAILABLE", "不可用余额"), + OPERATION("OPERATION", "运营户"), + BASIC("BASIC", "基本账户(含可用余额和不可用余额)"); + + private String value; + private String text; + + RefundFundsAccountEnum(String value, String text) { + this.value = value; + this.text = text; + } + + public String getValue() { + return value == null ? "" : value.trim(); + } + + public String getText() { + return text == null ? "" : text.trim(); + } +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailScopeEnum.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailScopeEnum.java new file mode 100644 index 00000000..7c9a1204 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailScopeEnum.java @@ -0,0 +1,30 @@ +package ink.wgink.module.wechat.pay.enums; + +/** + * @ClassName: RefundsPromotionDetailScopeEnum + * @Description: 退款优惠范围枚举 + * @Author: wanggeng + * @Date: 2022/11/11 17:59 + * @Version: 1.0 + */ +public enum RefundPromotionDetailScopeEnum { + + GLOBAL("GLOBAL", "全场代金券"), + SINGLE("SINGLE", "单品优惠"); + + private String value; + private String text; + + RefundPromotionDetailScopeEnum(String value, String text) { + this.value = value; + this.text = text; + } + + public String getValue() { + return value == null ? "" : value.trim(); + } + + public String getText() { + return text == null ? "" : text.trim(); + } +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailTypeEnum.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailTypeEnum.java new file mode 100644 index 00000000..4d8840f9 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundPromotionDetailTypeEnum.java @@ -0,0 +1,30 @@ +package ink.wgink.module.wechat.pay.enums; + +/** + * @ClassName: RefundsPromotionDetailTypeEnum + * @Description: 退款优惠类型枚举 + * @Author: wanggeng + * @Date: 2022/11/11 18:01 + * @Version: 1.0 + */ +public enum RefundPromotionDetailTypeEnum { + + COUPON("COUPON", "代金券,需要走结算资金的充值型代金券"), + DISCOUNT("DISCOUNT", "优惠券,不走结算资金的免充值型优惠券"); + + private String value; + private String text; + + RefundPromotionDetailTypeEnum(String value, String text) { + this.value = value; + this.text = text; + } + + public String getValue() { + return value == null ? "" : value.trim(); + } + + public String getText() { + return text == null ? "" : text.trim(); + } +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundStatusEnum.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundStatusEnum.java new file mode 100644 index 00000000..a6cc5f16 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/enums/RefundStatusEnum.java @@ -0,0 +1,32 @@ +package ink.wgink.module.wechat.pay.enums; + +/** + * @ClassName: RefundsStatusEnum + * @Description: 退款状态 + * @Author: wanggeng + * @Date: 2022/11/10 21:41 + * @Version: 1.0 + */ +public enum RefundStatusEnum { + + SUCCESS("SUCCESS","退款成功"), + CLOSED("CLOSED", "退款关闭"), + PROCESSING("PROCESSING", "退款处理中"), + ABNORMAL("ABNORMAL", "退款异常"); + + private String value; + private String text; + + RefundStatusEnum(String value, String text) { + this.value = value; + this.text = text; + } + + public String getValue() { + return value == null ? "" : value.trim(); + } + + public String getText() { + return text == null ? "" : text.trim(); + } +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/PayNotice.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/Notice.java similarity index 99% rename from module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/PayNotice.java rename to module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/Notice.java index 39221bd0..d1206fdd 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/PayNotice.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/Notice.java @@ -10,7 +10,7 @@ import ink.wgink.annotation.CheckEmptyAnnotation; * @Date: 2021/8/20 8:07 上午 * @Version: 1.0 */ -public class PayNotice { +public class Notice { @CheckEmptyAnnotation(name = "通知ID") private String id; diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPlaceOrder.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPlaceOrder.java index cdfc6c3b..a5ce421f 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPlaceOrder.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPlaceOrder.java @@ -35,6 +35,10 @@ public class PayPlaceOrder { * 订单优惠标记 */ private String goodsTag; + /** + * 电子发票入口开放标识 + */ + private Boolean supportFapiao; @CheckNullAnnotation(name = "订单金额") @CheckBeanAnnotation private Amount amount; @@ -121,6 +125,14 @@ public class PayPlaceOrder { this.goodsTag = goodsTag; } + public Boolean getSupportFapiao() { + return supportFapiao == null ? false : supportFapiao; + } + + public void setSupportFapiao(Boolean supportFapiao) { + this.supportFapiao = supportFapiao; + } + public Amount getAmount() { return amount; } diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPrepay.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPrepay.java index 17d0762d..bc465806 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPrepay.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/jsapi/pojo/PayPrepay.java @@ -9,21 +9,21 @@ package ink.wgink.module.wechat.pay.pojo.v3.jsapi.pojo; */ public class PayPrepay { - private String prepayId; + private String prepay_id; - public String getPrepayId() { - return prepayId == null ? "" : prepayId.trim(); + public String getPrepay_id() { + return prepay_id == null ? "" : prepay_id.trim(); } - public void setPrepayId(String prepayId) { - this.prepayId = prepayId; + public void setPrepay_id(String prepay_id) { + this.prepay_id = prepay_id; } @Override public String toString() { final StringBuilder sb = new StringBuilder("{"); sb.append("\"prepayId\":\"") - .append(prepayId).append('\"'); + .append(prepay_id).append('\"'); sb.append('}'); return sb.toString(); } diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/nav/PayCodeUrl.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/nav/PayCodeUrl.java new file mode 100644 index 00000000..c392169a --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/nav/PayCodeUrl.java @@ -0,0 +1,30 @@ +package ink.wgink.module.wechat.pay.pojo.v3.nav; + +/** + * @ClassName: PayCodeUrl + * @Description: native下单 + * @Author: wanggeng + * @Date: 2022/11/10 17:55 + * @Version: 1.0 + */ +public class PayCodeUrl { + + private String code_url; + + public String getCode_url() { + return code_url == null ? "" : code_url.trim(); + } + + public void setCode_url(String code_url) { + this.code_url = code_url; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"codeUrl\":\"") + .append(code_url).append('\"'); + sb.append('}'); + return sb.toString(); + } +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/AmountFrom.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/AmountFrom.java new file mode 100644 index 00000000..fc538b9e --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/AmountFrom.java @@ -0,0 +1,40 @@ +package ink.wgink.module.wechat.pay.pojo.v3.refund; + +import ink.wgink.module.wechat.pay.enums.RefundAmountFromAccountEnum; + +/** + * @ClassName: AmountFrom + * @Description: 退款出资账户及金额 + * @Author: wanggeng + * @Date: 2022/11/11 18:41 + * @Version: 1.0 + */ +public class AmountFrom { + + /** + * 出资账户类型 + */ + private RefundAmountFromAccountEnum account; + /** + * 出资金额
+ * 对应账户出资金额 + */ + private Integer amount; + + public RefundAmountFromAccountEnum getAccount() { + return account; + } + + public void setAccount(RefundAmountFromAccountEnum account) { + this.account = account; + } + + public Integer getAmount() { + return amount == null ? 0 : amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/GoodsDetail.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/GoodsDetail.java new file mode 100644 index 00000000..622d06c9 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/GoodsDetail.java @@ -0,0 +1,90 @@ +package ink.wgink.module.wechat.pay.pojo.v3.refund; + +/** + * @ClassName: GoodsDetail + * @Description: 退款商品 + * @Author: wanggeng + * @Date: 2022/11/11 18:44 + * @Version: 1.0 + */ +public class GoodsDetail { + + /** + * 商户侧商品编码
+ * 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成 + */ + private String merchant_goods_id; + /** + * 微信支付商品编码
+ * 微信支付定义的统一商品编号(没有可不传) + */ + private String wechatpay_goods_id; + /** + * 商品名称
+ * 商品的实际名称 + */ + private String goods_name; + /** + * 商品单价
+ * 商品单价金额,单位为分 + */ + private Integer unit_price; + /** + * 商品退款金额
+ * 商品退款金额,单位为分 + */ + private Integer refund_amount; + /** + * 商品退货数量 + */ + private Integer refund_quantity; + + public String getMerchant_goods_id() { + return merchant_goods_id == null ? "" : merchant_goods_id.trim(); + } + + public void setMerchant_goods_id(String merchant_goods_id) { + this.merchant_goods_id = merchant_goods_id; + } + + public String getWechatpay_goods_id() { + return wechatpay_goods_id == null ? "" : wechatpay_goods_id.trim(); + } + + public void setWechatpay_goods_id(String wechatpay_goods_id) { + this.wechatpay_goods_id = wechatpay_goods_id; + } + + public String getGoods_name() { + return goods_name == null ? "" : goods_name.trim(); + } + + public void setGoods_name(String goods_name) { + this.goods_name = goods_name; + } + + public Integer getUnit_price() { + return unit_price == null ? 0 : unit_price; + } + + public void setUnit_price(Integer unit_price) { + this.unit_price = unit_price; + } + + public Integer getRefund_amount() { + return refund_amount == null ? 0 : refund_amount; + } + + public void setRefund_amount(Integer refund_amount) { + this.refund_amount = refund_amount; + } + + public Integer getRefund_quantity() { + return refund_quantity == null ? 0 : refund_quantity; + } + + public void setRefund_quantity(Integer refund_quantity) { + this.refund_quantity = refund_quantity; + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/Refund.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/Refund.java new file mode 100644 index 00000000..92c30137 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/Refund.java @@ -0,0 +1,132 @@ +package ink.wgink.module.wechat.pay.pojo.v3.refund; + +import java.util.ArrayList; +import java.util.List; + +/** + * @ClassName: Refund + * @Description: 退款 + * @Author: wanggeng + * @Date: 2022/11/11 18:49 + * @Version: 1.0 + */ +public class Refund { + + private String out_refund_no; + private String reason; + private String notify_url; + private String funds_account; + private Amount amount; + private List goods_detail; + + public String getOut_refund_no() { + return out_refund_no == null ? "" : out_refund_no.trim(); + } + + public void setOut_refund_no(String out_refund_no) { + this.out_refund_no = out_refund_no; + } + + public String getReason() { + return reason == null ? "" : reason.trim(); + } + + public void setReason(String reason) { + this.reason = reason; + } + + public String getNotify_url() { + return notify_url == null ? "" : notify_url.trim(); + } + + public void setNotify_url(String notify_url) { + this.notify_url = notify_url; + } + + public String getFunds_account() { + return funds_account == null ? "" : funds_account.trim(); + } + + public void setFunds_account(String funds_account) { + this.funds_account = funds_account; + } + + public Amount getAmount() { + return amount; + } + + public void setAmount(Amount amount) { + this.amount = amount; + } + + public List getGoods_detail() { + return goods_detail == null ? new ArrayList() : goods_detail; + } + + public void setGoods_detail(List goods_detail) { + this.goods_detail = goods_detail; + } + + public static class Amount { + /** + * 退款金额
+ * 退款金额,单位为分,只能为整数,不能超过原订单支付金额。 + */ + private Integer refund; + /** + * 退款出资账户及金额
+ * 退款需要从指定账户出资时,传递此参数指定出资金额(币种的最小单位,只能为整数)。
+ * 同时指定多个账户出资退款的使用场景需要满足以下条件:
+ * 1、未开通退款支出分离产品功能;
+ * 2、订单属于分账订单,且分账处于待分账或分账中状态。
+ * 参数传递需要满足条件:
+ * 1、基本账户可用余额出资金额与基本账户不可用余额出资金额之和等于退款金额;
+ * 2、账户类型不能重复。
+ * 上述任一条件不满足将返回错误 + */ + private List from; + /** + * 原订单金额
+ * 原支付交易的订单总金额,单位为分,只能为整数。 + */ + private Integer total; + /** + * 退款币种
+ * 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。 + */ + private String currency; + + public Integer getRefund() { + return refund == null ? 0 : refund; + } + + public void setRefund(Integer refund) { + this.refund = refund; + } + + public List getFrom() { + return from == null ? new ArrayList() : from; + } + + public void setFrom(List from) { + this.from = from; + } + + public Integer getTotal() { + return total == null ? 0 : total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public String getCurrency() { + return currency == null ? "" : currency.trim(); + } + + public void setCurrency(String currency) { + this.currency = currency; + } + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundNoticeCiphertext.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundNoticeCiphertext.java new file mode 100644 index 00000000..9196fae5 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundNoticeCiphertext.java @@ -0,0 +1,205 @@ +package ink.wgink.module.wechat.pay.pojo.v3.refund; + +import ink.wgink.module.wechat.pay.enums.RefundStatusEnum; + +/** + * @ClassName: RefundNoticeCiphertext + * @Description: 退款通知密文 + * @Author: wanggeng + * @Date: 2022/11/12 00:00 + * @Version: 1.0 + */ +public class RefundNoticeCiphertext { + + /** + * 直连商户号
+ * 直连商户的商户号,由微信支付生成并下发。 + */ + private String mchid; + /** + * 商户订单号
+ * 返回的商户订单号 + */ + private String out_trade_no; + /** + * 微信支付订单号 + */ + private String transaction_id; + /** + * 商户退款单号 + */ + private String out_refund_no; + /** + * 微信支付退款单号 + */ + private String refund_id; + /** + * 退款状态 + */ + private RefundStatusEnum refund_status; + /** + * 退款成功时间 + */ + private String success_time; + /** + * 退款入账账户 + */ + private String user_received_account; + /** + * 金额信息 + */ + private Amount amount; + + public String getMchid() { + return mchid == null ? "" : mchid.trim(); + } + + public void setMchid(String mchid) { + this.mchid = mchid; + } + + public String getOut_trade_no() { + return out_trade_no == null ? "" : out_trade_no.trim(); + } + + public void setOut_trade_no(String out_trade_no) { + this.out_trade_no = out_trade_no; + } + + public String getTransaction_id() { + return transaction_id == null ? "" : transaction_id.trim(); + } + + public void setTransaction_id(String transaction_id) { + this.transaction_id = transaction_id; + } + + public String getOut_refund_no() { + return out_refund_no == null ? "" : out_refund_no.trim(); + } + + public void setOut_refund_no(String out_refund_no) { + this.out_refund_no = out_refund_no; + } + + public String getRefund_id() { + return refund_id == null ? "" : refund_id.trim(); + } + + public void setRefund_id(String refund_id) { + this.refund_id = refund_id; + } + + public RefundStatusEnum getRefund_status() { + return refund_status; + } + + public void setRefund_status(RefundStatusEnum refund_status) { + this.refund_status = refund_status; + } + + public String getSuccess_time() { + return success_time == null ? "" : success_time.trim(); + } + + public void setSuccess_time(String success_time) { + this.success_time = success_time; + } + + public String getUser_received_account() { + return user_received_account == null ? "" : user_received_account.trim(); + } + + public void setUser_received_account(String user_received_account) { + this.user_received_account = user_received_account; + } + + public Amount getAmount() { + return amount; + } + + public void setAmount(Amount amount) { + this.amount = amount; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"mchid\":\"") + .append(mchid).append('\"'); + sb.append(",\"out_trade_no\":\"") + .append(out_trade_no).append('\"'); + sb.append(",\"transaction_id\":\"") + .append(transaction_id).append('\"'); + sb.append(",\"out_refund_no\":\"") + .append(out_refund_no).append('\"'); + sb.append(",\"refund_id\":\"") + .append(refund_id).append('\"'); + sb.append(",\"refund_status\":") + .append(refund_status); + sb.append(",\"success_time\":\"") + .append(success_time).append('\"'); + sb.append(",\"user_received_account\":\"") + .append(user_received_account).append('\"'); + sb.append(",\"amount\":") + .append(amount); + sb.append('}'); + return sb.toString(); + } + + public static class Amount { + /** + * 订单金额
+ * 订单总金额,单位为分,只能为整数,详见支付金额 + */ + private Integer total; + /** + * 退款金额
+ * 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额,如果有使用券,后台会按比例退。 + */ + private Integer refund; + /** + * 用户支付金额
+ * 用户实际支付金额,单位为分,只能为整数,详见支付金额 + */ + private Integer payer_total; + /** + * 用户退款金额
+ * 退款给用户的金额,不包含所有优惠券金额 + */ + private Integer payer_refund; + + public Integer getTotal() { + return total == null ? 0 : total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public Integer getRefund() { + return refund == null ? 0 : refund; + } + + public void setRefund(Integer refund) { + this.refund = refund; + } + + public Integer getPayer_total() { + return payer_total == null ? 0 : payer_total; + } + + public void setPayer_total(Integer payer_total) { + this.payer_total = payer_total; + } + + public Integer getPayer_refund() { + return payer_refund == null ? 0 : payer_refund; + } + + public void setPayer_refund(Integer payer_refund) { + this.payer_refund = payer_refund; + } + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundResult.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundResult.java new file mode 100644 index 00000000..b862b9f5 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/pojo/v3/refund/RefundResult.java @@ -0,0 +1,392 @@ +package ink.wgink.module.wechat.pay.pojo.v3.refund; + +import ink.wgink.module.wechat.pay.enums.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * @ClassName: Refunds + * @Description: 退款 + * @Author: wanggeng + * @Date: 2022/11/10 21:31 + * @Version: 1.0 + */ +public class RefundResult { + + /** + * 微信支付退款单号 + */ + private String refund_id; + /** + * 商户退款单号
+ * 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 + */ + private String out_refund_no; + /** + * 微信支付交易订单号 + */ + private String transaction_id; + /** + * 商户订单号
+ * 原支付交易对应的商户订单号 + */ + private String out_trade_no; + /** + * 退款渠道 + */ + private RefundChannelEnum channel; + /** + * 退款入账账户
+ * 取当前退款单的退款入账方,有以下几种情况:
+ * 1)退回银行卡:{银行名称}{卡类型}{卡尾号}
+ * 2)退回支付用户零钱:支付用户零钱
+ * 3)退还商户:商户基本账户商户结算银行账户
+ * 4)退回支付用户零钱通:支付用户零钱通 + */ + private String user_received_account; + /** + * 退款成功时间
+ * 退款成功时间,当退款状态为退款成功时有返回。 + */ + private String success_time; + /** + * 退款创建时间
+ * 退款受理时间 + */ + private String create_time; + /** + * 退款状态
+ * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。 + */ + private RefundStatusEnum status; + /** + * 资金账户
+ * 退款所使用资金对应的资金账户类型 + */ + private RefundFundsAccountEnum funds_account; + /** + * 金额信息 + */ + private Amount amount; + /** + * 优惠退款信息 + */ + private PromotionDetail promotion_detail; + + public String getRefund_id() { + return refund_id == null ? "" : refund_id.trim(); + } + + public void setRefund_id(String refund_id) { + this.refund_id = refund_id; + } + + public String getOut_refund_no() { + return out_refund_no == null ? "" : out_refund_no.trim(); + } + + public void setOut_refund_no(String out_refund_no) { + this.out_refund_no = out_refund_no; + } + + public String getTransaction_id() { + return transaction_id == null ? "" : transaction_id.trim(); + } + + public void setTransaction_id(String transaction_id) { + this.transaction_id = transaction_id; + } + + public String getOut_trade_no() { + return out_trade_no == null ? "" : out_trade_no.trim(); + } + + public void setOut_trade_no(String out_trade_no) { + this.out_trade_no = out_trade_no; + } + + public RefundChannelEnum getChannel() { + return channel; + } + + public void setChannel(RefundChannelEnum channel) { + this.channel = channel; + } + + public String getUser_received_account() { + return user_received_account == null ? "" : user_received_account.trim(); + } + + public void setUser_received_account(String user_received_account) { + this.user_received_account = user_received_account; + } + + public String getSuccess_time() { + return success_time == null ? "" : success_time.trim(); + } + + public void setSuccess_time(String success_time) { + this.success_time = success_time; + } + + public String getCreate_time() { + return create_time == null ? "" : create_time.trim(); + } + + public void setCreate_time(String create_time) { + this.create_time = create_time; + } + + public RefundStatusEnum getStatus() { + return status; + } + + public void setStatus(RefundStatusEnum status) { + this.status = status; + } + + public RefundFundsAccountEnum getFunds_account() { + return funds_account; + } + + public void setFunds_account(RefundFundsAccountEnum funds_account) { + this.funds_account = funds_account; + } + + public Amount getAmount() { + return amount; + } + + public void setAmount(Amount amount) { + this.amount = amount; + } + + public PromotionDetail getPromotion_detail() { + return promotion_detail; + } + + public void setPromotion_detail(PromotionDetail promotion_detail) { + this.promotion_detail = promotion_detail; + } + + /** + * 金额信息 + */ + public static class Amount { + /** + * 订单金额
+ * 订单总金额,单位为分 + */ + private Integer total; + /** + * 退款金额
+ * 退款标价金额,单位为分,可以做部分退款 + */ + private Integer refund; + /** + * 退款出资账户及金额 + */ + private List from; + /** + * 用户支付金额
+ * 现金支付金额,单位为分,只能为整数 + */ + private Integer payer_total; + /** + * 用户退款金额
+ * 退款给用户的金额,不包含所有优惠券金额 + */ + private Integer payer_refund; + /** + * 应结退款金额
+ * 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 + */ + private Integer settlement_refund; + /** + * 应结订单金额
+ * 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分 + */ + private Integer settlement_total; + /** + * 优惠退款金额
+ * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分 + */ + private Integer discount_refund; + /** + * 退款币种
+ * 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。 + */ + private String currency; + /** + * 手续费退款金额
+ * 手续费退款金额,单位为分。 + */ + private Integer refund_fee; + + public Integer getTotal() { + return total == null ? 0 : total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public Integer getRefund() { + return refund == null ? 0 : refund; + } + + public void setRefund(Integer refund) { + this.refund = refund; + } + + public List getFrom() { + return from == null ? new ArrayList() : from; + } + + public void setFrom(List from) { + this.from = from; + } + + public Integer getPayer_total() { + return payer_total == null ? 0 : payer_total; + } + + public void setPayer_total(Integer payer_total) { + this.payer_total = payer_total; + } + + public Integer getPayer_refund() { + return payer_refund == null ? 0 : payer_refund; + } + + public void setPayer_refund(Integer payer_refund) { + this.payer_refund = payer_refund; + } + + public Integer getSettlement_refund() { + return settlement_refund == null ? 0 : settlement_refund; + } + + public void setSettlement_refund(Integer settlement_refund) { + this.settlement_refund = settlement_refund; + } + + public Integer getSettlement_total() { + return settlement_total == null ? 0 : settlement_total; + } + + public void setSettlement_total(Integer settlement_total) { + this.settlement_total = settlement_total; + } + + public Integer getDiscount_refund() { + return discount_refund == null ? 0 : discount_refund; + } + + public void setDiscount_refund(Integer discount_refund) { + this.discount_refund = discount_refund; + } + + public String getCurrency() { + return currency == null ? "" : currency.trim(); + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public Integer getRefund_fee() { + return refund_fee == null ? 0 : refund_fee; + } + + public void setRefund_fee(Integer refund_fee) { + this.refund_fee = refund_fee; + } + + } + + /** + * 优惠退款信息 + */ + public static class PromotionDetail { + /** + * 券ID + */ + private String promotion_id; + /** + * 优惠范围 + */ + private RefundPromotionDetailScopeEnum scope; + /** + * 优惠类型 + */ + private RefundPromotionDetailTypeEnum type; + /** + * 优惠券面额
+ * 用户享受优惠的金额(优惠券面额=微信出资金额+商家出资金额+其他出资方金额 ),单位为分 + */ + private Integer amount; + /** + * 优惠退款金额
+ * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为用户支付的现金,说明详见代金券或立减优惠,单位为分 + */ + private Integer refund_amount; + /** + * 商品列表
+ * 优惠商品发生退款时返回商品信息 + */ + private List goods_detail; + + public String getPromotion_id() { + return promotion_id == null ? "" : promotion_id.trim(); + } + + public void setPromotion_id(String promotion_id) { + this.promotion_id = promotion_id; + } + + public RefundPromotionDetailScopeEnum getScope() { + return scope; + } + + public void setScope(RefundPromotionDetailScopeEnum scope) { + this.scope = scope; + } + + public RefundPromotionDetailTypeEnum getType() { + return type; + } + + public void setType(RefundPromotionDetailTypeEnum type) { + this.type = type; + } + + public Integer getAmount() { + return amount == null ? 0 : amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public Integer getRefund_amount() { + return refund_amount == null ? 0 : refund_amount; + } + + public void setRefund_amount(Integer refund_amount) { + this.refund_amount = refund_amount; + } + + public List getGoods_detail() { + return goods_detail == null ? new ArrayList<>() : goods_detail; + } + + public void setGoods_detail(List goods_detail) { + this.goods_detail = goods_detail; + } + + } + + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IOrderPayRemoteService.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IOrderPayRemoteService.java index a16168b7..f6c074b1 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IOrderPayRemoteService.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IOrderPayRemoteService.java @@ -6,6 +6,7 @@ import ink.wgink.annotation.rpc.rest.method.RemoteGetMethod; import ink.wgink.annotation.rpc.rest.method.RemotePostMethod; import ink.wgink.annotation.rpc.rest.params.*; import ink.wgink.module.wechat.pay.pojo.v3.jsapi.pojo.PayPrepay; +import ink.wgink.module.wechat.pay.pojo.v3.nav.PayCodeUrl; import ink.wgink.module.wechat.pay.pojo.v3.order.OrderSearch; /** @@ -33,6 +34,21 @@ public interface IOrderPayRemoteService { @RemoteHeaderParams(IPayRemoteService.HEADER_WECHATPAY_SERIAL) String serialNumber, @RemoteJsonBodyParams JSONObject payPlaceOrder); + /** + * 下单 + * + * @param server + * @param authorization + * @param serialNumber + * @param payPlaceOrder + * @return + */ + @RemotePostMethod("/v3/pay/transactions/native") + PayCodeUrl placeOrderNative(@RemoteServerParams String server, + @RemoteHeaderParams(IPayRemoteService.HEADER_AUTHORIZATION) String authorization, + @RemoteHeaderParams(IPayRemoteService.HEADER_WECHATPAY_SERIAL) String serialNumber, + @RemoteJsonBodyParams JSONObject payPlaceOrder); + /** * 关闭订单 * diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IRefundsRemoteService.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IRefundsRemoteService.java new file mode 100644 index 00000000..b5e29d40 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/remote/v3/IRefundsRemoteService.java @@ -0,0 +1,45 @@ +package ink.wgink.module.wechat.pay.remote.v3; + +import com.alibaba.fastjson2.JSONObject; +import ink.wgink.annotation.rpc.rest.RemoteService; +import ink.wgink.annotation.rpc.rest.method.RemoteGetMethod; +import ink.wgink.annotation.rpc.rest.method.RemotePostMethod; +import ink.wgink.annotation.rpc.rest.params.RemoteHeaderParams; +import ink.wgink.annotation.rpc.rest.params.RemoteJsonBodyParams; +import ink.wgink.annotation.rpc.rest.params.RemotePathParams; +import ink.wgink.annotation.rpc.rest.params.RemoteServerParams; +import ink.wgink.module.wechat.pay.pojo.v3.refund.RefundResult; + +/** + * @ClassName: IRefundsRemoteService + * @Description: 退款 + * @Author: wanggeng + * @Date: 2022/11/10 21:30 + * @Version: 1.0 + */ +@RemoteService +public interface IRefundsRemoteService { + + /** + * 申请退款 + * + * @param server + * @param authorization + * @param serialNumber + * @param body + * @return + */ + @RemotePostMethod("/v3/refund/domestic/refunds") + RefundResult refund(@RemoteServerParams String server, + @RemoteHeaderParams(IPayRemoteService.HEADER_AUTHORIZATION) String authorization, + @RemoteHeaderParams(IPayRemoteService.HEADER_WECHATPAY_SERIAL) String serialNumber, + @RemoteJsonBodyParams JSONObject body); + + + @RemoteGetMethod("/v3/refund/domestic/refunds/{outRefundNo}") + RefundResult get(@RemoteServerParams String server, + @RemoteHeaderParams(IPayRemoteService.HEADER_AUTHORIZATION) String authorization, + @RemoteHeaderParams(IPayRemoteService.HEADER_WECHATPAY_SERIAL) String serialNumber, + @RemotePathParams("out_refund_no") String outRefundNo); + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/DefaultPayNoticeService.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/DefaultPayNoticeServiceImpl.java similarity index 86% rename from module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/DefaultPayNoticeService.java rename to module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/DefaultPayNoticeServiceImpl.java index 68c1361b..5573e70c 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/DefaultPayNoticeService.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/DefaultPayNoticeServiceImpl.java @@ -13,7 +13,7 @@ import org.springframework.stereotype.Service; * @Version: 1.0 */ @Service -public class DefaultPayNoticeService extends DefaultBaseService implements IPayNoticeService { +public class DefaultPayNoticeServiceImpl extends DefaultBaseService implements IPayNoticeService { @Override public void handle(PayNoticeCiphertext payNoticeCiphertext) throws Exception { diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/PayServiceImpl.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/PayServiceImpl.java index fd59bfe7..6b02c8de 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/PayServiceImpl.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/impl/PayServiceImpl.java @@ -2,28 +2,20 @@ package ink.wgink.module.wechat.pay.service.v3.impl; import com.alibaba.fastjson.JSONObject; import ink.wgink.common.base.DefaultBaseService; -import ink.wgink.exceptions.ParamsException; +import ink.wgink.exceptions.base.SystemException; import ink.wgink.module.wechat.pay.enums.PayErrorMsgEnum; -import ink.wgink.module.wechat.pay.manager.v3.PayManager; -import ink.wgink.module.wechat.pay.pojo.v3.PayErrorResponse; -import ink.wgink.module.wechat.pay.pojo.v3.PayNotice; +import ink.wgink.module.wechat.pay.pojo.v3.Notice; import ink.wgink.module.wechat.pay.pojo.v3.PayNoticeCiphertext; import ink.wgink.module.wechat.pay.service.v3.IPayNoticeService; import ink.wgink.module.wechat.pay.service.v3.IPayService; -import ink.wgink.module.wechat.pay.utils.v3.PayAesUtil; -import ink.wgink.module.wechat.pay.utils.v3.PayVerifyUtil; +import ink.wgink.module.wechat.pay.utils.v3.AsyncNoticeResultUtil; import ink.wgink.properties.wechat.pay.v3.PayProperties; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.BufferedReader; import java.io.IOException; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; /** * @ClassName: IPayServiceImpl @@ -42,79 +34,22 @@ public class PayServiceImpl extends DefaultBaseService implements IPayService { @Override public void notice(HttpServletRequest request, HttpServletResponse response, String nonce, String timestamp, String wechatpaySignature) throws IOException { - String payNoticeJsonString = getBodyString(request); - if (StringUtils.isBlank(payNoticeJsonString)) { - throw new ParamsException("请求body为空"); - } - LOG.debug("payNoticeJsonString: {}", payNoticeJsonString); - PayNotice payNotice = JSONObject.parseObject(payNoticeJsonString, PayNotice.class); try { - LOG.debug("wechatpaySignature: {}", wechatpaySignature); - LOG.debug("验证签名"); - boolean checkVerify = PayVerifyUtil.verifySignature(wechatpaySignature, payNoticeJsonString, nonce, timestamp, PayManager.getInstance().getPlatformCertificate()); - if (!checkVerify) { - errorResult(response, PayErrorMsgEnum.SIGN_ERROR_401); - return; - } - - LOG.debug("解密内容"); - PayNotice.Resource resource = payNotice.getResource(); - PayAesUtil aesUtil = new PayAesUtil(payProperties.getApiV3Secretkey().getBytes(StandardCharsets.UTF_8)); - String payNoticeCiphertextJsonString = aesUtil.decryptToString(resource.getAssociated_data().getBytes(StandardCharsets.UTF_8), resource.getNonce().getBytes(StandardCharsets.UTF_8), resource.getCiphertext()); - PayNoticeCiphertext payNoticeCiphertext = JSONObject.parseObject(payNoticeCiphertextJsonString, PayNoticeCiphertext.class); + Notice notice = AsyncNoticeResultUtil.getNotice(request, nonce, timestamp, wechatpaySignature); + String noticeCiphertextJsonString = AsyncNoticeResultUtil.getNoticeCiphertext(notice, payProperties.getApiV3Secretkey()); + PayNoticeCiphertext payNoticeCiphertext = JSONObject.parseObject(noticeCiphertextJsonString, PayNoticeCiphertext.class); payNoticeService.handle(payNoticeCiphertext); } catch (Exception e) { LOG.error(e.getMessage(), e); - errorResult(response, PayErrorMsgEnum.ERROR_500); + if (e instanceof SystemException) { + AsyncNoticeResultUtil.errorResult(response, PayErrorMsgEnum.SIGN_ERROR_401); + return; + } + AsyncNoticeResultUtil.errorResult(response, PayErrorMsgEnum.ERROR_500); return; } - successResult(response); + AsyncNoticeResultUtil.successResult(response); } - /** - * 错误结果 - * - * @param payErrorMsgEnum - */ - private void errorResult(HttpServletResponse response, PayErrorMsgEnum payErrorMsgEnum) { - PayErrorResponse payErrorResponse = new PayErrorResponse(); - payErrorResponse.setCode(payErrorMsgEnum.getErrorCode()); - payErrorResponse.setMessage(payErrorMsgEnum.getSummary()); - response.setContentType("application/json; charset=UTF-8"); - response.setStatus(payErrorMsgEnum.getCode()); - try (PrintWriter writer = response.getWriter()) { - writer.println(JSONObject.toJSONString(payErrorResponse)); - writer.flush(); - } catch (IOException e) { - LOG.error(e.getMessage(), e); - } - } - - /** - * 成功结果 - */ - private void successResult(HttpServletResponse response) { - response.setContentType("application/json; charset=UTF-8"); - response.setStatus(HttpStatus.OK.value()); - JSONObject successJsonObject = new JSONObject(); - successJsonObject.put("code", "SUCCESS"); - successJsonObject.put("message", "成功"); - try (PrintWriter writer = response.getWriter()) { - writer.println(successJsonObject.toJSONString()); - writer.flush(); - } catch (IOException e) { - LOG.error(e.getMessage(), e); - } - } - - private String getBodyString(HttpServletRequest request) throws IOException { - BufferedReader reader = request.getReader(); - StringBuilder bodyString = new StringBuilder(); - for (String line; (line = reader.readLine()) != null; ) { - bodyString.append(line); - } - return bodyString.toString(); - } - } diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/jsapi/impl/JsapiServiceImpl.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/jsapi/impl/JsapiServiceImpl.java index 117630fd..415b1866 100644 --- a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/jsapi/impl/JsapiServiceImpl.java +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/jsapi/impl/JsapiServiceImpl.java @@ -1,6 +1,5 @@ package ink.wgink.module.wechat.pay.service.v3.jsapi.impl; -import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import ink.wgink.module.wechat.pay.enums.PayAuthorizationTypeEnum; import ink.wgink.module.wechat.pay.pojo.v3.PaySign; @@ -9,6 +8,7 @@ import ink.wgink.module.wechat.pay.pojo.v3.jsapi.pojo.PayPrepay; import ink.wgink.module.wechat.pay.remote.v3.IOrderPayRemoteService; import ink.wgink.module.wechat.pay.service.BasePayService; import ink.wgink.module.wechat.pay.service.v3.jsapi.IJsapiService; +import ink.wgink.module.wechat.pay.utils.v3.PayOrderUtil; import ink.wgink.module.wechat.pay.utils.v3.PayPrivateKeyUtil; import ink.wgink.properties.wechat.miniapp.MiniappProperties; import ink.wgink.util.BeanPropertyCheckUtil; @@ -18,8 +18,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestMethod; -import java.util.List; - /** * @ClassName: JsapiServiceImpl * @Description: Jsapi业务 @@ -47,7 +45,7 @@ public class JsapiServiceImpl extends BasePayService implements IJsapiService { String serialNumber = getSerialNumber(); LOG.debug("调用微信支付,发起预支付"); - JSONObject body = getPlaceOrderJsonObject(payPlaceOrder); + JSONObject body = PayOrderUtil.getPlaceOrderJsonObject(payPlaceOrder); String authorization = getAuthorization(RequestMethod.POST, urlSuffix, serialNumber, payProperties, body.toString(), PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048); LOG.debug("请求获得结果"); @@ -59,47 +57,13 @@ public class JsapiServiceImpl extends BasePayService implements IJsapiService { PaySign paySign = new PaySign(); paySign.setAppId(miniappProperties.getAppKey()); - paySign.setPrepayId(payPrepay.getPrepayId()); + paySign.setPrepayId(payPrepay.getPrepay_id()); paySign.setNonceStr(nonceStr); paySign.setTimestamp(String.valueOf(timestamp)); - paySign.setPaySign(getPaySign(timestamp, nonceStr, payPrepay.getPrepayId())); + paySign.setPaySign(getPaySign(timestamp, nonceStr, payPrepay.getPrepay_id())); return paySign; } - /** - * 没问题会删除` - public PaySign placeOrder(PayPlaceOrder payPlaceOrder) throws Exception { - payPlaceOrder.setAppid(miniappProperties.getAppKey()); - payPlaceOrder.setMchid(payProperties.getMchid()); - BeanPropertyCheckUtil.checkField(payPlaceOrder); - String urlSuffix = "/v3/pay/transactions/jsapi"; - String url = getPayBaseUrl() + urlSuffix; - - LOG.debug("获得证书序列号"); - String serialNumber = getSerialNumber(); - - LOG.debug("调用微信支付,发起预支付"); - PlaceOrderPayRequestImpl placeOrderPayRequest = new PlaceOrderPayRequestImpl(); - JSONObject body = getPlaceOrderJsonObject(payPlaceOrder); - String authorization = getAuthorization(RequestMethod.POST, urlSuffix, serialNumber, payProperties, body.toString(), PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048); - - LOG.debug("请求获得结果"); - PayPrepay payPrepay = placeOrderPayRequest.post(url, authorization, serialNumber, body); - - LOG.debug("生成返回结果"); - long timestamp = System.currentTimeMillis() / 1000; - String nonceStr = WStringUtil.randomSubStr(UUIDUtil.get32UUID(), 10).toUpperCase(); - - PaySign paySign = new PaySign(); - paySign.setAppId(miniappProperties.getAppKey()); - paySign.setPrepayId(payPrepay.getPrepayId()); - paySign.setNonceStr(nonceStr); - paySign.setTimestamp(String.valueOf(timestamp)); - paySign.setPaySign(getPaySign(timestamp, nonceStr, payPrepay.getPrepayId())); - return paySign; - } - **/ - /** * 获得小程序签名 * @@ -110,147 +74,14 @@ public class JsapiServiceImpl extends BasePayService implements IJsapiService { * @throws Exception */ private String getPaySign(long timestamp, String nonceStr, String prepayId) throws Exception { - StringBuilder signBS = new StringBuilder(); - signBS.append(miniappProperties.getAppKey()).append("\n") + StringBuilder signSB = new StringBuilder(); + signSB.append(miniappProperties.getAppKey()).append("\n") .append(timestamp).append("\n") .append(nonceStr).append("\n") .append("prepay_id=").append(prepayId).append("\n"); String key = PayPrivateKeyUtil.getPrivateKey(payProperties.getKeyFilePath()); // 生成签名 - return PayPrivateKeyUtil.encryptByPrivateKey(signBS.toString(), key); - } - - /** - * 获取 - * @param payPlaceOrder - * @return - */ - private JSONObject getPlaceOrderJsonObject(PayPlaceOrder payPlaceOrder) { - JSONObject bodyObject = new JSONObject(); - bodyObject.put("appid", payPlaceOrder.getAppid()); - bodyObject.put("mchid", payPlaceOrder.getMchid()); - bodyObject.put("description", payPlaceOrder.getDescription()); - bodyObject.put("out_trade_no", payPlaceOrder.getOutTradeNo()); - bodyObject.put("time_expire", payPlaceOrder.getTimeExpire()); - bodyObject.put("attach", payPlaceOrder.getAttach()); - bodyObject.put("notify_url", payPlaceOrder.getNotifyUrl()); - bodyObject.put("goods_tag", payPlaceOrder.getGoodsTag()); - bodyObject.put("amount", getAmountJsonObject(payPlaceOrder.getAmount())); - bodyObject.put("payer", getPayer(payPlaceOrder.getPayer())); - if (payPlaceOrder.getDetail() != null) { - bodyObject.put("detail", getDetail(payPlaceOrder.getDetail())); - } - if (payPlaceOrder.getSceneInfo() != null) { - bodyObject.put("scene_info", getSceneInfo(payPlaceOrder.getSceneInfo())); - } - if (payPlaceOrder.getSettleInfo() != null) { - bodyObject.put("settle_info", getSettleInfo(payPlaceOrder.getSettleInfo())); - } - return bodyObject; - } - - /** - * 订单金额 - * - * @param amount - * @return - */ - private JSONObject getAmountJsonObject(PayPlaceOrder.Amount amount) { - JSONObject amountJsonObject = new JSONObject(); - amountJsonObject.put("total", amount.getTotal()); - amountJsonObject.put("currency", amount.getCurrency()); - return amountJsonObject; - } - - /** - * 支付者 - * - * @param payer - * @return - */ - private JSONObject getPayer(PayPlaceOrder.Payer payer) { - JSONObject payerJsonObject = new JSONObject(); - payerJsonObject.put("openid", payer.getOpenid()); - return payerJsonObject; - } - - /** - * 优惠功能 - * - * @param detail - * @return - */ - private JSONObject getDetail(PayPlaceOrder.Detail detail) { - JSONObject detailJsonObject = new JSONObject(); - detailJsonObject.put("cost_price", detail.getCostPrice()); - detailJsonObject.put("invoice_id", detail.getInvoiceId()); - if (detail.getGoodsDetail() != null || !detail.getGoodsDetail().isEmpty()) { - detailJsonObject.put("goods_detail", listGoodsDetail(detail.getGoodsDetail())); - } - return detailJsonObject; - } - - /** - * 商品列表 - * - * @param goodsList - * @return - */ - private JSONArray listGoodsDetail(List goodsList) { - JSONArray goodsDetailJsonArray = new JSONArray(); - for (PayPlaceOrder.Detail.Goods goods : goodsList) { - JSONObject goodsJsonObject = new JSONObject(); - goodsJsonObject.put("merchant_goods_id", goods.getMerchantGoodsId()); - goodsJsonObject.put("wechatpay_goods_id", goods.getWechatpayGoodsId()); - goodsJsonObject.put("goods_name", goods.getGoodsName()); - goodsJsonObject.put("quantity", goods.getQuantity()); - goodsJsonObject.put("unit_price", goods.getUnitPrice()); - goodsDetailJsonArray.add(goodsJsonObject); - } - return goodsDetailJsonArray; - } - - /** - * 场景信息 - * - * @param sceneInfo - * @return - */ - private JSONObject getSceneInfo(PayPlaceOrder.SceneInfo sceneInfo) { - JSONObject sceneInfoJsonObject = new JSONObject(); - sceneInfoJsonObject.put("payer_client_ip", sceneInfo.getPayerClientIp()); - sceneInfoJsonObject.put("device_id", sceneInfo.getDeviceId()); - if (sceneInfo.getStoreInfo() != null) { - sceneInfoJsonObject.put("store_info", getStoreInfo(sceneInfo.getStoreInfo())); - } - return sceneInfoJsonObject; - } - - /** - * 商户门店信息 - * - * @param storeInfo - * @return - */ - private JSONObject getStoreInfo(PayPlaceOrder.SceneInfo.StoreInfo storeInfo) { - JSONObject storeInfoJsonObject = new JSONObject(); - storeInfoJsonObject.put("id", storeInfo.getId()); - storeInfoJsonObject.put("name", storeInfo.getName()); - storeInfoJsonObject.put("area_code", storeInfo.getAreaCode()); - storeInfoJsonObject.put("address", storeInfo.getAddress()); - return storeInfoJsonObject; - } - - /** - * 是否指定分账 - * - * @param settleInfo - * @return - */ - private JSONObject getSettleInfo(PayPlaceOrder.SettleInfo settleInfo) { - JSONObject settleJsonObject = new JSONObject(); - settleJsonObject.put("profit_sharing", settleInfo.getProfitSharing()); - return settleJsonObject; + return PayPrivateKeyUtil.encryptByPrivateKey(signSB.toString(), key); } } diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/INativeService.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/INativeService.java new file mode 100644 index 00000000..ac272f51 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/INativeService.java @@ -0,0 +1,25 @@ +package ink.wgink.module.wechat.pay.service.v3.nav; + +import ink.wgink.module.wechat.pay.pojo.v3.jsapi.pojo.PayPlaceOrder; + +/** + * @ClassName: INativeService + * @Description: native支付 + * @Author: wanggeng + * @Date: 2022/11/10 17:15 + * @Version: 1.0 + */ +public interface INativeService { + + /** + * 下单 + * + * @param payPlaceOrder + * @return 此URL用于生成支付二维码,然后提供给用户扫码支付。
+ * 注意:code_url并非固定值,使用时按照URL格式转成二维码即可。
+ * 示例值:weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00 + * @throws Exception + */ + String placeOrder(PayPlaceOrder payPlaceOrder) throws Exception; + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/impl/NativeServiceImpl.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/impl/NativeServiceImpl.java new file mode 100644 index 00000000..01a75ce2 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/nav/impl/NativeServiceImpl.java @@ -0,0 +1,52 @@ +package ink.wgink.module.wechat.pay.service.v3.nav.impl; + +import com.alibaba.fastjson.JSONObject; +import ink.wgink.module.wechat.pay.enums.PayAuthorizationTypeEnum; +import ink.wgink.module.wechat.pay.pojo.v3.jsapi.pojo.PayPlaceOrder; +import ink.wgink.module.wechat.pay.pojo.v3.nav.PayCodeUrl; +import ink.wgink.module.wechat.pay.remote.v3.IOrderPayRemoteService; +import ink.wgink.module.wechat.pay.service.BasePayService; +import ink.wgink.module.wechat.pay.service.v3.nav.INativeService; +import ink.wgink.module.wechat.pay.utils.v3.PayOrderUtil; +import ink.wgink.properties.wechat.miniapp.MiniappProperties; +import ink.wgink.util.BeanPropertyCheckUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * @ClassName: NativeServiceImpl + * @Description: native支付 + * @Author: wanggeng + * @Date: 2022/11/10 17:53 + * @Version: 1.0 + */ +@Service +public class NativeServiceImpl extends BasePayService implements INativeService { + + @Autowired + private MiniappProperties miniappProperties; + @Autowired + private IOrderPayRemoteService orderPayRemoteService; + + @Override + public String placeOrder(PayPlaceOrder payPlaceOrder) throws Exception { + payPlaceOrder.setAppid(miniappProperties.getAppKey()); + payPlaceOrder.setMchid(payProperties.getMchid()); + BeanPropertyCheckUtil.checkField(payPlaceOrder); + String urlSuffix = "/v3/pay/transactions/native"; + + LOG.debug("获得证书序列号"); + String serialNumber = getSerialNumber(); + + LOG.debug("调用微信支付,发起预支付"); + JSONObject body = PayOrderUtil.getPlaceOrderJsonObject(payPlaceOrder); + String authorization = getAuthorization(RequestMethod.POST, urlSuffix, serialNumber, payProperties, body.toString(), PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048); + + LOG.debug("请求获得结果"); + PayCodeUrl payCodeUrl = orderPayRemoteService.placeOrderNative(getPayBaseUrl(), authorization, serialNumber, body); + + return payCodeUrl.getCode_url(); + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundNoticeService.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundNoticeService.java new file mode 100644 index 00000000..902e9fd4 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundNoticeService.java @@ -0,0 +1,21 @@ +package ink.wgink.module.wechat.pay.service.v3.refund; + +import ink.wgink.module.wechat.pay.pojo.v3.refund.RefundNoticeCiphertext; + +/** + * @ClassName: IRefundNoticeService + * @Description: 退款通知业务,完成退款后,微信支付发起异步通知,实现该类完成业务处理 + * @Author: wanggeng + * @Date: 2022/11/11 23:47 + * @Version: 1.0 + */ +public interface IRefundNoticeService { + + /** + * 退款通知结果,该方法在所有校验通过,加密文本解密之后调用。 + * + * @param refundNoticeCiphertext + */ + void handle(RefundNoticeCiphertext refundNoticeCiphertext); + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundService.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundService.java new file mode 100644 index 00000000..e95aa1b2 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/IRefundService.java @@ -0,0 +1,56 @@ +package ink.wgink.module.wechat.pay.service.v3.refund; + +import ink.wgink.module.wechat.pay.pojo.v3.refund.Refund; +import ink.wgink.module.wechat.pay.pojo.v3.refund.RefundResult; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @ClassName: IRefundsService + * @Description: 退款 + * @Author: wanggeng + * @Date: 2022/11/10 21:29 + * @Version: 1.0 + */ +public interface IRefundService { + + /** + * 退款 + * + * @param transactionId + * @param refund + * @return + */ + RefundResult refund(String transactionId, Refund refund) throws Exception; + + /** + * 业务订单退款 + * + * @param businessOrderId + * @param refund + * @return + */ + RefundResult refundByBusinessOrder(String businessOrderId, Refund refund) throws Exception; + + /** + * 详情 + * + * @param businessRefundId + * @return + * @throws Exception + */ + RefundResult getByBusinessRefundId(String businessRefundId) throws Exception; + + /** + * 通知 + * + * @param request + * @param response + * @param nonce + * @param timestamp + * @param wechatpaySignature + */ + void notice(HttpServletRequest request, HttpServletResponse response, String nonce, String timestamp, String wechatpaySignature) throws IOException; +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/DefaultRefundNoticeServiceImpl.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/DefaultRefundNoticeServiceImpl.java new file mode 100644 index 00000000..ed53cc57 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/DefaultRefundNoticeServiceImpl.java @@ -0,0 +1,23 @@ +package ink.wgink.module.wechat.pay.service.v3.refund.impl; + +import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.module.wechat.pay.pojo.v3.refund.RefundNoticeCiphertext; +import ink.wgink.module.wechat.pay.service.v3.refund.IRefundNoticeService; +import org.springframework.stereotype.Service; + +/** + * @ClassName: DefaultRefundNoticeServiceImpl + * @Description: 默认退款通知 + * @Author: wanggeng + * @Date: 2022/11/12 00:36 + * @Version: 1.0 + */ +@Service +public class DefaultRefundNoticeServiceImpl extends DefaultBaseService implements IRefundNoticeService { + + @Override + public void handle(RefundNoticeCiphertext refundNoticeCiphertext) { + LOG.debug("Pay notice: {}", refundNoticeCiphertext.toString()); + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/RefundServiceImpl.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/RefundServiceImpl.java new file mode 100644 index 00000000..8fe42f03 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/service/v3/refund/impl/RefundServiceImpl.java @@ -0,0 +1,113 @@ +package ink.wgink.module.wechat.pay.service.v3.refund.impl; + +import com.alibaba.fastjson2.JSONObject; +import ink.wgink.exceptions.ParamsException; +import ink.wgink.exceptions.base.SystemException; +import ink.wgink.module.wechat.pay.enums.PayAuthorizationTypeEnum; +import ink.wgink.module.wechat.pay.enums.PayErrorMsgEnum; +import ink.wgink.module.wechat.pay.pojo.v3.Notice; +import ink.wgink.module.wechat.pay.pojo.v3.refund.Refund; +import ink.wgink.module.wechat.pay.pojo.v3.refund.RefundNoticeCiphertext; +import ink.wgink.module.wechat.pay.pojo.v3.refund.RefundResult; +import ink.wgink.module.wechat.pay.remote.v3.IRefundsRemoteService; +import ink.wgink.module.wechat.pay.service.BasePayService; +import ink.wgink.module.wechat.pay.service.v3.refund.IRefundNoticeService; +import ink.wgink.module.wechat.pay.service.v3.refund.IRefundService; +import ink.wgink.module.wechat.pay.utils.v3.AsyncNoticeResultUtil; +import ink.wgink.properties.wechat.pay.v3.PayProperties; +import ink.wgink.util.map.HashMapUtil; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; + +/** + * @ClassName: RefundsServiceImpl + * @Description: 退款 + * @Author: wanggeng + * @Date: 2022/11/10 21:29 + * @Version: 1.0 + */ +@Service +public class RefundServiceImpl extends BasePayService implements IRefundService { + @Autowired + private PayProperties payProperties; + @Autowired + private IRefundsRemoteService refundsRemoteService; + @Autowired + private IRefundNoticeService refundNoticeService; + + @Override + public RefundResult refund(String transactionId, Refund refund) throws Exception { + if (StringUtils.isBlank(transactionId)) { + throw new ParamsException("微信业务订单ID不能为空"); + } + if (refund == null) { + throw new ParamsException("退款内容不能为空"); + } + Map refundMap = HashMapUtil.beanToMap(refund); + refundMap.put("transaction_id", transactionId); + return refund(refundMap); + } + + @Override + public RefundResult refundByBusinessOrder(String businessOrderId, Refund refund) throws Exception { + if (StringUtils.isBlank(businessOrderId)) { + throw new ParamsException("业务订单ID不能为空"); + } + if (refund == null) { + throw new ParamsException("退款内容不能为空"); + } + Map refundMap = HashMapUtil.beanToMap(refund); + refundMap.put("out_trade_no", businessOrderId); + return refund(refundMap); + } + + @Override + public RefundResult getByBusinessRefundId(String businessRefundId) throws Exception { + String urlSuffix = "/v3/refund/domestic/refunds/" + businessRefundId; + String serialNumber = getSerialNumber(); + String authorization = getAuthorization(RequestMethod.GET, urlSuffix, serialNumber, payProperties, null, PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048); + return refundsRemoteService.get(getPayBaseUrl(), authorization, serialNumber, businessRefundId); + } + + @Override + public void notice(HttpServletRequest request, HttpServletResponse response, String nonce, String timestamp, String wechatpaySignature) throws IOException { + try { + Notice notice = AsyncNoticeResultUtil.getNotice(request, nonce, timestamp, wechatpaySignature); + String noticeCiphertextJsonString = AsyncNoticeResultUtil.getNoticeCiphertext(notice, payProperties.getApiV3Secretkey()); + RefundNoticeCiphertext refundNoticeCiphertext = JSONObject.parseObject(noticeCiphertextJsonString, RefundNoticeCiphertext.class); + refundNoticeService.handle(refundNoticeCiphertext); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + if (e instanceof SystemException) { + AsyncNoticeResultUtil.errorResult(response, PayErrorMsgEnum.SIGN_ERROR_401); + return; + } + AsyncNoticeResultUtil.errorResult(response, PayErrorMsgEnum.ERROR_500); + return; + } + AsyncNoticeResultUtil.successResult(response); + } + + /** + * 退款 + * + * @param refundMap + * @return + * @throws Exception + */ + private RefundResult refund(Map refundMap) throws Exception { + String urlSuffix = "/v3/refund/domestic/refunds"; + String serialNumber = getSerialNumber(); + JSONObject body = new JSONObject(refundMap); + String authorization = getAuthorization(RequestMethod.POST, urlSuffix, serialNumber, payProperties, body.toString(), PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048); + return refundsRemoteService.refund(getPayBaseUrl(), authorization, serialNumber, body); + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/AsyncNoticeResultUtil.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/AsyncNoticeResultUtil.java new file mode 100644 index 00000000..5727da14 --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/AsyncNoticeResultUtil.java @@ -0,0 +1,118 @@ +package ink.wgink.module.wechat.pay.utils.v3; + +import com.alibaba.fastjson.JSONObject; +import ink.wgink.exceptions.ParamsException; +import ink.wgink.exceptions.base.SystemException; +import ink.wgink.module.wechat.pay.enums.PayErrorMsgEnum; +import ink.wgink.module.wechat.pay.manager.v3.PayManager; +import ink.wgink.module.wechat.pay.pojo.v3.Notice; +import ink.wgink.module.wechat.pay.pojo.v3.PayErrorResponse; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; + +/** + * @ClassName: AsyncNoticeResultUtil + * @Description: 异步通知返回工具 + * @Author: wanggeng + * @Date: 2022/11/11 23:27 + * @Version: 1.0 + */ +public class AsyncNoticeResultUtil { + + private static final Logger LOG = LoggerFactory.getLogger(AsyncNoticeResultUtil.class); + + /** + * 获取通知内容 + * + * @param request + * @return + * @throws IOException + */ + public static Notice getNotice(HttpServletRequest request, String nonce, String timestamp, String wechatpaySignature) throws Exception { + String payNoticeJsonString = AsyncNoticeResultUtil.getBodyString(request); + if (StringUtils.isBlank(payNoticeJsonString)) { + throw new ParamsException("请求body为空"); + } + LOG.debug("NoticeJsonString: {}", payNoticeJsonString); + Notice notice = JSONObject.parseObject(payNoticeJsonString, Notice.class); + LOG.debug("wechatpaySignature: {}", wechatpaySignature); + LOG.debug("验证签名"); + boolean checkVerify = PayVerifyUtil.verifySignature(wechatpaySignature, payNoticeJsonString, nonce, timestamp, PayManager.getInstance().getPlatformCertificate()); + if (!checkVerify) { + throw new SystemException("签名验证失败"); + } + return notice; + } + + /** + * 获取通知密文 + * + * @param notice + * @param apiV3Secretkey + * @return + * @throws GeneralSecurityException + * @throws IOException + */ + public static String getNoticeCiphertext(Notice notice, String apiV3Secretkey) throws GeneralSecurityException, IOException { + LOG.debug("解密内容"); + Notice.Resource resource = notice.getResource(); + PayAesUtil aesUtil = new PayAesUtil(apiV3Secretkey.getBytes(StandardCharsets.UTF_8)); + return aesUtil.decryptToString(resource.getAssociated_data().getBytes(StandardCharsets.UTF_8), resource.getNonce().getBytes(StandardCharsets.UTF_8), resource.getCiphertext()); + } + + /** + * 错误结果 + * + * @param payErrorMsgEnum + */ + public static void errorResult(HttpServletResponse response, PayErrorMsgEnum payErrorMsgEnum) { + PayErrorResponse payErrorResponse = new PayErrorResponse(); + payErrorResponse.setCode(payErrorMsgEnum.getErrorCode()); + payErrorResponse.setMessage(payErrorMsgEnum.getSummary()); + response.setContentType("application/json; charset=UTF-8"); + response.setStatus(payErrorMsgEnum.getCode()); + try (PrintWriter writer = response.getWriter()) { + writer.println(JSONObject.toJSONString(payErrorResponse)); + writer.flush(); + } catch (IOException e) { + LOG.error(e.getMessage(), e); + } + } + + /** + * 成功结果 + */ + public static void successResult(HttpServletResponse response) { + response.setContentType("application/json; charset=UTF-8"); + response.setStatus(HttpStatus.OK.value()); + JSONObject successJsonObject = new JSONObject(); + successJsonObject.put("code", "SUCCESS"); + successJsonObject.put("message", "成功"); + try (PrintWriter writer = response.getWriter()) { + writer.println(successJsonObject.toJSONString()); + writer.flush(); + } catch (IOException e) { + LOG.error(e.getMessage(), e); + } + } + + public static String getBodyString(HttpServletRequest request) throws IOException { + BufferedReader reader = request.getReader(); + StringBuilder bodyString = new StringBuilder(); + for (String line; (line = reader.readLine()) != null; ) { + bodyString.append(line); + } + return bodyString.toString(); + } + +} diff --git a/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/PayOrderUtil.java b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/PayOrderUtil.java new file mode 100644 index 00000000..94401c7a --- /dev/null +++ b/module-wechat-pay/src/main/java/ink/wgink/module/wechat/pay/utils/v3/PayOrderUtil.java @@ -0,0 +1,153 @@ +package ink.wgink.module.wechat.pay.utils.v3; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import ink.wgink.module.wechat.pay.pojo.v3.jsapi.pojo.PayPlaceOrder; + +import java.util.List; + +/** + * @ClassName: PayOrderUtil + * @Description: 订单工具类 + * @Author: wanggeng + * @Date: 2022/11/10 18:33 + * @Version: 1.0 + */ +public class PayOrderUtil { + + /** + * 获取 + * + * @param payPlaceOrder + * @return + */ + public static JSONObject getPlaceOrderJsonObject(PayPlaceOrder payPlaceOrder) { + JSONObject bodyObject = new JSONObject(); + bodyObject.put("appid", payPlaceOrder.getAppid()); + bodyObject.put("mchid", payPlaceOrder.getMchid()); + bodyObject.put("description", payPlaceOrder.getDescription()); + bodyObject.put("out_trade_no", payPlaceOrder.getOutTradeNo()); + bodyObject.put("time_expire", payPlaceOrder.getTimeExpire()); + bodyObject.put("attach", payPlaceOrder.getAttach()); + bodyObject.put("notify_url", payPlaceOrder.getNotifyUrl()); + bodyObject.put("goods_tag", payPlaceOrder.getGoodsTag()); + bodyObject.put("support_fapiao", payPlaceOrder.getSupportFapiao()); + bodyObject.put("amount", getAmountJsonObject(payPlaceOrder.getAmount())); + bodyObject.put("payer", getPayer(payPlaceOrder.getPayer())); + if (payPlaceOrder.getDetail() != null) { + bodyObject.put("detail", getDetail(payPlaceOrder.getDetail())); + } + if (payPlaceOrder.getSceneInfo() != null) { + bodyObject.put("scene_info", getSceneInfo(payPlaceOrder.getSceneInfo())); + } + if (payPlaceOrder.getSettleInfo() != null) { + bodyObject.put("settle_info", getSettleInfo(payPlaceOrder.getSettleInfo())); + } + return bodyObject; + } + + /** + * 订单金额 + * + * @param amount + * @return + */ + private static JSONObject getAmountJsonObject(PayPlaceOrder.Amount amount) { + JSONObject amountJsonObject = new JSONObject(); + amountJsonObject.put("total", amount.getTotal()); + amountJsonObject.put("currency", amount.getCurrency()); + return amountJsonObject; + } + + /** + * 支付者 + * + * @param payer + * @return + */ + private static JSONObject getPayer(PayPlaceOrder.Payer payer) { + JSONObject payerJsonObject = new JSONObject(); + payerJsonObject.put("openid", payer.getOpenid()); + return payerJsonObject; + } + + /** + * 优惠功能 + * + * @param detail + * @return + */ + private static JSONObject getDetail(PayPlaceOrder.Detail detail) { + JSONObject detailJsonObject = new JSONObject(); + detailJsonObject.put("cost_price", detail.getCostPrice()); + detailJsonObject.put("invoice_id", detail.getInvoiceId()); + if (detail.getGoodsDetail() != null || !detail.getGoodsDetail().isEmpty()) { + detailJsonObject.put("goods_detail", listGoodsDetail(detail.getGoodsDetail())); + } + return detailJsonObject; + } + + /** + * 商品列表 + * + * @param goodsList + * @return + */ + private static JSONArray listGoodsDetail(List goodsList) { + JSONArray goodsDetailJsonArray = new JSONArray(); + for (PayPlaceOrder.Detail.Goods goods : goodsList) { + JSONObject goodsJsonObject = new JSONObject(); + goodsJsonObject.put("merchant_goods_id", goods.getMerchantGoodsId()); + goodsJsonObject.put("wechatpay_goods_id", goods.getWechatpayGoodsId()); + goodsJsonObject.put("goods_name", goods.getGoodsName()); + goodsJsonObject.put("quantity", goods.getQuantity()); + goodsJsonObject.put("unit_price", goods.getUnitPrice()); + goodsDetailJsonArray.add(goodsJsonObject); + } + return goodsDetailJsonArray; + } + + /** + * 场景信息 + * + * @param sceneInfo + * @return + */ + private static JSONObject getSceneInfo(PayPlaceOrder.SceneInfo sceneInfo) { + JSONObject sceneInfoJsonObject = new JSONObject(); + sceneInfoJsonObject.put("payer_client_ip", sceneInfo.getPayerClientIp()); + sceneInfoJsonObject.put("device_id", sceneInfo.getDeviceId()); + if (sceneInfo.getStoreInfo() != null) { + sceneInfoJsonObject.put("store_info", getStoreInfo(sceneInfo.getStoreInfo())); + } + return sceneInfoJsonObject; + } + + /** + * 商户门店信息 + * + * @param storeInfo + * @return + */ + private static JSONObject getStoreInfo(PayPlaceOrder.SceneInfo.StoreInfo storeInfo) { + JSONObject storeInfoJsonObject = new JSONObject(); + storeInfoJsonObject.put("id", storeInfo.getId()); + storeInfoJsonObject.put("name", storeInfo.getName()); + storeInfoJsonObject.put("area_code", storeInfo.getAreaCode()); + storeInfoJsonObject.put("address", storeInfo.getAddress()); + return storeInfoJsonObject; + } + + /** + * 是否指定分账 + * + * @param settleInfo + * @return + */ + private static JSONObject getSettleInfo(PayPlaceOrder.SettleInfo settleInfo) { + JSONObject settleJsonObject = new JSONObject(); + settleJsonObject.put("profit_sharing", settleInfo.getProfitSharing()); + return settleJsonObject; + } + +}