## 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中訂閱多個事件
### 單次購買流程

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/
第一次付款流程:

第二次以後流程:

| 名稱 | 內容
| ---- | -------
| 商品 | 商品名稱、簡介、類型
| 計畫 | 價格(可更改)、訂閱週期(不能更改)
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)

- 使用者直接在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版本
:::
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