package com.kk.alipay.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alipay.api.AlipayApiException; import com.alipay.api.request.AlipayTradePrecreateRequest; import com.alipay.api.request.AlipayTradeQueryRequest; import com.alipay.api.response.AlipayTradePrecreateResponse; import com.alipay.api.response.AlipayTradeQueryResponse; import com.kk.alipay.client.DefaultAliPayParser; import com.kk.alipay.client.MyDefaultAliPayClient; import com.kk.alipay.exception.AliPayException; import com.kk.alipay.response.AliPayNotifyResponse; import com.kk.api.response.PayNotifyResponse; import com.kk.api.service.InternalPayService; import com.kk.platform.enums.PayException; import com.kk.platform.enums.PayStatus; import com.kk.platform.enums.ResultCode; import com.kk.platform.enums.TradeTypeCode; import com.kk.platform.model.PayChannel; import com.kk.platform.model.PayOrder; import com.kk.platform.service.PayChannelService; import com.kk.platform.service.PayOrderService; import com.kk.util.DateUtil; import com.kk.util.SignUtils; import com.kk.utils.WebPropertiesUtil; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.URLEncoder; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 扫码支付wiki:https://doc.open.alipay.com/doc2/detail?spm=0.0.0.0.E3tvGh&treeId=26&articleId=103286&docType=1 * 扫码支付,异步回调,订单查询,申请退款 * <p/> * 网页支付wiki:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.08w3Le&treeId=60&articleId=103564&docType=1 * 网页支付接口,demo,签名规则,异步回调,同步回调,申请退款 * <p/> * 移动支付(APP支付)wiki: * https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.Tn7dlq&treeId=59&articleId=103663&docType=1 * app支付,签名机制,客户端调用, 异步回调 * <p/> * 开发工具包下载, 扫码支付demo,app支付demo: * https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.S9aWYF&treeId=54&articleId=104506&docType=1 * <p/> * 支付API: * https://doc.open.alipay.com/docs/api.htm?spm=a219a.7386797.0.0.ppgPy5&docType=4&apiId=757 * <p/> * 支付宝秘钥见:README.md */ @Service public class AliPayService implements InternalPayService { private Log logger = LogFactory.getLog(this.getClass()); private static final BigDecimal HUNDRED = new BigDecimal("100"); // 通知url必须为直接可访问的url,不能携带参数 private String notifyUrl = WebPropertiesUtil.getInstance().getValue("ali.pay.notify.url"); @Autowired private PayOrderService payOrderService; @Autowired PayChannelService payChannelService; @Override public Object pay(PayChannel payChannel, PayOrder order) { switch (order.getTradeTypeCodeEnum()) { case NATIVE: { if (order.getStatus() == PayStatus.CREATE_PAYMENT_SUCCESS.getValue()) { return order.getCodeUrl(); } else if (order.getStatus() == PayStatus.CREATE_PAYMENT.getValue()) { return getNativePayUrl(payChannel, order); } else { throw new PayException("订单状态异常"); } } case WAP: { return getWapPayMap(payChannel, order); } case APP: { return getAppPayQuery(payChannel, order); } } return null; } // 网页支付 private Map<String, String> getWapPayMap(PayChannel payChannel, PayOrder order) { String signType = payChannel.getSignType(); Map<String, String> params = new HashMap<String, String>(); params.put("service", "alipay.wap.create.direct.pay.by.user"); params.put("partner", payChannel.getMchId()); params.put("seller_id", payChannel.getMchId()); params.put("_input_charset", "UTF-8"); params.put("sign_type", signType); params.put("payment_type", "1"); params.put("notify_url", notifyUrl); params.put("return_url", order.getReturnUrl()); params.put("out_trade_no", order.getPayOrderNo()); params.put("subject", order.getSubject()); params.put("total_fee", new BigDecimal(order.getPayAmount()).divide(HUNDRED).setScale(2).toString()); // params.put("show_url", show_url); params.put("body", order.getDetail()); String sign = SignUtils.rsa(params, payChannel.getMchKey()); params.put("sign", sign); return params; } // App支付 private Map<String, String> getAppPayQuery(PayChannel payChannel, PayOrder order) { String signType = payChannel.getSignType(); Map<String, String> params = new HashMap<String, String>(); params.put("service", "mobile.securitypay.pay"); params.put("partner", payChannel.getMchId()); params.put("seller_id", payChannel.getMchId()); params.put("_input_charset", "UTF-8"); params.put("sign_type", signType); params.put("payment_type", "1"); params.put("notify_url", notifyUrl); params.put("out_trade_no", order.getPayOrderNo()); params.put("subject", order.getSubject()); params.put("total_fee", new BigDecimal(order.getPayAmount()).divide(HUNDRED).setScale(2).toString()); params.put("body", order.getDetail()); String sign = SignUtils.rsa(params, payChannel.getMchKey()); params.put("sign", sign); return params; // 直接返回拼好的query // String sign; // try { // sign = "\"" + URLEncoder.encode(SignUtils.rsa(params, payChannel.getMchKey()), "UTF-8") + "\""; // } catch (UnsupportedEncodingException e) { // throw new PayException("签名失败"); // } // Map<String, String> resultMap = new HashMap<String, String>(1); // resultMap.put("query", SignUtils.getSignContent(params, false) + "&sign=" + sign + "&sign_type=\"" + signType + "\""); // return resultMap; } // 扫码支付,获取支付二维码 private String getNativePayUrl(PayChannel payChannel, PayOrder order) { MyDefaultAliPayClient client = new MyDefaultAliPayClient(payChannel.getAppId(), payChannel.getMchKey(), payChannel.getPlatformKey()); AlipayTradePrecreateResponse response = null; try { AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); request.setNotifyUrl(notifyUrl); Map<String, String> params = new HashMap<String, String>(); params.put("out_trade_no", order.getPayOrderNo()); params.put("seller_id", payChannel.getMchId()); params.put("total_amount", new BigDecimal(order.getPayAmount()).divide(HUNDRED).setScale(2).toString()); params.put("subject", order.getSubject()); params.put("body", order.getDetail()); // 一些非必填参数,这里就没有设置 request.setBizContent(JSON.toJSONString(params)); 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); order.setStatus(PayStatus.CREATE_PAYMENT_FAIL.getValue()); order.setErrorCode(e.getErrCode()); order.setErrorMsg(e.getErrMsg()); payOrderService.updateStatus(order.getId(), PayStatus.CREATE_PAYMENT_FAIL.getValue(), e.getErrCode(), e.getErrMsg()); throw new PayException(e); } // 10000表示成功 if (response.isSuccess() && "10000".equals(response.getCode())) { order.setStatus(PayStatus.CREATE_PAYMENT_SUCCESS.getValue()); order.setCodeUrl(response.getQrCode()); // 支付的二维码地址 payOrderService.updatePayRequest(order.getId(), null, response.getQrCode()); } else { order.setStatus(PayStatus.CREATE_PAYMENT_FAIL.getValue()); order.setErrorCode(response.getCode()); order.setErrorMsg(response.getMsg()); payOrderService.updateStatus(order.getId(), PayStatus.CREATE_PAYMENT_FAIL.getValue(), response.getCode(), response.getMsg()); throw new PayException(response.getMsg()); } return response.getQrCode(); } @Override public String getOutTradeNo(String notify) { JSONObject object = JSON.parseObject(notify); return object.getString("out_trade_no"); } @Override public PayNotifyResponse parse(PayChannel channel, PayOrder order, String notify) { if (StringUtils.isBlank(notify)) { return new PayNotifyResponse(ResultCode.FAIL.getValue(), ResultCode.FAIL.getValue()); } String mchId = channel.getMchId(); String platformKey = channel.getPlatformKey(); DefaultAliPayParser parser = new DefaultAliPayParser(channel.getSignType(), mchId, platformKey); AliPayNotifyResponse response; try { response = parser.parse(AliPayNotifyResponse.class, notify); } catch (Exception e) { logger.error(e.getMessage(), e); return new PayNotifyResponse(ResultCode.FAIL.getValue(), ResultCode.FAIL.getValue()); } PayNotifyResponse notifyResponse = new PayNotifyResponse(); notifyResponse.setTradePayNo(order.getTradePayNo()); notifyResponse.setPayAmount(order.getPayAmount()); notifyResponse.setExtra(order.getExtra()); notifyResponse.setMerchantId(order.getMerchantId()); notifyResponse.setPayOrderNo(order.getPayOrderNo()); if (response.isSuccess()) { setOrderPaySuccess(order, response.getBuyerId(), response.getBuyerEmailOrLogonId(), response.getGmtPayment(), response.getTradeNo()); notifyResponse.setCode(ResultCode.SUCCESS.getValue()); notifyResponse.setMsg(ResultCode.SUCCESS.getValue()); notifyResponse.setStatus(ResultCode.SUCCESS.getValue()); notifyResponse.setPayTime(DateUtil.defaultTime(order.getPayTime())); } else { payOrderService.updateStatus(order.getId(), PayStatus.PAY_FAIL.getValue(), response.getTradeStatus(), "未成功"); notifyResponse.setCode(ResultCode.FAIL.getValue()); notifyResponse.setMsg("未成功"); notifyResponse.setStatus(ResultCode.FAIL.getValue()); } return notifyResponse; } // 订单支付成功, 设置payOrder状态 private void setOrderPaySuccess(PayOrder order, String buyerId, String buyerLogonId, Date payTime, String tradeNo) { order.setStatus(PayStatus.PAY_SUCCESS.getValue()); order.setOpenId(buyerId); order.setBuyerLogonId(buyerLogonId); order.setPayTime(payTime); order.setPayId(tradeNo); payOrderService.updatePayOrder(order); } //下单,异步通知 时候, 如果是创建的应用,则使用合作伙伴秘钥加密; 如果是服务窗应用,则使用开放平台秘钥。 /** * 支付宝查询订单时候, 需要注意签名, 注意使用的是 开放平台的私钥还是合作商户的私钥。 * <p/> * 使用开放平台私钥查询订单。 * * @param payChannel 如果payChannel传入的是合作伙伴秘钥信息,需要转成开放平台秘钥信息 * @param order * @return */ @Override public boolean synchronize(PayChannel payChannel, PayOrder order) { if (payChannel.getQueryChannelId() != 0) { payChannel = payChannelService.getPayChannel(payChannel.getQueryChannelId()); } MyDefaultAliPayClient client = new MyDefaultAliPayClient(payChannel.getAppId(), payChannel.getMchKey(), payChannel.getPlatformKey()); AlipayTradeQueryResponse response = null; try { AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); Map<String, String> params = new HashMap<String, String>(); params.put("out_trade_no", order.getPayOrderNo()); params.put("trade_no", order.getPayId()); request.setBizContent(JSON.toJSONString(params)); 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); throw new PayException(e.getMessage()); } if (PayStatus.CREATE_PAYMENT.getValue() == order.getStatus() || PayStatus.CREATE_PAYMENT_SUCCESS.getValue() == order.getStatus() || PayStatus.PAY_CHECKING.getValue() == order.getStatus()) { if (querySuccess(response)) { setOrderPaySuccess(order, response.getBuyerUserId(), response.getBuyerLogonId(), new Date(), response.getTradeNo()); return true; } else if (queryNotpay(response)) { Date now = new Date(); if (now.after(order.getExpireTime())) { payOrderService.updateStatus(order.getId(), PayStatus.PAY_CLOSE.getValue(), null, null); return true; } else { return false; } } else { payOrderService.updateStatus(order.getId(), PayStatus.PAY_CLOSE.getValue(), null, null); return true; } } return false; } // 支付成功 public boolean querySuccess(AlipayTradeQueryResponse response) { return response != null && ("10000".equals(response.getCode())) && ( "TRADE_SUCCESS".equals(response.getTradeStatus()) || "TRADE_FINISHED".equals(response.getTradeStatus())); } //支付中 public boolean queryNotpay(AlipayTradeQueryResponse response) { return response != null && "10000".equals(response.getCode()) && "WAIT_BUYER_PAY".equals(response.getTradeStatus()); } }