|
|
|
@@ -0,0 +1,636 @@ |
|
|
|
package com.iformall.service.pay.service.refund.wx; |
|
|
|
|
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.SortedMap; |
|
|
|
import java.util.TreeMap; |
|
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
|
|
|
|
import com.alibaba.fastjson.JSON; |
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.iformall.common.ErrorCode; |
|
|
|
import com.iformall.common.Result; |
|
|
|
import com.iformall.common.ResultData; |
|
|
|
import com.iformall.domain.po.WxAppinfo; |
|
|
|
import com.iformall.domain.po.WxPayAccount; |
|
|
|
import com.iformall.domain.po.WxPayOrder; |
|
|
|
import com.iformall.domain.po.WxRefundOrder; |
|
|
|
import com.iformall.enums.EnumPayMode; |
|
|
|
import com.iformall.enums.EnumPayType; |
|
|
|
import com.iformall.enums.EnumPayWay; |
|
|
|
import com.iformall.enums.EnumRefundStatus; |
|
|
|
import com.iformall.exception.MallinkException; |
|
|
|
import com.iformall.mapper.WxAppinfoMapper; |
|
|
|
import com.iformall.mapper.WxPayAccountMapper; |
|
|
|
import com.iformall.mapper.WxPayOrderMapper; |
|
|
|
import com.iformall.mapper.WxRefundOrderMapper; |
|
|
|
import com.iformall.pay.WxPay; |
|
|
|
import com.iformall.pay.WxPayment; |
|
|
|
import com.iformall.pay.WxRefundOrderP; |
|
|
|
import com.iformall.pay.WxRefundOrderSP; |
|
|
|
import com.iformall.service.pay.entity.RefundAdapterResult; |
|
|
|
import com.iformall.service.pay.entity.RefundNotifyAdapterResult; |
|
|
|
import com.iformall.service.pay.service.refund.RefundPayAdapterService; |
|
|
|
import com.iformall.utils.BeanUtils; |
|
|
|
import com.iformall.utils.CipherUtil; |
|
|
|
import com.iformall.utils.Utility; |
|
|
|
import com.iformall.utils.XmlUtil; |
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
|
|
|
|
@Slf4j |
|
|
|
@Service |
|
|
|
public class WxRefundAdapterService implements RefundPayAdapterService{ |
|
|
|
|
|
|
|
JSONObject errorRefundReqMap = JSON.parseObject("{\n" + |
|
|
|
" \"SYSTEMERROR\": {\n" + |
|
|
|
" \"detail\": \"接口返回错误\",\n" + |
|
|
|
" \"reason\": \"系统超时等\",\n" + |
|
|
|
" \"resolution\": \"请不要更换商户退款单号,请使用相同参数再次调用API。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"BIZERR_NEED_RETRY\": {\n" + |
|
|
|
" \"detail\": \"退款业务流程错误,需要商户触发重试来解决\",\n" + |
|
|
|
" \"reason\": \"并发情况下,业务被拒绝,商户重试即可解决\",\n" + |
|
|
|
" \"resolution\": \"请不要更换商户退款单号,请使用相同参数再次调用API。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"TRADE_OVERDUE\": {\n" + |
|
|
|
" \"detail\": \"订单已经超过退款期限\",\n" + |
|
|
|
" \"reason\": \"订单已经超过可退款的最大期限(支付后一年内可退款)\\t\",\n" + |
|
|
|
" \"resolution\": \"请选择其他方式自行退款\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"ERROR\": {\n" + |
|
|
|
" \"detail\": \"业务错误\",\n" + |
|
|
|
" \"reason\": \"申请退款业务发生错误\",\n" + |
|
|
|
" \"resolution\": \"该错误都会返回具体的错误原因,请根据实际返回做相应处理。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"USER_ACCOUNT_ABNORMAL\": {\n" + |
|
|
|
" \"detail\": \"退款请求失败\",\n" + |
|
|
|
" \"reason\": \"用户帐号注销\",\n" + |
|
|
|
" \"resolution\": \"此状态代表退款申请失败,商户可自行处理退款。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"INVALID_REQ_TOO_MUCH\": {\n" + |
|
|
|
" \"detail\": \"无效请求过多\",\n" + |
|
|
|
" \"reason\": \"连续错误请求数过多被系统短暂屏蔽\\t\",\n" + |
|
|
|
" \"resolution\": \"请检查业务是否正常,确认业务正常后请在1分钟后再来重试\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"NOTENOUGH\": {\n" + |
|
|
|
" \"detail\": \"余额不足\",\n" + |
|
|
|
" \"reason\": \"商户可用退款余额不足\",\n" + |
|
|
|
" \"resolution\": \"此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"INVALID_TRANSACTIONID\": {\n" + |
|
|
|
" \"detail\": \"无效transaction_id\",\n" + |
|
|
|
" \"reason\": \"请求参数未按指引进行填写\",\n" + |
|
|
|
" \"resolution\": \"请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"PARAM_ERROR\": {\n" + |
|
|
|
" \"detail\": \"参数错误\",\n" + |
|
|
|
" \"reason\": \"请求参数未按指引进行填写\",\n" + |
|
|
|
" \"resolution\": \"请求参数错误,请重新检查再调用退款申请\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"APPID_NOT_EXIST\": {\n" + |
|
|
|
" \"detail\": \"APPID不存在\",\n" + |
|
|
|
" \"reason\": \"参数中缺少APPID\",\n" + |
|
|
|
" \"resolution\": \"请检查APPID是否正确\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"MCHID_NOT_EXIST\": {\n" + |
|
|
|
" \"detail\": \"MCHID不存在\",\n" + |
|
|
|
" \"reason\": \"参数中缺少MCHID\",\n" + |
|
|
|
" \"resolution\": \"请检查MCHID是否正确\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"REQUIRE_POST_METHOD\": {\n" + |
|
|
|
" \"detail\": \"请使用post方法\",\n" + |
|
|
|
" \"reason\": \"未使用post传递参数\",\n" + |
|
|
|
" \"resolution\": \"请检查请求参数是否通过post方法提交\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"SIGNERROR\": {\n" + |
|
|
|
" \"detail\": \"签名错误\",\n" + |
|
|
|
" \"reason\": \"参数签名结果不正确\",\n" + |
|
|
|
" \"resolution\": \"请检查签名参数和方法是否都符合签名算法要求\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"XML_FORMAT_ERROR\": {\n" + |
|
|
|
" \"detail\": \"XML格式错误\",\n" + |
|
|
|
" \"reason\": \"XML格式错误\",\n" + |
|
|
|
" \"resolution\": \"请检查XML参数格式是否正确\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"FREQUENCY_LIMITED\": {\n" + |
|
|
|
" \"detail\": \"频率限制\",\n" + |
|
|
|
" \"reason\": \"2个月之前的订单申请退款有频率限制\",\n" + |
|
|
|
" \"resolution\": \"该笔退款未受理,请降低频率后重试\"\n" + |
|
|
|
" }\n" + |
|
|
|
"}"); |
|
|
|
|
|
|
|
JSONObject errorRefundQueryMap = JSON.parseObject("{\n" + |
|
|
|
" \"SYSTEMERROR\": {\n" + |
|
|
|
" \"detail\": \"接口返回错误\",\n" + |
|
|
|
" \"reason\": \"系统超时等\",\n" + |
|
|
|
" \"resolution\": \"请不要更换商户退款单号,请使用相同参数再次调用API。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"BIZERR_NEED_RETRY\": {\n" + |
|
|
|
" \"detail\": \"退款业务流程错误,需要商户触发重试来解决\",\n" + |
|
|
|
" \"reason\": \"并发情况下,业务被拒绝,商户重试即可解决\",\n" + |
|
|
|
" \"resolution\": \"请不要更换商户退款单号,请使用相同参数再次调用API。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"TRADE_OVERDUE\": {\n" + |
|
|
|
" \"detail\": \"订单已经超过退款期限\",\n" + |
|
|
|
" \"reason\": \"订单已经超过可退款的最大期限(支付后一年内可退款)\\t\",\n" + |
|
|
|
" \"resolution\": \"请选择其他方式自行退款\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"ERROR\": {\n" + |
|
|
|
" \"detail\": \"业务错误\",\n" + |
|
|
|
" \"reason\": \"申请退款业务发生错误\",\n" + |
|
|
|
" \"resolution\": \"该错误都会返回具体的错误原因,请根据实际返回做相应处理。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"USER_ACCOUNT_ABNORMAL\": {\n" + |
|
|
|
" \"detail\": \"退款请求失败\",\n" + |
|
|
|
" \"reason\": \"用户帐号注销\",\n" + |
|
|
|
" \"resolution\": \"此状态代表退款申请失败,商户可自行处理退款。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"INVALID_REQ_TOO_MUCH\": {\n" + |
|
|
|
" \"detail\": \"无效请求过多\",\n" + |
|
|
|
" \"reason\": \"连续错误请求数过多被系统短暂屏蔽\\t\",\n" + |
|
|
|
" \"resolution\": \"请检查业务是否正常,确认业务正常后请在1分钟后再来重试\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"NOTENOUGH\": {\n" + |
|
|
|
" \"detail\": \"余额不足\",\n" + |
|
|
|
" \"reason\": \"商户可用退款余额不足\",\n" + |
|
|
|
" \"resolution\": \"此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"INVALID_TRANSACTIONID\": {\n" + |
|
|
|
" \"detail\": \"无效transaction_id\",\n" + |
|
|
|
" \"reason\": \"请求参数未按指引进行填写\",\n" + |
|
|
|
" \"resolution\": \"请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"PARAM_ERROR\": {\n" + |
|
|
|
" \"detail\": \"参数错误\",\n" + |
|
|
|
" \"reason\": \"请求参数未按指引进行填写\",\n" + |
|
|
|
" \"resolution\": \"请求参数错误,请重新检查再调用退款申请\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"APPID_NOT_EXIST\": {\n" + |
|
|
|
" \"detail\": \"APPID不存在\",\n" + |
|
|
|
" \"reason\": \"参数中缺少APPID\",\n" + |
|
|
|
" \"resolution\": \"请检查APPID是否正确\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"MCHID_NOT_EXIST\": {\n" + |
|
|
|
" \"detail\": \"MCHID不存在\",\n" + |
|
|
|
" \"reason\": \"参数中缺少MCHID\",\n" + |
|
|
|
" \"resolution\": \"请检查MCHID是否正确\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"REQUIRE_POST_METHOD\": {\n" + |
|
|
|
" \"detail\": \"请使用post方法\",\n" + |
|
|
|
" \"reason\": \"未使用post传递参数\",\n" + |
|
|
|
" \"resolution\": \"请检查请求参数是否通过post方法提交\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"SIGNERROR\": {\n" + |
|
|
|
" \"detail\": \"签名错误\",\n" + |
|
|
|
" \"reason\": \"参数签名结果不正确\",\n" + |
|
|
|
" \"resolution\": \"请检查签名参数和方法是否都符合签名算法要求\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"XML_FORMAT_ERROR\": {\n" + |
|
|
|
" \"detail\": \"XML格式错误\",\n" + |
|
|
|
" \"reason\": \"XML格式错误\",\n" + |
|
|
|
" \"resolution\": \"请检查XML参数格式是否正确\"\n" + |
|
|
|
" },\n" + |
|
|
|
" \"FREQUENCY_LIMITED\": {\n" + |
|
|
|
" \"detail\": \"频率限制\",\n" + |
|
|
|
" \"reason\": \"2个月之前的订单申请退款有频率限制\",\n" + |
|
|
|
" \"resolution\": \"该笔退款未受理,请降低频率后重试\"\n" + |
|
|
|
" }\n" + |
|
|
|
"}"); |
|
|
|
|
|
|
|
private WxRefundOrderP generateWxRefundOrderP(WxPayAccount payAccount,WxAppinfo appInfo,WxRefundOrder record,WxPayOrder payOrder,EnumPayType payType) { |
|
|
|
String noncestr = Utility.generate32UUID(); |
|
|
|
WxRefundOrderP wxRefundOrderP = new WxRefundOrderP(); |
|
|
|
wxRefundOrderP.setAppid(appInfo.getAppId()); |
|
|
|
wxRefundOrderP.setMch_id(payAccount.getMchId()); |
|
|
|
wxRefundOrderP.setNonce_str(noncestr); |
|
|
|
// 微信内部订单号 |
|
|
|
if (record.getTransactionId() != null) |
|
|
|
wxRefundOrderP.setTransaction_id(record.getTransactionId()); |
|
|
|
// 支付订单号 |
|
|
|
wxRefundOrderP.setOut_trade_no(payOrder.getPayOrderNo()); |
|
|
|
// 退款支付订单号 |
|
|
|
wxRefundOrderP.setOut_refund_no(String.valueOf(record.getId())); |
|
|
|
wxRefundOrderP.setTotal_fee(record.getTotalFee()); |
|
|
|
wxRefundOrderP.setRefund_fee(record.getRefundFee()); |
|
|
|
if(null != payType) { |
|
|
|
if (payType == EnumPayType.PAY_B_REFUND) { |
|
|
|
wxRefundOrderP.setRefund_desc("B端商户退款"); |
|
|
|
} else if (payType == EnumPayType.PAY_ADMIN_REFUND) { |
|
|
|
wxRefundOrderP.setRefund_desc("管理端商户退款"); |
|
|
|
} else if (payType == EnumPayType.PAY_AUTO_REFUND) { |
|
|
|
wxRefundOrderP.setRefund_desc("超期自动退款"); |
|
|
|
} else { |
|
|
|
wxRefundOrderP.setRefund_desc("用户自己退款"); |
|
|
|
} |
|
|
|
} |
|
|
|
wxRefundOrderP.setNotify_url(payAccount.getRefundNotifyUrl()); |
|
|
|
return wxRefundOrderP; |
|
|
|
} |
|
|
|
|
|
|
|
private RefundAdapterResult getReusltFromp(String response,WxRefundOrder record,WxPayOrder payOrder) throws MallinkException{ |
|
|
|
Map<String, String> returnMap = WxPayment.xmlToMap(response); |
|
|
|
String result_no = returnMap.get("result_code"); |
|
|
|
String refund_id = returnMap.get("refund_id"); |
|
|
|
// 设置 微信退款订单号 |
|
|
|
record.setRefundId(refund_id); |
|
|
|
record.setRefundVendor(payOrder.getPayVendor()); |
|
|
|
if ("SUCCESS".equals(result_no)) { |
|
|
|
log.info("微信退款订单申请成功: " + returnMap.toString()); |
|
|
|
try { |
|
|
|
return new RefundAdapterResult(true,EnumRefundStatus.REFUND_REQ_SUCCESS.getCode(),"退款订单申请成功",returnMap); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("微信退款订单更新入库出错: " + e.getMessage() + ", record: " + record.toString()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR); |
|
|
|
} |
|
|
|
} else { |
|
|
|
String errDesc = returnMap.get("err_code_des"); |
|
|
|
if (errDesc.equalsIgnoreCase("订单已全额退款")) { |
|
|
|
return new RefundAdapterResult(true,EnumRefundStatus.REFUND_REQ_SUCCESS.getCode(),"退款订单申请成功",returnMap); |
|
|
|
} |
|
|
|
log.error("微信退款订单申请失败: " + response); |
|
|
|
String errMsg = ""; |
|
|
|
JSONObject errObj = errorRefundReqMap.getJSONObject(result_no); |
|
|
|
if (errObj != null) { |
|
|
|
errMsg = errObj.toJSONString(); |
|
|
|
record.setFailReason(errMsg); |
|
|
|
} else { |
|
|
|
errMsg = returnMap.get("return_msg"); |
|
|
|
record.setFailReason(errMsg); |
|
|
|
} |
|
|
|
if (errDesc.equalsIgnoreCase("超期订单不允许退款")) { |
|
|
|
// 更新券状态 |
|
|
|
throw new MallinkException(ErrorCode.REFUND_NOT_ALLOW); |
|
|
|
} |
|
|
|
return new RefundAdapterResult(false, EnumRefundStatus.REFUND_REQ_FAIL.getCode(), errMsg, returnMap); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private WxRefundOrderSP generateWxRefundOrderSP(WxPayAccount payAccount,WxAppinfo appInfo,WxRefundOrder record,WxPayOrder payOrder,EnumPayType payType) { |
|
|
|
String noncestr = Utility.generate32UUID(); |
|
|
|
WxRefundOrderSP wxRefundOrderSP = new WxRefundOrderSP(); |
|
|
|
wxRefundOrderSP.setAppid(appInfo.getParentAppId()); |
|
|
|
wxRefundOrderSP.setSub_appid(appInfo.getAppId()); |
|
|
|
wxRefundOrderSP.setMch_id(payAccount.getMchId()); |
|
|
|
wxRefundOrderSP.setSub_mch_id(payAccount.getSubMchId()); |
|
|
|
wxRefundOrderSP.setNonce_str(noncestr); |
|
|
|
// 微信内部订单号 |
|
|
|
if (record.getTransactionId() != null) |
|
|
|
wxRefundOrderSP.setTransaction_id(record.getTransactionId()); |
|
|
|
// 支付订单号 |
|
|
|
wxRefundOrderSP.setOut_trade_no(payOrder.getPayOrderNo()); |
|
|
|
// 退款支付订单号 |
|
|
|
wxRefundOrderSP.setOut_refund_no(String.valueOf(record.getId())); |
|
|
|
wxRefundOrderSP.setTotal_fee(record.getTotalFee()); |
|
|
|
wxRefundOrderSP.setRefund_fee(record.getRefundFee()); |
|
|
|
if(null != payType) { |
|
|
|
if (payType == EnumPayType.PAY_B_REFUND) { |
|
|
|
wxRefundOrderSP.setRefund_desc("B端商户退款"); |
|
|
|
} else if (payType == EnumPayType.PAY_ADMIN_REFUND) { |
|
|
|
wxRefundOrderSP.setRefund_desc("管理端商户退款"); |
|
|
|
} else if (payType == EnumPayType.PAY_AUTO_REFUND) { |
|
|
|
wxRefundOrderSP.setRefund_desc("超期自动退款"); |
|
|
|
} else { |
|
|
|
wxRefundOrderSP.setRefund_desc("用户自己退款"); |
|
|
|
} |
|
|
|
} |
|
|
|
wxRefundOrderSP.setNotify_url(payAccount.getRefundNotifyUrl()); |
|
|
|
wxRefundOrderSP.setSign_type("HMAC-SHA256"); |
|
|
|
return wxRefundOrderSP; |
|
|
|
} |
|
|
|
|
|
|
|
private RefundAdapterResult getResultFromSp(String response,WxRefundOrder record,WxPayOrder payOrder) { |
|
|
|
Map<String, String> returnMap = WxPayment.xmlToMap(response); |
|
|
|
String result_no = returnMap.get("result_code"); |
|
|
|
String refund_id = returnMap.get("refund_id"); |
|
|
|
// 设置 微信退款订单号 |
|
|
|
record.setRefundId(refund_id); |
|
|
|
record.setRefundVendor(payOrder.getPayVendor()); |
|
|
|
if ("SUCCESS".equals(result_no)) { |
|
|
|
log.info("微信退款订单申请成功: " + returnMap.toString()); |
|
|
|
try { |
|
|
|
return new RefundAdapterResult(true, EnumRefundStatus.REFUND_REQ_SUCCESS.getCode(), "退款订单申请成功", returnMap); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("微信退款订单更新入库出错: " + e.getMessage() + ", record: " + record.toString()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), "订单状态更新失败"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 特殊处理 |
|
|
|
String errDesc = returnMap.get("err_code_des"); |
|
|
|
if (errDesc.equalsIgnoreCase("订单已全额退款")) { |
|
|
|
return new RefundAdapterResult(true, EnumRefundStatus.REFUND_REQ_SUCCESS.getCode(), "退款订单申请成功", returnMap); |
|
|
|
} |
|
|
|
log.error("微信退款订单申请失败: " + response); |
|
|
|
String errMsg = ""; |
|
|
|
JSONObject errObj = errorRefundReqMap.getJSONObject(result_no); |
|
|
|
if (errObj != null) { |
|
|
|
errMsg = errObj.toJSONString(); |
|
|
|
record.setFailReason(errMsg); |
|
|
|
} else { |
|
|
|
errMsg = returnMap.get("err_code_des"); |
|
|
|
record.setFailReason(errMsg); |
|
|
|
} |
|
|
|
if (errDesc.equalsIgnoreCase("超期订单不允许退款")) { |
|
|
|
// 更新券状态 |
|
|
|
throw new MallinkException(ErrorCode.REFUND_NOT_ALLOW); |
|
|
|
} |
|
|
|
return new RefundAdapterResult(false,EnumRefundStatus.REFUND_REQ_FAIL.getCode(),errMsg,returnMap); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public RefundAdapterResult refund(WxPayAccount payAccount,WxAppinfo appInfo,WxRefundOrder record,WxPayOrder payOrder,EnumPayType payType) { |
|
|
|
if (StringUtils.isBlank(payAccount.getApiKey())) { |
|
|
|
log.error("支付密钥为空"); |
|
|
|
throw new MallinkException(ErrorCode.API_KEY_NOT_FOUND.getCode(), "支付密钥为空,请联系商城管理员"); |
|
|
|
} |
|
|
|
if (StringUtils.isBlank(payAccount.getCertPath())) { |
|
|
|
log.error("证书路径为空"); |
|
|
|
throw new MallinkException(ErrorCode.CERT_PATH_NOT_FOUND.getCode(), "证书路径为空,请联系商城管理员"); |
|
|
|
} |
|
|
|
if (!Utility.isFileExist(payAccount.getCertPath())) { |
|
|
|
log.error("证书文件不存在"); |
|
|
|
throw new MallinkException(ErrorCode.CERT_PATH_NOT_FOUND.getCode(), "证书文件不存在,请联系商城管理员"); |
|
|
|
} |
|
|
|
|
|
|
|
// 实际支付 |
|
|
|
// 向微信提交退款申请 |
|
|
|
if (payAccount.getType() == EnumPayMode.MCH.getCode()) { |
|
|
|
// 普通商户模式 |
|
|
|
WxRefundOrderP wxRefundOrderP = generateWxRefundOrderP(payAccount, appInfo, record, payOrder, payType); |
|
|
|
|
|
|
|
Map signMap = null; |
|
|
|
try { |
|
|
|
signMap = BeanUtils.toStringMap(wxRefundOrderP); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("退款命令生辰: " + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "退款签名异常"); |
|
|
|
} |
|
|
|
|
|
|
|
String signAgent = WxPayment.createSign(signMap, payAccount.getApiKey()); |
|
|
|
signMap.put("sign", signAgent); |
|
|
|
String response = null; |
|
|
|
try { |
|
|
|
response = WxPay.orderRefund(signMap, payAccount.getCertPath(), payAccount.getMchId()); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("退款异常: " + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), "退款异常"); |
|
|
|
} |
|
|
|
log.info("微信退款订单:" + wxRefundOrderP.toString() + ", response: " + response.toString()); |
|
|
|
return getReusltFromp(response, record, payOrder); |
|
|
|
} else { |
|
|
|
// 服务商模式 |
|
|
|
WxRefundOrderSP wxRefundOrderSP = generateWxRefundOrderSP(payAccount, appInfo, record, payOrder, payType); |
|
|
|
|
|
|
|
Map signMap = null; |
|
|
|
try { |
|
|
|
signMap = BeanUtils.toStringMap(wxRefundOrderSP); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("退款命令生成: " + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "退款签名异常"); |
|
|
|
} |
|
|
|
|
|
|
|
String signAgent = WxPayment.createSignHMAC(signMap, payAccount.getApiKey()); |
|
|
|
signMap.put("sign", signAgent); |
|
|
|
String response = null; |
|
|
|
try { |
|
|
|
response = WxPay.orderRefund(signMap, payAccount.getCertPath(), payAccount.getMchId()); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("退款异常: " + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), "退款异常"); |
|
|
|
} |
|
|
|
log.info("微信退款订单:" + wxRefundOrderSP.toString() + ", response: " + response.toString()); |
|
|
|
return getResultFromSp(response, record, payOrder); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public RefundAdapterResult queryRefund(WxAppinfo appInfo,WxPayAccount payAccount, WxRefundOrder record) { |
|
|
|
if (payAccount.getType() == EnumPayMode.MCH.getCode()) { |
|
|
|
Map<String, String> paramMap = WxPayment.buildWeappRefundQueryMap( |
|
|
|
appInfo.getAppId(), payAccount.getMchId(), |
|
|
|
record.getTransactionId(), record.getPayOrderNo(), String.valueOf(record.getId()), |
|
|
|
record.getRefundId(), payAccount.getApiKey()); |
|
|
|
String response = WxPay.orderRefundQuery(paramMap); |
|
|
|
Map<String, String> returnMap = WxPayment.xmlToMap(response); |
|
|
|
String result_no = returnMap.get("result_code"); |
|
|
|
if ("SUCCESS".equals(result_no)) { |
|
|
|
String refund_id = returnMap.get("refund_id"); |
|
|
|
return new RefundAdapterResult(true,0,"退款",returnMap); |
|
|
|
} else { |
|
|
|
String errMsg = ""; |
|
|
|
JSONObject errObj = errorRefundReqMap.getJSONObject(result_no); |
|
|
|
if (errObj != null) { |
|
|
|
errMsg = errObj.toJSONString(); |
|
|
|
} else { |
|
|
|
errMsg = returnMap.get("return_msg"); |
|
|
|
} |
|
|
|
return new RefundAdapterResult(false,ErrorCode.REFUND_ORDER_ERROR.getCode(),errMsg,returnMap); |
|
|
|
} |
|
|
|
} else { |
|
|
|
Map<String, String> paramMap = WxPayment.buildSWeappRefundQueryMap( |
|
|
|
appInfo.getParentAppId(), appInfo.getAppId(), payAccount.getMchId(), payAccount.getSubMchId(), |
|
|
|
record.getTransactionId(), record.getPayOrderNo(), String.valueOf(record.getId()), record.getRefundId(), payAccount.getApiKey()); |
|
|
|
String response = WxPay.orderRefundQuery(paramMap); |
|
|
|
Map<String, String> returnMap = WxPayment.xmlToMap(response); |
|
|
|
String result_no = returnMap.get("result_code"); |
|
|
|
if ("SUCCESS".equals(result_no)) { |
|
|
|
String refund_id = returnMap.get("refund_id"); |
|
|
|
return new RefundAdapterResult(true,0,"退款",returnMap); |
|
|
|
} else { |
|
|
|
String errMsg = ""; |
|
|
|
JSONObject errObj = errorRefundQueryMap.getJSONObject(result_no); |
|
|
|
if (errObj != null) { |
|
|
|
errMsg = errObj.toJSONString(); |
|
|
|
} else { |
|
|
|
errMsg = returnMap.get("return_msg"); |
|
|
|
} |
|
|
|
return new RefundAdapterResult(false,ErrorCode.REFUND_ORDER_ERROR.getCode(),errMsg,returnMap); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Autowired |
|
|
|
WxAppinfoMapper wxAppinfoMapper; |
|
|
|
@Autowired |
|
|
|
WxPayAccountMapper wxPayAccountMapper; |
|
|
|
@Autowired |
|
|
|
WxPayOrderMapper wxPayOrderMapper; |
|
|
|
@Autowired |
|
|
|
WxRefundOrderMapper wxRefundOrderMapper; |
|
|
|
|
|
|
|
|
|
|
|
private RefundNotifyAdapterResult notifyErrorResult(String msg) { |
|
|
|
SortedMap resultMap = new TreeMap(); |
|
|
|
resultMap.put("return_code", "FAIL"); |
|
|
|
resultMap.put("return_msg", msg); |
|
|
|
return new RefundNotifyAdapterResult(false,ErrorCode.REFUND_ORDER_ERROR.getCode(),msg,XmlUtil.getRequestXml(resultMap)); |
|
|
|
} |
|
|
|
|
|
|
|
private RefundNotifyAdapterResult notifySuccessResult(String transactionId,String refundId,WxRefundOrder refundOrder) { |
|
|
|
SortedMap resultMap = new TreeMap(); |
|
|
|
resultMap.put("return_code", "SUCCESS"); |
|
|
|
resultMap.put("return_msg", "OK"); |
|
|
|
return new RefundNotifyAdapterResult(transactionId, refundId,refundOrder,XmlUtil.getRequestXml(resultMap)); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public RefundNotifyAdapterResult notify(Map<String, String> paramMap, EnumPayWay payWay) { |
|
|
|
// how to get wechatAppId, wechatMchId, partnerKey |
|
|
|
String appId = paramMap.get("appid"); |
|
|
|
String subAppId = paramMap.get("sub_appid"); |
|
|
|
String mchId = paramMap.get("mch_id"); |
|
|
|
String subMchId = paramMap.get("sub_mch_id"); |
|
|
|
if (StringUtils.isBlank(subAppId) && StringUtils.isBlank(subMchId)) { |
|
|
|
// 普通商户号 |
|
|
|
WxAppinfo appinfo = wxAppinfoMapper.findByAppId(appId); |
|
|
|
if (appinfo == null) { |
|
|
|
log.error("未找到appid信息:" + appId); |
|
|
|
throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND); |
|
|
|
} |
|
|
|
WxPayAccount payAccount = wxPayAccountMapper.selectById(appinfo.getPayId()); |
|
|
|
if (payAccount == null) { |
|
|
|
log.error("未找到mch_id信息:" + mchId); |
|
|
|
throw new MallinkException(ErrorCode.MCH_INFO_NOT_FOUND); |
|
|
|
} |
|
|
|
if (!mchId.equalsIgnoreCase(payAccount.getMchId())) { |
|
|
|
log.error("mch_id不对应:" + mchId + ",account:" + payAccount.getMchId()); |
|
|
|
throw new MallinkException(ErrorCode.MCH_INFO_NOT_EQUAL); |
|
|
|
} |
|
|
|
String partnerKey = payAccount.getApiKey(); |
|
|
|
try { |
|
|
|
if (payWay.getType() == EnumPayWay.EnumPayWayType.WX_MINIPAY) { |
|
|
|
if (!"SUCCESS".equals(paramMap.get("return_code"))) { |
|
|
|
log.warn("notify refund, wxpay status not success, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("订单状态码非SUCCESS"); |
|
|
|
} |
|
|
|
|
|
|
|
// 解密req_info |
|
|
|
String req_info = CipherUtil.decryptReqInfo(paramMap.get("req_info"), partnerKey); |
|
|
|
Map<String, String> reqMap = WxPayment.xmlToMap(req_info); |
|
|
|
|
|
|
|
String payOrderNo = reqMap.get("out_trade_no"); |
|
|
|
Long payOrderId = Long.valueOf(payOrderNo); |
|
|
|
WxPayOrder payOrder = wxPayOrderMapper.selectById(payOrderId); |
|
|
|
if (payOrder == null) { |
|
|
|
log.warn("notify refund, wxpay check pay order not exists, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("支付订单不存在"); |
|
|
|
} |
|
|
|
// 验证支付金额 |
|
|
|
if (!reqMap.get("total_fee").equals(payOrder.getPayAmount().toString())) { |
|
|
|
log.warn("notify refund, wxpay check total_fee is invalid, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("支付订单总金额不一致"); |
|
|
|
} |
|
|
|
String refundIdStr = reqMap.get("out_refund_no"); |
|
|
|
Long refundOrderId = Long.valueOf(refundIdStr); |
|
|
|
WxRefundOrder refundOrder = wxRefundOrderMapper.selectById(refundOrderId); |
|
|
|
if (refundOrder == null) { |
|
|
|
log.warn("notify refund, wxpay check pay order not exists, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("退款订单不存在"); |
|
|
|
} |
|
|
|
// 验证支付金额 |
|
|
|
if (!reqMap.get("total_fee").equals(refundOrder.getTotalFee().toString())) { |
|
|
|
log.warn("notify refund, wxpay check total_fee is invalid, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("订单总金额不一致"); |
|
|
|
} |
|
|
|
// 验证退款金额 |
|
|
|
if (!reqMap.get("refund_fee").equals(refundOrder.getRefundFee().toString())) { |
|
|
|
log.warn("notify refund, wxpay check refund_fee is invalid, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("退款总金额不一致"); |
|
|
|
} |
|
|
|
|
|
|
|
// 处理支付成功 |
|
|
|
//handleRefundSuccess(refundOrder, reqMap.get("transaction_id"), reqMap.get("refund_id")); |
|
|
|
log.info("notify refund, wxpay checksign success, paramMap:{}, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifySuccessResult(reqMap.get("transaction_id"), reqMap.get("refund_id"), refundOrder); |
|
|
|
} |
|
|
|
} catch (RuntimeException e) { |
|
|
|
log.warn("notify refund paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString() + ", e:" + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), e.getMessage()); |
|
|
|
} catch (Exception e) { |
|
|
|
log.warn("notify refund, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString() + ", e:" + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), e.getMessage()); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 服务号回调 |
|
|
|
WxAppinfo appinfo = wxAppinfoMapper.findByAppId(subAppId); |
|
|
|
if (appinfo == null) { |
|
|
|
log.error("未找到appid信息:" + appId); |
|
|
|
throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND); |
|
|
|
} |
|
|
|
WxPayAccount payAccount = wxPayAccountMapper.selectById(appinfo.getPayId()); |
|
|
|
if (payAccount == null) { |
|
|
|
log.error("未找到mch_id信息:" + mchId); |
|
|
|
throw new MallinkException(ErrorCode.MCH_INFO_NOT_FOUND); |
|
|
|
} |
|
|
|
if (!mchId.equalsIgnoreCase(payAccount.getMchId())) { |
|
|
|
log.error("mch_id不对应:" + mchId + ",account:" + payAccount.getMchId()); |
|
|
|
throw new MallinkException(ErrorCode.MCH_INFO_NOT_EQUAL); |
|
|
|
} |
|
|
|
String partnerKey = payAccount.getApiKey(); |
|
|
|
try { |
|
|
|
if (payWay.getType() == EnumPayWay.EnumPayWayType.WX_MINIPAY) { |
|
|
|
if (!"SUCCESS".equals(paramMap.get("return_code"))) { |
|
|
|
log.warn("notify order, wxpay status not success, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("订单状态码非SUCCESS"); |
|
|
|
} |
|
|
|
|
|
|
|
// 解密req_info |
|
|
|
String req_info = CipherUtil.decryptReqInfo(paramMap.get("req_info"), partnerKey); |
|
|
|
log.info(req_info); |
|
|
|
Map<String, String> reqMap = WxPayment.xmlToMap(req_info); |
|
|
|
|
|
|
|
String payOrderNo = reqMap.get("out_trade_no"); |
|
|
|
if (payOrderNo == null) { |
|
|
|
return notifySuccessResult(reqMap.get("transaction_id"), reqMap.get("refund_id"), null); |
|
|
|
} |
|
|
|
Long payOrderId = Long.valueOf(payOrderNo); |
|
|
|
WxPayOrder payOrder = wxPayOrderMapper.selectById(payOrderId); |
|
|
|
if (payOrder == null) { |
|
|
|
log.warn("notify refund, wxpay check pay order not exists, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("支付订单不存在"); |
|
|
|
} |
|
|
|
// 验证支付金额 |
|
|
|
if (!reqMap.get("total_fee").equals(payOrder.getPayAmount().toString())) { |
|
|
|
log.warn("notify refund, wxpay check total_fee is invalid, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("支付订单总金额不一致"); |
|
|
|
} |
|
|
|
String refundIdStr = reqMap.get("out_refund_no"); |
|
|
|
Long refundOrderId = Long.valueOf(refundIdStr); |
|
|
|
WxRefundOrder refundOrder = wxRefundOrderMapper.selectById(refundOrderId); |
|
|
|
if (refundOrder == null) { |
|
|
|
log.warn("notify refund, wxpay check pay order not exists, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("退款订单不存在"); |
|
|
|
} |
|
|
|
// 验证支付金额 |
|
|
|
if (!reqMap.get("total_fee").equals(refundOrder.getTotalFee().toString())) { |
|
|
|
log.warn("notify refund, wxpay check total_fee is invalid, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("订单总金额不一致"); |
|
|
|
} |
|
|
|
// 验证退款金额 |
|
|
|
if (!reqMap.get("refund_fee").equals(refundOrder.getRefundFee().toString())) { |
|
|
|
log.warn("notify refund, wxpay check refund_fee is invalid, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifyErrorResult("退款总金额不一致"); |
|
|
|
} |
|
|
|
|
|
|
|
log.info("notify refund, wxpay checksign success, paramMap:{}, paramMap: " + reqMap.toString() + ", payWay:" + payWay.toString()); |
|
|
|
return notifySuccessResult(reqMap.get("transaction_id"), reqMap.get("refund_id"), refundOrder); |
|
|
|
} |
|
|
|
} catch (RuntimeException e) { |
|
|
|
log.warn("notify refund, wepay checksign error, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString() + ", e:" + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR); |
|
|
|
} catch (Exception e) { |
|
|
|
log.warn("notify refund, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString() + ", e:" + e.getMessage()); |
|
|
|
throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), e.getMessage()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return notifyErrorResult("FAILED"); |
|
|
|
} |
|
|
|
|
|
|
|
} |