|
|
@@ -1,5 +1,8 @@
|
|
|
package shop.alien.store.strategy.payment.impl;
|
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
import com.alipay.api.AlipayApiException;
|
|
|
import com.alipay.api.AlipayClient;
|
|
|
import com.alipay.api.AlipayConfig;
|
|
|
@@ -7,19 +10,30 @@ import com.alipay.api.DefaultAlipayClient;
|
|
|
import com.alipay.api.domain.*;
|
|
|
import com.alipay.api.request.AlipayTradeAppPayRequest;
|
|
|
import com.alipay.api.request.AlipayTradeQueryRequest;
|
|
|
+import com.alipay.api.request.AlipayTradeRefundRequest;
|
|
|
import com.alipay.api.response.AlipayTradeAppPayResponse;
|
|
|
import com.alipay.api.response.AlipayTradeQueryResponse;
|
|
|
+import com.alipay.api.response.AlipayTradeRefundResponse;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.collections4.map.HashedMap;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import shop.alien.entity.result.R;
|
|
|
+import shop.alien.entity.store.RefundRecord;
|
|
|
+import shop.alien.entity.store.StoreAliPayRefundLog;
|
|
|
import shop.alien.mapper.AlipayZftCreateRecordMapper;
|
|
|
+import shop.alien.store.service.RefundRecordService;
|
|
|
+import shop.alien.store.service.StoreAliPayRefundLogService;
|
|
|
import shop.alien.store.strategy.payment.PaymentStrategy;
|
|
|
+import shop.alien.util.common.UniqueRandomNumGenerator;
|
|
|
import shop.alien.util.common.constant.PaymentEnum;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
+import java.text.ParseException;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
@@ -31,6 +45,8 @@ import java.util.Random;
|
|
|
public class AlipayPartnerPaymentStrategyImpl implements PaymentStrategy {
|
|
|
|
|
|
private final AlipayZftCreateRecordMapper alipayZftCreateRecordMapper;
|
|
|
+ private final StoreAliPayRefundLogService storeAliPayRefundLogService;
|
|
|
+ private final RefundRecordService refundRecordService;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -192,13 +208,14 @@ public class AlipayPartnerPaymentStrategyImpl implements PaymentStrategy {
|
|
|
|
|
|
Map<String, Object> data = buildAlipayTradeQueryData(response);
|
|
|
if (response.isSuccess()) {
|
|
|
- return R.success("订单状态:" + data.get("tradeStatus"));
|
|
|
+// data.get("tradeStatus")
|
|
|
+ return R.data(200,"","支付成功" );
|
|
|
}
|
|
|
String sub = response.getSubMsg() != null ? response.getSubMsg() : "";
|
|
|
- return R.fail("订单查询失败:" + response.getMsg() + "(" + sub + ")");
|
|
|
+ return R.data(200,"","支付失败");
|
|
|
} catch (AlipayApiException e) {
|
|
|
log.error("支付宝订单查询异常, transactionId={}", transactionId, e);
|
|
|
- return R.fail("查询异常:" + e.getMessage());
|
|
|
+ return R.data(200,"","查询失败");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -255,7 +272,263 @@ public class AlipayPartnerPaymentStrategyImpl implements PaymentStrategy {
|
|
|
|
|
|
@Override
|
|
|
public String handleRefund(Map<String, String> params) throws Exception {
|
|
|
- return "";
|
|
|
+ log.info("[支付宝服务商退款] 处理退款请求,参数:{}", params);
|
|
|
+ try {
|
|
|
+ // 调用 alipay.trade.refund 接口发起退款
|
|
|
+ AlipayTradeRefundRequest refundRequest = buildRefundRequest(params);
|
|
|
+ AlipayTradeRefundResponse response = refundRun(refundRequest);
|
|
|
+ String refundResult = "";
|
|
|
+ JSONObject responseBody = JSONObject.parseObject(response.getBody());
|
|
|
+ JSONObject refundResponse = responseBody.getJSONObject("alipay_trade_refund_response");
|
|
|
+
|
|
|
+ if (response.isSuccess()) {
|
|
|
+ refundResult = "调用成功";
|
|
|
+ log.info("[支付宝服务商退款] 退款成功 outTradeNo={}, tradeNo={}, refundFee={}",
|
|
|
+ refundResponse.getString("out_trade_no"),
|
|
|
+ refundResponse.getString("trade_no"),
|
|
|
+ refundResponse.getString("refund_fee"));
|
|
|
+
|
|
|
+ // 保存退款信息到支付宝退款记录表
|
|
|
+ StoreAliPayRefundLog refundLog = new StoreAliPayRefundLog();
|
|
|
+ refundLog.setResponseCode(refundResponse.getString("code"));
|
|
|
+ refundLog.setResponseMsg(refundResponse.getString("msg"));
|
|
|
+ refundLog.setBuyerLogonId(refundResponse.getString("buyer_logon_id"));
|
|
|
+ refundLog.setBuyerOpenId(refundResponse.getString("buyer_open_id"));
|
|
|
+ refundLog.setFundChange(refundResponse.getString("fund_change"));
|
|
|
+ refundLog.setOutTradeNo(refundResponse.getString("out_trade_no"));
|
|
|
+ refundLog.setTradeNo(refundResponse.getString("trade_no"));
|
|
|
+ refundLog.setRefundFee(refundResponse.getString("refund_fee"));
|
|
|
+ refundLog.setSendBackFee(refundResponse.getString("send_back_fee"));
|
|
|
+ if (refundResponse.containsKey("refund_detail_item_list")) {
|
|
|
+ JSONArray refundDetailList = refundResponse.getJSONArray("refund_detail_item_list");
|
|
|
+ if (refundDetailList != null) {
|
|
|
+ refundLog.setRefundDetailItemList(JSON.toJSONString(refundDetailList));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String gmtRefundPayStr = refundResponse.getString("gmt_refund_pay");
|
|
|
+ if (StringUtils.isNotBlank(gmtRefundPayStr)) {
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ refundLog.setGmtRefundPay(sdf.parse(gmtRefundPayStr));
|
|
|
+ } catch (ParseException e) {
|
|
|
+ log.warn("[支付宝服务商退款] 解析退款时间失败: {}", gmtRefundPayStr, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ refundLog.setRefundReason(params.get("refundReason"));
|
|
|
+ refundLog.setOutRequestNo(params.get("partialRefundCode"));
|
|
|
+ refundLog.setAlipayCertSn(responseBody.getString("alipay_cert_sn"));
|
|
|
+ refundLog.setSign(responseBody.getString("sign"));
|
|
|
+ refundLog.setDeleteFlag(0);
|
|
|
+ refundLog.setCreatedTime(new Date());
|
|
|
+ storeAliPayRefundLogService.save(refundLog);
|
|
|
+
|
|
|
+ // 保存到通用退款记录表
|
|
|
+ try {
|
|
|
+ RefundRecord refundRecord = buildRefundRecordFromAlipayResponse(refundResponse, responseBody, params);
|
|
|
+ if (refundRecord != null) {
|
|
|
+ long count = refundRecordService.lambdaQuery()
|
|
|
+ .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
|
|
|
+ .count();
|
|
|
+ if (count == 0) {
|
|
|
+ refundRecordService.save(refundRecord);
|
|
|
+ log.info("[支付宝服务商退款] 退款记录已保存,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ } else {
|
|
|
+ log.info("[支付宝服务商退款] 退款记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[支付宝服务商退款] 保存 RefundRecord 失败", e);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.warn("[支付宝服务商退款] 退款失败 body={}", response.getBody());
|
|
|
+ refundResult = refundResponse.getString("sub_msg");
|
|
|
+
|
|
|
+ try {
|
|
|
+ RefundRecord refundRecord = buildRefundRecordFromAlipayError(refundResponse, responseBody, params);
|
|
|
+ if (refundRecord != null) {
|
|
|
+ long count = refundRecordService.lambdaQuery()
|
|
|
+ .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
|
|
|
+ .count();
|
|
|
+ if (count == 0) {
|
|
|
+ refundRecordService.save(refundRecord);
|
|
|
+ log.info("[支付宝服务商退款] 退款失败记录已保存,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ } else {
|
|
|
+ log.info("[支付宝服务商退款] 退款失败记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[支付宝服务商退款] 保存退款失败 RefundRecord 失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return refundResult;
|
|
|
+ } catch (AlipayApiException e) {
|
|
|
+ log.error("[支付宝服务商退款] 调用 alipay.trade.refund 异常 outTradeNo={}", params.get("outTradeNo"), e);
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建 alipay.trade.refund 退款请求。
|
|
|
+ */
|
|
|
+ private AlipayTradeRefundRequest buildRefundRequest(Map<String, String> params) {
|
|
|
+ AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
|
|
|
+ AlipayTradeRefundModel model = new AlipayTradeRefundModel();
|
|
|
+ model.setOutTradeNo(params.get("outTradeNo"));
|
|
|
+ model.setRefundAmount(params.get("refundAmount"));
|
|
|
+ model.setRefundReason(params.get("refundReason"));
|
|
|
+ if (StringUtils.isNotBlank(params.get("partialRefundCode"))) {
|
|
|
+ model.setOutRequestNo(params.get("partialRefundCode"));
|
|
|
+ }
|
|
|
+ // 设置退分账明细信息
|
|
|
+ List<OpenApiRoyaltyDetailInfoPojo> refundRoyaltyParameters = new ArrayList<OpenApiRoyaltyDetailInfoPojo>();
|
|
|
+ OpenApiRoyaltyDetailInfoPojo refundRoyaltyParameters0 = new OpenApiRoyaltyDetailInfoPojo();
|
|
|
+// refundRoyaltyParameters0.setAmount("0.01");
|
|
|
+// refundRoyaltyParameters0.setTransIn("2088101126708402");
|
|
|
+// refundRoyaltyParameters0.setRoyaltyType("transfer");
|
|
|
+// refundRoyaltyParameters0.setTransOut("2088101126765726");
|
|
|
+// refundRoyaltyParameters0.setTransOutType("userId");
|
|
|
+// refundRoyaltyParameters0.setRoyaltyScene("达人佣金");
|
|
|
+// refundRoyaltyParameters0.setTransInType("userId");
|
|
|
+// refundRoyaltyParameters0.setTransInName("张三");
|
|
|
+// refundRoyaltyParameters0.setDesc("分账给2088101126708402");
|
|
|
+// refundRoyaltyParameters.add(refundRoyaltyParameters0);
|
|
|
+
|
|
|
+ model.setRefundRoyaltyParameters(refundRoyaltyParameters);
|
|
|
+ request.setBizModel(model);
|
|
|
+ return request;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行退款请求(直付通使用公钥模式,调用 execute)。
|
|
|
+ */
|
|
|
+ private AlipayTradeRefundResponse refundRun(AlipayTradeRefundRequest request) throws AlipayApiException {
|
|
|
+ AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
|
|
|
+ return alipayClient.execute(request);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从支付宝退款成功响应构建 RefundRecord。
|
|
|
+ */
|
|
|
+ private RefundRecord buildRefundRecordFromAlipayResponse(JSONObject refundResponse, JSONObject responseBody, Map<String, String> params) {
|
|
|
+ try {
|
|
|
+ RefundRecord record = new RefundRecord();
|
|
|
+ record.setPayType(PaymentEnum.ALIPAY_PARTNER.getType());
|
|
|
+ record.setOutTradeNo(refundResponse.getString("out_trade_no"));
|
|
|
+ record.setTransactionId(refundResponse.getString("trade_no"));
|
|
|
+ String outRefundNo = params.get("partialRefundCode");
|
|
|
+ if (StringUtils.isBlank(outRefundNo)) {
|
|
|
+ outRefundNo = UniqueRandomNumGenerator.generateUniqueCode(19);
|
|
|
+ }
|
|
|
+ record.setOutRefundNo(outRefundNo);
|
|
|
+ record.setRefundStatus("SUCCESS");
|
|
|
+
|
|
|
+ String refundFeeStr = refundResponse.getString("refund_fee");
|
|
|
+ String sendBackFeeStr = refundResponse.getString("send_back_fee");
|
|
|
+ if (StringUtils.isNotBlank(refundFeeStr)) {
|
|
|
+ record.setRefundAmount(new BigDecimal(refundFeeStr).multiply(new BigDecimal(100)).longValue());
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(sendBackFeeStr)) {
|
|
|
+ record.setActualRefundAmount(new BigDecimal(sendBackFeeStr).multiply(new BigDecimal(100)).longValue());
|
|
|
+ }
|
|
|
+ String totalAmountStr = params.get("totalAmount");
|
|
|
+ if (StringUtils.isNotBlank(totalAmountStr)) {
|
|
|
+ record.setTotalAmount(new BigDecimal(totalAmountStr).multiply(new BigDecimal(100)).longValue());
|
|
|
+ }
|
|
|
+ record.setCurrency("CNY");
|
|
|
+ record.setRefundReason(params.get("refundReason"));
|
|
|
+
|
|
|
+ if (refundResponse.containsKey("refund_detail_item_list")) {
|
|
|
+ JSONArray refundDetailList = refundResponse.getJSONArray("refund_detail_item_list");
|
|
|
+ if (refundDetailList != null) {
|
|
|
+ record.setRefundDetail(JSON.toJSONString(refundDetailList));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ record.setUserReceivedAccount(refundResponse.getString("buyer_logon_id"));
|
|
|
+
|
|
|
+ String gmtRefundPayStr = refundResponse.getString("gmt_refund_pay");
|
|
|
+ if (StringUtils.isNotBlank(gmtRefundPayStr)) {
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ Date refundTime = sdf.parse(gmtRefundPayStr);
|
|
|
+ record.setRefundSuccessTime(refundTime);
|
|
|
+ record.setRefundCreateTime(refundTime);
|
|
|
+ } catch (ParseException e) {
|
|
|
+ log.warn("[支付宝服务商退款] 解析退款时间失败: {}", gmtRefundPayStr, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fillRefundRecordBusinessFields(record, params);
|
|
|
+ record.setResponseData(responseBody.toJSONString());
|
|
|
+ record.setDeleteFlag(0);
|
|
|
+ record.setCreatedTime(new Date());
|
|
|
+ return record;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[支付宝服务商退款] 构建 RefundRecord 失败", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从支付宝退款失败响应构建 RefundRecord。
|
|
|
+ */
|
|
|
+ private RefundRecord buildRefundRecordFromAlipayError(JSONObject refundResponse, JSONObject responseBody, Map<String, String> params) {
|
|
|
+ try {
|
|
|
+ RefundRecord record = new RefundRecord();
|
|
|
+ record.setPayType(PaymentEnum.ALIPAY_PARTNER.getType());
|
|
|
+ record.setOutTradeNo(params.get("outTradeNo"));
|
|
|
+ String outRefundNo = params.get("partialRefundCode");
|
|
|
+ if (StringUtils.isBlank(outRefundNo)) {
|
|
|
+ outRefundNo = UniqueRandomNumGenerator.generateUniqueCode(19);
|
|
|
+ }
|
|
|
+ record.setOutRefundNo(outRefundNo);
|
|
|
+ record.setRefundStatus("ABNORMAL");
|
|
|
+
|
|
|
+ String refundAmountStr = params.get("refundAmount");
|
|
|
+ if (StringUtils.isNotBlank(refundAmountStr)) {
|
|
|
+ record.setRefundAmount(new BigDecimal(refundAmountStr).multiply(new BigDecimal(100)).longValue());
|
|
|
+ }
|
|
|
+ String totalAmountStr = params.get("totalAmount");
|
|
|
+ if (StringUtils.isNotBlank(totalAmountStr)) {
|
|
|
+ record.setTotalAmount(new BigDecimal(totalAmountStr).multiply(new BigDecimal(100)).longValue());
|
|
|
+ }
|
|
|
+ record.setCurrency("CNY");
|
|
|
+ record.setRefundReason(params.get("refundReason"));
|
|
|
+ record.setErrorCode(refundResponse.getString("code"));
|
|
|
+ record.setErrorMsg(refundResponse.getString("sub_msg"));
|
|
|
+
|
|
|
+ fillRefundRecordBusinessFields(record, params);
|
|
|
+ record.setResponseData(responseBody.toJSONString());
|
|
|
+ record.setDeleteFlag(0);
|
|
|
+ record.setCreatedTime(new Date());
|
|
|
+ record.setRefundCreateTime(new Date());
|
|
|
+ return record;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[支付宝服务商退款] 构建退款失败 RefundRecord 失败", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 填充退款记录中的业务字段(userId、orderId、storeId)。
|
|
|
+ */
|
|
|
+ private void fillRefundRecordBusinessFields(RefundRecord record, Map<String, String> params) {
|
|
|
+ if (params.containsKey("userId")) {
|
|
|
+ try {
|
|
|
+ record.setUserId(Integer.parseInt(params.get("userId")));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("[支付宝服务商退款] 解析 userId 失败: {}", params.get("userId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (params.containsKey("orderId")) {
|
|
|
+ record.setOrderId(params.get("orderId"));
|
|
|
+ }
|
|
|
+ if (params.containsKey("storeId")) {
|
|
|
+ try {
|
|
|
+ record.setStoreId(Integer.parseInt(params.get("storeId")));
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("[支付宝服务商退款] 解析 storeId 失败: {}", params.get("storeId"));
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|