需求分析
在完成支付后,修改訂單狀態為已支付,並記錄訂單日志。
實現思路
(1)接受微信支付平台的回調信息(xml)
<xml><appid><![CDATA[wx8397f8696b538317]]></appid> <bank_type><![CDATA[CFT]]></bank_type> <cash_fee><![CDATA[1]]></cash_fee> <fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[N]]></is_subscribe> <mch_id><![CDATA[1473426802]]></mch_id> <nonce_str><![CDATA[c6bea293399a40e0a873df51e667f45a]]></nonce_str> <openid><![CDATA[oNpSGwbtNBQROpN_dL8WUZG3wRkM]]></openid> <out_trade_no><![CDATA[1553063775279]]></out_trade_no> <result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code> <sign><![CDATA[DD4E5DF5AF8D8D8061B0B8BF210127DE]]></sign> <time_end><![CDATA[20190320143646]]></time_end> <total_fee>1</total_fee> <trade_type><![CDATA[NATIVE]]></trade_type> <transaction_id><![CDATA[4200000248201903206581106357]]></transaction_id> </xml>
(2)收到通知后,調用查詢接口查詢訂單。
(3)如果支付結果為成功,則調用修改訂單狀態和記錄訂單日志的方法。
代碼實現
內網映射工具EchoSite
在請求統一下單接口時,有個參數notify_url ,這個是回調地址,也就是在支付成功后微信支付會自動訪問這個地址,通知業務系統支付完成。但這個地址必須是互聯網可以訪問的(也就是有域名的)。
那么如何測試呢?我們可以借助一個工具 EchoSite 內網映射工具
(1)打開網址: https://www.echosite.cn/ 注冊用戶,登錄到控制台下載客戶端。
(2)支付3元買一個域名(可以用1個月),點擊域名端口---搶注域名
(3)使用課程提供的軟件echosite ,添加config.yml
# 這是你的 EchoSite 購買域名的服務器標志 server_addr: cross.echosite.cn:4443 trust_host_root_certs: false echosite_id: 17799998888 echosite_token: $2y$10$bY081mt/2KAFJLJXrsSNHe3f.6SM.SGfXgu1gG7aJvmPMPN9BTqrS # 以下是你需要開啟的通道,只能開啟屬於你的域名通道 # 以下分別是 http 和 https 以及 tcp 協議的示例 tunnels: name1: subdomain: "changgou" proto: http: 127.0.0.1:9010
然后在echosite目錄中輸入以下命令
echosite -config=config.yml start-all
運行效果如下:
這樣你購買的域名就映射到127.0.0.1:9011上了。 ctrl+c 結束程序
怎么才能驗證域名是否映射到你的計算機上了呢?
WxPayController新增notifyLogic方法
/** * 回調 */ @RequestMapping("/notify") public void notifyLogic(){ System.out.println("支付成功回調。。。。"); }
測試:
1)通過本地方式訪問該接口
2)通過域名形式訪問該接口
接收回調信息
(1)修改支付微服務配置文件
wxpay: notify_url: http://weizhaohui.cross.echosite.cn/wxpay/notify #回調地址
(2)修改WxPayServiceImpl ,引入
@Value( "${wxpay.notify_url}" ) private String notifyUrl;
(3)修改WxPayServiceImpl 的nativePay方法
map.put("notify_url",notifyUrl);//回調地址
測試: 重新生成訂單並支付。可以發現控制台在不斷的觸發支付回調通知。這是為什么呢?
注意:
1、同樣的通知可能會多次發送給商戶系統。商戶系統必須能夠正確處理重復的通知。
2、后台通知交互時,如果微信收到商戶的應答不符合規范或超時,微信會判定本次通知失敗,重新發送通知,直到成功為止(在通知一直不成功的情況下,微信總共會發起10次通知,通知頻率為15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 總計 24h4m),但微信不保證通知最終一定能成功。
回調消息接收並轉換
微信支付平台發送給回調地址的數據是二進制流,我們需要提取二進制流轉換為字符串,這個字符串就是xml格式。
(1)在changgou_common工程添加工具類ConvertUtils。(資源提供:資源\類文件\工具類)
/** * 轉換工具類 */ public class ConvertUtils { /** * 輸入流轉換為xml字符串 * @param inputStream * @return */ public static String convertToString(InputStream inputStream) throws IOException { ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inputStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inputStream.close(); String result = new String(outSteam.toByteArray(), "utf-8"); return result; } }
(2)修改notifyLogic方法
/** * 回調 */ @RequestMapping("/notify") public void notifyLogic(HttpServletRequest request, HttpServletResponse response) throws IOException { System.out.println("支付成功回調。。。。"); try { //輸入流轉換為xml字符串 String xml = ConvertUtils.convertToString( request.getInputStream() ); System.out.println(xml); //給微信支付一個成功的響應 response.setContentType("text/xml"); String data = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; response.getWriter().write(data); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }
測試后,在控制台看到輸出的消息
<xml><appid><![CDATA[wx8397f8696b538317]]></appid> <bank_type><![CDATA[CFT]]></bank_type> <cash_fee><![CDATA[1]]></cash_fee> <fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[N]]></is_subscribe> <mch_id><![CDATA[1473426802]]></mch_id> <nonce_str><![CDATA[c6bea293399a40e0a873df51e667f45a]]></nonce_str> <openid><![CDATA[oNpSGwbtNBQROpN_dL8WUZG3wRkM]]></openid> <out_trade_no><![CDATA[1553063775279]]></out_trade_no> <result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code> <sign><![CDATA[DD4E5DF5AF8D8D8061B0B8BF210127DE]]></sign> <time_end><![CDATA[20190320143646]]></time_end> <total_fee>1</total_fee> <trade_type><![CDATA[NATIVE]]></trade_type> <transaction_id><![CDATA[4200000248201903206581106357]]></transaction_id> </xml>
我們可以將此xml字符串,轉換為map,提取其中的out_trade_no(訂單號),根據訂單號修改訂單狀態。
查詢訂單驗證通知
(1)WxPayService新增方法定義
/** * 查詢訂單 * @param orderId * @return */ Map queryOrder(String orderId);
(2)WxPayServiceImpl實現方法
@Override public Map queryOrder(String orderId) { Map map=new HashMap( ); map.put( "out_trade_no", orderId ); try { return wxPay.orderQuery( map ); } catch (Exception e) { e.printStackTrace(); return null; } }
(3)修改notifyLogic方法
@Autowired private RabbitTemplate rabbitTemplate; /** * 回調 */ @RequestMapping("/notify") public void notifyLogic(HttpServletRequest request, HttpServletResponse response) throws IOException { System.out.println("支付成功回調。。。。"); try { //輸入流轉換為xml字符串 String xml = ConvertUtils.convertToString( request.getInputStream() ); System.out.println(xml); //解析 Map<String, String> map = WXPayUtil.xmlToMap( xml ); //查詢訂單 if("SUCCESS".equals(map.get( "result_code" ) )){ //如果返回的結果是成功 Map result = wxPayService.queryOrder( map.get( "out_trade_no" ) ); System.out.println("查詢訂單返回結果:"+result); //如果查詢結果是成功發送到mq if("SUCCESS".equals( result.get( "result_code" ) )){ Map m=new HashMap(); m.put( "orderId",result.get( "out_trade_no" ) ); m.put( "transactionId",result.get( "transaction_id" )); rabbitTemplate.convertAndSend( "","order_pay", JSON.toJSONString(m) ); //如果成功,給微信支付一個成功的響應 response.setContentType("text/xml"); String data = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; response.getWriter().write(data); } }else { System.out.println(map.get( "err_code_des" ));//錯誤信息描述 } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }
(4)rabbitmq中添加order_pay隊列
修改訂單狀態
(1)com.changgou.order.listener包下創建OrderPayListener
@Component @RabbitListener(queues = "order_pay") public class OrderPayListener { @Autowired private OrderService orderService; /** * 更新支付狀態 * @param message */ @RabbitHandler public void updatePayStatus(String message){ System.out.println("接收到消息:"+message); Map map = JSON.parseObject( message, Map.class ); orderService.updatePayStatus( (String)map.get("orderId"), (String)map.get("transactionId") ); } }
(2)OrderService接口新增方法定義
/** * 修改訂單狀態為已支付 * @param orderId * @param transactionId */ void updatePayStatus(String orderId,String transactionId);
(3)OrderServiceImpl新增方法實現
@Autowired private OrderLogMapper orderLogMapper; @Override public void updatePayStatus(String orderId, String transactionId) { Order order = orderMapper.selectByPrimaryKey(orderId); if(order!=null && "0".equals(order.getPayStatus())){ //存在訂單且狀態為0 order.setPayStatus("1"); order.setOrderStatus("1"); order.setUpdateTime(new Date()); order.setPayTime(new Date()); order.setTransactionId(transactionId);//微信返回的交易流水號 orderMapper.updateByPrimaryKeySelective(order); //記錄訂單變動日志 OrderLog orderLog=new OrderLog(); orderLog.setId( idWorker.nextId()+"" ); orderLog.setOperater("system");// 系統 orderLog.setOperateTime(new Date());//當前日期 orderLog.setOrderStatus("1"); orderLog.setPayStatus("1"); orderLog.setRemarks("支付流水號"+transactionId); orderLog.setOrderId(order.getId()); orderLogMapper.insert(orderLog); } }