package com.kk.alipay.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alipay.api.AlipayApiException; import com.alipay.api.request.AlipayTradeRefundRequest; import com.alipay.api.response.AlipayTradeRefundResponse; import com.kk.alipay.client.DefaultAliPayParser; import com.kk.alipay.client.MyDefaultAliPayClient; import com.kk.alipay.response.AliPayRefundNotifyResponse; import com.kk.api.response.RefundResponse; import com.kk.api.service.InternalRefundService; import com.kk.platform.enums.PayException; import com.kk.platform.enums.RefundStatus; import com.kk.platform.enums.ResultCode; import com.kk.platform.enums.TradeTypeCode; import com.kk.platform.model.PayChannel; import com.kk.platform.model.RefundOrder; import com.kk.platform.service.FileResourceService; import com.kk.platform.service.PayChannelService; import com.kk.platform.service.PayOrderService; import com.kk.platform.service.RefundOrderService; import com.kk.util.SignUtils; import com.kk.utils.WebPropertiesUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 支付宝退款 * <p/> * 扫码退款WIki: https://doc.open.alipay.com/doc2/detail?treeId=26&articleId=103269&docType=1 * 扫码支付,不会异步通知 * <p/> * 即时到账有密退款接口,会异步通知 * wiki:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7386797.0.0.RUVbIh&treeId=62&articleId=104744&docType=1#s1 */ @Service public class AliRefundService implements InternalRefundService { private Log logger = LogFactory.getLog(this.getClass()); private static final BigDecimal HUNDRED = new BigDecimal("100"); // 即时到账有密退款接口,异步通知url private String refundNotifyUrl = WebPropertiesUtil.getInstance().getValue("ali.refund.notify.url"); @Autowired private PayOrderService payOrderService; @Autowired private RefundOrderService refundOrderService; @Autowired PayChannelService payChannelService; /** * 支付宝退款的时候, 需要注意签名, 注意使用的是 开放平台的私钥还是合作商户的私钥。 * <p/> * 使用开放平台私钥查询订单。 * * @param payChannel 如果payChannel传入的是合作伙伴秘钥信息,需要转成开放平台秘钥信息 * @return */ @Override public Object refund(PayChannel payChannel, RefundOrder refundOrder) { if (payChannel.getQueryChannelId() != 0) { payChannel = payChannelService.getPayChannel(payChannel.getQueryChannelId()); } MyDefaultAliPayClient client = new MyDefaultAliPayClient(payChannel.getAppId(), payChannel.getMchKey(), payChannel.getPlatformKey()); AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); Map<String, String> params = new HashMap<String, String>(); params.put("out_trade_no", refundOrder.getPayOrderNo()); params.put("trade_no", refundOrder.getPayId()); params.put("seller_id", payChannel.getMchId()); params.put("refund_amount", new BigDecimal(refundOrder.getRefundAmount()).divide(HUNDRED).setScale(2).toString()); // 退款原因 params.put("refund_reason", refundOrder.getRefundReason()); // 商户退款请求号 params.put("out_request_no", refundOrder.getRefundOrderNo()); request.setBizContent(JSON.toJSONString(params)); AlipayTradeRefundResponse response; try { logger.info("request=" + JSON.toJSONString(request, SerializerFeature.WriteDateUseDateFormat)); response = client.execute(request); logger.info("response=" + JSON.toJSONString(response, SerializerFeature.WriteDateUseDateFormat)); } catch (AlipayApiException e) { logger.error(e.getMessage(), e); refundOrderService.updateStatus(refundOrder.getId(), RefundStatus.REFUND_FAIL.getValue(), e.getErrCode(), e.getErrMsg()); throw new PayException(e); } if (response.isSuccess()) { refundOrder.setRefundTime(response.getGmtRefundPay()); refundOrder.setStatus(RefundStatus.REFUND_SUCCESS.getValue()); refundOrderService.updateRefundOrder(refundOrder); payOrderService.updateRefundStatus(refundOrder.getPayOrderNo(), refundOrder.getRefundAmount()); return true; } else { refundOrder.setErrorCode(response.getSubCode()); refundOrder.setErrorMsg(response.getSubMsg()); refundOrder.setStatus(RefundStatus.REFUND_FAIL.getValue()); refundOrderService.updateRefundOrder(refundOrder); } throw new PayException("不支持退款"); } // 即时到账有密退款接口 public Map<String, String> refundWithPwd(PayChannel payChannel, RefundOrder refundOrder) { Map<String, String> params = new HashMap<String, String>(); params.put("service", "refund_fastpay_by_platform_pwd"); params.put("partner", payChannel.getMchId()); params.put("_input_charset", "UTF-8"); params.put("sign_type", payChannel.getSignType()); params.put("notify_url", refundNotifyUrl); /** * 每进行一次即时到账批量退款,都需要提供一个批次号,通过该批次号可以查询这一批次的退款交易记录,对于每一个合作伙伴,传递的每一个批次号都必须保证唯一性。 格式为:退款日期(8位)+流水号(3~24位)。 不可重复,且退款日期必须是当天日期。流水号可以接受数字或英文字符,建议使用数字,但不可接受“000”。 */ params.put("batch_no", refundOrder.getRefundOrderNo()); //卖家用户ID, 与seller_email(卖家支付宝账号)不能同时为空 params.put("seller_user_id", payChannel.getMchId()); // 退款请求的当前时间。 格式为:yyyy-MM-dd HH:mm:ss。 params.put("refund_date", new DateTime().toString("yyyy-MM-dd hh:mm:ss")); params.put("batch_num", "1"); // detail_data中的退款笔数总和要等于参数batch_num的值; // “退款理由”长度不能大于256字节,“退款理由”中不能有“^”、“|”、“$”、“#”等影响detail_data格式的特殊字符; params.put("detail_data", refundOrder.getPayId() + "^" + new BigDecimal(refundOrder.getRefundAmount()).divide(HUNDRED).setScale(2).toString() + "^" + refundOrder.getRefundReason());//原付款支付宝交易号^退款总金额^退款理由 String sign = SignUtils.rsa(params, payChannel.getMchKey()); params.put("sign", sign); return params; } /** * 即时到账有密退款接口 异步通知 * wiki:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7386797.0.0.RUVbIh&treeId=62&articleId=104744&docType=1#s1 * * @param channel * @param refundOrder * @param notify * @return */ @Override public RefundResponse parse(PayChannel channel, RefundOrder refundOrder, String notify) { if (StringUtils.isBlank(notify)) { return new RefundResponse(ResultCode.FAIL.getValue(), ResultCode.FAIL.getValue()); } String mchId = channel.getMchId(); String platformKey = channel.getPlatformKey(); DefaultAliPayParser parser = new DefaultAliPayParser(channel.getSignType(), mchId, platformKey); AliPayRefundNotifyResponse response = null; try { response = parser.parse(AliPayRefundNotifyResponse.class, notify); } catch (Exception e) { logger.error(e.getMessage(), e); return new RefundResponse(ResultCode.FAIL.getValue(), ResultCode.FAIL.getValue()); } RefundResponse refundResponse = new RefundResponse(); refundResponse.setTradePayNo(refundOrder.getTradePayNo()); refundResponse.setPayAmount(refundOrder.getPayAmount()); refundResponse.setMerchantId(refundOrder.getMerchantId()); refundResponse.setPayOrderNo(refundOrder.getPayOrderNo()); refundResponse.setRefundAmount(refundOrder.getRefundAmount()); if (response.isSuccess()) { refundOrder.setRefundTime(new Date()); refundOrder.setStatus(RefundStatus.REFUND_SUCCESS.getValue()); refundOrderService.updateRefundOrder(refundOrder); payOrderService.updateRefundStatus(refundOrder.getPayOrderNo(), refundOrder.getRefundAmount()); refundResponse.setCode(ResultCode.SUCCESS.getValue()); refundResponse.setMsg(ResultCode.SUCCESS.getValue()); } else { refundOrderService.updateStatus(refundOrder.getId(), RefundStatus.REFUND_FAIL.getValue(), "-1", "未成功"); refundResponse.setCode(ResultCode.FAIL.getValue()); refundResponse.setMsg("未成功"); } return refundResponse; } }