## PayPal 筆記 ### :pushpin: 需要先知道的事 - 呼叫PayPal的API,header都要加上Bearer {{ACCESS-TOKEN}} ACCESS-TOKEN取得方式:呼叫POST:/v1/oauth2/token取得 參數需帶入ClientId和Secret :::spoiler PayPal金鑰查詢方式 1. 登入[PayPal開發者中心](https://developer.paypal.com) 2. 導覽列 > Apps & Credentials,即可確認 ::: ### webhook 讓兩個網站間互相溝通的機制,當訂閱的事件發生時立即呼叫指定的Url 使用PayPal舉例: - **step1**: 建立webhook,PayPal API 參考文件:https://developer.paypal.com/docs/api/webhooks/v1/#webhooks_post 內容如下: ```C=1 { "Url": "https://tenji.money-link.com.tw/PayPalTest/api/PayPal/order/capture", "EventName": "CHECKOUT.ORDER.APPROVED" // 此事件為買家同意訂單 } ``` - **step2**: 當CHECKOUT.ORDER.APPROVED事件發生後會立即觸發webhook,呼叫指定Url並且body會帶該事件的詳細資訊 body範例: ```C=1 { "id": "WH-6W380007F74365212-2VC02462WS389632N", "create_time": "2024-02-26T05:42:49.363Z", "resource_type": "checkout-order", "event_type": "CHECKOUT.ORDER.APPROVED", "summary": "An order has been approved by buyer", "resource": { "create_time": "2024-02-26T05:36:36Z", "id": "9CJ84087S75111404", // 訂單編號 "status": "APPROVED" // 訂單狀態 } } ``` - 使用到的webhook event: 單次購買: | Event Name | 觸發時機 | ------------------------- |--------- | CHECKOUT.ORDER.APPROVED |使用者同意訂單 | PAYMENT.CAPTURE.COMPLETED |成功將款項移動到商家帳號 訂閱: | Event Name | 觸發時機 | ---------------------------------- |--------- | PAYMENT.SALE.COMPLETED |訂閱付款完成 | BILLING.SUBSCRIPTION.CANCELLED |取消訂閱 | BILLING.SUBSCRIPTION.PAYMENT.FAILED|訂閱付款失敗 - webhook事件清單:https://developer.paypal.com/api/rest/webhooks/event-names/ **補充**: - PayPal的Webhook必須使用<font color=red>https</font>,http以及localhost被視為無效連結 - PayPal Retry機制: 若PayPal的Webhook接受到的回應不是HTTP 200,會在<font color=red>3天內</font>重新發送請求,<font color=red>最多25次</font> - webhook是<font color=red>非同步</font>的,無法確定執行順序 - 不同webhook設定的Url不能重複 - 可以在同一個webhook中訂閱多個事件 ### 單次購買流程 ![PayPal單次購買時序圖](https://hackmd.io/_uploads/H1GDI0Mfee.jpg) API: - 建立訂單 - PayPal API參考文件:https://developer.paypal.com/docs/api/orders/v2/#orders_create - 功能: 取得跳轉至PayPal付款頁面的連結 - 較重要的Response參數(PayPal回傳的): ```C#=1 "id":"9BC48837FS518181G" // PayPal訂單編號, "links":[{ "href":"https://www.sandbox.paypal.com/checkoutnow?token=9BC48837FS518181G", // 導向PayPal付款頁面的連結 "rel":"approve", "method":"GET" },...] ``` - 向PayPal要求款項 (讓webhook呼叫的) - PayPal API參考文件: https://developer.paypal.com/docs/api/orders/v2/#orders_capture - 使用時機:當使用者同意訂單後呼叫此API (CHECKOUT.ORDER.APPROVED 事件觸發) - 功能:修改PayPal的訂單狀態並將款項撥至商家帳戶 - 取得PayPal訂單詳情 - PayPal API參考文件: https://developer.paypal.com/docs/api/orders/v2/#orders_get - query參數須帶入PayPal訂單編號 ### 定期定額訂單流程 參考文件:https://developer.paypal.com/docs/subscriptions/ 第一次付款流程: ![messageImage_1748325278832](https://hackmd.io/_uploads/r10kBRfzxe.jpg) 第二次以後流程: ![PayPal定期定額非首次付款時序圖](https://hackmd.io/_uploads/H1crr0fzxe.jpg) | 名稱 | 內容 | ---- | ------- | 商品 | 商品名稱、簡介、類型 | 計畫 | 價格(可更改)、訂閱週期(不能更改) API: - 建立商品 - PayPal API參考文件: https://developer.paypal.com/docs/api/catalog-products/v1/#products_create - 較重要的Response參數(PayPal回傳的): ```C=1 "id":"1708592390" // 商品編號,建立計畫須帶入 ``` - 建立計畫 - PayPal API參考文件: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_create - 較重要的Response參數(PayPal回傳的): ```C=1 "id":"P-1TP44450WC6357503MXLQ4SY" // 計畫編號,建立訂閱須帶入 ``` - 建立訂閱 (產生定期定額訂單) - PayPal API參考文件: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create - 較重要的Response參數(PayPal回傳的): ```C=1 "id":"I-3X4FDVF4N2DP" // 訂閱編號(類似綠界訂單編號),取消訂閱時會用到 "links":[{ "href":"https://www.sandbox.paypal.com/webapps/billing/subscriptions?ba_token=BA-2BP71128M6394474E", // 導向PayPal付款頁面的連結 "rel":"approve", "method":"GET" },...] ``` - 取消訂閱 (讓webhook呼叫的) - PayPal API參考文件:https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_cancel 使用時機:使用者從PayPal執行取消訂閱動作,webhook呼叫此API執行取消訂閱後續流程 - 取消訂閱 (一般API) - PayPal API參考文件:https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_cancel - 功能:跟PayPal說要取消訂閱,下期不再扣款 - 使用時機:使用者從我們的站台執行取消訂閱動作 - 停用計畫 - PayPal API參考文件: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_deactivate - 功能:為了避免使用者在取消訂閱後到PayPal介面重新訂閱,需要把計畫停用讓使用者無法重新訂閱 - 使用時機:使用者取消訂閱、銷包下架 - 取得訂閱詳情 - PayPal API參考文件: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_deactivate - 功能:由於停用計畫時需要計畫Id,在只記錄PayPal定期定額訂單編號的情況下需要此API取得 - 取得訂閱付款結果(讓webhook呼叫的) - 功能:透過webhook通知站台付款結果 ### 訂閱制 #### 前置條件 1. 支援國家有限制,詳情請參考[官方文件] (https://developer.paypal.com/docs/checkout/save-payment-methods/during-purchase/orders-api/cards/) 2. 商業帳號需要有 advanced credit and debit card payments 功能 ([官方文件](https://developer.paypal.com/docs/multiparty/checkout/advanced/)) 3. 需要PayPal審核通過才能使用 #### 步驟 1. 使用者設定未來扣款的方式,不會扣款 (建立setup-token) ![image](https://hackmd.io/_uploads/r1bCodeZlx.png) - 使用者直接在PayPal的頁面上進行授權動作 - 若未來站台的前端會收集信用卡資訊,可以跳過此步驟 > 需符合 PCI DSS 規範 (較嚴格的信用卡管理方式) 或 > 使用 PayPal JS SDK :::spoiler 串接API POST:https://api-m.sandbox.paypal.com/v3/vault/setup-tokens (PayPal測試環境 API) 取得Response Data的Id參數 ::: 2. 建立 Payment Tokens - 已建立的Payment Token只能透過API取得,無法從商業帳號後台查詢 :::spoiler 串接API POST:https://api-m.sandbox.paypal.com/v3/vault/payment-tokens (PayPal測試環境 API),payment_source:token:id 帶入步驟1的Id 取得Response Data的Id參數 ```c=0 Request Sample: { "payment_source": { "token": { "id": "0AR382999B0832256", "type": "SETUP_TOKEN" } } } ``` ::: 3. 需要扣款時由我們的站台呼叫建立訂單API (可以在任意時間進行) :::spoiler 串接API POST:https://api-m.sandbox.paypal.com/v2/checkout/orders payment_source:paypal:vault_id帶入步驟二得到的Id ```c=0 Request Sample: { "intent": "CAPTURE", "purchase_units": [ { "amount": { "currency_code": "USD", "value": "12.34" } } ], "payment_source": { "paypal": { "vault_id":"8vm1526934563590h" } } } ``` ::: #### 流程 ``` mermaid graph TD; 前端呼叫產生訂單API-->有Payment_Token; 前端呼叫產生訂單API-->沒有Payment_Token; 有Payment_Token-->產生PayPal訂單; 沒有Payment_Token-->建立setup_tokens; 建立setup_tokens-->建立Payment_Token; 建立Payment_Token-->產生PayPal訂單; ``` ### 幣別 PayPal的商家帳號可以設定收款幣別,共有三種設定 ::: spoiler 設定方式 ![螢幕擷取畫面 2025-01-03 160932](https://hackmd.io/_uploads/rJaCsfHUkl.png) 附圖為2025-01-03版本 ::: 1. 接受所有付款並轉換成首選幣種 2. 只接受商家擁有幣種的付款,拒收其他幣種的支付 3. 接受所有付款,當付款的幣種為商家未擁有自動增加幣種和餘額 ### 補充 - webhook可以透過API或是PayPal開發者中心(PayPal Developer Dashboard)增加 - PayPal有提供介面可以更改計畫的價格,訂閱週期無法更改,更改價格PayPal沒有規定需要使用者重新授權 - 需要有計畫Id才可以停用計畫,若不紀錄該Id需要call PayPal取得訂閱詳情API >取得訂閱詳情 API - 開發文件:https://developer.paypal.com/docs/api/subscriptions/v1/#plans_get - 若更改計畫金額,PayPal沒有規定要使用者重新批准,對於原先的用戶在價格變更的10天內會按照舊價格計費 ### 測試工具整理 - PayPal 整合模擬器:https://developer.paypal.com/integration-builder/ - 信用卡產生器:https://developer.paypal.com/api/rest/sandbox/card-testing/ - webhook模擬器:https://developer.paypal.com/dashboard/webhooksSimulator/ - webhook事件清單:https://developer.paypal.com/api/rest/webhooks/event-names/ - PayPal貨幣代碼:https://developer.paypal.com/api/rest/reference/currency-codes/ ### 待研究 - [ ] 由使用者觸發訂單爭議的處理 - [x] ACCSEE_TOKEN儲存方式 - 維持不儲存 > 目前未儲存,因此每次呼叫PayPal API前需要先呼叫取得ACCSEE_TOKEN API - [x] 定期定額訂單付款成功的通知 > 目前透過Webhook事件-PAYMENT.SALE.COMPLETED通知 ~~需要再驗證此事件的觸發條件是否為訂單付款成功~~ - [ ] 驗證webhook