Commit f108d478a8206b5a0a76968014d6e7001235a31b
1 parent
1c5544fc
调整渠道支付通知地址为统一的地址
Showing
14 changed files
with
332 additions
and
232 deletions
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java
| ... | ... | @@ -24,9 +24,9 @@ public interface PayErrorCodeCoreConstants { |
| 24 | 24 | ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在"); |
| 25 | 25 | ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道"); |
| 26 | 26 | ErrorCode CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v2版本中商户密钥不可为空"); |
| 27 | - ErrorCode CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本apiclient_key.pem不可为空"); | |
| 28 | - ErrorCode CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本中apiclient_cert.pem不可为空"); | |
| 29 | - | |
| 27 | + ErrorCode CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL = new ErrorCode(1007001007,"微信渠道v3版本apiclient_key.pem不可为空"); | |
| 28 | + ErrorCode CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL = new ErrorCode(1007001008,"微信渠道v3版本中apiclient_cert.pem不可为空"); | |
| 29 | + ErrorCode PAY_CHANNEL_NOTIFY_VERIFY_FAILED = new ErrorCode(1007001009, "渠道通知校验失败"); | |
| 30 | 30 | /** |
| 31 | 31 | * ========== ORDER 模块 1-007-002-000 ========== |
| 32 | 32 | */ | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayCommonCoreService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 支付通用 Core Service | |
| 7 | + * | |
| 8 | + * @author jason | |
| 9 | + */ | |
| 10 | +public interface PayCommonCoreService { | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * 验证是否是渠道通知 | |
| 14 | + * @param notifyData 通知数据 | |
| 15 | + */ | |
| 16 | + void verifyNotifyData(Long channelId, PayNotifyDataDTO notifyData); | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 支付宝的支付回调通知,和退款回调通知 地址是同一个 | |
| 20 | + * 是否是退款回调通知 | |
| 21 | + * @param notifyData 通知数据 | |
| 22 | + * @return | |
| 23 | + */ | |
| 24 | + boolean isRefundNotify(Long channelId, PayNotifyDataDTO notifyData); | |
| 25 | +} | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayOrderCoreService.java
| ... | ... | @@ -43,10 +43,9 @@ public interface PayOrderCoreService { |
| 43 | 43 | * 通知支付单成功 |
| 44 | 44 | * |
| 45 | 45 | * @param channelId 渠道编号 |
| 46 | - * @param channelCode 渠道编码 | |
| 47 | 46 | * @param notifyData 通知数据 |
| 48 | 47 | */ |
| 49 | - void notifyPayOrder(Long channelId, String channelCode, PayNotifyDataDTO notifyData) throws Exception; | |
| 48 | + void notifyPayOrder(Long channelId, PayNotifyDataDTO notifyData) throws Exception; | |
| 50 | 49 | |
| 51 | 50 | |
| 52 | 51 | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundCoreService.java
| ... | ... | @@ -23,11 +23,10 @@ public interface PayRefundCoreService { |
| 23 | 23 | /** |
| 24 | 24 | * 渠道的退款通知 |
| 25 | 25 | * @param channelId 渠道编号 |
| 26 | - * @param channelCode 渠道编码 | |
| 27 | 26 | * @param notifyData 通知数据 |
| 28 | 27 | * @throws Exception 退款通知异常 |
| 29 | 28 | */ |
| 30 | - void notifyPayRefund(Long channelId, String channelCode, PayNotifyDataDTO notifyData) throws Exception; | |
| 29 | + void notifyPayRefund(Long channelId, PayNotifyDataDTO notifyData) throws Exception; | |
| 31 | 30 | |
| 32 | 31 | |
| 33 | 32 | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayCommonCoreServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | |
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService; | |
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayCommonCoreService; | |
| 6 | +import cn.iocoder.yudao.framework.pay.core.client.PayClient; | |
| 7 | +import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; | |
| 8 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | |
| 9 | +import lombok.extern.slf4j.Slf4j; | |
| 10 | +import org.springframework.stereotype.Service; | |
| 11 | + | |
| 12 | +import javax.annotation.Resource; | |
| 13 | + | |
| 14 | +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.PAY_CHANNEL_CLIENT_NOT_FOUND; | |
| 15 | +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.PAY_CHANNEL_NOTIFY_VERIFY_FAILED; | |
| 16 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * 支付通用 Core Service 实现类 | |
| 20 | + * | |
| 21 | + * @author jason | |
| 22 | + */ | |
| 23 | +@Service | |
| 24 | +@Slf4j | |
| 25 | +public class PayCommonCoreServiceImpl implements PayCommonCoreService { | |
| 26 | + | |
| 27 | + @Resource | |
| 28 | + private PayChannelCoreService payChannelCoreService; | |
| 29 | + | |
| 30 | + @Resource | |
| 31 | + private PayClientFactory payClientFactory; | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public void verifyNotifyData(Long channelId, PayNotifyDataDTO notifyData) { | |
| 35 | + // 校验支付渠道是否有效 | |
| 36 | + PayChannelDO channel = payChannelCoreService.validPayChannel(channelId); | |
| 37 | + // 校验支付客户端是否正确初始化 | |
| 38 | + PayClient client = payClientFactory.getPayClient(channel.getId()); | |
| 39 | + if (client == null) { | |
| 40 | + log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); | |
| 41 | + throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); | |
| 42 | + } | |
| 43 | + boolean verifyResult = client.verifyNotifyData(notifyData); | |
| 44 | + if(!verifyResult){ | |
| 45 | + //渠道通知验证失败 | |
| 46 | + throw exception(PAY_CHANNEL_NOTIFY_VERIFY_FAILED); | |
| 47 | + } | |
| 48 | + } | |
| 49 | + | |
| 50 | + @Override | |
| 51 | + public boolean isRefundNotify(Long channelId, PayNotifyDataDTO notifyData) { | |
| 52 | + // 校验支付渠道是否有效 | |
| 53 | + PayChannelDO channel = payChannelCoreService.validPayChannel(channelId); | |
| 54 | + // 校验支付客户端是否正确初始化 | |
| 55 | + PayClient client = payClientFactory.getPayClient(channel.getId()); | |
| 56 | + if (client == null) { | |
| 57 | + log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); | |
| 58 | + throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); | |
| 59 | + } | |
| 60 | + return client.isRefundNotify(notifyData); | |
| 61 | + } | |
| 62 | +} | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java
| ... | ... | @@ -155,24 +155,22 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { |
| 155 | 155 | |
| 156 | 156 | /** |
| 157 | 157 | * 根据支付渠道的编码,生成支付渠道的返回地址 |
| 158 | - * @param channel | |
| 159 | - * @return | |
| 158 | + * @param channel 支付渠道 | |
| 159 | + * @return 支付成功返回的地址。 配置地址 + "/" + channel id | |
| 160 | 160 | */ |
| 161 | 161 | private String genChannelReturnUrl(PayChannelDO channel) { |
| 162 | - return payProperties.getPayReturnUrl() + "/" + StrUtil.replace(channel.getCode(), "_", "-") | |
| 163 | - + "/" + channel.getId(); | |
| 162 | + return payProperties.getPayReturnUrl() + "/" + channel.getId(); | |
| 164 | 163 | } |
| 165 | 164 | |
| 166 | 165 | /** |
| 167 | 166 | * 根据支付渠道的编码,生成支付渠道的回调地址 |
| 168 | 167 | * |
| 169 | 168 | * @param channel 支付渠道 |
| 170 | - * @return 支付渠道的回调地址 | |
| 169 | + * @return 支付渠道的回调地址 配置地址 + "/" + channel id | |
| 171 | 170 | */ |
| 172 | 171 | private String genChannelPayNotifyUrl(PayChannelDO channel) { |
| 173 | - // _ 转化为 - 的原因,是因为 URL 我们统一采用中划线的原则 | |
| 174 | - return payProperties.getPayNotifyUrl() + "/" + StrUtil.replace(channel.getCode(), "_", "-") | |
| 175 | - + "/" + channel.getId(); | |
| 172 | + //去掉channel code, 似乎没啥用, 用统一的回调地址 | |
| 173 | + return payProperties.getPayNotifyUrl() + "/" + channel.getId(); | |
| 176 | 174 | } |
| 177 | 175 | |
| 178 | 176 | private String generateOrderExtensionNo() { |
| ... | ... | @@ -195,7 +193,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { |
| 195 | 193 | |
| 196 | 194 | @Override |
| 197 | 195 | @Transactional |
| 198 | - public void notifyPayOrder(Long channelId, String channelCode, PayNotifyDataDTO notifyData) throws Exception { | |
| 196 | + public void notifyPayOrder(Long channelId, PayNotifyDataDTO notifyData) throws Exception { | |
| 199 | 197 | // TODO 芋艿,记录回调日志 |
| 200 | 198 | log.info("[notifyPayOrder][channelId({}) 回调数据({})]", channelId, notifyData.getBody()); |
| 201 | 199 | |
| ... | ... | @@ -207,7 +205,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { |
| 207 | 205 | log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); |
| 208 | 206 | throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); |
| 209 | 207 | } |
| 210 | - //TODO @jason 校验 是否支付宝调用。 使用 支付宝publickey 或者payclient 加一个校验方法 | |
| 208 | + | |
| 211 | 209 | // 解析支付结果 |
| 212 | 210 | PayOrderNotifyRespDTO notifyRespDTO = client.parseOrderNotify(notifyData); |
| 213 | 211 | |
| ... | ... | @@ -222,7 +220,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { |
| 222 | 220 | throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); |
| 223 | 221 | } |
| 224 | 222 | // 1.2 更新 PayOrderExtensionDO |
| 225 | - //TODO @jason notifyRespDTO.getTradeStatus() 需要根据不同的状态更新成不同的值 PayOrderStatusEnum | |
| 223 | + //TODO 支付宝交易超时 TRADE_FINISHED 需要更新交易关闭 | |
| 226 | 224 | int updateCounts = payOrderExtensionCoreMapper.updateByIdAndStatus(orderExtension.getId(), |
| 227 | 225 | PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId()) |
| 228 | 226 | .status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(notifyData.getBody()).build()); |
| ... | ... | @@ -241,7 +239,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { |
| 241 | 239 | } |
| 242 | 240 | // 2.2 更新 PayOrderDO |
| 243 | 241 | updateCounts = payOrderCoreMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(), |
| 244 | - PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelId(channelId).channelCode(channelCode) | |
| 242 | + PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelId(channelId).channelCode(channel.getCode()) | |
| 245 | 243 | .successTime(notifyRespDTO.getSuccessTime()).successExtensionId(orderExtension.getId()) |
| 246 | 244 | .channelOrderNo(notifyRespDTO.getChannelOrderNo()).channelUserId(notifyRespDTO.getChannelUserId()) |
| 247 | 245 | .notifyTime(new Date()).build()); | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundChannelQueryHandler.java
| ... | ... | @@ -40,7 +40,9 @@ public class PayRefundChannelQueryHandler extends PayRefundAbstractChannelPostHa |
| 40 | 40 | //更新退款单表 |
| 41 | 41 | PayRefundDO updateRefundDO = new PayRefundDO(); |
| 42 | 42 | updateRefundDO.setId(respBO.getRefundId()) |
| 43 | - .setStatus(refundStatus.getStatus()); | |
| 43 | + .setStatus(refundStatus.getStatus()) | |
| 44 | + .setChannelErrorCode(respBO.getChannelErrCode()) | |
| 45 | + .setChannelErrorMsg(respBO.getChannelErrMsg()); | |
| 44 | 46 | updatePayRefund(updateRefundDO); |
| 45 | 47 | |
| 46 | 48 | PayOrderDO updateOrderDO = new PayOrderDO(); | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java
| ... | ... | @@ -182,7 +182,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { |
| 182 | 182 | |
| 183 | 183 | |
| 184 | 184 | @Override |
| 185 | - public void notifyPayRefund(Long channelId, String channelCode, PayNotifyDataDTO notifyData) { | |
| 185 | + public void notifyPayRefund(Long channelId, PayNotifyDataDTO notifyData) { | |
| 186 | 186 | log.info("[notifyPayRefund][channelId({}) 回调数据({})]", channelId, notifyData.getBody()); |
| 187 | 187 | // 校验支付渠道是否有效 |
| 188 | 188 | PayChannelDO channel = payChannelCoreService.validPayChannel(channelId); | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
| ... | ... | @@ -49,4 +49,22 @@ public interface PayClient { |
| 49 | 49 | */ |
| 50 | 50 | PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData); |
| 51 | 51 | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 验证是否渠道通知 | |
| 55 | + * @param notifyData 通知数据 | |
| 56 | + * @return 默认是 true | |
| 57 | + */ | |
| 58 | + default boolean verifyNotifyData(PayNotifyDataDTO notifyData){ | |
| 59 | + return true; | |
| 60 | + } | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * 是否退款通知 | |
| 64 | + * @param notifyData 通知数据 | |
| 65 | + * @return 默认是 false | |
| 66 | + */ | |
| 67 | + default boolean isRefundNotify(PayNotifyDataDTO notifyData){ | |
| 68 | + return false; | |
| 69 | + } | |
| 52 | 70 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | |
| 2 | + | |
| 3 | +import cn.hutool.core.bean.BeanUtil; | |
| 4 | +import cn.hutool.core.date.DateUtil; | |
| 5 | +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; | |
| 6 | +import cn.iocoder.yudao.framework.pay.core.client.dto.*; | |
| 7 | +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | |
| 8 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | |
| 9 | +import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum; | |
| 10 | +import com.alipay.api.AlipayApiException; | |
| 11 | +import com.alipay.api.AlipayConfig; | |
| 12 | +import com.alipay.api.DefaultAlipayClient; | |
| 13 | +import com.alipay.api.domain.AlipayTradeRefundModel; | |
| 14 | +import com.alipay.api.internal.util.AlipaySignature; | |
| 15 | +import com.alipay.api.request.AlipayTradeRefundRequest; | |
| 16 | +import com.alipay.api.response.AlipayTradeRefundResponse; | |
| 17 | +import lombok.SneakyThrows; | |
| 18 | +import lombok.extern.slf4j.Slf4j; | |
| 19 | + | |
| 20 | +import java.net.SocketTimeoutException; | |
| 21 | +import java.nio.charset.StandardCharsets; | |
| 22 | +import java.util.Map; | |
| 23 | + | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | |
| 25 | + | |
| 26 | +/** | |
| 27 | + * 支付宝抽象类, 实现支付宝统一的接口。如退款 | |
| 28 | + * | |
| 29 | + * @author jason | |
| 30 | + */ | |
| 31 | +@Slf4j | |
| 32 | +public abstract class AbstractAlipayClient extends AbstractPayClient<AlipayPayClientConfig> { | |
| 33 | + | |
| 34 | + protected DefaultAlipayClient client; | |
| 35 | + | |
| 36 | + public AbstractAlipayClient(Long channelId, String channelCode, | |
| 37 | + AlipayPayClientConfig config, AbstractPayCodeMapping codeMapping) { | |
| 38 | + super(channelId, channelCode, config, codeMapping); | |
| 39 | + } | |
| 40 | + | |
| 41 | + @Override | |
| 42 | + @SneakyThrows | |
| 43 | + protected void doInit() { | |
| 44 | + AlipayConfig alipayConfig = new AlipayConfig(); | |
| 45 | + BeanUtil.copyProperties(config, alipayConfig, false); | |
| 46 | + this.client = new DefaultAlipayClient(alipayConfig); | |
| 47 | + } | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * 从支付宝通知返回参数中解析 PayOrderNotifyRespDTO, 通知具体参数参考 | |
| 51 | + * //https://opendocs.alipay.com/open/203/105286 | |
| 52 | + * @param data 通知结果 | |
| 53 | + * @return 解析结果 PayOrderNotifyRespDTO | |
| 54 | + * @throws Exception 解析失败,抛出异常 | |
| 55 | + */ | |
| 56 | + @Override | |
| 57 | + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception { | |
| 58 | + Map<String, String> params = data.getParams(); | |
| 59 | + return PayOrderNotifyRespDTO.builder().orderExtensionNo(params.get("out_trade_no")) | |
| 60 | + .channelOrderNo(params.get("trade_no")).channelUserId(params.get("seller_id")) | |
| 61 | + .tradeStatus(params.get("trade_status")) | |
| 62 | + .successTime(DateUtil.parse(params.get("notify_time"), "yyyy-MM-dd HH:mm:ss")) | |
| 63 | + .data(data.getBody()).build(); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @Override | |
| 67 | + public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { | |
| 68 | + Map<String, String> params = notifyData.getParams(); | |
| 69 | + PayRefundNotifyDTO notifyDTO = PayRefundNotifyDTO.builder().channelOrderNo(params.get("trade_no")) | |
| 70 | + .tradeNo(params.get("out_trade_no")) | |
| 71 | + .reqNo(params.get("out_biz_no")) | |
| 72 | + .status(PayNotifyRefundStatusEnum.SUCCESS) | |
| 73 | + .refundSuccessTime(DateUtil.parse(params.get("gmt_refund"), "yyyy-MM-dd HH:mm:ss")) | |
| 74 | + .build(); | |
| 75 | + return notifyDTO; | |
| 76 | + } | |
| 77 | + | |
| 78 | + @Override | |
| 79 | + public boolean isRefundNotify(PayNotifyDataDTO notifyData) { | |
| 80 | + if (notifyData.getParams().containsKey("refund_fee")) { | |
| 81 | + return true; | |
| 82 | + } else { | |
| 83 | + return false; | |
| 84 | + } | |
| 85 | + } | |
| 86 | + | |
| 87 | + @Override | |
| 88 | + public boolean verifyNotifyData(PayNotifyDataDTO notifyData) { | |
| 89 | + boolean verifyResult = false; | |
| 90 | + try { | |
| 91 | + verifyResult = AlipaySignature.rsaCheckV1(notifyData.getParams(), config.getAlipayPublicKey(), StandardCharsets.UTF_8.name(), "RSA2"); | |
| 92 | + } catch (AlipayApiException e) { | |
| 93 | + log.error("[AlipayClient verifyNotifyData][(notify param is :{}) 验证失败]", toJsonString(notifyData.getParams()), e); | |
| 94 | + } | |
| 95 | + return verifyResult; | |
| 96 | + } | |
| 97 | + | |
| 98 | + /** | |
| 99 | + * 支付宝统一的退款接口 alipay.trade.refund | |
| 100 | + * @param reqDTO 退款请求 request DTO | |
| 101 | + * @return 退款请求 Response | |
| 102 | + */ | |
| 103 | + @Override | |
| 104 | + protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { | |
| 105 | + AlipayTradeRefundModel model=new AlipayTradeRefundModel(); | |
| 106 | + model.setTradeNo(reqDTO.getChannelOrderNo()); | |
| 107 | + model.setOutTradeNo(reqDTO.getPayTradeNo()); | |
| 108 | + model.setOutRequestNo(reqDTO.getRefundReqNo()); | |
| 109 | + model.setRefundAmount(calculateAmount(reqDTO.getAmount()).toString()); | |
| 110 | + model.setRefundReason(reqDTO.getReason()); | |
| 111 | + AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest(); | |
| 112 | + refundRequest.setBizModel(model); | |
| 113 | + PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO(); | |
| 114 | + try { | |
| 115 | + AlipayTradeRefundResponse response = client.execute(refundRequest); | |
| 116 | + log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response)); | |
| 117 | + if (response.isSuccess()) { | |
| 118 | + //退款成功,更新为PROCESSING_NOTIFY, 而不是 SYNC_SUCCESS 通过支付宝回调接口处理。退款导致触发的异步通知, | |
| 119 | + //退款导致触发的异步通知是发送到支付接口中设置的notify_url | |
| 120 | + //TODO 沙箱环境 返回 的tradeNo(渠道退款单号) 和 订单的tradNo 是一个值,是不是理解不对? | |
| 121 | + respDTO.setRespEnum(PayChannelRespEnum.PROCESSING_NOTIFY); | |
| 122 | + }else{ | |
| 123 | + //特殊处理 sub_code ACQ.SYSTEM_ERROR(系统错误), 需要调用重试任务 | |
| 124 | + //沙箱环境返回的貌似是”aop.ACQ.SYSTEM_ERROR“, 用contain | |
| 125 | + if (response.getSubCode().contains("ACQ.SYSTEM_ERROR")) { | |
| 126 | + respDTO.setRespEnum(PayChannelRespEnum.RETRY_FAILURE) | |
| 127 | + .setChannelErrMsg(response.getSubMsg()) | |
| 128 | + .setChannelErrCode(response.getSubCode()); | |
| 129 | + }else{ | |
| 130 | + //交易已关闭,需要查询确认退款是否已经完成 | |
| 131 | + if("ACQ.TRADE_HAS_CLOSE".equals(response.getSubCode())){ | |
| 132 | + respDTO.setRespEnum(PayChannelRespEnum.PROCESSING_QUERY) | |
| 133 | + .setChannelErrMsg(response.getSubMsg()) | |
| 134 | + .setChannelErrCode(response.getSubCode()); | |
| 135 | + }else { | |
| 136 | + //其他当做不可以重试的错误 | |
| 137 | + respDTO.setRespEnum(PayChannelRespEnum.CAN_NOT_RETRY_FAILURE) | |
| 138 | + .setChannelErrCode(response.getSubCode()) | |
| 139 | + .setChannelErrMsg(response.getSubMsg()); | |
| 140 | + } | |
| 141 | + } | |
| 142 | + } | |
| 143 | + return respDTO; | |
| 144 | + } catch (AlipayApiException e) { | |
| 145 | + //TODO 记录异常日志 | |
| 146 | + log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e); | |
| 147 | + Throwable cause = e.getCause(); | |
| 148 | + //网络 read time out 异常, 退款状态未知 | |
| 149 | + if (cause instanceof SocketTimeoutException) { | |
| 150 | + respDTO.setExceptionMsg(e.getMessage()) | |
| 151 | + .setRespEnum(PayChannelRespEnum.READ_TIME_OUT_EXCEPTION); | |
| 152 | + }else{ | |
| 153 | + respDTO.setExceptionMsg(e.getMessage()) | |
| 154 | + .setChannelErrCode(e.getErrCode()) | |
| 155 | + .setChannelErrMsg(e.getErrMsg()) | |
| 156 | + .setRespEnum(PayChannelRespEnum.CALL_EXCEPTION); | |
| 157 | + } | |
| 158 | + return respDTO; | |
| 159 | + } | |
| 160 | + } | |
| 161 | + | |
| 162 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
| 1 | 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.bean.BeanUtil; | |
| 4 | -import cn.hutool.core.date.DateUtil; | |
| 5 | 3 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; |
| 6 | -import cn.iocoder.yudao.framework.pay.core.client.dto.*; | |
| 7 | -import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | |
| 4 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; | |
| 8 | 5 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| 9 | 6 | import com.alipay.api.AlipayApiException; |
| 10 | -import com.alipay.api.AlipayConfig; | |
| 11 | -import com.alipay.api.DefaultAlipayClient; | |
| 12 | 7 | import com.alipay.api.domain.AlipayTradePrecreateModel; |
| 13 | 8 | import com.alipay.api.request.AlipayTradePrecreateRequest; |
| 14 | 9 | import com.alipay.api.response.AlipayTradePrecreateResponse; |
| 15 | -import lombok.SneakyThrows; | |
| 16 | 10 | import lombok.extern.slf4j.Slf4j; |
| 17 | 11 | |
| 18 | -import java.util.Map; | |
| 19 | - | |
| 20 | 12 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 21 | 13 | |
| 22 | 14 | /** |
| ... | ... | @@ -26,24 +18,13 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString |
| 26 | 18 | * @author 芋道源码 |
| 27 | 19 | */ |
| 28 | 20 | @Slf4j |
| 29 | -public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> { | |
| 30 | - | |
| 31 | - private DefaultAlipayClient client; | |
| 21 | +public class AlipayQrPayClient extends AbstractAlipayClient { | |
| 32 | 22 | |
| 33 | 23 | public AlipayQrPayClient(Long channelId, AlipayPayClientConfig config) { |
| 34 | 24 | super(channelId, PayChannelEnum.ALIPAY_QR.getCode(), config, new AlipayPayCodeMapping()); |
| 35 | 25 | } |
| 36 | 26 | |
| 37 | 27 | @Override |
| 38 | - @SneakyThrows | |
| 39 | - protected void doInit() { | |
| 40 | - AlipayConfig alipayConfig = new AlipayConfig(); | |
| 41 | - BeanUtil.copyProperties(config, alipayConfig, false); | |
| 42 | - // 真实客户端 | |
| 43 | - this.client = new DefaultAlipayClient(alipayConfig); | |
| 44 | - } | |
| 45 | - | |
| 46 | - @Override | |
| 47 | 28 | public PayCommonResult<AlipayTradePrecreateResponse> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { |
| 48 | 29 | // 构建 AlipayTradePrecreateModel 请求 |
| 49 | 30 | AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); |
| ... | ... | @@ -56,7 +37,7 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> |
| 56 | 37 | AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); |
| 57 | 38 | request.setBizModel(model); |
| 58 | 39 | request.setNotifyUrl(reqDTO.getNotifyUrl()); |
| 59 | - | |
| 40 | + request.setReturnUrl(reqDTO.getReturnUrl()); | |
| 60 | 41 | // 执行请求 |
| 61 | 42 | AlipayTradePrecreateResponse response; |
| 62 | 43 | try { |
| ... | ... | @@ -68,30 +49,4 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> |
| 68 | 49 | // TODO 芋艿:sub Code 需要测试下各种失败的情况 |
| 69 | 50 | return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping); |
| 70 | 51 | } |
| 71 | - | |
| 72 | - | |
| 73 | - | |
| 74 | - @Override | |
| 75 | - public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception { | |
| 76 | - //结果转换 | |
| 77 | - Map<String, String> params = data.getParams(); | |
| 78 | - return PayOrderNotifyRespDTO.builder().orderExtensionNo(params.get("out_trade_no")) | |
| 79 | - .channelOrderNo(params.get("trade_no")).channelUserId(params.get("seller_id")) | |
| 80 | - .tradeStatus(params.get("trade_status")) | |
| 81 | - .successTime(DateUtil.parse(params.get("notify_time"), "yyyy-MM-dd HH:mm:ss")) | |
| 82 | - .data(data.getBody()).build(); | |
| 83 | - | |
| 84 | - } | |
| 85 | - | |
| 86 | - @Override | |
| 87 | - public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { | |
| 88 | - //TODO 需要实现 | |
| 89 | - throw new UnsupportedOperationException("需要实现"); | |
| 90 | - } | |
| 91 | - | |
| 92 | - @Override | |
| 93 | - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { | |
| 94 | - //TODO 需要实现 | |
| 95 | - throw new UnsupportedOperationException(); | |
| 96 | - } | |
| 97 | 52 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
| 1 | 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.bean.BeanUtil; | |
| 4 | 3 | import cn.hutool.core.date.DateUtil; |
| 5 | 4 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; |
| 6 | -import cn.iocoder.yudao.framework.pay.core.client.dto.*; | |
| 7 | -import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | |
| 5 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; | |
| 8 | 6 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| 9 | -import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum; | |
| 10 | -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | |
| 11 | 7 | import com.alipay.api.AlipayApiException; |
| 12 | -import com.alipay.api.AlipayConfig; | |
| 13 | -import com.alipay.api.DefaultAlipayClient; | |
| 14 | -import com.alipay.api.domain.AlipayTradeRefundModel; | |
| 15 | 8 | import com.alipay.api.domain.AlipayTradeWapPayModel; |
| 16 | -import com.alipay.api.request.AlipayTradeRefundRequest; | |
| 17 | 9 | import com.alipay.api.request.AlipayTradeWapPayRequest; |
| 18 | -import com.alipay.api.response.AlipayTradeRefundResponse; | |
| 19 | 10 | import com.alipay.api.response.AlipayTradeWapPayResponse; |
| 20 | -import lombok.SneakyThrows; | |
| 21 | 11 | import lombok.extern.slf4j.Slf4j; |
| 22 | 12 | |
| 23 | -import java.net.SocketTimeoutException; | |
| 24 | -import java.util.Map; | |
| 25 | 13 | import java.util.Objects; |
| 26 | 14 | |
| 27 | -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | |
| 28 | - | |
| 29 | 15 | /** |
| 30 | 16 | * 支付宝【手机网站】的 PayClient 实现类 |
| 31 | 17 | * 文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay |
| ... | ... | @@ -33,23 +19,14 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString |
| 33 | 19 | * @author 芋道源码 |
| 34 | 20 | */ |
| 35 | 21 | @Slf4j |
| 36 | -public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> { | |
| 22 | +public class AlipayWapPayClient extends AbstractAlipayClient { | |
| 37 | 23 | |
| 38 | - private DefaultAlipayClient client; | |
| 39 | 24 | |
| 40 | 25 | public AlipayWapPayClient(Long channelId, AlipayPayClientConfig config) { |
| 41 | 26 | super(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), config, new AlipayPayCodeMapping()); |
| 42 | 27 | } |
| 43 | 28 | |
| 44 | 29 | @Override |
| 45 | - @SneakyThrows | |
| 46 | - protected void doInit() { | |
| 47 | - AlipayConfig alipayConfig = new AlipayConfig(); | |
| 48 | - BeanUtil.copyProperties(config, alipayConfig, false); | |
| 49 | - this.client = new DefaultAlipayClient(alipayConfig); | |
| 50 | - } | |
| 51 | - | |
| 52 | - @Override | |
| 53 | 30 | public PayCommonResult<AlipayTradeWapPayResponse> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { |
| 54 | 31 | // 构建 AlipayTradeWapPayModel 请求 |
| 55 | 32 | AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); |
| ... | ... | @@ -69,6 +46,7 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> |
| 69 | 46 | request.setBizModel(model); |
| 70 | 47 | request.setNotifyUrl(reqDTO.getNotifyUrl()); |
| 71 | 48 | request.setReturnUrl(reqDTO.getReturnUrl()); |
| 49 | + | |
| 72 | 50 | // 执行请求 |
| 73 | 51 | AlipayTradeWapPayResponse response; |
| 74 | 52 | try { |
| ... | ... | @@ -87,85 +65,11 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> |
| 87 | 65 | } |
| 88 | 66 | |
| 89 | 67 | |
| 90 | - /** | |
| 91 | - * 从支付宝通知返回参数中解析 PayOrderNotifyRespDTO, 通知具体参数参考 | |
| 92 | - * //https://opendocs.alipay.com/open/203/105286 | |
| 93 | - * @param data 通知结果 | |
| 94 | - * @return 解析结果 PayOrderNotifyRespDTO | |
| 95 | - * @throws Exception 解析失败,抛出异常 | |
| 96 | - */ | |
| 97 | - @Override | |
| 98 | - public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception { | |
| 99 | - Map<String, String> params = data.getParams(); | |
| 100 | - return PayOrderNotifyRespDTO.builder().orderExtensionNo(params.get("out_trade_no")) | |
| 101 | - .channelOrderNo(params.get("trade_no")).channelUserId(params.get("seller_id")) | |
| 102 | - .tradeStatus(params.get("trade_status")) | |
| 103 | - .successTime(DateUtil.parse(params.get("notify_time"), "yyyy-MM-dd HH:mm:ss")) | |
| 104 | - .data(data.getBody()).build(); | |
| 105 | - } | |
| 106 | 68 | |
| 107 | - @Override | |
| 108 | - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { | |
| 109 | - AlipayTradeRefundModel model=new AlipayTradeRefundModel(); | |
| 110 | - model.setTradeNo(reqDTO.getChannelOrderNo()); | |
| 111 | - model.setOutTradeNo(reqDTO.getPayTradeNo()); | |
| 112 | - model.setOutRequestNo(reqDTO.getRefundReqNo()); | |
| 113 | - model.setRefundAmount(calculateAmount(reqDTO.getAmount()).toString()); | |
| 114 | - model.setRefundReason(reqDTO.getReason()); | |
| 115 | - AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest(); | |
| 116 | - refundRequest.setBizModel(model); | |
| 117 | - PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO(); | |
| 118 | - try { | |
| 119 | - AlipayTradeRefundResponse response = client.execute(refundRequest); | |
| 120 | - log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response)); | |
| 121 | - if (response.isSuccess()) { | |
| 122 | - //退款成功,更新为PROCESSING_NOTIFY, 而不是 SYNC_SUCCESS 通过支付宝回调接口处理。退款导致触发的异步通知, | |
| 123 | - //退款导致触发的异步通知是发送到支付接口中设置的notify_url | |
| 124 | - //TODO 沙箱环境 返回 的tradeNo(渠道退款单号) 和 订单的tradNo 是一个值,是不是理解不对? | |
| 125 | - respDTO.setRespEnum(PayChannelRespEnum.PROCESSING_NOTIFY); | |
| 126 | - }else{ | |
| 127 | - //特殊处理 sub_code ACQ.SYSTEM_ERROR(系统错误), 需要调用重试任务 | |
| 128 | - //沙箱环境返回的貌似是”aop.ACQ.SYSTEM_ERROR“, 用contain | |
| 129 | - if (response.getSubCode().contains("ACQ.SYSTEM_ERROR")) { | |
| 130 | - respDTO.setRespEnum(PayChannelRespEnum.RETRY_FAILURE) | |
| 131 | - .setChannelErrMsg(response.getSubMsg()) | |
| 132 | - .setChannelErrCode(response.getSubCode()); | |
| 133 | - }else{ | |
| 134 | - //其他当做不可以重试的错误 | |
| 135 | - respDTO.setRespEnum(PayChannelRespEnum.CAN_NOT_RETRY_FAILURE) | |
| 136 | - .setChannelErrCode(response.getSubCode()) | |
| 137 | - .setChannelErrMsg(response.getSubMsg()); | |
| 138 | - } | |
| 139 | - } | |
| 140 | - return respDTO; | |
| 141 | - } catch (AlipayApiException e) { | |
| 142 | - //TODO 记录异常日志 | |
| 143 | - log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e); | |
| 144 | - Throwable cause = e.getCause(); | |
| 145 | - //网络 read time out 异常, 退款状态未知 | |
| 146 | - if (cause instanceof SocketTimeoutException) { | |
| 147 | - respDTO.setExceptionMsg(e.getMessage()) | |
| 148 | - .setRespEnum(PayChannelRespEnum.READ_TIME_OUT_EXCEPTION); | |
| 149 | - }else{ | |
| 150 | - respDTO.setExceptionMsg(e.getMessage()) | |
| 151 | - .setChannelErrCode(e.getErrCode()) | |
| 152 | - .setChannelErrMsg(e.getErrMsg()) | |
| 153 | - .setRespEnum(PayChannelRespEnum.CALL_EXCEPTION); | |
| 154 | - } | |
| 155 | - return respDTO; | |
| 156 | - } | |
| 157 | - } | |
| 158 | 69 | |
| 159 | - @Override | |
| 160 | - public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { | |
| 161 | - Map<String, String> params = notifyData.getParams(); | |
| 162 | - PayRefundNotifyDTO notifyDTO = PayRefundNotifyDTO.builder().channelOrderNo(params.get("trade_no")) | |
| 163 | - .tradeNo(params.get("out_trade_no")) | |
| 164 | - .reqNo(params.get("out_biz_no")) | |
| 165 | - .status(PayNotifyRefundStatusEnum.SUCCESS) | |
| 166 | - .refundSuccessTime(DateUtil.parse(params.get("gmt_refund"), "yyyy-MM-dd HH:mm:ss")) | |
| 167 | - .build(); | |
| 168 | - return notifyDTO; | |
| 169 | - } | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 170 | 74 | |
| 171 | 75 | } | ... | ... |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java
| ... | ... | @@ -4,7 +4,8 @@ import org.springframework.boot.SpringApplication; |
| 4 | 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| 5 | 5 | |
| 6 | 6 | @SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${yudao.info.base-package} 和 ${yudao.core-service.base-package} |
| 7 | -@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}", "${yudao.core-service.base-package}"})public class UserServerApplication { | |
| 7 | +@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}", "${yudao.core-service.base-package}"}) | |
| 8 | +public class UserServerApplication { | |
| 8 | 9 | |
| 9 | 10 | public static void main(String[] args) { |
| 10 | 11 | SpringApplication.run(UserServerApplication.class, args); | ... | ... |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/PayOrderController.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.userserver.modules.pay.controller.order; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.bean.BeanUtil; |
| 4 | 4 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; |
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayCommonCoreService; | |
| 5 | 6 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService; |
| 6 | 7 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService; |
| 7 | 8 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO; |
| ... | ... | @@ -38,6 +39,9 @@ public class PayOrderController { |
| 38 | 39 | @Resource |
| 39 | 40 | private PayRefundCoreService payRefundCoreService; |
| 40 | 41 | |
| 42 | + @Resource PayCommonCoreService commonCoreService; | |
| 43 | + | |
| 44 | + | |
| 41 | 45 | @PostMapping("/submit") |
| 42 | 46 | @ApiOperation("提交支付订单") |
| 43 | 47 | // @PreAuthenticated // TODO 暂时不加登陆验证,前端暂时没做好 |
| ... | ... | @@ -57,82 +61,53 @@ public class PayOrderController { |
| 57 | 61 | } |
| 58 | 62 | |
| 59 | 63 | // ========== 支付渠道的回调 ========== |
| 60 | - | |
| 64 | + //TODO 芋道源码 换成了统一的地址了 /notify/{channelId},测试通过可以删除 | |
| 61 | 65 | @PostMapping("/notify/wx-pub/{channelId}") |
| 62 | 66 | @ApiOperation("通知微信公众号支付的结果") |
| 63 | 67 | public String notifyWxPayOrder(@PathVariable("channelId") Long channelId, |
| 64 | 68 | @RequestBody String xmlData) throws Exception { |
| 65 | - payOrderCoreService.notifyPayOrder(channelId, PayChannelEnum.WX_PUB.getCode(), PayNotifyDataDTO.builder().body(xmlData).build()); | |
| 69 | + payOrderCoreService.notifyPayOrder(channelId, PayNotifyDataDTO.builder().body(xmlData).build()); | |
| 66 | 70 | return "success"; |
| 67 | 71 | } |
| 68 | 72 | |
| 69 | - @PostMapping("/notify/alipay-qr/{channelId}") | |
| 70 | - @ApiOperation("通知支付宝扫码支付的结果") | |
| 71 | - public String notifyAlipayQrPayOrder(@PathVariable("channelId") Long channelId, | |
| 72 | - @RequestParam Map<String, String> params, | |
| 73 | - @RequestBody String originData) throws Exception{ | |
| 74 | - payOrderCoreService.notifyPayOrder(channelId, PayChannelEnum.ALIPAY_QR.getCode(), | |
| 75 | - PayNotifyDataDTO.builder().params(params).body(originData).build()); | |
| 76 | - return "success"; | |
| 77 | - } | |
| 78 | - | |
| 79 | - @GetMapping(value = "/return/alipay-qr/{channelId}") | |
| 80 | - @ApiOperation("支付宝 wap 页面回跳") | |
| 81 | - public String returnAliPayQrPayOrder(@PathVariable("channelId") Long channelId){ | |
| 82 | - //TODO @jason 校验 是否支付宝调用。 支付宝publickey 可以根据 appId 跳转不同的页面 | |
| 83 | - System.out.println("支付成功"); | |
| 84 | - return "支付成功"; | |
| 85 | - } | |
| 86 | - | |
| 87 | - @PostMapping(value = "/notify/alipay-wap/{channelId}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |
| 88 | - @ApiOperation("支付宝 wap 页面回调") | |
| 89 | - public String notifyAliPayWapPayOrder(@PathVariable("channelId") Long channelId, | |
| 90 | - @RequestParam Map<String, String> params, | |
| 91 | - @RequestBody String originData) throws Exception { | |
| 92 | - //TODO 校验是否支付宝调用。 payclient 中加一个校验方法 | |
| 93 | - //支付宝退款交易也会触发支付回调接口 | |
| 94 | - //参考 https://opensupport.alipay.com/support/helpcenter/193/201602484851 | |
| 95 | - //判断是否为支付宝的退款交易 | |
| 96 | - if(isAliPayRefund(params)) { | |
| 97 | - //退款通知 | |
| 98 | - payRefundCoreService.notifyPayRefund(channelId,PayChannelEnum.ALIPAY_WAP.getCode(), PayNotifyDataDTO.builder().params(params).body(originData).build()); | |
| 99 | - }else{ | |
| 100 | - //支付通知 | |
| 101 | - payOrderCoreService.notifyPayOrder(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), PayNotifyDataDTO.builder().params(params).body(originData).build()); | |
| 102 | - } | |
| 103 | - return "success"; | |
| 104 | - } | |
| 105 | - | |
| 106 | - | |
| 107 | 73 | /** |
| 74 | + * 统一的跳转页面, 支付宝跳转参数说明 | |
| 108 | 75 | * https://opendocs.alipay.com/open/203/105285#%E5%89%8D%E5%8F%B0%E5%9B%9E%E8%B7%B3%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E |
| 109 | 76 | * @param channelId 渠道id |
| 110 | 77 | * @return 返回跳转页面 |
| 111 | 78 | */ |
| 112 | - @GetMapping(value = "/return/alipay-wap/{channelId}") | |
| 113 | - @ApiOperation("支付宝 wap 页面回跳") | |
| 114 | - public String returnAliPayWapPayOrder(@PathVariable("channelId") Long channelId){ | |
| 115 | - //TODO 校验 是否支付宝调用。 可以根据 appId 跳转不同的页面 | |
| 116 | - return "支付成功"; | |
| 79 | + @GetMapping(value = "/return/{channelId}") | |
| 80 | + @ApiOperation("渠道统一的支付成功返回地址") | |
| 81 | + public String returnAliPayOrder(@PathVariable("channelId") Long channelId, @RequestParam Map<String, String> params){ | |
| 82 | + //TODO 可以根据渠道和 app_id 返回不同的页面 | |
| 83 | + log.info("app_id is {}", params.get("app_id")); | |
| 84 | + return String.format("渠道[%s]支付成功", String.valueOf(channelId)); | |
| 117 | 85 | } |
| 118 | 86 | |
| 119 | 87 | /** |
| 120 | - * 是否是支付宝的退款交易 | |
| 121 | - * @param params http content-type application/x-www-form-urlencoded 的参数 | |
| 122 | - * @return | |
| 88 | + * 统一的渠道支付回调,支付宝的退款回调 | |
| 89 | + * @param channelId 渠道编号 | |
| 90 | + * @param params form 参数 | |
| 91 | + * @param originData http request body | |
| 92 | + * @return 成功返回 "success" | |
| 123 | 93 | */ |
| 124 | - private boolean isAliPayRefund(Map<String, String> params) { | |
| 125 | - if (params.containsKey("refund_fee")) { | |
| 126 | - return true; | |
| 127 | - } else { | |
| 128 | - return false; | |
| 94 | + @PostMapping(value = "/notify/{channelId}") | |
| 95 | + @ApiOperation("渠道统一的支付成功,或退款成功 通知url") | |
| 96 | + public String notifyChannelPay(@PathVariable("channelId") Long channelId, | |
| 97 | + @RequestParam Map<String, String> params, | |
| 98 | + @RequestBody String originData) throws Exception { | |
| 99 | + //校验是否是渠道回调 | |
| 100 | + commonCoreService.verifyNotifyData(channelId, PayNotifyDataDTO.builder().params(params).body(originData).build()); | |
| 101 | + //支付宝退款交易也会触发支付回调接口 | |
| 102 | + //参考 https://opensupport.alipay.com/support/helpcenter/193/201602484851 | |
| 103 | + //判断是否为退款通知 | |
| 104 | + if(commonCoreService.isRefundNotify(channelId, PayNotifyDataDTO.builder().params(params).body(originData).build())) { | |
| 105 | + //退款通知 | |
| 106 | + payRefundCoreService.notifyPayRefund(channelId,PayNotifyDataDTO.builder().params(params).body(originData).build()); | |
| 107 | + }else{ | |
| 108 | + //支付通知 | |
| 109 | + payOrderCoreService.notifyPayOrder(channelId,PayNotifyDataDTO.builder().params(params).body(originData).build()); | |
| 129 | 110 | } |
| 130 | - } | |
| 131 | - | |
| 132 | - @RequestMapping("/notify/test") | |
| 133 | - @ApiOperation("通知的测试接口") | |
| 134 | - public String notifyTest() { | |
| 135 | -// System.out.println(data); | |
| 136 | 111 | return "success"; |
| 137 | 112 | } |
| 138 | 113 | ... | ... |