package com.qxgmat.service.extend; import com.alibaba.fastjson.JSONObject; import com.nuliji.tools.AbstractService; import com.nuliji.tools.Tools; import com.nuliji.tools.exception.ParameterException; import com.nuliji.tools.exception.SystemException; import com.nuliji.tools.pay.PaySource; import com.nuliji.tools.pay.common.PayInfo; import com.nuliji.tools.pay.common.ResultInfo; import com.nuliji.tools.pay.data.PayRequestData; import com.nuliji.tools.pay.data.PayResponseData; import com.qxgmat.data.constants.enums.module.PayModule; import com.qxgmat.data.constants.enums.trade.PayChannel; import com.qxgmat.data.constants.enums.trade.PayType; import com.qxgmat.data.constants.enums.trade.TradeStatus; import com.qxgmat.data.dao.PayMapper; import com.qxgmat.data.dao.entity.Pay; import com.qxgmat.data.dao.entity.UserPay; import com.qxgmat.help.PayHelp; import com.qxgmat.service.inline.PayService; import com.qxgmat.service.inline.UserCourseService; import com.qxgmat.service.inline.UserPayService; import com.qxgmat.service.inline.UserServiceService; import com.qxgmat.util.annotation.Callback; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.util.*; /** * Created by gaojie on 2017/11/13. */ @Service public class TradeService extends AbstractService { @Resource private PayHelp payHelp; @Resource private PayMapper payMapper; @Resource private PayService payService; @Resource private UserServiceService userServiceService; @Resource private UserCourseService userCourseService; private Map payCallback = new HashMap<>();; @Resource private UserPayService userPayService; public interface Source { String WECHAT = "wechat"; String ALIPAY = "alipay"; } public static HashMap channelMap = new HashMap<>(); static{ channelMap.put(PayChannel.ALIPAY_APP, Source.ALIPAY); channelMap.put(PayChannel.ALIPAY_QR, Source.ALIPAY); channelMap.put(PayChannel.WECHAT_APP, Source.WECHAT); channelMap.put(PayChannel.WECHAT_QR, Source.WECHAT); } public TradeService(){ payCallback.put(PayModule.SERVICE, (obj)->{ Pay pay = (Pay) obj; Integer userId = pay.getUserId(); JSONObject extend = JSONObject.parseObject(pay.getModuleExtend()); // 写入用户购买 userPayService.add(UserPay.builder() .module(pay.getModule()) .moduleExtend(extend) .isUse(0) .userId(userId).build()); return true; }); payCallback.put(PayModule.CLASS, (obj)->{ Pay pay = (Pay) obj; Integer userId = pay.getUserId(); JSONObject extend = JSONObject.parseObject(pay.getModuleExtend()); // 写入用户购买 userPayService.add(UserPay.builder() .module(pay.getModule()) .moduleExtend(extend) .isUse(0) .userId(userId).build()); return true; }); payCallback.put(PayModule.DATA, (obj)->{ Pay pay = (Pay) obj; Integer userId = pay.getUserId(); JSONObject extend = JSONObject.parseObject(pay.getModuleExtend()); // 写入用户购买 userPayService.add(UserPay.builder() .module(pay.getModule()) .moduleExtend(extend) .isUse(0) .userId(userId).build()); // 直接绑定数据关系 return true; }); } /** * 对于用户的支付方式 * @param userId * @param subject * @param body * @param module * @param moduleExtend * @param money * @param channel * @param request * @return */ public PayResponseData pay(Integer userId, String subject, String body, String module, JSONObject moduleExtend, BigDecimal money, PayChannel channel, HttpServletRequest request) throws Exception { if(!payCallback.containsKey(module)) throw new ParameterException("不支持的支付模块"); PayRequestData data = new PayRequestData(); data.subject = subject; data.body = body; data.channel = channel.key; data.money = money; String pid = preprocess(data); String clientIp = Tools.getClientIp(request); PayType payType = PayType.FromChannel(channel); Pay pay = payService.make(userId, payType == null ? "":payType.key, channel.key, money, module, moduleExtend.toJSONString(), pid, subject, body, clientIp); PayInfo info = process(pay, channel); PayResponseData payResponseData = new PayResponseData(); payResponseData.payNo = pay.getNo(); payResponseData.request = info.getRequest(); payResponseData.status = info.getStatus(); return payResponseData; } private PayInfo prepay(Pay pay){ // 直接支付完成 payService.payed(pay,new ResultInfo(), false); payed(pay); // 构建模拟支付信息 PayInfo info = new PayInfo(); info.setStatus("ok"); return info; } public Pay payed(Pay pay){ if(pay == null) throw new ParameterException("支付信息错误"); if(pay.getTradeStatus() == TradeStatus.SUCCESS.index || pay.getTradeStatus() == TradeStatus.FINISH.index) return pay; if(pay.getTradeStatus() == TradeStatus.CANCEL.index || pay.getTradeStatus() == TradeStatus.CLOSE.index) throw new ParameterException("该支付无效"); Callback callback = payCallback.get(pay.getModule()); try { boolean r = callback.callback(pay); if (!r) throw new SystemException("支付回调失败"); }catch (Exception e){ throw new SystemException("支付回调失败", e); } return pay; } /** * 异步通知处理 * @param channel * @param request * @return * @throws ParameterException * @throws SystemException */ public ResultInfo notifyPay(PayChannel channel, HttpServletRequest request) throws ParameterException,SystemException, Exception{ if(channel == null) throw new ParameterException(104, "第三方支付错误"); PaySource paySource = getHandler(channel); return paySource.notifyTrade(request); } /** * 同步结果处理 * @param channel * @param info * @return * @throws ParameterException * @throws SystemException */ public ResultInfo returnPay(PayChannel channel, ResultInfo info) throws ParameterException,SystemException { if(channel == null) throw new ParameterException(104, "第三方支付错误"); PaySource paySource = getHandler(channel); return paySource.returnTrade(info); } /** * 查询支付结果 * @param pay * @return * @throws SystemException * @throws Exception */ public ResultInfo query(Pay pay) throws SystemException, Exception{ PaySource paySource = getHandler(PayChannel.ValueOf(pay.getChannel())); return paySource.getTrade(pay.getNo(), pay.getTransactionNo(), pay.getPid()); } /** * 返回服务端结果 * @param pay * @param info * @return * @throws SystemException */ public String server(Pay pay, ResultInfo info) throws SystemException{ PaySource paySource = getHandler(PayChannel.ValueOf(pay.getChannel())); if(paySource.validTrade(info, payService.getInfo(pay))){ throw new SystemException(206, "支付校验错误"); } if(pay.getMoney().equals(info.getMoney())){ throw new SystemException(206, "支付金额错误"); } analyse(pay, paySource, info); if(payService.isPayed(pay)){ payed(pay); } return info.getMessage(); } /** * 返回客户端处理结果 * @param pay * @param info * @return * @throws SystemException * @throws ParameterException */ public Pay client(Pay pay, ResultInfo info) throws SystemException, ParameterException{ PaySource paySource = getHandler(PayChannel.ValueOf(pay.getChannel())); if(paySource.validTrade(info, payService.getInfo(pay))){ throw new SystemException(206, "支付校验错误"); } if(pay.getMoney().equals(info.getMoney())){ throw new SystemException(206, "支付金额错误"); } analyse(pay, paySource, info); if(Objects.equals(info.getStatus(), "system")) throw new SystemException(500, info.getMessage()); if(payService.isPayed(pay)){ return payed(pay); }else if(payService.isPaying(pay)){ throw new SystemException(210, "支付中"); }else{ throw new SystemException(210, info.getMessage()); } } /** * 支付预处理,校验支付请求 * @param payRequest * @return * @throws ParameterException */ public String preprocess(PayRequestData payRequest) throws ParameterException { if(payRequest.channel.isEmpty()) throw new ParameterException(103, "支付渠道为空"); String source = getSource(PayChannel.ValueOf(payRequest.channel)); if(source.isEmpty()) throw new ParameterException(104, "支付渠道错误"); if(source.equals(Source.ALIPAY)) return payHelp.getAlipayPid(); if(source.equals(Source.WECHAT)) return payHelp.getWechatPid(); throw new ParameterException(104, "支付渠道错误"); } /** * 发起支付 * @param pay * @return * @throws SystemException * @throws Exception */ public PayInfo process(Pay pay, PayChannel channel) throws SystemException, Exception { String notifyUrl = getNotifyUrl(channel); String subject = pay.getSubject(); String payNo = pay.getNo(); String body = pay.getBody(); BigDecimal money = pay.getMoney(); String pid = pay.getPid(); PayInfo info = null; switch(channel){ case ALIPAY_APP: info = payHelp.getAlipay().appTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp()); break; case ALIPAY_QR: info = payHelp.getAlipay().preTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp()); break; case WECHAT_APP: info = payHelp.getWechatPay().appTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp()); break; case WECHAT_QR: info = payHelp.getWechatPay().preTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp()); break; case WECHAT_JS: info = payHelp.getWechatPay().jsTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp()); break; case OFFLINE: // 直接完成支付 info = prepay(pay); default: throw new SystemException(204, "支付渠道错误方式:"+channel); } if(Objects.equals(info.getStatus(), "ok")) { payService.start(pay, info); } else { payService.system(pay, info); throw new SystemException(205, "支付调用错误"); } return info; } /** * 同步处理支付记录 * @param pay * @param paySource * @param info */ private void analyse(Pay pay, PaySource paySource, ResultInfo info) throws SystemException { String status = info.getStatus(); if(payService.isPayed(pay)){ if(status.equals("error") || status.equals("close") || status.equals("cancel")){ throw new SystemException(301, "支付异常:已支付,第三方未支付"); } return ; }else if(payService.isInvalid(pay)){ if(status.equals("success") || status.equals("finish")){ throw new SystemException(301, "支付异常:已撤销或者关闭,第三方支付成功"); } return ; } switch (status) { case "success": // 支付成功 payService.payed(pay, info, false); break; case "finish": // 完成,无法撤销或者关闭 payService.payed(pay, info, true); break; case "error": // 支付错误,需要关闭支付交易 payService.error(pay, info); break; case "cancel": // 对方已撤销 payService.cancel(pay, info); break; case "close": // 对方已经关闭 payService.close(pay, info); break; case "system": // system 开发接口错误 payService.system(pay, info); break; case "exception": // exception 支付流程异常 payService.exception(pay, info); break; } } /** * 获取支付 * @param channel * @return */ private String getSource(PayChannel channel){ return channelMap.get(channel); } /** * 获取渠道异步通知 * @param channel * @return */ private String getNotifyUrl(PayChannel channel){ return payHelp.getNotifyUrl() + channel.key; } /** * 获取支付实例 * @param channel * @return PaySource * @throws SystemException */ private PaySource getHandler(PayChannel channel) throws SystemException { String source = getSource(channel); if(source.equals(Source.ALIPAY)) return payHelp.getAlipay(); if(source.equals(Source.WECHAT)) return payHelp.getWechatPay(); throw new SystemException(204, "支付支付渠道错误方式"); } }