ApiPayController.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. package com.kmall.api.api;
  2. import com.kmall.api.annotation.IgnoreAuth;
  3. import com.kmall.api.annotation.LoginUser;
  4. import com.kmall.common.constant.Dict;
  5. import com.kmall.api.dao.ApiOrderRefundMapper;
  6. import com.kmall.api.entity.*;
  7. import com.kmall.api.service.*;
  8. import com.kmall.api.service.pay.wxpay.WxPayPropertiesBuilder;
  9. import com.kmall.api.util.ApiBaseAction;
  10. import com.kmall.api.util.CommonUtil;
  11. import com.kmall.common.utils.CharUtil;
  12. import com.kmall.common.utils.Constant;
  13. import com.kmall.common.utils.MapUtils;
  14. import com.kmall.common.utils.XmlUtil;
  15. import com.kmall.common.utils.wechat.AESUtil;
  16. import com.kmall.common.utils.wechat.WechatRefundApiResult;
  17. import com.kmall.common.utils.wechat.WechatRefundNotifyResult;
  18. import com.kmall.common.utils.wechat.WechatUtil;
  19. import org.apache.log4j.Logger;
  20. import org.springframework.beans.factory.annotation.Autowired;
  21. import org.springframework.web.bind.annotation.*;
  22. import javax.servlet.http.HttpServletRequest;
  23. import javax.servlet.http.HttpServletResponse;
  24. import java.io.ByteArrayOutputStream;
  25. import java.io.InputStream;
  26. import java.math.BigDecimal;
  27. import java.util.*;
  28. /**
  29. * 作者: @author Scott <br>
  30. * 时间: 2017-08-11 08:32<br>
  31. * 描述: ApiIndexController <br>
  32. */
  33. @RestController
  34. @RequestMapping("/api/pay")
  35. public class ApiPayController extends ApiBaseAction {
  36. private Logger logger = Logger.getLogger(ApiPayController.class);
  37. @Autowired
  38. private ApiOrderService orderService;
  39. @Autowired
  40. private ApiOrderGoodsService orderGoodsService;
  41. @Autowired
  42. private ApiPayService apiPayService;
  43. @Autowired
  44. private ApiOrderProcessRecordService orderProcessRecordService;
  45. @Autowired
  46. private ApiOrderExceptionRecordService apiOrderExceptionRecordService;
  47. @Autowired
  48. private ApiOrderRefundMapper mallOrderRefundMapper;
  49. /**
  50. */
  51. @RequestMapping("index")
  52. public Object index(@LoginUser UserVo loginUser) {
  53. return toResponsSuccess("");
  54. }
  55. /**
  56. * 获取支付的请求参数,用户生成订单, 商户订单号、预支付编号,且支付状态、订单状态均为支付中;
  57. */
  58. @GetMapping("pay_prepay")
  59. public Object payPrepay(@LoginUser UserVo loginUser, Long[] orderIds,String isMergePay) {
  60. List<Long> orderIdList=new ArrayList<>();
  61. String merchOrderSn = "";
  62. for(int i=0;i<orderIds.length;i++){
  63. orderIdList.add(orderIds[i]);
  64. }
  65. BigDecimal actual_price = Constant.ZERO;
  66. List<OrderVo> orderVoList = orderService.queryObjectByIdList(orderIdList);
  67. List<OrderProcessRecordEntity> processRecordEntityList = new ArrayList<>();
  68. for (OrderVo orderInfo:orderVoList) {
  69. if (orderInfo.getOrder_status() == 101) {
  70. return toResponsObject(400, "订单号为"+orderInfo.getOrder_sn()+"的订单超时支付,已取消", "");
  71. }
  72. if (orderInfo.getPay_status() != 0 && orderInfo.getPay_status() != 1) {
  73. return toResponsObject(400, "订单号为"+orderInfo.getOrder_sn()+"的订单已支付,请不要重复操作", "");
  74. }
  75. merchOrderSn = orderInfo.getMerchOrderSn();
  76. //如此次是单笔支付,则判断下单时该商户订单是否是拆单订单,如果都满足,则更新商户订单号,生成新的预支付信息
  77. if(Dict.isMergePay.item_0.getItem().equalsIgnoreCase(isMergePay)) {
  78. if ("2".equalsIgnoreCase(orderInfo.getIsMergePay())) {//多笔订单单笔支付,则商户订单号,生成新的预支付信息
  79. // merchOrderSn = "EMATO" + new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
  80. merchOrderSn = "EMATO"+ CommonUtil.generateOrderNumber();
  81. orderInfo.setMerchOrderSn(merchOrderSn);
  82. }
  83. }
  84. OrderProcessRecordEntity processRecordEntity = orderProcessRecordService.queryObjectByOrderSn(orderInfo.getOrder_sn());
  85. if(processRecordEntity != null){
  86. OrderProcessRecordEntity entity = new OrderProcessRecordEntity();
  87. entity.setOrderSn(orderInfo.getOrder_sn());
  88. entity.setUserId(Integer.valueOf(loginUser.getId()+""));
  89. entity.setPayStartTime(new Date());
  90. entity.setId(processRecordEntity.getId());
  91. processRecordEntityList.add(entity);
  92. }
  93. actual_price = actual_price.add(orderInfo.getActual_price());
  94. }
  95. String nonceStr = CharUtil.getRandomString(32);
  96. //https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
  97. Map<Object, Object> resultObj = new HashMap();
  98. try {
  99. Map<Object, Object> parame = new TreeMap<>();
  100. //parame.put("appid", ResourceUtil.getConfigByName("wx.appId"));
  101. parame.put("appid", WxPayPropertiesBuilder.instance().getAppId());
  102. //parame.put("mch_id", ResourceUtil.getConfigByName("wx.mchId"));// 商家账号。
  103. parame.put("mch_id", WxPayPropertiesBuilder.instance().getMchId());// 商家账号。
  104. String randomStr = CharUtil.getRandomNum(18).toUpperCase();
  105. parame.put("nonce_str", randomStr);// 随机字符串
  106. parame.put("out_trade_no", merchOrderSn);// 商户订单编号
  107. parame.put("body", "商城-支付");// 商品描述
  108. //订单的商品
  109. List<OrderGoodsVo> orderGoods = orderGoodsService.queryListByIds(orderIdList);
  110. if (null != orderGoods) {
  111. String body = "商城-";
  112. for (OrderGoodsVo goodsVo : orderGoods) {
  113. body = body + goodsVo.getGoods_name() + "、";
  114. }
  115. if (body.length() > 0) {
  116. body = body.substring(0, body.length() - 1);
  117. }
  118. parame.put("body", body);// 商品描述
  119. }
  120. //支付金额
  121. // parame.put("total_fee", orderInfo.getActual_price().multiply(Constant.ONE_HUNDRED).intValue()));//todo 消费金额
  122. parame.put("total_fee", actual_price.multiply(Constant.ONE_HUNDRED).intValue());// 消费金额
  123. //parame.put("notify_url", ResourceUtil.getConfigByName("wx.notifyUrl"));// 回调地址
  124. parame.put("notify_url", WxPayPropertiesBuilder.instance().getNotifyUrl());// 回调地址
  125. //parame.put("trade_type", ResourceUtil.getConfigByName("wx.tradeType"));// 交易类型APP
  126. parame.put("trade_type", WxPayPropertiesBuilder.instance().getTradeType());// 交易类型APP
  127. // parame.put("spbill_create_ip", getClientIp());
  128. //parame.put("spbill_create_ip", ResourceUtil.getConfigByName("spbillCreateIp"));
  129. parame.put("spbill_create_ip", WxPayPropertiesBuilder.instance().getSpbillCreateIp());
  130. parame.put("openid", loginUser.getWeixin_openid());
  131. // parame.put("sign_type", "MD5");
  132. //String sign = WechatUtil.arraySign(parame, ResourceUtil.getConfigByName("wx.paySignKey"));
  133. String sign = WechatUtil.arraySign(parame, WxPayPropertiesBuilder.instance().getPaySignKey());
  134. parame.put("sign", sign);// 数字签证
  135. String xml = MapUtils.convertMap2Xml(parame);
  136. logger.info("xml:" + xml);
  137. //Map<String, Object> resultUn = XmlUtil.xmlStrToMap(WechatUtil.requestOnce(ResourceUtil.getConfigByName("wx.uniformorder"), xml));
  138. Map<String, Object> resultUn = XmlUtil.xmlStrToMap(WechatUtil.requestOnce(WxPayPropertiesBuilder.instance().getUniformorder(), xml));
  139. // 响应报文
  140. String return_code = MapUtils.getString("return_code", resultUn);
  141. String return_msg = MapUtils.getString("return_msg", resultUn);
  142. //
  143. if (return_code.equalsIgnoreCase("FAIL")) {
  144. updateFailProcessBatch(processRecordEntityList, "支付失败," +return_msg);//更新订单流转信息
  145. addOrderExceptionRecord(orderVoList, Dict.exceptionStatus.item_00.getItem(),"支付失败," +return_msg);//记录订单异常信息
  146. return toResponsFail("支付失败," + return_msg);
  147. } else if (return_code.equalsIgnoreCase(WechatUtil.WXTradeState.SUCCESS.getCode())) {
  148. // 返回数据
  149. String result_code = MapUtils.getString("result_code", resultUn);
  150. String err_code_des = MapUtils.getString("err_code_des", resultUn);
  151. if (result_code.equalsIgnoreCase("FAIL")) {
  152. updateFailProcessBatch(processRecordEntityList, "支付失败," +err_code_des);//更新订单流转信息
  153. addOrderExceptionRecord(orderVoList, Dict.exceptionStatus.item_00.getItem(),"支付失败," +err_code_des);//记录订单异常信息
  154. return toResponsFail("支付失败," + err_code_des);
  155. } else if (result_code.equalsIgnoreCase(WechatUtil.WXTradeState.SUCCESS.getCode())) {
  156. apiPayService.payPrepay(resultObj, resultUn, nonceStr, orderVoList);
  157. orderProcessRecordService.updateBatch(processRecordEntityList);//更新订单流转信息
  158. return toResponsObject(0, "微信统一订单下单成功", resultObj);
  159. }
  160. }
  161. } catch (Exception e) {
  162. e.printStackTrace();
  163. updateFailProcessBatch(processRecordEntityList, "下单失败,error=" + e.getMessage());//更新订单流转信息
  164. return toResponsFail("下单失败,error=" + e.getMessage());
  165. }
  166. return toResponsFail("下单失败");
  167. }
  168. /**
  169. * 更新订单支付失败流转信息
  170. * @param processRecordEntityList
  171. * @param return_msg
  172. */
  173. public void updateFailProcessBatch(List<OrderProcessRecordEntity> processRecordEntityList,String return_msg){
  174. for (OrderProcessRecordEntity orderProcessRecordEntity : processRecordEntityList) {
  175. orderProcessRecordEntity.setIsPaymentSend(Dict.isSend.item_0.getItem());
  176. orderProcessRecordEntity.setProcessContent(return_msg);
  177. }
  178. orderProcessRecordService.updateBatch(processRecordEntityList);
  179. }
  180. /**
  181. * 记录订单异常信息
  182. * @param orderVoList
  183. * @param exceptionStatus
  184. * @param expContent
  185. */
  186. public void addOrderExceptionRecord(List<OrderVo> orderVoList,String exceptionStatus,String expContent){
  187. for (OrderVo orderInfo: orderVoList) {
  188. MallOrderExceptionRecord mallOrderExceptionRecord = new MallOrderExceptionRecord();
  189. mallOrderExceptionRecord.setUserId(Integer.parseInt(orderInfo.getUser_id()+""));
  190. mallOrderExceptionRecord.setOrderSn(orderInfo.getOrder_sn());
  191. mallOrderExceptionRecord.setExceptionStatus(exceptionStatus);
  192. mallOrderExceptionRecord.setExceptionContent(expContent);
  193. mallOrderExceptionRecord.setCreateTime(new Date());
  194. apiOrderExceptionRecordService.save(mallOrderExceptionRecord);
  195. }
  196. }
  197. /**
  198. * 微信订单回调接口
  199. *
  200. * @return
  201. */
  202. @IgnoreAuth
  203. @RequestMapping(value = "/notify", produces = "text/html;charset=UTF-8")
  204. @ResponseBody
  205. public void notify(HttpServletRequest request, HttpServletResponse response) {
  206. logger.info("微信订单回调接口>>>>>>notify start");
  207. try {
  208. request.setCharacterEncoding("UTF-8");
  209. response.setCharacterEncoding("UTF-8");
  210. response.setContentType("text/html;charset=UTF-8");
  211. response.setHeader("Access-Control-Allow-Origin", "*");
  212. InputStream in = request.getInputStream();
  213. ByteArrayOutputStream out = new ByteArrayOutputStream();
  214. byte[] buffer = new byte[1024];
  215. int len = 0;
  216. while ((len = in.read(buffer)) != -1) {
  217. out.write(buffer, 0, len);
  218. }
  219. out.close();
  220. in.close();
  221. String reponseXml = new String(out.toByteArray(), "utf-8");//xml数据
  222. logger.error("reponseXml:" + reponseXml);
  223. WechatRefundApiResult result = (WechatRefundApiResult) XmlUtil.xmlStrToBean(reponseXml, WechatRefundApiResult.class);
  224. String result_code = result.getResult_code();
  225. if (result_code.equalsIgnoreCase("FAIL")) {
  226. String out_trade_no = result.getOut_trade_no();//订单编号
  227. logger.error("订单" + out_trade_no + "支付失败");
  228. response.getWriter().write(setXml("SUCCESS", "OK"));
  229. response.getWriter().close();
  230. } else if (result_code.equalsIgnoreCase(WechatUtil.WXTradeState.SUCCESS.getCode())) {
  231. //验签
  232. String time_end = result.getTime_end();
  233. String out_trade_no = result.getOut_trade_no();//商户订单号
  234. String transaction_id = result.getTransaction_id();//微信支付订单号
  235. String total_fee = result.getTotal_fee();//订单编号
  236. logger.error("订单" + out_trade_no + "支付成功");
  237. // 业务处理
  238. apiPayService.notify(out_trade_no,total_fee,transaction_id,time_end);
  239. response.getWriter().write(setXml("SUCCESS", "OK"));
  240. }
  241. } catch (Exception e) {
  242. e.printStackTrace();
  243. return;
  244. }
  245. }
  246. /**
  247. * 订单退款请求 todo 小程序用户暂不给退款
  248. */
  249. /*@GetMapping("refund")
  250. public Object refund(@LoginUser UserVo loginUser, Long orderId) {
  251. //
  252. OrderVo orderInfo = orderService.queryObject(orderId);
  253. if (null == orderInfo) {
  254. return toResponsObject(400, "订单已取消", "");
  255. }
  256. if (orderInfo.getOrder_status() == 401 || orderInfo.getOrder_status() == 402) {
  257. return toResponsObject(400, "订单已退款", "");
  258. }
  259. if (orderInfo.getPay_status() != 2) {
  260. return toResponsObject(400, "订单未付款,不能退款", "");
  261. }
  262. // WechatRefundApiResult result = WechatUtil.wxRefund(orderInfo.getId().toString(),
  263. // orderInfo.getActual_price().doubleValue(), orderInfo.getActual_price().doubleValue());
  264. WechatRefundApiResult result = WechatUtil.wxRefund(orderInfo.getMerchOrderSn().toString(),
  265. orderInfo.getActual_price().doubleValue(), orderInfo.getActual_price().doubleValue());
  266. if (result.getResult_code().equals("SUCCESS")) {
  267. apiPayService.refund(orderInfo,result,"");
  268. return toResponsObject(400, "成功退款", "");
  269. } else {
  270. MallOrderRefund mallOrderRefund = mallOrderRefundMapper.queryObjectByOrderId(orderInfo.getId()+"");
  271. MallOrderRefund orderRefund = new MallOrderRefund();
  272. orderRefund.setRefundType(Integer.parseInt(Dict.RefundType.item_1.getItem()));
  273. orderRefund.setRefundMoney(BigDecimal.valueOf(orderInfo.getActual_price().multiply(Constant.ONE_HUNDRED).doubleValue()));
  274. orderRefund.setRefundStatus(Integer.parseInt(Dict.RefundStatus.item_3.getItem()));
  275. orderRefund.setModTime(new Date());
  276. if(mallOrderRefund !=null){
  277. orderRefund.setId(mallOrderRefund.getId());
  278. mallOrderRefundMapper.update(orderRefund);
  279. }
  280. MallOrderExceptionRecord mallOrderExceptionRecord = new MallOrderExceptionRecord();
  281. mallOrderExceptionRecord.setUserId(Integer.parseInt(orderInfo.getUser_id()+""));
  282. mallOrderExceptionRecord.setOrderSn(orderInfo.getOrder_sn());
  283. mallOrderExceptionRecord.setExceptionStatus(Dict.exceptionStatus.item_03.getItem());
  284. mallOrderExceptionRecord.setExceptionContent("退款失败"+result.getErr_code_des());
  285. mallOrderExceptionRecord.setCreateTime(new Date());
  286. apiOrderExceptionRecordService.save(mallOrderExceptionRecord);
  287. return toResponsObject(400, "退款失败"+result.getErr_code_des(), "");
  288. }
  289. }*/
  290. /**
  291. * 微信订单退款回调接口
  292. *
  293. * @return
  294. */
  295. @IgnoreAuth
  296. @RequestMapping(value = "/refundNotify", produces = "text/html;charset=UTF-8")
  297. @ResponseBody
  298. public void refundNotify(HttpServletRequest request, HttpServletResponse response) {
  299. logger.error("refundNotify start");
  300. try {
  301. request.setCharacterEncoding("UTF-8");
  302. response.setCharacterEncoding("UTF-8");
  303. response.setContentType("text/html;charset=UTF-8");
  304. response.setHeader("Access-Control-Allow-Origin", "*");
  305. InputStream in = request.getInputStream();
  306. ByteArrayOutputStream out = new ByteArrayOutputStream();
  307. byte[] buffer = new byte[1024];
  308. int len = 0;
  309. while ((len = in.read(buffer)) != -1) {
  310. out.write(buffer, 0, len);
  311. }
  312. out.close();
  313. in.close();
  314. String reponseXml = new String(out.toByteArray(), "utf-8");//xml数据
  315. logger.error("reponseXml:" + reponseXml);
  316. WechatRefundNotifyResult result = (WechatRefundNotifyResult) XmlUtil.xmlStrToBean(reponseXml, WechatRefundNotifyResult.class);
  317. if (result.getReturn_code().equalsIgnoreCase("FAIL")) {
  318. logger.info("微信查询接口调用失败: "+result.getReturn_msg());
  319. response.getWriter().write(setXml("SUCCESS", "OK"));
  320. response.getWriter().close();
  321. }else {
  322. String req_info = result.getReq_info();//加密信息
  323. String aesResult = AESUtil.decryptData(req_info);
  324. System.out.println(aesResult);
  325. WechatRefundApiResult refundApiResult = (WechatRefundApiResult) XmlUtil.xmlStrToBean(aesResult, WechatRefundApiResult.class);
  326. logger.error("订单" + refundApiResult.getOut_trade_no() + "退款成功");
  327. // 业务处理
  328. apiPayService.refundNotify(refundApiResult);
  329. response.getWriter().write(setXml("SUCCESS", "OK"));
  330. }
  331. } catch (Exception e) {
  332. e.printStackTrace();
  333. return;
  334. }
  335. }
  336. /**
  337. * 订单退款请求
  338. */
  339. @IgnoreAuth
  340. @GetMapping("test")
  341. public Object test(Long orderId) {
  342. OrderVo orderInfo = orderService.queryObject(orderId);
  343. // 微信通知
  344. orderService.notifyPaySuccess(orderInfo);
  345. return null;
  346. }
  347. //返回微信服务
  348. public static String setXml(String return_code, String return_msg) {
  349. return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
  350. }
  351. //模拟微信回调接口
  352. public static String callbakcXml(String orderNum) {
  353. return "<xml><appid><![CDATA[wx2421b1c4370ec43b]]></appid><attach><![CDATA[支付测试]]></attach><bank_type><![CDATA[CFT]]></bank_type><fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[10000100]]></mch_id><nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str><openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid> <out_trade_no><![CDATA[" + orderNum + "]]></out_trade_no> <result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign><sub_mch_id><![CDATA[10000100]]></sub_mch_id> <time_end><![CDATA[20140903131540]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id></xml>";
  354. }
  355. }