WechatH5PayController.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. package com.example.controller;
  2. import com.alibaba.fastjson.JSON;
  3. import com.example.base.*;
  4. import com.example.entity.OmsOrder;
  5. import com.example.entity.RefundOrder;
  6. import com.example.entity.WxPayFrom;
  7. import com.example.enums.PayStatusEnum;
  8. import com.example.enums.ResultEnum;
  9. import com.example.util.GeneratorIdUtils;
  10. import com.example.util.RedisUtil;
  11. import com.example.util.UUIDUtils;
  12. import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
  13. import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
  14. import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
  15. import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  16. import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
  17. import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
  18. import com.github.binarywang.wxpay.constant.WxPayConstants;
  19. import com.github.binarywang.wxpay.exception.WxPayException;
  20. import com.github.binarywang.wxpay.service.WxPayService;
  21. import io.swagger.annotations.Api;
  22. import io.swagger.annotations.ApiOperation;
  23. import lombok.extern.slf4j.Slf4j;
  24. import me.chanjar.weixin.common.bean.WxJsapiSignature;
  25. import me.chanjar.weixin.common.error.WxErrorException;
  26. import me.chanjar.weixin.mp.api.WxMpService;
  27. import org.springframework.beans.factory.annotation.Autowired;
  28. import org.springframework.beans.factory.annotation.Value;
  29. import org.springframework.web.bind.annotation.*;
  30. import javax.servlet.http.HttpServletRequest;
  31. import javax.servlet.http.HttpServletResponse;
  32. import java.io.ByteArrayOutputStream;
  33. import java.io.IOException;
  34. import java.io.InputStream;
  35. import java.io.PrintWriter;
  36. import java.math.BigDecimal;
  37. @Slf4j
  38. @RestController
  39. @Api(tags = "公众号支付", description = "WechatH5PayController")
  40. @RequestMapping("/app")
  41. public class WechatH5PayController extends BaseController {
  42. private final static String OMSORDER = "oms_order_";
  43. @Value("${wx.pay.wxpayAppcallbackurl}")
  44. private String wxpayCallbackUrl;
  45. @Value("${wx.pay.wxpayRefundCallBackUrl}")
  46. private String wxpayRefundCallBackUrl;
  47. @Autowired
  48. private WxPayService wxPayService;
  49. @Autowired
  50. private RedisUtil redisUtil;
  51. @PostMapping("/pay")
  52. public ResponseBase pay(@RequestBody WxPayFrom wxPayFrom, HttpServletRequest request) {
  53. String orderId = wxPayFrom.getOrderId();
  54. String openid = wxPayFrom.getOpenid();
  55. String tradeType = wxPayFrom.getTradeType();
  56. OmsOrder omsOrder = JSON.parseObject(redisUtil.get(OMSORDER + orderId).toString(), OmsOrder.class);
  57. Integer status = omsOrder.getStatus();
  58. //查询订单是否支付
  59. if (status.equals(PayStatusEnum.SUCCESS.getIndex())) {
  60. throw new RuntimeException("订单已支付");
  61. }
  62. WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
  63. //随机字符串
  64. String s = UUIDUtils.randomUUID();
  65. orderRequest.setNonceStr(s);
  66. //商品描述
  67. String productName = omsOrder.getProductName();
  68. orderRequest.setBody(productName);
  69. //商户订单号(支付编号)
  70. // GeneratorIdUtils generatorIdUtils = new GeneratorIdUtils();
  71. // String orderNum = generatorIdUtils.nextId();
  72. // log.info("orderNum:" + orderNum);
  73. //设置商户订单号
  74. orderRequest.setOutTradeNo(orderId);
  75. //设置金额
  76. //data.getTotalPrice();
  77. BigDecimal money = omsOrder.getPayAmount();
  78. //元转成分
  79. money = money.multiply(new BigDecimal(100));
  80. int i = money.intValue();
  81. if ("test".equals(tradeType)) {
  82. //测试环境一分钱
  83. orderRequest.setTotalFee(1);
  84. } else {
  85. orderRequest.setTotalFee(i);
  86. }
  87. //终端ip
  88. String ipAddress = getIpAddress(request);
  89. orderRequest.setSpbillCreateIp(ipAddress);
  90. // 通知地址 支付成功后回调地址
  91. log.info("wxpayCallbackUrl=================>{}", wxpayCallbackUrl);
  92. orderRequest.setNotifyUrl(wxpayCallbackUrl);
  93. //交易类型
  94. orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
  95. //用户标识 获取用户openid
  96. orderRequest.setOpenid(openid);
  97. // 这个可能是偏向原生一点的统一下单,返回的参数有很多没用的 或者null值 建议使用 createOrder下单
  98. Object unOrder = null;
  99. try {
  100. unOrder = wxPayService.createOrder(orderRequest);
  101. } catch (WxPayException e) {
  102. //e.printStackTrace();
  103. log.error(e.getMessage());
  104. //签名完成新增支付订单
  105. return responseError(500, "微信支付失败");
  106. }
  107. //更新本地订单支付id
  108. //orderClient.update(data);
  109. //签名完成新增支付订单
  110. return responseSuccess(new ResultVO(ResultEnum.SUCCESS, unOrder));
  111. }
  112. @Autowired
  113. private WxMpService wxMpService;
  114. /*微信js签名接口*/
  115. @GetMapping("/getJsapiTicket")
  116. public ResponseBase getJsapiTicket(String url) {
  117. WxJsapiSignature jsapiSignature = null;
  118. try {
  119. jsapiSignature = wxMpService.createJsapiSignature(url);
  120. } catch (WxErrorException e) {
  121. e.printStackTrace();
  122. System.err.print(e.getError());
  123. }
  124. return responseSuccess(new ResultVO(ResultEnum.SUCCESS, jsapiSignature));
  125. }
  126. /**
  127. * wx支付成功回调接口
  128. *
  129. * @param request
  130. * @param response
  131. * @throws IOException
  132. */
  133. @RequestMapping("/payResult")
  134. @ApiOperation("微信支付返回通知")
  135. public void payResult(HttpServletRequest request, HttpServletResponse response) throws IOException {
  136. log.info("微信支付返回通知函数开始---------------------");
  137. InputStream inStream = request.getInputStream();
  138. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  139. byte[] buffer = new byte[1024];
  140. int len = 0;
  141. while ((len = inStream.read(buffer)) != -1) {
  142. outSteam.write(buffer, 0, len);
  143. }
  144. outSteam.close();
  145. inStream.close();
  146. String result = new String(outSteam.toByteArray(), "utf-8");
  147. boolean isPayOk = false;
  148. WxPayOrderNotifyResult wxPayOrderNotifyResult = null;
  149. // 此处调用订单查询接口验证是否交易成功
  150. try {
  151. wxPayOrderNotifyResult = wxPayService.parseOrderNotifyResult(result);
  152. if ("SUCCESS".equals(wxPayOrderNotifyResult.getResultCode())) {
  153. isPayOk = true;
  154. }
  155. log.info("解析数据:" + wxPayOrderNotifyResult.toString());
  156. } catch (WxPayException e) {
  157. e.printStackTrace();
  158. }
  159. String noticeStr = "";
  160. // 支付成功,商户处理后同步返回给微信参数
  161. PrintWriter writer = response.getWriter();
  162. if (isPayOk) {
  163. //建议在这里处理付款完成的业务(虽然前端也可以处理后续业务,但是前端处理并不安全,例如:客户突然关闭浏览器了等情况,付款成功后续的业务将中断)
  164. //支付订单支付编号
  165. System.out.println("===============付款成功,业务开始处理==============");
  166. String orderNum = wxPayOrderNotifyResult.getOutTradeNo();
  167. log.info("orderNum:" + orderNum);
  168. //将订单更新为支付
  169. OmsOrder omsOrder = JSON.parseObject(redisUtil.get(OMSORDER + orderNum).toString(), OmsOrder.class);
  170. omsOrder.setStatus(PayStatusEnum.SUCCESS.getIndex());
  171. redisUtil.set(OMSORDER + omsOrder.getOrderId(), omsOrder);
  172. /**
  173. * 自己的业务
  174. */
  175. System.out.println("===============付款成功,业务处理完毕==============");
  176. // 通知微信已经收到消息,不要再给我发消息了,否则微信会8连击调用本接口
  177. noticeStr = setXML("SUCCESS", "OK");
  178. log.info("收到通知返回给微信api信息:-----------" + noticeStr);
  179. writer.write(noticeStr);
  180. writer.flush();
  181. } else {
  182. // 支付失败, 记录流水失败
  183. noticeStr = setXML("FAIL", "");
  184. writer.write(noticeStr);
  185. writer.flush();
  186. System.out.println("===============支付失败==============");
  187. }
  188. }
  189. public static String setXML(String return_code, String return_msg) {
  190. return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
  191. }
  192. public static String getIpAddress(HttpServletRequest request) {
  193. String ip = request.getHeader("x-forwarded-for");
  194. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  195. ip = request.getHeader("Proxy-Client-IP");
  196. }
  197. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  198. ip = request.getHeader("WL-Proxy-Client-IP");
  199. }
  200. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  201. ip = request.getHeader("HTTP_CLIENT_IP");
  202. }
  203. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  204. ip = request.getHeader("HTTP_X_FORWARDED_FOR");
  205. }
  206. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  207. ip = request.getRemoteAddr();
  208. }
  209. String[] split = ip.split(",");
  210. ip = split[0];
  211. return ip;
  212. }
  213. @GetMapping("/order/{orderId}")
  214. @ApiOperation("查询订单状态")
  215. public ResponseBase queryOrder(@PathVariable(value = "orderId") String orderId) {
  216. try {
  217. WxPayOrderQueryResult orderQueryResult = wxPayService.queryOrder(null, orderId);
  218. return responseSuccess(new ResultVO(ResultEnum.SUCCESS, orderQueryResult));
  219. } catch (WxPayException e) {
  220. e.printStackTrace();
  221. return responseError(500, "查询订单异常");
  222. }
  223. }
  224. @PostMapping("/order/refund")
  225. @ApiOperation("申请退款")
  226. public ResponseBase refundOrder(@RequestBody RefundOrder refundOrder) {
  227. WxPayRefundRequest refundRequest = new WxPayRefundRequest();
  228. try {
  229. OmsOrder omsOrder = JSON.parseObject(redisUtil.get(OMSORDER + refundOrder.getOrderId()).toString(), OmsOrder.class);
  230. refundRequest.setOutTradeNo(refundOrder.getOrderId());
  231. GeneratorIdUtils generatorIdUtils = new GeneratorIdUtils();
  232. String refundNo = generatorIdUtils.nextId();
  233. refundRequest.setOutRefundNo(refundNo);
  234. refundRequest.setRefundDesc(refundOrder.getRefundDesc());
  235. refundRequest.setTotalFee(omsOrder.getTotalAmount().intValue());
  236. refundRequest.setRefundFee(refundRequest.getTotalFee());
  237. refundRequest.setOpUserId(refundRequest.getMchId());
  238. refundRequest.setNotifyUrl(wxpayRefundCallBackUrl);
  239. wxPayService.refund(refundRequest);
  240. } catch (WxPayException e) {
  241. e.printStackTrace();
  242. return responseError(500, "申请退款异常");
  243. }
  244. return responseSuccess(new ResultVO());
  245. }
  246. @RequestMapping("/refundResult")
  247. @ApiOperation("退款回调")
  248. public void refundResult(HttpServletRequest request, HttpServletResponse response) throws IOException {
  249. log.info("微信退款回调通知函数开始---------------------");
  250. InputStream inStream = request.getInputStream();
  251. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  252. byte[] buffer = new byte[1024];
  253. int len = 0;
  254. while ((len = inStream.read(buffer)) != -1) {
  255. outSteam.write(buffer, 0, len);
  256. }
  257. outSteam.close();
  258. inStream.close();
  259. String result = new String(outSteam.toByteArray(), "utf-8");
  260. boolean refundOk = false;
  261. WxPayRefundNotifyResult wxPayRefundNotifyResult = null;
  262. try {
  263. wxPayRefundNotifyResult = wxPayService.parseRefundNotifyResult(result);
  264. if ("SUCCESS".equals(wxPayRefundNotifyResult.getReturnCode())) {
  265. refundOk = true;
  266. }
  267. log.info("解析数据:" + wxPayRefundNotifyResult.toString());
  268. } catch (WxPayException e) {
  269. e.printStackTrace();
  270. }
  271. String noticeStr = "";
  272. // 支付成功,商户处理后同步返回给微信参数
  273. PrintWriter writer = response.getWriter();
  274. if (refundOk) {
  275. //建议在这里处理付款完成的业务(虽然前端也可以处理后续业务,但是前端处理并不安全,例如:客户突然关闭浏览器了等情况,付款成功后续的业务将中断)
  276. //支付订单支付编号
  277. System.out.println("===============退款成功,业务开始处理==============");
  278. String refundNo = wxPayRefundNotifyResult.getReqInfo().getOutRefundNo();
  279. log.info("refundNo:" + refundNo);
  280. //将订单更新已退款
  281. /**
  282. * 自己的业务
  283. */
  284. System.out.println("===============退款成功,业务处理完毕==============");
  285. //todo:关闭订单
  286. noticeStr = setXML("SUCCESS", "OK");
  287. log.info("收到通知返回给微信api信息:-----------" + noticeStr);
  288. writer.write(noticeStr);
  289. writer.flush();
  290. } else {
  291. // 支付失败, 记录流水失败
  292. noticeStr = setXML("FAIL", "");
  293. writer.write(noticeStr);
  294. writer.flush();
  295. System.out.println("===============退款失败==============");
  296. }
  297. }
  298. @ApiOperation("退款订单查询")
  299. @GetMapping("/order/refund/{orderId}")
  300. public ResponseBase refundQuery(@PathVariable(value = "orderId") String orderId) {
  301. try {
  302. WxPayRefundQueryResult refundQueryResult = wxPayService.refundQuery(null, orderId, null, null);
  303. } catch (WxPayException e) {
  304. e.printStackTrace();
  305. return responseError(500, "查询退款异常");
  306. }
  307. return responseSuccess(new ResultVO());
  308. }
  309. }