TradeService.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. package com.qxgmat.service.extend;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.nuliji.tools.AbstractService;
  4. import com.nuliji.tools.Tools;
  5. import com.nuliji.tools.exception.ParameterException;
  6. import com.nuliji.tools.exception.SystemException;
  7. import com.nuliji.tools.pay.PaySource;
  8. import com.nuliji.tools.pay.common.PayInfo;
  9. import com.nuliji.tools.pay.common.ResultInfo;
  10. import com.nuliji.tools.pay.data.PayRequestData;
  11. import com.nuliji.tools.pay.data.PayResponseData;
  12. import com.qxgmat.data.constants.enums.module.PayModule;
  13. import com.qxgmat.data.constants.enums.trade.PayChannel;
  14. import com.qxgmat.data.constants.enums.trade.PayType;
  15. import com.qxgmat.data.constants.enums.trade.TradeStatus;
  16. import com.qxgmat.data.dao.PayMapper;
  17. import com.qxgmat.data.dao.entity.Pay;
  18. import com.qxgmat.data.dao.entity.UserPay;
  19. import com.qxgmat.help.PayHelp;
  20. import com.qxgmat.service.inline.PayService;
  21. import com.qxgmat.service.inline.UserCourseService;
  22. import com.qxgmat.service.inline.UserPayService;
  23. import com.qxgmat.service.inline.UserServiceService;
  24. import com.qxgmat.util.annotation.Callback;
  25. import org.springframework.stereotype.Service;
  26. import javax.annotation.Resource;
  27. import javax.servlet.http.HttpServletRequest;
  28. import java.math.BigDecimal;
  29. import java.util.*;
  30. /**
  31. * Created by gaojie on 2017/11/13.
  32. */
  33. @Service
  34. public class TradeService extends AbstractService {
  35. @Resource
  36. private PayHelp payHelp;
  37. @Resource
  38. private PayMapper payMapper;
  39. @Resource
  40. private PayService payService;
  41. @Resource
  42. private UserServiceService userServiceService;
  43. @Resource
  44. private UserCourseService userCourseService;
  45. private Map<PayModule, Callback> payCallback = new HashMap<>();;
  46. @Resource
  47. private UserPayService userPayService;
  48. public interface Source {
  49. String WECHAT = "wechat";
  50. String ALIPAY = "alipay";
  51. }
  52. public static HashMap<PayChannel, String> channelMap = new HashMap<>();
  53. static{
  54. channelMap.put(PayChannel.ALIPAY_APP, Source.ALIPAY);
  55. channelMap.put(PayChannel.ALIPAY_QR, Source.ALIPAY);
  56. channelMap.put(PayChannel.WECHAT_APP, Source.WECHAT);
  57. channelMap.put(PayChannel.WECHAT_QR, Source.WECHAT);
  58. }
  59. public TradeService(){
  60. payCallback.put(PayModule.SERVICE, (obj)->{
  61. Pay pay = (Pay) obj;
  62. Integer userId = pay.getUserId();
  63. JSONObject extend = JSONObject.parseObject(pay.getModuleExtend());
  64. // 写入用户购买
  65. userPayService.add(UserPay.builder()
  66. .module(pay.getModule())
  67. .moduleExtend(extend)
  68. .isUse(0)
  69. .userId(userId).build());
  70. return true;
  71. });
  72. payCallback.put(PayModule.CLASS, (obj)->{
  73. Pay pay = (Pay) obj;
  74. Integer userId = pay.getUserId();
  75. JSONObject extend = JSONObject.parseObject(pay.getModuleExtend());
  76. // 写入用户购买
  77. userPayService.add(UserPay.builder()
  78. .module(pay.getModule())
  79. .moduleExtend(extend)
  80. .isUse(0)
  81. .userId(userId).build());
  82. return true;
  83. });
  84. payCallback.put(PayModule.DATA, (obj)->{
  85. Pay pay = (Pay) obj;
  86. Integer userId = pay.getUserId();
  87. JSONObject extend = JSONObject.parseObject(pay.getModuleExtend());
  88. // 写入用户购买
  89. userPayService.add(UserPay.builder()
  90. .module(pay.getModule())
  91. .moduleExtend(extend)
  92. .isUse(0)
  93. .userId(userId).build());
  94. // 直接绑定数据关系
  95. return true;
  96. });
  97. }
  98. /**
  99. * 对于用户的支付方式
  100. * @param userId
  101. * @param subject
  102. * @param body
  103. * @param module
  104. * @param moduleExtend
  105. * @param money
  106. * @param channel
  107. * @param request
  108. * @return
  109. */
  110. public PayResponseData pay(Integer userId, String subject, String body, String module, JSONObject moduleExtend, BigDecimal money, PayChannel channel, HttpServletRequest request) throws Exception {
  111. if(!payCallback.containsKey(module))
  112. throw new ParameterException("不支持的支付模块");
  113. PayRequestData data = new PayRequestData();
  114. data.subject = subject;
  115. data.body = body;
  116. data.channel = channel.key;
  117. data.money = money;
  118. String pid = preprocess(data);
  119. String clientIp = Tools.getClientIp(request);
  120. PayType payType = PayType.FromChannel(channel);
  121. Pay pay = payService.make(userId, payType == null ? "":payType.key, channel.key, money, module, moduleExtend.toJSONString(), pid, subject, body, clientIp);
  122. PayInfo info = process(pay, channel);
  123. PayResponseData payResponseData = new PayResponseData();
  124. payResponseData.payNo = pay.getNo();
  125. payResponseData.request = info.getRequest();
  126. payResponseData.status = info.getStatus();
  127. return payResponseData;
  128. }
  129. private PayInfo prepay(Pay pay){
  130. // 直接支付完成
  131. payService.payed(pay,new ResultInfo(), false);
  132. payed(pay);
  133. // 构建模拟支付信息
  134. PayInfo info = new PayInfo();
  135. info.setStatus("ok");
  136. return info;
  137. }
  138. public Pay payed(Pay pay){
  139. if(pay == null)
  140. throw new ParameterException("支付信息错误");
  141. if(pay.getTradeStatus() == TradeStatus.SUCCESS.index
  142. || pay.getTradeStatus() == TradeStatus.FINISH.index)
  143. return pay;
  144. if(pay.getTradeStatus() == TradeStatus.CANCEL.index
  145. || pay.getTradeStatus() == TradeStatus.CLOSE.index)
  146. throw new ParameterException("该支付无效");
  147. Callback callback = payCallback.get(pay.getModule());
  148. try {
  149. boolean r = callback.callback(pay);
  150. if (!r)
  151. throw new SystemException("支付回调失败");
  152. }catch (Exception e){
  153. throw new SystemException("支付回调失败", e);
  154. }
  155. return pay;
  156. }
  157. /**
  158. * 异步通知处理
  159. * @param channel
  160. * @param request
  161. * @return
  162. * @throws ParameterException
  163. * @throws SystemException
  164. */
  165. public ResultInfo notifyPay(PayChannel channel, HttpServletRequest request) throws ParameterException,SystemException, Exception{
  166. if(channel == null) throw new ParameterException(104, "第三方支付错误");
  167. PaySource paySource = getHandler(channel);
  168. return paySource.notifyTrade(request);
  169. }
  170. /**
  171. * 同步结果处理
  172. * @param channel
  173. * @param info
  174. * @return
  175. * @throws ParameterException
  176. * @throws SystemException
  177. */
  178. public ResultInfo returnPay(PayChannel channel, ResultInfo info) throws ParameterException,SystemException {
  179. if(channel == null) throw new ParameterException(104, "第三方支付错误");
  180. PaySource paySource = getHandler(channel);
  181. return paySource.returnTrade(info);
  182. }
  183. /**
  184. * 查询支付结果
  185. * @param pay
  186. * @return
  187. * @throws SystemException
  188. * @throws Exception
  189. */
  190. public ResultInfo query(Pay pay) throws SystemException, Exception{
  191. PaySource paySource = getHandler(PayChannel.ValueOf(pay.getChannel()));
  192. return paySource.getTrade(pay.getNo(), pay.getTransactionNo(), pay.getPid());
  193. }
  194. /**
  195. * 返回服务端结果
  196. * @param pay
  197. * @param info
  198. * @return
  199. * @throws SystemException
  200. */
  201. public String server(Pay pay, ResultInfo info) throws SystemException{
  202. PaySource paySource = getHandler(PayChannel.ValueOf(pay.getChannel()));
  203. if(paySource.validTrade(info, payService.getInfo(pay))){
  204. throw new SystemException(206, "支付校验错误");
  205. }
  206. if(pay.getMoney().equals(info.getMoney())){
  207. throw new SystemException(206, "支付金额错误");
  208. }
  209. analyse(pay, paySource, info);
  210. if(payService.isPayed(pay)){
  211. payed(pay);
  212. }
  213. return info.getMessage();
  214. }
  215. /**
  216. * 返回客户端处理结果
  217. * @param pay
  218. * @param info
  219. * @return
  220. * @throws SystemException
  221. * @throws ParameterException
  222. */
  223. public Pay client(Pay pay, ResultInfo info) throws SystemException, ParameterException{
  224. PaySource paySource = getHandler(PayChannel.ValueOf(pay.getChannel()));
  225. if(paySource.validTrade(info, payService.getInfo(pay))){
  226. throw new SystemException(206, "支付校验错误");
  227. }
  228. if(pay.getMoney().equals(info.getMoney())){
  229. throw new SystemException(206, "支付金额错误");
  230. }
  231. analyse(pay, paySource, info);
  232. if(Objects.equals(info.getStatus(), "system")) throw new SystemException(500, info.getMessage());
  233. if(payService.isPayed(pay)){
  234. return payed(pay);
  235. }else if(payService.isPaying(pay)){
  236. throw new SystemException(210, "支付中");
  237. }else{
  238. throw new SystemException(210, info.getMessage());
  239. }
  240. }
  241. /**
  242. * 支付预处理,校验支付请求
  243. * @param payRequest
  244. * @return
  245. * @throws ParameterException
  246. */
  247. public String preprocess(PayRequestData payRequest) throws ParameterException {
  248. if(payRequest.channel.isEmpty()) throw new ParameterException(103, "支付渠道为空");
  249. String source = getSource(PayChannel.ValueOf(payRequest.channel));
  250. if(source.isEmpty()) throw new ParameterException(104, "支付渠道错误");
  251. if(source.equals(Source.ALIPAY)) return payHelp.getAlipayPid();
  252. if(source.equals(Source.WECHAT)) return payHelp.getWechatPid();
  253. throw new ParameterException(104, "支付渠道错误");
  254. }
  255. /**
  256. * 发起支付
  257. * @param pay
  258. * @return
  259. * @throws SystemException
  260. * @throws Exception
  261. */
  262. public PayInfo process(Pay pay, PayChannel channel) throws SystemException, Exception {
  263. String notifyUrl = getNotifyUrl(channel);
  264. String subject = pay.getSubject();
  265. String payNo = pay.getNo();
  266. String body = pay.getBody();
  267. BigDecimal money = pay.getMoney();
  268. String pid = pay.getPid();
  269. PayInfo info = null;
  270. switch(channel){
  271. case ALIPAY_APP:
  272. info = payHelp.getAlipay().appTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp());
  273. break;
  274. case ALIPAY_QR:
  275. info = payHelp.getAlipay().preTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp());
  276. break;
  277. case WECHAT_APP:
  278. info = payHelp.getWechatPay().appTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp());
  279. break;
  280. case WECHAT_QR:
  281. info = payHelp.getWechatPay().preTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp());
  282. break;
  283. case WECHAT_JS:
  284. info = payHelp.getWechatPay().jsTrade(payNo, pid, money, subject, body, notifyUrl, pay.getClientIp());
  285. break;
  286. case OFFLINE:
  287. // 直接完成支付
  288. info = prepay(pay);
  289. default:
  290. throw new SystemException(204, "支付渠道错误方式:"+channel);
  291. }
  292. if(Objects.equals(info.getStatus(), "ok")) {
  293. payService.start(pay, info);
  294. } else {
  295. payService.system(pay, info);
  296. throw new SystemException(205, "支付调用错误");
  297. }
  298. return info;
  299. }
  300. /**
  301. * 同步处理支付记录
  302. * @param pay
  303. * @param paySource
  304. * @param info
  305. */
  306. private void analyse(Pay pay, PaySource paySource, ResultInfo info) throws SystemException {
  307. String status = info.getStatus();
  308. if(payService.isPayed(pay)){
  309. if(status.equals("error") || status.equals("close") || status.equals("cancel")){
  310. throw new SystemException(301, "支付异常:已支付,第三方未支付");
  311. }
  312. return ;
  313. }else if(payService.isInvalid(pay)){
  314. if(status.equals("success") || status.equals("finish")){
  315. throw new SystemException(301, "支付异常:已撤销或者关闭,第三方支付成功");
  316. }
  317. return ;
  318. }
  319. switch (status) {
  320. case "success":
  321. // 支付成功
  322. payService.payed(pay, info, false);
  323. break;
  324. case "finish":
  325. // 完成,无法撤销或者关闭
  326. payService.payed(pay, info, true);
  327. break;
  328. case "error":
  329. // 支付错误,需要关闭支付交易
  330. payService.error(pay, info);
  331. break;
  332. case "cancel":
  333. // 对方已撤销
  334. payService.cancel(pay, info);
  335. break;
  336. case "close":
  337. // 对方已经关闭
  338. payService.close(pay, info);
  339. break;
  340. case "system":
  341. // system 开发接口错误
  342. payService.system(pay, info);
  343. break;
  344. case "exception":
  345. // exception 支付流程异常
  346. payService.exception(pay, info);
  347. break;
  348. }
  349. }
  350. /**
  351. * 获取支付
  352. * @param channel
  353. * @return
  354. */
  355. private String getSource(PayChannel channel){
  356. return channelMap.get(channel);
  357. }
  358. /**
  359. * 获取渠道异步通知
  360. * @param channel
  361. * @return
  362. */
  363. private String getNotifyUrl(PayChannel channel){
  364. return payHelp.getNotifyUrl() + channel.key;
  365. }
  366. /**
  367. * 获取支付实例
  368. * @param channel
  369. * @return PaySource
  370. * @throws SystemException
  371. */
  372. private PaySource getHandler(PayChannel channel) throws SystemException {
  373. String source = getSource(channel);
  374. if(source.equals(Source.ALIPAY)) return payHelp.getAlipay();
  375. if(source.equals(Source.WECHAT)) return payHelp.getWechatPay();
  376. throw new SystemException(204, "支付支付渠道错误方式");
  377. }
  378. }