前置作業
首先你得先按照Apple網站指示完成merchant id and certificate的申請,完成後你會拿到一個payment processing certificate跟一個private key,然後就可以在你的網站或APP上面插入付款的程式碼跟加入Pay按鈕。以上各大金流服務商都有詳細的說明可以參考,e.g. 綠界
當使用者完成付款流程之後你會拿到一個payment token,長得類似下面這個:
{
"version": "EC_v1",
"data": "vxae4VFHqdtWakaJ1wqQHyel.....ggVQsfUxBXR8=",
"signature": "MIAGCSqGSIb3DQEHAqCA......MAAAAAAAA=",
"header": {
"ephemeralPublicKey": "MFkwEwYHKoZIzj....xOTAy4jejmO0A==",
"publicKeyHash": "0aB0KxDCKoZ....6bKxuqPe+F6yQco=",
"transactionId": "54829332dd6db37.....59acad43133d792fc139"
}
}
正常流程就是把這個token送到第三方payment processor之後交易就算完成可以等收錢了 lol 不過我們不送給payment processor就得自己把這個token解密然後再送給銀行,然後配合銀行授權請款流程再看你們簽的合約是怎麼收款最後才能拿到錢💰。
解密Payment token
所以我們就來解密吧,根據官方文件,這個token包含了許多加密流程,如果你不熟悉密碼學的話可能會一個頭兩個大。不過官方解說還算詳細,照流程一步一步來還是可以解開的。
按照文件我們有七個步驟要做:
- Verify the signature
- Determine which merchant public key was used
- Restore the symmetric key
- Use the symmetric key to decrypt the value of the data key
- Confirm that this payment has not yet been credited
- Verify the transaction details: currencyCode, transactionAmount, and applicationData
- Use the decrypted payment data to process the payment
Verify the signature
首先第一個也是最麻煩的一個就是驗證簽名(signature),主要是確保這個token是正確且合法的以避免偽造或駭客攻擊。這個步驟又分為五個小步驟:a. 確保簽名當中包含正確的custom OIDs,b. 下載官方Root CA(https://www.apple.com/certificateauthority/),c. 確認x.509 chain of trust可以一路追朔到Apple Root CA -G3,d. 驗證簽名, e. 檢查簽發時間。
因為這個簽名是detached PKCS #7 signature in CMS format,可是在node.js的環境下其實很少關於這方面的支援,npm中做apple pay token解密的套件全部都跳過驗證簽名這一步(囧),而且最常用的node-forge套件竟然沒有支援底層使用的ecdsa-with-SHA256演算法,所以我找了很久才在另一個套件PKI.js裡面找到可以用來做驗證的cmsSignedData class。找到這個class後就簡單了,取出custom OIDs,X.509 chain of trust,verification,and CMS signing time都可以拿到,於是這一關就通了。
Determine which merchant public key was used
這個就是把之前申請apple pay拿到的merchant certificate裡面的public key取出,然後取sha256 hash value再base64 encode之後跟token中的publicKeyHash比對,相同的話就通過。
補充:感謝Tom更正,實際上從apple拿到的東西叫做Apple pay pay processing certificate,應該用這個來比對,merchant certificate是另一個東西。
這個應該是給大型購物網站來使用的,因為內部可能會拆分成很多小的商家各自有不同的merchant id,所以需要透過這個欄位來找出正確的certificate/private key。
Restore the symmetric key
先把private key跟token中的ephemeralPublicKey使用 Elliptic Curve Diffie-Hellman演算法算出shared secret。然後再用certificate中的public key跟shared secret算出symmetric key。
這個部分就真的非常複雜了,我是參考了其他人已經做出來的github repo才成功解開的。
Use the symmetric key to decrypt the value of the data key
這一步就是典型的AES-256-GCM演算法,使用node.js內建的crypto就可以解開了。到這一步就完成了token解密,解開的內容如下:
{
"applicationExpirationDate": "231231",
"applicationPrimaryAccountNumber": "4802********4384",
"currencyCode": "901",
"deviceManufacturerIdentifier": "0400*****273",
"paymentDataType": "3DSecure",
"transactionAmount": 89900,
"paymentData": {
"eciIndicator": "7",
"onlinePaymentCryptogram": "Aj+W784****************MAABAAA="
}
後續步驟5~7就是自己的業務邏輯了,確保交易的單一不重複,還有數量跟幣別 blah blah,就不贅述了。都ok後就可以把這個解密過的token送去銀行完成授權/請款步驟。
以上步驟實作的source code放在我的github上,有興趣的人可以參考一下。
結語
其實解密的過程雖然有點複雜跟麻煩,不過如果有人做成一個套件的話並不會造成太多的困擾,而蘋果官方卻強烈建議大多數人找payment processor處理,我猜主要因為大部分的銀行並沒有這麼數位開放,沒辦法做到像綠界、藍新這樣提供一個自動化的平台去服務各個中小型企業及工作室,所以現階段如果你沒有辦法跟銀行去談合作串接的話還是得找payment processor來處理收款的一段囉。