From 6fb6fa20d46538d642263ca9eb5c87daf965a3b2 Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Thu, 23 Apr 2026 14:52:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=94=AF=E4=BB=98=E5=AE=9D=E6=94=AF?= =?UTF-8?q?=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constant/pay/ThirdPayOrderStatusEnum.java | 2 +- .../entity/pay/ThirdPayOrderEntity.java | 3 + .../pay/alipay/ThirdALiPayCallbackVo.java | 200 ++++++++++++++++++ .../thirdparty/config/AppPlusPayConfig.java | 20 ++ .../appplus/ThirdMinAppPayCotroller.java | 41 +++- .../service/ThirdAppPlusAliPayService.java | 18 ++ .../service/ThirdAppPlusService.java | 8 +- .../ThirdAppPlusAliPayServiceImpl.java | 140 +++++++++++- .../impl/appplus/ThirdAppPlusServiceImpl.java | 91 ++++++-- .../src/main/resources/application.yml | 8 +- .../WalletRechargePackageController.java | 8 +- 11 files changed, 511 insertions(+), 28 deletions(-) create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/vo/appplus/pay/alipay/ThirdALiPayCallbackVo.java diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/constant/pay/ThirdPayOrderStatusEnum.java b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/constant/pay/ThirdPayOrderStatusEnum.java index 85edf5fde..239e4804c 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/constant/pay/ThirdPayOrderStatusEnum.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/constant/pay/ThirdPayOrderStatusEnum.java @@ -14,7 +14,7 @@ import java.util.Arrays; public enum ThirdPayOrderStatusEnum { PAY_UNPAID("10", "未支付"), PAY_SUCCESS("20", "支付成功"), - PAY_FAILED("30", "已经支付"), + PAY_FAILED("30", "支付失败"), REFUNDING("100", "退款中"), REFUND_SUCCESS("200", "退款成功"), diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/entity/pay/ThirdPayOrderEntity.java b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/entity/pay/ThirdPayOrderEntity.java index 138eb5b4c..0df0ad34a 100755 --- a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/entity/pay/ThirdPayOrderEntity.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/entity/pay/ThirdPayOrderEntity.java @@ -1,11 +1,14 @@ package com.bsd.pjcloud.thirdparty.entity.pay; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.extension.activerecord.Model; import com.bsd.pjcloud.common.core.util.TenantTable; +import com.bsd.pjcloud.thirdparty.vo.appplus.pay.wechat.ThirdMiniWechatRefundQueryVo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.Getter; import java.time.LocalDateTime; diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/vo/appplus/pay/alipay/ThirdALiPayCallbackVo.java b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/vo/appplus/pay/alipay/ThirdALiPayCallbackVo.java new file mode 100644 index 000000000..5cacb80b2 --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/vo/appplus/pay/alipay/ThirdALiPayCallbackVo.java @@ -0,0 +1,200 @@ +package com.bsd.pjcloud.thirdparty.vo.appplus.pay.alipay; + +import cn.hutool.core.util.ObjectUtil; +import com.bsd.pjcloud.thirdparty.vo.appplus.pay.wechat.ThirdMiniWechatRefundQueryVo; +import lombok.Data; +import lombok.Getter; + +import java.time.LocalDateTime; + +/** + * 支付宝支付回调 + */ +@Data +public class ThirdALiPayCallbackVo { + + /** + * 通知时间。通知的发送时间。格式为 yyyy-MM-dd HH:mm:ss。 + */ + private LocalDateTime notify_time; + + /** + * 通知类型。 + * 枚举值:trade_status_sync。 + */ + private String notify_type; + + /** + * 通知校验 ID。 + */ + private String notify_id; + + /** + * 签名类型。商家生成签名字符串所使用的签名算法类型,目前支持 RSA2 和 RSA,推荐使用 RSA2(如果开发者手动验签,不使用 SDK 验签,可以不传此参数)。 + */ + private String sign_type; + + /** + * 签名。可查看异步返回结果的验签(如果开发者手动验签,不使用 SDK 验签,可以不传此参数)。 + */ + private String sign; + + /** + * 支付宝交易号。支付宝交易凭证号。 + */ + private String trade_no; + + /** + * 开发者的 app_id。支付宝分配给开发者的应用 APPID。 + */ + private String app_id; + + /** + * 开发者的 app_id,在服务商调用的场景下为授权方的 app_id。 + */ + private String auth_app_id; + + /** + * 商户订单号。 + */ + private String out_trade_no; + + /** + * 商家业务号。商家业务 ID,主要是退款通知中返回退款申请的流水号。 + */ + private String out_biz_no; + + /** + * 买家支付宝账号。 + */ + private String buyer_logon_id; + + /** + * 卖家支付宝用户号。 + */ + private String seller_id; + + /** + * 卖家支付宝账号。 + */ + private String seller_email; + + /** + * 交易状态。咨询目前所处的状态。 + */ + private String trade_status; + + /** + * 订单金额。本次交易支付的订单金额,单位为人民币(元)。支持小数点后两位。 + */ + private Double total_amount; + + /** + * 实收金额。商家在交易中实际收到的款项,单位为人民币(元)。支持小数点后两位。 + */ + private Double receipt_amount; + + /** + * 开票金额。用户在交易中支付的可开发票的金额。支持小数点后两位。 + */ + private Double invoice_amount; + + /** + * 付款金额。用户在交易中支付的金额。支持小数点后两位。 + */ + private Double buyer_pay_amount; + + /** + * 集分宝金额。使用集分宝支付的金额。支持小数点后两位。 + */ + private Double point_amount; + + /** + * 总退款金额。退款通知中,返回总退款金额,单位为元,支持小数点后两位。 + */ + private Double refund_fee; + + /** + * 实际退款金额。商家实际退款给用户的金额,单位为元,支持小数点后两位。 + */ + private Double send_back_fee; + + /** + * 订单标题。商品的标题/交易标题/订单标题/订单关键字等,是请求时对应的参数,原样通知回来。 + */ + private String subject; + + /** + * 商品描述。该订单的备注、描述、明细等。对应请求时的 body 参数,原样通知回来。 + */ + private String body; + + /** + * 公共回传参数,如果请求时传递了该参数,则返回给商家时会在异步通知时将该参数原样返回。本参数必须进行 UrlEncode 之后才可以发送给支付宝。 + */ + private String passback_params; + + /** + * 交易创建时间。该笔交易创建的时间。格式 为 yyyy-MM-dd HH:mm:ss。 + */ + private LocalDateTime gmt_create; + + /** + * 交易 付款时间。该笔交易的买家付款时间。格式为 yyyy-MM-dd HH:mm:ss。 + */ + private LocalDateTime gmt_payment; + + /** + * 交易退款时间。该笔交易的退款时间。格式 为 yyyy-MM-dd HH:mm:ss.SS。 + */ + private LocalDateTime gmt_refund; + + /** + * 交易结束时间。该笔交易结束时间。格式为 yyyy-MM-dd HH:mm:ss。 + */ + private LocalDateTime gmt_close; + + /** + * 支付金额信息。支付成功的各个渠道金额信息,详请可查看下表 资金明细信息说明 。 + */ + private String fund_bill_list; + + /** + * 优惠券信息。本交易支付时所使用的所有优惠券信息,详请可查看下表 优惠券信息说明 。 + */ + private String voucher_detail_list; + + static public String aliPaySuccess() { + return "success"; + } + + static public String aliPayFail() { + return "fail"; + } + + + public boolean isPaySucess() { + return ObjectUtil.equals(TradeStatus.TRADE_SUCCESS.getCode(), trade_status); + } + + public boolean isRefundSucess() { + return ObjectUtil.equals(TradeStatus.TRADE_SUCCESS.getCode(), trade_status); + } + + + @Getter + public enum TradeStatus { + WAIT_BUYER_PAY("WAIT_BUYER_PAY", "等待支付"), + TRADE_CLOSED("TRADE_CLOSED", "交易关闭"), + TRADE_SUCCESS("TRADE_SUCCESS", "支付成功"), + TRADE_FINISHED("TRADE_FINISHED", "交易完成"); + private String code; + private String desc; + + TradeStatus(String code, String desc) { + this.code = code; + this.desc = desc; + } + } + +} diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/AppPlusPayConfig.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/AppPlusPayConfig.java index 5853b8acc..bb26a2912 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/AppPlusPayConfig.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/AppPlusPayConfig.java @@ -84,6 +84,26 @@ public class AppPlusPayConfig { */ private String alipayPublicKey; + /** + * 基础回调地址 + */ + private String baseCallbackUrl; + + /** + * 支付宝支付回调地址 + */ + private String payCallbackUrl; + + /** + * 支付宝退款回调地址 + */ + private String refundCallbackUrl; + + /** + * 支付宝订阅地址 + */ + private String subscribeUrl; + } } diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/appplus/ThirdMinAppPayCotroller.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/appplus/ThirdMinAppPayCotroller.java index be10d640e..d7bb363dd 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/appplus/ThirdMinAppPayCotroller.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/appplus/ThirdMinAppPayCotroller.java @@ -11,6 +11,7 @@ import com.bsd.pjcloud.thirdparty.vo.appplus.pay.ThirdMiniAppPayVo; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpEntity; @@ -18,6 +19,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.HashMap; import java.util.Map; @RestController @@ -30,7 +32,7 @@ public class ThirdMinAppPayCotroller { private final ThirdAppPlusService thirdMiniAppService; - @Operation(summary = "小程序支付退款") + @Operation(summary = "支付退款") @PostMapping("/refund") public R miniRefund(@Valid @RequestBody ThirdMiniWechatRefundDto dto) { return R.ok(thirdMiniAppService.miniRefund(dto)); @@ -38,27 +40,50 @@ public class ThirdMinAppPayCotroller { /* ************************************* start app start ************************************* */ - @Operation(summary = "小程序支付") + @Operation(summary = "支付") @PostMapping("/app/pay") public R miniPay(@Validated @RequestBody ThirdMiniAppPayDto dto) { PjcloudUser user = SecurityUtils.getUser(); return R.ok(thirdMiniAppService.miniPay(user.getId(), dto)); } - @Operation(summary = "小程序支付回调") + @Operation(summary = "支付回调") @PostMapping("/app/pay-callback") @Inner(value = false) - public R miniPayCallback(HttpEntity httpEntity, @RequestHeader Map headers) { + public R miniPayCallback(HttpServletRequest request, HttpEntity httpEntity, @RequestHeader Map headers) { String body = httpEntity.getBody(); - return R.ok(thirdMiniAppService.miniPayCallback(body, headers)); + Map parameterMap = request.getParameterMap(); + Map params = new HashMap<>(); + for (Map.Entry entry : parameterMap.entrySet()) { + params.put(entry.getKey(), entry.getValue()[0]); + } + return R.ok(thirdMiniAppService.miniPayCallback(params, body, headers)); } - @Operation(summary = "小程序退款回调") + @Operation(summary = "退款回调") @PostMapping("/app/refund-callback") @Inner(value = false) - public R miniRefundCallback(HttpEntity httpEntity, @RequestHeader Map headers) { + public R miniRefundCallback(HttpServletRequest request, HttpEntity httpEntity, @RequestHeader Map headers) { String body = httpEntity.getBody(); - return R.ok(thirdMiniAppService.miniRefundCallback(body, headers)); + Map parameterMap = request.getParameterMap(); + Map params = new HashMap<>(); + for (Map.Entry entry : parameterMap.entrySet()) { + params.put(entry.getKey(), entry.getValue()[0]); + } + return R.ok(thirdMiniAppService.miniRefundCallback(params, body, headers)); + } + + @Operation(summary = "支付消息订阅") + @PostMapping("/app/subscribe-callback") + @Inner(value = false) + public R miniSubscribeCallback(HttpServletRequest request, HttpEntity httpEntity, @RequestHeader Map headers) { + String body = httpEntity.getBody(); + Map parameterMap = request.getParameterMap(); + Map params = new HashMap<>(); + for (Map.Entry entry : parameterMap.entrySet()) { + params.put(entry.getKey(), entry.getValue()[0]); + } + return R.ok(thirdMiniAppService.miniSubscribeCallback(params, body, headers)); } /* ************************************* end app end ************************************* */ diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusAliPayService.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusAliPayService.java index eef7f8a0e..0683dd703 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusAliPayService.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusAliPayService.java @@ -2,6 +2,10 @@ package com.bsd.pjcloud.thirdparty.service; import com.bsd.pjcloud.thirdparty.dto.appplus.pay.wechat.ThirdAppPlusPayDto; +import com.bsd.pjcloud.thirdparty.dto.appplus.pay.wechat.ThirdMiniWechatRefundDto; +import com.bsd.pjcloud.thirdparty.entity.pay.ThirdPayOrderEntity; + +import java.util.Map; public interface ThirdAppPlusAliPayService { @@ -10,4 +14,18 @@ public interface ThirdAppPlusAliPayService { */ String appPay(ThirdAppPlusPayDto dto); + /** + * 退款 + */ + boolean refund(ThirdPayOrderEntity thirdPayOrder, ThirdMiniWechatRefundDto dto); + + /** + * 支付回调 + */ + ThirdPayOrderEntity payCallback(Map params); + + /** + * 退款回调 + */ + ThirdPayOrderEntity refundCallback(Map params, String body, Map headers); } diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusService.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusService.java index f14974351..d7ebb68c8 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusService.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/ThirdAppPlusService.java @@ -34,11 +34,15 @@ public interface ThirdAppPlusService { /** * 小程序支付回调 */ - String miniPayCallback(String body, Map headers); + String miniPayCallback(Map parameterMap, String body, Map headers); /** * 小程序退款回调 */ - String miniRefundCallback(String body, Map headers); + String miniRefundCallback(Map params, String body, Map headers); + /** + * 支付消息订阅 + */ + String miniSubscribeCallback(Map params, String body, Map headers); } diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusAliPayServiceImpl.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusAliPayServiceImpl.java index 58812a5de..546950428 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusAliPayServiceImpl.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusAliPayServiceImpl.java @@ -3,21 +3,26 @@ package com.bsd.pjcloud.thirdparty.service.impl.appplus; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.alipay.api.AlipayClient; import com.alipay.api.AlipayConfig; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.domain.AlipayTradeAppPayModel; +import com.alipay.api.domain.AlipayTradeRefundModel; import com.alipay.api.request.AlipayTradeAppPayRequest; +import com.alipay.api.request.AlipayTradeRefundRequest; import com.alipay.api.response.AlipayTradeAppPayResponse; +import com.alipay.api.response.AlipayTradeRefundResponse; import com.bsd.pjcloud.thirdparty.config.AppPlusPayConfig; import com.bsd.pjcloud.thirdparty.constant.miniplatform.AppPlusPlatformTypeEnum; import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayOrderStatusEnum; import com.bsd.pjcloud.thirdparty.dto.appplus.pay.wechat.ThirdAppPlusPayDto; +import com.bsd.pjcloud.thirdparty.dto.appplus.pay.wechat.ThirdMiniWechatRefundDto; import com.bsd.pjcloud.thirdparty.entity.pay.ThirdPayOrderEntity; import com.bsd.pjcloud.thirdparty.service.ThirdAppPlusAliPayService; import com.bsd.pjcloud.thirdparty.service.ThirdPayOrderService; -import com.bsd.pjcloud.thirdparty.vo.appplus.pay.wechat.ThirdMiniJsapiPayVo; +import com.bsd.pjcloud.thirdparty.vo.appplus.pay.alipay.ThirdALiPayCallbackVo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -26,6 +31,7 @@ import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; +import java.util.Map; /** * 支付宝小程序(app版本) @@ -81,6 +87,7 @@ public class ThirdAppPlusAliPayServiceImpl implements ThirdAppPlusAliPayService * app支付 */ public String appPay(ThirdAppPlusPayDto dto) { + AppPlusPayConfig.AliPayConfig aliPayConfig = appPlusPayConfig.getAliPayConfig(); LocalDateTime now = LocalDateTime.now(); ThirdPayOrderEntity payOrder = new ThirdPayOrderEntity(); @@ -94,6 +101,7 @@ public class ThirdAppPlusAliPayServiceImpl implements ThirdAppPlusAliPayService payOrder.setPayNo(dto.getOutTradeNo()); AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); + request.setNotifyUrl(aliPayConfig.getBaseCallbackUrl() + aliPayConfig.getPayCallbackUrl()); AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); model.setOutTradeNo(dto.getOutTradeNo()); @@ -120,4 +128,134 @@ public class ThirdAppPlusAliPayServiceImpl implements ThirdAppPlusAliPayService } } + /** + * 退款 + */ + @Override + public boolean refund(ThirdPayOrderEntity thirdPayOrder, ThirdMiniWechatRefundDto dto) { + AppPlusPayConfig.AliPayConfig aliPayConfig = appPlusPayConfig.getAliPayConfig(); + String refundNo = "refund_" + thirdPayOrder.getPayNo(); + + Integer refundFee = dto.getRefundPrice(); + if (ObjectUtil.isEmpty(refundFee)) { + refundFee = thirdPayOrder.getPrice(); + } + + ThirdPayOrderEntity updateObj = new ThirdPayOrderEntity(); + updateObj.setId(thirdPayOrder.getId()); + + AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); + request.setNotifyUrl(aliPayConfig.getBaseCallbackUrl() + aliPayConfig.getRefundCallbackUrl()); + + AlipayTradeRefundModel model = new AlipayTradeRefundModel(); + model.setOutTradeNo(thirdPayOrder.getPayNo()); + model.setTradeNo(getTradeNo(thirdPayOrder)); + model.setRefundAmount(fenToYuan(BigDecimal.valueOf(refundFee))); + model.setRefundReason("饭卡充值退款"); + model.setOutRequestNo(refundNo); + + updateObj.setRefundRep(JSONUtil.toJsonStr(model)); + request.setBizModel(model); + try { + AlipayTradeRefundResponse response = getAlipayClient().execute(request); + boolean success = response.isSuccess(); + if (success) { + String orderStr = response.getBody(); + updateObj.setRefundRes(orderStr); + updateObj.setRefundNo(refundNo); + updateObj.setRefundTime(LocalDateTime.now()); + updateObj.setStatus(ThirdPayOrderStatusEnum.REFUNDING.getType()); + // 判断是否退款成功 + if (ObjectUtil.equals(response.getCode(), "10000")) { + updateObj.setStatus(ThirdPayOrderStatusEnum.REFUND_SUCCESS.getType()); + return true; + } else { + updateObj.setStatus(ThirdPayOrderStatusEnum.REFUND_FAILED.getType()); + } + } + } catch (Exception e) { + e.printStackTrace(); + log.error("|{}|app退款异常,error:|{}|", TAG, e.getMessage()); + } finally { + thirdPayOrderService.updateById(updateObj); + } + return false; + } + + /** + * 支付回调 + */ + @Override + public ThirdPayOrderEntity payCallback(Map params) { + String paramsJson = JSONUtil.toJsonStr(params); + ThirdALiPayCallbackVo vo = JSONUtil.toBean(paramsJson, ThirdALiPayCallbackVo.class); + if (ObjectUtil.isEmpty(vo)) { + return null; + } + boolean paySucess = vo.isPaySucess(); + // 查询订单 + ThirdPayOrderEntity payOrderEntity = thirdPayOrderService.getByPayNo(vo.getOut_trade_no()); + if (ObjectUtil.isEmpty(payOrderEntity)) { + log.error("|{}|payCallback,意外的错误,订单不存在,微信返回json|{}|", TAG, paramsJson); + return null; + } + // 重复的回调 + if (ObjectUtil.notEqual(payOrderEntity.getStatus(), ThirdPayOrderStatusEnum.PAY_UNPAID.getType())) { + log.error("|{}|payCallback,意外的错误,重复的回调,微信返回json|{}|", TAG, paramsJson); + return null; + } + // 更新订单状态 + ThirdPayOrderEntity updateObj = new ThirdPayOrderEntity(); + updateObj.setId(payOrderEntity.getId()); + updateObj.setPayCallbackRes(paramsJson); + updateObj.setStatus(paySucess ? ThirdPayOrderStatusEnum.PAY_SUCCESS.getType() : ThirdPayOrderStatusEnum.PAY_FAILED.getType()); + thirdPayOrderService.updateById(updateObj); + return thirdPayOrderService.getById(payOrderEntity.getId()); + } + + /** + * 退款回调 + */ + @Override + public ThirdPayOrderEntity refundCallback(Map params, String body, Map headers) { + String paramsJson = JSONUtil.toJsonStr(params); + ThirdALiPayCallbackVo vo = JSONUtil.toBean(paramsJson, ThirdALiPayCallbackVo.class); + if (ObjectUtil.isEmpty(vo)) { + return null; + } + boolean refundSucess = vo.isRefundSucess(); + // 查询订单 + ThirdPayOrderEntity payOrderEntity = thirdPayOrderService.getByPayNo(vo.getOut_trade_no()); + if (ObjectUtil.isEmpty(payOrderEntity)) { + log.error("|{}|refundCallback,意外的错误,订单不存在,微信返回json|{}|", TAG, JSONUtil.toJsonStr(vo)); + return null; + } + // 重复的回调 + if (ObjectUtil.notEqual(payOrderEntity.getStatus(), ThirdPayOrderStatusEnum.REFUNDING.getType())) { + log.error("|{}|refundCallback,意外的错误,重复的回调,微信返回json|{}|", TAG, JSONUtil.toJsonStr(vo)); + return null; + } + // 更新订单状态 + ThirdPayOrderEntity updateObj = new ThirdPayOrderEntity(); + updateObj.setId(payOrderEntity.getId()); + updateObj.setRefundCallbackRes(JSONUtil.toJsonStr(vo)); + updateObj.setStatus(refundSucess ? ThirdPayOrderStatusEnum.REFUND_SUCCESS.getType() : ThirdPayOrderStatusEnum.REFUND_FAILED.getType()); + thirdPayOrderService.updateById(updateObj); + return thirdPayOrderService.getById(payOrderEntity.getId()); + } + + private String getTradeNo(ThirdPayOrderEntity payOrder) { + if (ObjectUtil.isEmpty(payOrder)) { + return null; + } + if (ObjectUtil.isNotEmpty(payOrder.getPayCallbackRes())) { + ThirdALiPayCallbackVo vo = JSONUtil.toBean(payOrder.getPayCallbackRes(), ThirdALiPayCallbackVo.class); + if (ObjectUtil.isEmpty(vo)) { + return null; + } + return vo.getTrade_no(); + } + return null; + } + } diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusServiceImpl.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusServiceImpl.java index da668e320..b47716572 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusServiceImpl.java +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/impl/appplus/ThirdAppPlusServiceImpl.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.XmlUtil; import cn.hutool.json.JSONUtil; +import com.alipay.api.internal.util.AlipaySignature; import com.bsd.pjcloud.admin.api.dto.UserInfo; import com.bsd.pjcloud.admin.api.dto.UserTokenDTO; import com.bsd.pjcloud.admin.api.feign.RemoteTokenService; @@ -13,6 +14,7 @@ import com.bsd.pjcloud.app.api.constant.AppUserTypeEnum; import com.bsd.pjcloud.app.api.dto.AppAdminSyncDto; import com.bsd.pjcloud.app.api.dto.AppUserInfo; import com.bsd.pjcloud.app.api.feign.RemoteAppUserService; +import com.bsd.pjcloud.thirdparty.config.AppPlusPayConfig; import com.bsd.pjcloud.thirdparty.constant.miniplatform.AppPlusPlatformLoginErrorEnum; import com.bsd.pjcloud.thirdparty.constant.miniplatform.AppPlusPlatformTypeEnum; import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayOrderBizTypeEnum; @@ -26,6 +28,7 @@ import com.bsd.pjcloud.thirdparty.entity.pay.ThirdPayOrderEntity; import com.bsd.pjcloud.thirdparty.service.*; import com.bsd.pjcloud.thirdparty.vo.appplus.ThirdMinAppOAth2LoginVo; import com.bsd.pjcloud.thirdparty.vo.appplus.pay.ThirdMiniAppPayVo; +import com.bsd.pjcloud.thirdparty.vo.appplus.pay.alipay.ThirdALiPayCallbackVo; import com.bsd.pjcloud.thirdparty.vo.appplus.pay.wechat.*; import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePaySuccess; import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundSuccess; @@ -52,6 +55,7 @@ public class ThirdAppPlusServiceImpl implements ThirdAppPlusService { static private final String TAG = "小程序服务类"; + private final AppPlusPayConfig appPlusPayConfig; private final ThirdAppPlusWechatService thirdMiniAppWechatService; private final ThirdAppPlusAliPayService thirdMiniAppAliPayService; private final ThirdPayLogService thirdPayLogService; @@ -216,7 +220,12 @@ public class ThirdAppPlusServiceImpl implements ThirdAppPlusService { } // 支付宝 else if (ObjectUtil.equals(thirdPayOrder.getPayPlatformType(), AppPlusPlatformTypeEnum.ALI_PAY.getType())) { - + boolean isRefund = thirdMiniAppAliPayService.refund(thirdPayOrder, dto); + if (isRefund) { + // 再查询一下 + thirdPayOrder = thirdPayOrderService.getByPayNo(dto.getPayNo()); + disposeRefundSuccessAfter(thirdPayOrder); + } } // 不支持的支付类型 else { @@ -229,8 +238,8 @@ public class ThirdAppPlusServiceImpl implements ThirdAppPlusService { * 小程序支付回调 */ @Override - public String miniPayCallback(String body, Map headers) { - AppPlusPlatformTypeEnum platformType = checkMiniPlatformType(body); + public String miniPayCallback(Map params, String body, Map headers) { + AppPlusPlatformTypeEnum platformType = checkMiniPlatformType(params, body); // 微信小程序 if (ObjectUtil.equals(platformType, AppPlusPlatformTypeEnum.WECHAT)) { ThirdPayOrderEntity thirdPayOrder = thirdMiniAppWechatService.payCallback(body, headers); @@ -243,7 +252,13 @@ public class ThirdAppPlusServiceImpl implements ThirdAppPlusService { } // 支付宝 else if (ObjectUtil.equals(platformType, AppPlusPlatformTypeEnum.ALI_PAY)) { - return null; + ThirdPayOrderEntity thirdPayOrder = thirdMiniAppAliPayService.payCallback(params); + if (ObjectUtil.isEmpty(thirdPayOrder)) { + return ThirdALiPayCallbackVo.aliPayFail(); + } + // 根据订单bizType来处理 + boolean isSuccess = disposePaySuccessAfter(thirdPayOrder); + return isSuccess ? ThirdALiPayCallbackVo.aliPaySuccess() : ThirdALiPayCallbackVo.aliPayFail(); } // 不支持的支付类型 else { @@ -255,8 +270,8 @@ public class ThirdAppPlusServiceImpl implements ThirdAppPlusService { * 小程序退款回调 */ @Override - public String miniRefundCallback(String body, Map headers) { - AppPlusPlatformTypeEnum platformType = checkRefundMiniPlatformType(body); + public String miniRefundCallback(Map params, String body, Map headers) { + AppPlusPlatformTypeEnum platformType = checkRefundMiniPlatformType(params, body); // 微信小程序 if (ObjectUtil.equals(platformType, AppPlusPlatformTypeEnum.WECHAT)) { ThirdPayOrderEntity thirdPayOrder = thirdMiniAppWechatService.refundCallback(body, headers); @@ -269,7 +284,13 @@ public class ThirdAppPlusServiceImpl implements ThirdAppPlusService { } // 支付宝 else if (ObjectUtil.equals(platformType, AppPlusPlatformTypeEnum.ALI_PAY)) { - return null; + ThirdPayOrderEntity thirdPayOrder = thirdMiniAppAliPayService.refundCallback(params, body, headers); + if (ObjectUtil.isEmpty(thirdPayOrder)) { + return ThirdALiPayCallbackVo.aliPayFail(); + } + // 根据订单bizType来处理 + boolean isSuccess = disposeRefundSuccessAfter(thirdPayOrder); + return isSuccess ? ThirdALiPayCallbackVo.aliPaySuccess() : ThirdALiPayCallbackVo.aliPayFail(); } // 不支持的支付类型 else { @@ -277,21 +298,65 @@ public class ThirdAppPlusServiceImpl implements ThirdAppPlusService { } } - private AppPlusPlatformTypeEnum checkMiniPlatformType(String body) { - ThirdMiniWechatRefundQueryVo vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(body)), ThirdMiniWechatRefundQueryVo.class); - // 查询订单 + /** + * 支付消息订阅 + */ + @Override + public String miniSubscribeCallback(Map params, String body, Map headers) { + return ""; + } + + // 判断支付回调 + private AppPlusPlatformTypeEnum checkMiniPlatformType(Map params, String body) { + ThirdMiniWechatRefundQueryVo vo = null; + try { + vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(body)), ThirdMiniWechatRefundQueryVo.class); + } catch (Exception e) { + log.error("|{}|判断支付回调wx xml解析错误,body:|{}|", TAG, body); + } + // 微信 if (ObjectUtil.isNotEmpty(vo) && ObjectUtil.isNotEmpty(vo.getTransaction_id())) { return AppPlusPlatformTypeEnum.WECHAT; } + // 支付宝 + if (ObjectUtil.isNotEmpty(params)) { + try { + boolean flag = AlipaySignature.rsaCheckV1(params, appPlusPayConfig.getAliPayConfig().getAlipayPublicKey(), "UTF-8", "RSA2"); + if (flag) { + return AppPlusPlatformTypeEnum.ALI_PAY; + } + } catch (Exception e) { + log.error("|{}|判断支付回调alipay sdk验签错误,body:|{}|parameterMap:{||}", TAG, body, JSONUtil.toJsonStr(params)); + } + } return null; } - private AppPlusPlatformTypeEnum checkRefundMiniPlatformType(String body) { - ThirdMiniWechantRefunCallbackSoureVo vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(body)), ThirdMiniWechantRefunCallbackSoureVo.class); - // 查询订单 + /** + * 判断退款回调 + */ + private AppPlusPlatformTypeEnum checkRefundMiniPlatformType(Map params, String body) { + ThirdMiniWechantRefunCallbackSoureVo vo = null; + try { + vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(body)), ThirdMiniWechantRefunCallbackSoureVo.class); + } catch (Exception e) { + log.error("|{}|判断退款回调wx xml解析错误,body:|{}|", TAG, body); + } + // 微信 if (ObjectUtil.isNotEmpty(vo) && ObjectUtil.isNotEmpty(vo.getAppid()) && ObjectUtil.isNotEmpty(vo.getMch_id()) && ObjectUtil.isNotEmpty(vo.getNonce_str()) && ObjectUtil.isNotEmpty(vo.getReq_info())) { return AppPlusPlatformTypeEnum.WECHAT; } + // 支付宝 + if (ObjectUtil.isNotEmpty(params)) { + try { + boolean flag = AlipaySignature.rsaCheckV1(params, appPlusPayConfig.getAliPayConfig().getAlipayPublicKey(), "UTF-8", "RSA2"); + if (flag) { + return AppPlusPlatformTypeEnum.ALI_PAY; + } + } catch (Exception e) { + log.error("|{}|判断退款回调alipay sdk验签错误,body:|{}|params:{||}", TAG, body, JSONUtil.toJsonStr(params)); + } + } return null; } diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml index 383d22029..c29dd1f0d 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml @@ -114,14 +114,18 @@ app-plus: merchant-id: 1106210589 api-v2-key: qr0w9WkAu6GG3uuxPjDT7bcdIVoBr0rw merchant-serial-path: /Users/dengzhangchen/Desktop/backups/pay/third_cert/apiclient_cert.p12 - pay-callback-url: https://dzc-test-1.fy.takin.cc/third/third-mini-app-pay/app/pay-callback - refund-callback-url: https://dzc-test-1.fy.takin.cc/third/third-mini-app-pay/app/refund-callback + pay-callback-url: https://dzc-test-1.fy.takin.cc/third/third-app-pay/app/pay-callback + refund-callback-url: https://dzc-test-1.fy.takin.cc/third/third-app-pay/app/refund-callback spbill-create-ip: 59.202.170.152 base-url: https://api.mch.weixin.qq.com ali-pay-config: app-id: 2021006147685828 app-private-key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9OwO+0R0y3wxniFEzoDcVHuSB+5D49WYLi/Bgvqz04Y7dnbphih4ShKNByRxw046c+gG406EZ5KTbfB/AwUflYl0gANTwhuLjK2O+/MaKpfsE2m4vT8MuDsUW4Y00RO0DkLN/Kf9aLrQd62/GtLipVIejHCi9KevclKstO7NomCkJC3ucZ1FIqeeJEhM8aNq0Q7VAfJ0IFiY+yJuqz7Yxpy39tAW5TxFXCzkwQUjRoz4xOD8+N2Qo9OVn6rE1Z65LIa3c6uxTmg8/8s7thwralx57Rr6VDdFSMbnPKplNDFWKlZL8YAmk9PI/3Jg03HH+r3GxBNvTAp5Uk/CqPAAJAgMBAAECggEBAIQqDLKggSE/sf05s2U4WGBzBCxY1OddFhmocopPgqNCSrM0b7GicuCCLThMS4Xt7KRs5Pj/LnzUOeRmLBNHq3xlmB87FX6s5xi8/FyvVcgOp2S7XsvCjNLFVBhoDmRcY6Hi1ctSPNvy1LA0+Quyu2rg9B8TEs0T031rhKcCFItOzvamdTWPZ17AQBykSn+8F4Uhp6EiR17HWyS4TkmLfeS+Q6LgP6VLIhqPgBvC48hMc8pLDv97b26kTe8QcdOFdplqzdYAFCOlaXAS2OHXRieILAjaSVtdPfjqy6wu66PyUuMGnAA4HhvNeTbJIuXv0Y+PsGHXHmGL3YCsVdsVTzUCgYEA6vlP0yQ6+6IFwfMba+8BYEKpEaHT+BsqMuO+jDKINo2HwMA8WBQjkAfsDkgzQrFiicq4ENdSqihEtqbZrgb3lqOajzXU3BUtj49TjIbOmNgofy+du8KnpIGKIXe59T75E9jv8SByBBulGn3Sjwcby1jBEGeVXJ325sjRXFcCvyMCgYEAzinVFXTgJk0hWp4Tj9+4XaO299T85x27Uk3wO5Y5drmPJB/aqxHInzRSvDeyTeXF3++xf0EW1UXYSRazmKIBlr8JqnUZGuwMu4ZWvNGNuMsUA/R19tail6XtRc740kILO1r2DouWmqQtMIrgEyDANkIl+RKsaDNCofAYmuqrrOMCgYBJofDS0IKvQfmd6onIS/BZcuo6PUUIdbLGmbmng1pOBhHpZBRE4aK3StQ9MYDmwGGJnn4JwQAFlHLb7fM+5q4FhaU0hYD7qqNJXnenDMOHAA3ItWovYXNZSMK5dCRQ5vf0sUGXhTxVbzhQ0eb+rRKXqWtusZcLm/C4J2v8DMQukwKBgEZsjch1kzgiMtmUlBQalQ0PpqnEDNwaEOoNi5mfOGPxAQ+S/gxf18tigTygPf1UFyLm6xgQMO+Lm1VkNtdVx/spqPCcbYX/yl2ohyqa3rGYVW8Aa+d07DenS4UELYUNYG7XJKRVVI3gtTsWxXJjRqWCNRx/H3u8vu84Z/9LWHeHAoGADJmbuMUctjMfNVuh5B65VhLXYiOdta9smuAeAhoUiP9tlZuBvjEDVXHfu+NnRXL1yn0OhIK9N4Q3rS+2z/G5W1GvDxiSmk4fRQBwakybn4DqKorCxxwK5nGciZ+2Bde0IAcDe1no/YuxY5uC12LyQi7PINO18NBgBgXFWBYlYvM= alipay-public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoU7Iun0y/g/4UefwCc/TWudwRfgn302yQjI50u9gFek9YS5RxlIj4T+WfNv7a8rDKvTGjxeMG638rXuLHEuUMJCZadvjNYdVwvGo9mtmtwa+BK7q2ovpc5iTU9OYPTM0eOPX5FO8qkd15U1pvfBO1BFCDwpelGW+AZwObL/dFhU43j0EcVqLM3VrnLuynURPt2pX9oQ1Je65W0Q1IyVMw5FWm2+aayr/jlhJqtwdsWPuzGegBW7Pc2h/rugTeE+1Rx4+bvlNVaihCewyu+tzvUJzT0xekhQM1kH9um/ZyyivJOziuwGwFoqAdFB7FYM5yMUo1AKaZGjL3to9eWIGQIDAQAB + base-callback-url: https://dzc-test-1.fy.takin.cc + pay-callback-url: /third/third-app-pay/app/pay-callback + refund-callback-url: /third/third-app-pay/app/refund-callback + subscribe-url: /third/third-app-pay/app/subscribe-callback sms: api: base-url: "https://api.028lk.com/Sms/Api/" diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/recharge/WalletRechargePackageController.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/recharge/WalletRechargePackageController.java index 40be97c68..9876a6c7f 100644 --- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/recharge/WalletRechargePackageController.java +++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/recharge/WalletRechargePackageController.java @@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum; import com.bsd.pjcloud.common.core.util.R; import com.bsd.pjcloud.common.log.annotation.SysLog; import com.bsd.pjcloud.common.security.annotation.Inner; @@ -63,7 +64,11 @@ public class WalletRechargePackageController { @Operation(summary = "查询全部", description = "查询全部") @GetMapping("/app/list") public R appList() { - return R.ok(walletRechargePackageService.list(new LambdaQueryWrapper<>())); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(WalletRechargePackageEntity::getStatus, YesNoEnum.YES.getCode()) + .orderByAsc(WalletRechargePackageEntity::getPayPrice) + .orderByDesc(WalletRechargePackageEntity::getCreateTime); + return R.ok(walletRechargePackageService.list(wrapper)); } /* ************************************* end app end ************************************* */ @@ -72,6 +77,7 @@ public class WalletRechargePackageController { @PreAuthorize("@pms.hasPermission('wallet_walletRechargePackage_view')") public R getWalletRechargePackagePage(@ParameterObject Page page, @ParameterObject WalletRechargePackageEntity walletRechargePackage) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.orderByAsc(WalletRechargePackageEntity::getPayPrice); wrapper.orderByDesc(WalletRechargePackageEntity::getCreateTime); return R.ok(walletRechargePackageService.page(page, wrapper)); }