微信支付系統(tǒng)通過商家轉賬批次回調通知接口通知商戶系統(tǒng)批次處理到終態(tài)。包含批次處理完成通知、批次關單通知。
注意
1、同樣的通知可能會多次發(fā)送給商戶系統(tǒng)。商戶系統(tǒng)需要重視對重復通知的正確處理。
當商戶系統(tǒng)收到通知時,先檢查對應業(yè)務數(shù)據(jù)狀態(tài):
(1)如果未處理,進行處理;
(2)如果已處理,則直接返回結果成功;
在對業(yè)務數(shù)據(jù)進行狀態(tài)檢查和處理之前,要采用數(shù)據(jù)鎖進行并發(fā)控制,以避免函數(shù)重入造成的數(shù)據(jù)混亂。
2、如果在所有通知頻率后仍沒有收到微信側回調,商戶應主動調用查詢訂單接口確認訂單狀態(tài)。
3、商戶系統(tǒng)對于支付成功通知的內(nèi)容一定要做簽名驗證,并校驗通知的信息是否與商戶側的信息一致,防止數(shù)據(jù)泄露導致出現(xiàn)“假通知”,造成資金損失。
# 接口說明
支持商戶: 【普通商戶】
請求方式: POST
回調URL: 該鏈接是通過商家轉賬的請求參數(shù)“notify_url”來設置的,要求必須為HTTPS地址。請確保回調URL是外部可正常訪問的,且不能攜帶后綴參數(shù),否則可能導致商戶無法接收到微信的回調通知信息。回調URL示例:http://www.tg885.com/wxpay/pay.action (opens new window)
# 通知規(guī)則
商家轉賬批次單據(jù)到終態(tài)后(批次完成或者批次關閉,對應批次狀態(tài)batch_status的值為FINISHED和CLOSED),微信支付會把批次單據(jù)的信息發(fā)送給商戶,商戶需要接收處理該消息,并返回應答。
對后臺通知交互時,如果微信收到商戶的應答不符合規(guī)范或超時,微信認為通知失敗,微信會通過一定的策略定期重新發(fā)起通知,盡可能提高通知的成功率,但微信不保證通知最終能成功。 (通知頻率為0s/15s(嘗試10次)/300s(嘗試10次)/1800s(嘗試44次))
# 通知報文
通知的數(shù)據(jù)以JSON格式通過請求主體(BODY)傳輸。通知的數(shù)據(jù)包括了加密的授權/解除授權結果詳情。
注意
由于涉及到回調加密和解密,商戶必須先設置好APIv3密鑰后才能解密回調通知,APIv3密鑰設置文檔指引詳見 APIv3密鑰設置指引 (opens new window)
# 步驟說明
# 步驟一:驗證簽名
微信支付會對發(fā)送給商戶的通知進行簽名,并將簽名值放在通知的HTTP頭Wechatpay-Signature。商戶應當驗證簽名,以確認請求來自微信,而不是其他的第三方。簽名驗證的算法請參考 《微信支付API v3簽名驗證》。
# 步驟二:參數(shù)解密
為了保證安全性,微信支付在回調通知,對關鍵信息進行了AES-256-GCM加密。商戶應當按照以下的流程進行解密關鍵信息,解密的流程:
- 用商戶平臺上設置的APIv3密鑰【微信商戶平臺 (opens new window)—>賬戶設置—>API安全—>設置APIv3密鑰】,記為key;
- 獲取resource.algorithm中描述的算法(目前為AEAD_AES_256_GCM),以及resource.nonce和resource.associated_data;
- 使用key、nonce和associated_data,對數(shù)據(jù)密文resource.ciphertext進行解密,得到JSON形式的資源對象。
注意
- AEAD_AES_256_GCM算法的接口細節(jié),請參考rfc5116 (opens new window)。微信支付使用的密鑰key長度為32個字節(jié),隨機串nonce長度12個字節(jié),associated_data長度小于16個字節(jié)并可能為空。
- Java回調解密Json取值不帶引號。
# 通知參數(shù)
- id 必填 String(36)【通知ID】
通知的唯一ID - create_time 必填 String(32)【通知創(chuàng)建時間】
通知創(chuàng)建的時間,遵循rfc3339標準格式,格式為yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出現(xiàn)在字符串中,表示time元素的開頭,HH:mm:ss.表示時分秒,TIMEZONE表示時區(qū)(+08:00表示東八區(qū)時間,領先UTC 8小時,即北京時間)。例如:2015-05-20T13:29:35+08:00表示北京時間2015年05月20日13點29分35秒。 - event_type 必填 String(32)【通知類型】
通知的類型,商家轉賬通知的類型為MCHTRANSFER.BILL.FINISHED - resource_type 必填 String(32)【通知數(shù)據(jù)類型】
通知的資源數(shù)據(jù)類型,商家轉賬通知為encrypt-resource - resource 必填 object【通知數(shù)據(jù)】
json格式 - summary 必填 String(64)【回調摘要】
回調摘要
resource的格式:
- algorithm 必填 String(32)【加密算法類型】
對開啟結果數(shù)據(jù)進行加密的加密算法,目前只支持AEAD_AES_256_GCM - ciphertext 必填 String(1048576)【數(shù)據(jù)密文】
Base64編碼后的開啟/停用結果數(shù)據(jù)密文 - associated_data 選填 String(16)【附加數(shù)據(jù)】
附加數(shù)據(jù) - original_type 必填 String(16)【原始類型】
原始回調類型,為mch_payment - nonce 必填 String(16)【隨機串】
加密使用的隨機串
當 event_type為MCHTRANSFER.BATCH.FINISHED時,數(shù)據(jù)密文ciphertext解密之后的內(nèi)容
ObjectBatchFinished json結構:
- out_batch_no 必填 String(32)【商家批次單號】
商戶系統(tǒng)內(nèi)部的商家批次單號,在商戶系統(tǒng)內(nèi)部唯一 - batch_id 必填 String(64)【微信批次單號】
微信批次單號,微信商家轉賬系統(tǒng)返回的唯一標識 - batch_status 必填 String(32)【批次狀態(tài)】
WAIT_PAY: 待付款確認。需要付款出資商戶在商家助手小程序或服務商助手小程序進行付款確認
ACCEPTED:已受理。批次已受理成功,若發(fā)起批量轉賬的30分鐘后,轉賬批次單仍處于該狀態(tài),可能原因是商戶賬戶余額不足等。商戶可查詢賬戶資金流水,若該筆轉賬批次單的扣款已經(jīng)發(fā)生,則表示批次已經(jīng)進入轉賬中,請再次查單確認
PROCESSING:轉賬中。已開始處理批次內(nèi)的轉賬明細單
FINISHED:已完成。批次內(nèi)的所有轉賬明細單都已處理完成
CLOSED:已關閉。可查詢具體的批次關閉原因確認 - total_num 必填 integer【批次總筆數(shù)】
轉賬總筆數(shù)。 - total_amount 必填 integer【批次總金額】
轉賬總金額,單位為“分”。 - success_amount 必填 integer【轉賬成功金額】
轉賬成功的金額,單位為“分”。當批次狀態(tài)為“PROCESSING”(轉賬中)時,轉賬成功金額隨時可能變化 - success_num 必填 integer【轉賬成功筆數(shù)】
轉賬成功的筆數(shù)。當批次狀態(tài)為“PROCESSING”(轉賬中)時,轉賬成功筆數(shù)隨時可能變化 - fail_amount 必填 integer【轉賬失敗金額】
轉賬失敗的金額,單位為“分” - fail_num 必填 integer【轉賬失敗筆數(shù)】
轉賬失敗的筆數(shù) - update_time 必填 String(64)【批次更新時間】
遵循rfc3339標準格式,格式為yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出現(xiàn)在字符串中,表示time元素的開頭,HH:mm:ss.表示時分秒,TIMEZONE表示時區(qū)(+08:00表示東八區(qū)時間,領先UTC 8小時,即北京時間)。例如:2015-05-20T13:29:35+08:00表示北京時間2015年05月20日13點29分35秒。
當 event_type 為MCHTRANSFER.BATCH.CLOSED時,數(shù)據(jù)密文ciphertext解密之后的內(nèi)容
ObjectBatchClosed, json結構:
- mchid 必填 String(32)【商戶號】
微信支付分配的商戶號 - out_batch_no 必填 String(32)【商家批次單號】
商戶系統(tǒng)內(nèi)部的商家批次單號,在商戶系統(tǒng)內(nèi)部唯一 - batch_id 必填 String(64)【微信批次單號】
微信批次單號,微信商家轉賬系統(tǒng)返回的唯一標識 - batch_status 必填 String(32)【批次狀態(tài)】
WAIT_PAY: 待付款確認。需要付款出資商戶在商家助手小程序或服務商助手小程序進行付款確認
ACCEPTED:已受理。批次已受理成功,若發(fā)起批量轉賬的30分鐘后,轉賬批次單仍處于該狀態(tài),可能原因是商戶賬戶余額不足等。商戶可查詢賬戶資金流水,若該筆轉賬批次單的扣款已經(jīng)發(fā)生,則表示批次已經(jīng)進入轉賬中,請再次查單確認
PROCESSING:轉賬中。已開始處理批次內(nèi)的轉賬明細單
FINISHED:已完成。批次內(nèi)的所有轉賬明細單都已處理完成
CLOSED:已關閉。可查詢具體的批次關閉原因確認 - total_num 必填 integer【批次總筆數(shù)】
轉賬總筆數(shù)。 - total_amount 必填 integer【批次總金額】
轉賬總金額,單位為“分”。 - close_reason 必填 String(64)【批次關閉原因】
如果批次單狀態(tài)為“CLOSED”(已關閉),則有關閉原因
可選取值:
OVERDUE_CLOSE:系統(tǒng)超時關閉,可能原因賬戶余額不足或其他錯誤
TRANSFER_SCENE_INVALID:付款確認時,轉賬場景已不可用,系統(tǒng)做關單處理 - update_time 必填 String(64)【批次更新時間】
遵循rfc3339標準格式,格式為yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出現(xiàn)在字符串中,表示time元素的開頭,HH:mm:ss.表示時分秒,TIMEZONE表示時區(qū)(+08:00表示東八區(qū)時間,領先UTC 8小時,即北京時間)。例如:2015-05-20T13:29:35+08:00表示北京時間2015年05月20日13點29分35秒。
# 通知應答
接收成功:HTTP應答狀態(tài)碼需返回200,無需返回應答報文。 接收失敗:HTTP應答狀態(tài)碼需返回5XX或4XX,同時需返回應答報文,格式如下:
- code 必填 String(32)【返回狀態(tài)碼】
錯誤碼,SUCCESS為接收成功,其他錯誤碼為失敗。 - message 必填 String(64)【返回信息】
返回信息,如非空,為錯誤原因。
# 通知報文示例
header
1Content-Length: 756^M2User-Agent: Mozilla/4.0^M3Content-Type: application/json^M4Wechatpay-Nonce: LJCTbBBiwMkAzH80tCHsYYsMV6z5Ry7Z^M5Wechatpay-Timestamp: 1692175414^M6Wechatpay-Serial: 69B46F3CF558D60F47E6D4BAF8189C202275B397^M7Wechatpay-Signature: WECHATPAY/SIGNTEST/nTCS+cEBgZACF+F2fBpy+G2oIcwUWsmhMc/bWGy7iiA0pZiIuke+KMUmAJOAXDYUFiXgjKZ8U/haDTlH7+BMGOk7BKFNB6htmByGW2P78iwOQ0jzhHVyaRqNCl8soZF/KoqQyMzWktvfH1+Dv3QJn6ntHskjdetggPQUY6PDrVLVb6o4w67+vPBkusYJdHPAozoGW02ghDU8xT3F2ZI38m7Vx1zDPMLivvlQXRxHz6JJDmZAXKdx1Z1nKSIXR8JlkB2Ct5efogSynK77OgyOY6s9FHNAXm0UXarCCkGK7VwrvSFO3c+I3H60CA3dFJRMHPuIHW+z+y9LTw==^M8Wechatpay-Signature-Type: WECHATPAY2-SHA256-RSA2048^M9Pragma: no-cache^M10Connection: Keep-Alive^M11Host: wxpay.oa.com^M12Accept: */*^M13^M
body
1{2 "id": "1c8192d8-aba1-5898-a79c-7d3abb72eabe",3 "create_time": "2023-08-16T16:43:27+08:00",4 "resource_type": "encrypt-resource",5 "event_type": "MCHTRANSFER.BATCH.FINISHED",6 "summary": "商家轉賬批次完成通知",7 "resource": {8 "original_type": "mch_payment",9 "algorithm": "AEAD_AES_256_GCM",10 "ciphertext": "zTBf6DDPzZSoIBkoLFkC+ho97QrqnT6UU/ADM0tJP07ITaFPek4vofQjmclLUof78NqrPcJs5OIBl+gnKKJ4xCxcDmDnZZHvev5o1pk4gwtJIFIDxbq3piDr4Wq6cZpvGPPQTYC8YoVRTdVeeN+EcuklRrmaFzv8wCTSdI9wFJ9bsxtLedhq4gpkKqN5fbSguQg9JFsX3OJeT7KPfRd6SD1gu4Lpw5gwxthfOHcYsjM/eY5gaew8zzpN6mMUEJ1HqkNuQgOguHBxFnqFPiMz+Iadw7X38Yz+IgfUkOhN1iuvMhGYKbwKJ7rTiBVvGGpF6Wse1zFKgSiTLH2RnUAMkkHmxqk+JhbQKZpSWr6O8BfhHO1OKg7hpcHZtOJKNMjIF62WYDVf36w1h8h5fg==",11 "associated_data": "mch_payment",12 "nonce": "DdF3UJVNQaKT"13 }14}
解密后單據(jù)信息
1{2 "out_batch_no": "bfatestnotify000033",3 "batch_id": "131000007026709999520922023081519403795655",4 "batch_status": "FINISHED",5 "total_num": 2,6 "total_amount": 200,7 "success_amount": 100,8 "success_num": 1,9 "fail_amount": 100,10 "fail_num": 1,11 "mchid": "2483775951",12 "update_time": "2023-08-15T20:33:22+08:00"13}