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