# 一、回調描述
用戶使用微信支付分服務,當商戶調用完結支付分訂單API后,微信支付會輪詢向用戶發(fā)起扣款,當扣款成功時,微信支付會通過POST的請求方式,向商戶預先設置的回調地址發(fā)送回調通知,讓商戶知曉用戶已完成支付。
回調地址設置方式: 回調地址通過【創(chuàng)建支付分訂單】接口中的“notify_url”參數(shù)設置,回調地址的設置規(guī)范和回調IP列表請參考文檔:回調通知注意事項 (opens new window)。
# 二、回調處理步驟
# 1、商戶接收回調通知報文
微信支付會通過POST的方式向回調地址發(fā)送回調報文,回調報文的HTTP請求頭中會包含報文的簽名信息,用于后續(xù)驗簽,具體如下:
參數(shù) | 描述 |
---|---|
Wechatpay-Serial | 驗簽的“微信支付平臺證書”所對應的平臺證書序列號 |
Wechatpay-Signature | 驗簽的簽名值 |
Wechatpay-Timestamp | 驗簽的時間戳 |
Wechatpay-Nonce | 驗簽的隨機字符串 |
回調通知的請求主體中會包含JSON格式的通知參數(shù),具體的通知參數(shù)列表如下:
# 通知參數(shù)
- id 必填 string(36)【通知ID】回調通知的唯一編號。
- create_time 必填 string(32)【通知創(chuàng)建時間】回調通知創(chuàng)建的時間,遵循rfc3339標準格式,格式為yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出現(xiàn)在字符串中,HH:mm:ss表示時分秒,TIMEZONE表示時區(qū)(+08:00表示東八區(qū)時間,領先UTC 8小時,即北京時間)。例如2015-05-20T13:29:35+08:00表示,北京時間2015年5月20日13點29分35秒。
- event_type 必填 string(32)【通知類型】微信支付分回調通知的類型,支付分訂單支付成功通知為PAYSCORE.USER_PAID。
- resource_type 必填 string(32)【通知數(shù)據(jù)類型】通知的資源數(shù)據(jù)類型,固定為encrypt-resource。
- resource 必填 object【通知資源數(shù)據(jù)】json格式,見示例
- 屬性
- summary 必填 string(64)【回調摘要】微信支付對回調內(nèi)容的摘要備注。
支付成功結果通知
1{2 "id":"EV-2018022511223320873",3 "create_time":"2015-05-20T13:29:35+08:00",4 "resource_type":"encrypt-resource",5 "event_type":"PAYSCORE.USER_PAID",6 "resource" : {7 "algorithm":"AEAD_AES_256_GCM",8 "ciphertext": "...",9 "nonce": "...",10 "associated_data": ""11 },12 "summary": "支付成功"13}
# 2、回調驗簽與應答
商戶接收到回調通知報文后,需在5秒內(nèi)完成對報文的驗簽,并應答回調通知。
# 2.1、對回調通知進行驗簽
驗簽需使用請求頭中的【W(wǎng)echatpay-Timestamp】、【W(wǎng)echatpay-Nonce】以及請求主體中JSON格式的通知參數(shù)構建出驗簽串,然后使用【W(wǎng)echatpay-Serial】對應的“微信支付平臺證書 (opens new window)”對驗簽串和【W(wǎng)echatpay-Signature】進行驗簽,確保接收的回調內(nèi)容是來自微信支付。
詳細驗簽步驟請參考:非文件/圖片下載如何驗證簽名 (opens new window)
微信支付會在極少數(shù)通知回調中返回以“WECHATPAY/SIGNTEST/”開頭的錯誤【W(wǎng)echatpay-Signature】,以檢測商戶系統(tǒng)是否正確驗證簽名。商戶請參考:如何應對簽名探測流量 (opens new window) 進行處理。
# 2.2、對回調通知進行應答
商戶驗簽后,根據(jù)驗簽結果對回調進行應答:
驗簽通過:商戶需告知微信支付接收回調成功,HTTP應答狀態(tài)碼需返回200或204,無需返回應答報文。
驗簽不通過:商戶需告知微信支付接收回調失敗,HTTP應答狀態(tài)碼需返回5XX或4XX,同時需返回以下應答報文:
- code 必填 string(32)【返回狀態(tài)碼】
錯誤碼,F(xiàn)AIL為回調接收失敗。 - message 選填 string(256)【返回信息】
返回信息,回調接收失敗原因。
應答示例
1{ 2 "code": "FAIL",3 "message": "失敗"4}
微信支付接收到商戶的應答后,會根據(jù)應答結果做對應的邏輯處理:
若商戶應答回調接收成功,微信支付后續(xù)不再發(fā)送該回調內(nèi)容。若因網(wǎng)絡或其他原因,商戶收到了重復的回調通知,按正常業(yè)務流程應答即可。
若商戶應答回調接收失敗,或超時(5s)未應答時,微信支付會按照(15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h)的頻次重復發(fā)送回調通知,最多發(fā)送15次。
# 3、對回調通知內(nèi)容進行解密
為了保證業(yè)務信息的安全性,微信支付將業(yè)務信息進行了AES-256-GCM加密,并通過參數(shù)resource將加密信息回調給商戶,商戶需要進行解密后才能獲取到業(yè)務信息。
解密步驟如下:
- 獲取商戶平臺上設置的APIv3密鑰,記為key;
- 通過回調通知參數(shù)resource.algorithm確認加密算法(目前僅支持AEAD_AES_256_GCM,算法的接口細節(jié),請參考:rfc5116 (opens new window))。
- 使用key與回調通知參數(shù)resource.nonce和resource.associated_data,對數(shù)據(jù)密文resource.ciphertext進行解密,最終可得到JSON格式的業(yè)務信息。
解密示例代碼可參考文檔:如何解密回調報文 (opens new window)
注意
- 使用Java進行回調解密,取JSON串內(nèi)的參數(shù)值時,只需取引號內(nèi)的內(nèi)容進行解密。例:"nonce":"123",只需取值123,不用取加上引號的"123"。
# resource解密后字段
- appid 必填 string(32)【公眾賬號ID】是微信開放平臺和微信公眾平臺為開發(fā)者的應用程序(APP、小程序、公眾號)提供的一個唯一標識。 開發(fā)者需要先在微信開放平臺或微信公眾平臺中申請ID,然后在商戶平臺中綁定,詳見直連商戶與AppID賬號關聯(lián)管理
。完結訂單和取消訂單需要和創(chuàng)單傳入的appid保持一致。 - mchid 必填 string(32)【商戶號】調用支付分創(chuàng)單接口提交的商戶號,商戶號需開通支付分產(chǎn)品權限,且與appid有綁定關系,詳見直連商戶與AppID賬號關聯(lián)管理
。 - out_order_no 必填 string(32)【商戶服務訂單號】 商戶系統(tǒng)內(nèi)部服務訂單號,商戶在創(chuàng)建支付分接口中填入的out_order_no參數(shù),調用支付分查單接口時out_order_no字段和query_id字段必填一個(不允許都填寫或都不填寫)。
- service_id 必填 string(32)【支付分服務ID】商戶支付分服務的唯一標識,由32位數(shù)字組成。支付分產(chǎn)品權限審核通過后,微信支付運營會向商戶提供該ID。
- openid 必填 string(128)【用戶標識】用戶在商戶對應appid下的唯一標識。
- state 必填 string(32)【服務訂單狀態(tài)】 表示支付分訂單狀態(tài)
DONE:服務訂單完成(終態(tài))
該狀態(tài)需結合collection.state字段一起判斷,具體可參考支付分訂單狀態(tài)流轉圖。 - total_amount 選填 int(64)【總金額】 訂單最終收款總金額,整型,單位為分,商戶調用完結訂單接口和修改訂單金額接口傳入,受服務ID風險金額上限影響,服務ID風險金額上限具體請與BD確認。
先免模式:total_amount<=創(chuàng)單risk_fund.amount(押金金額)<=服務ID風險金額上限。
先享模式:total_amount<=服務ID風險金額上限。
需滿足計算條件:total_amount = 后付費項目金額(post_payments.amount總和) - 優(yōu)惠項目金額(post_discounts.amount總和),例如商戶后付費項目金額總和為10元,優(yōu)惠項目金額總和為2元,則訂單收款總金額為8元。 - service_introduction 必填 string(20)【服務信息】用于介紹本訂單所提供的服務 ,長度不能超過20個字符(漢字、數(shù)字、字母、特殊符號都按照1個字符計算)。
- post_payments 必填 array【后付費項目】用于展示訂單后付費項目明細,商戶需要按照所屬行業(yè)規(guī)程傳參,詳見post_payments(后付費項目)字段傳參說明
- 數(shù)組
- post_discounts 選填 array【商戶優(yōu)惠】用于展示訂單優(yōu)惠項目明細,最多30條,完結訂單時傳的收款總金額需滿足計算條件(收款總金額=后付費項目amount和-優(yōu)惠項目amount和)
- 數(shù)組
- risk_fund 必填 object【服務風險金】本筆訂單的風險金額描述
- 屬性
- time_range 必填 object【服務時間段】用于描述訂單的服務開始和結束時間。
- 屬性
- location 選填 object【服務位置】用于描述用戶使用服務的地理位置
- 屬性
- attach 選填 string(200)【商戶數(shù)據(jù)包】商戶在創(chuàng)建訂單時傳入的自定義數(shù)據(jù)包,用戶不可見。用于存放訂單的商戶自定義數(shù)據(jù),需要先進行urlencode編碼,總長度不超過256字符。確認訂單回調和支付成功回調時會回傳該字段給商戶。
- order_id 選填 string(64)【微信服務單號】 支付分訂單在微信側的唯一標識,31位數(shù)字,開頭由1000000000+年月日組成。
- need_collection 選填 bool【是否需要收款】訂單是否需要收款,固定返回true需收款。
- notify_url 必填 string(255)【商戶回調地址】 商戶接收確認訂單成功回調和支付成功回調的地址。
- collection 選填 object【收款信息】訂單收款信息,僅在調用完結訂單后返回(若完結訂單 total_amount 等于 0 元,則不返回此字段)。
- 屬性
商戶對resource對象進行解密后,得到的資源對象示例
1{2 "appid": "wxd678efh567hg6787", 3 "mchid": "1230000109", 4 "out_order_no": "1234323JKHDFE1243252", 5 "service_id": "500001", 6 "openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o",7 "state": "DONE", 8 "total_amount": "40000", 9 "service_introduction": "嗨客餐廳用餐",10 "post_payments": [11 {12 "name": "服務費", 13 "amount": 40000, 14 "description": "每分鐘1元"15 }16 ], 17 "post_discounts": [18 {19 "name": "滿20減1元", 20 "amount": 1, 21 "description": "不與其他優(yōu)惠疊加"22 }23 ], 24 "risk_fund": {25 "name": "ESTIMATE_ORDER_COST", 26 "amount": 10000, 27 "description": "就餐的預估費用"28 }, 29 "time_range": {30 "start_time": "20091225091010", 31 "end_time": "20091225091210"32 }, 33 "location": {34 "start_location": "嗨客時尚主題展餐廳", 35 "end_location": "嗨客時尚主題展餐廳"36 }, 37 "attach": "attach", 38 "notify_url": "https://api.test.com",39 "order_id": "165461131",40 "need_collection": true, 41 "collection": {42 "state": "USER_PAID", 43 "total_amount": 40000, 44 "paying_amount": 40000, 45 "paid_amount": 0, 46 "details": [47 { 48 "seq": 1, 49 "amount": 10000, 50 "paid_type": "MCH", 51 "paid_time": "20091225091210", 52 "transaction_id": "15646546545165651651", 53 "promotion_detail":[54 {55 "coupon_id": "123456", 56 "name": "單品優(yōu)惠-6", 57 "scope": "GLOBAL", 58 "type": "CASH", 59 "amount": 100, 60 "stock_id": "activity_id", 61 "wechatpay_contribute": 100, 62 "merchant_contribute": 100, 63 "other_contribute": 0, 64 "currency": "CNY", 65 "goods_detail": [66 {67 "goods_id": "M1006", 68 "quantity": 1, 69 "unit_price": 1, 70 "discount_amount": 0, 71 "goods_remark": "商品備注信息"72 }73 ]74 }75 ]76 }77 ]78 }79}