> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.qfapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 訂閱支付整合指南

> 開發者指南：涵蓋訂閱支付整合流程、資料模型、Webhook 事件與實務建議。

本指南說明如何整合 QFPay 訂閱支付（定期付款），包含必要條件、建議的 API 呼叫順序，以及正式環境常見的設計與最佳實踐。

***

## 理解訂閱支付的兩個步驟

當使用 **Payment Element** 建立訂閱支付時，整個流程包含兩個獨立步驟：

### 1. 建立付款 Token（3DS 驗證完成後）

當使用者在前端完成付款資料輸入並呼叫 `qfpay.confirmPayment()` 後，系統會完成 **3DS 驗證** 並建立一個 **付款 Token (`token_id`)**。

此步驟的目的為：

* 驗證持卡人身份（3DS）
* 安全地保存付款方式
* 取得可用於後續扣款的 `token_id`

### 2. 建立 Subscription（訂閱）

在取得 `token_id` 後，商戶後端需呼叫：

`POST /subscription/v1/create`

並帶入：

* `customer_id`
* `product_id`
* `token_id`

此步驟會建立訂閱並啟動後續的定期扣款。

***

## 為什麼會看到兩筆交易紀錄

由於 **Token 建立** 與 **訂閱建立** 屬於兩個不同的操作，因此在系統中通常會看到 **兩筆交易紀錄**：

* 一筆與 **Token 建立 / 卡片驗證** 有關
* 一筆與 **訂閱付款（Subscription Billing）** 有關

這是訂閱支付的正常流程，並不代表系統發生重複扣款。

<Note>
  Token 建立交易除了產生付款 Token 外，實際上也可能涉及一筆授權或扣款金額，具體取決於商戶的整合方式。
</Note>

***

## 首次扣款的兩種設計方式

商戶在整合訂閱支付時，通常會採用以下兩種方式之一：

### 方式一：驗證扣款 + 訂閱扣款

範例：每月訂閱 **HKD 100**，從 **1 月開始**

**Token 建立：**

* 扣款 HKD 1（僅作為驗證）
* 用於持卡人驗證與 Token 建立

**Subscription 建立：**

* 1 月收取 HKD 100
* 之後每月扣款 HKD 100

此方式適用於：

* 希望將 **卡片驗證** 與 **實際訂閱扣款** 分開處理

***

### 方式二：首次訂閱付款於 Token 建立時完成

範例：每月訂閱 **HKD 100**，從 **1 月開始**

**Token 建立：**

* 扣款 HKD 100
* 同時完成卡片驗證與第一次訂閱付款

**Subscription 建立：**

* 從 **2 月開始** 每月扣款 HKD 100

此方式的優點：

* 不需要額外的小額驗證交易
* Token 建立交易即為第一期訂閱付款

<Warning>
  若使用此方式，請確保 Subscription 的開始時間設定為 **下一個週期**，避免同一月份被扣款兩次。
</Warning>

***

## 前置條件

開始前請確認：

* 已取得 API 憑證：`AppCode`、`AppKey`
* 已設定 `notify_url`（Webhook 接收端），可接收訂閱相關事件
* 前端可整合並渲染 Payment Element（用於建立付款 Token）
* 具備基本 REST API 與 Webhook 整合能力

***

## 整合流程總覽

建議依以下順序整合：

1. **設定 Webhook 接收端**：接收 `payment_token`、`subscription`、`subscription_payment` 通知
2. **建立 Customer**：取得 `customer_id`
3. **前端渲染 Payment Element**：呼叫 `payment.pay()` 並帶入 `customer_id` 以建立付款 token
4. **取得 `token_id`**：透過 Webhook 通知（或 API 回應）取得 token
5. **建立 Product**：定義每期金額、幣別、週期與間隔
6. **建立 Subscription**：傳入 `customer_id`、`token_id` 與 `product_id`
7. （選用）**監控訂閱狀態**：透過 Webhook 或查詢 API 同步訂閱狀態

<Frame>
  ![](https://www.plantuml.com/plantuml/png/fLJDZYCr4BxxAKfp9n8iS9W3MYq8YTkYvRPQeOfTdPXnbpqkUqBpzCJT7NSxz92ivkPqVLN-VifvBmbZohrJg9EFeBCatrC4b7gUI-UJFY8dGAbdfGB6PBKDfT3_AOEKyaF5oY29-eS6zjm574ROxxz-n64JSmeZu5pkYHCSCD49XnPZHJB54VVRTFo0_FIWr27w7E3RtSVeJTP9OKwUSx-dg2gnRtwQw3w2ZeI98CpWyMifZpIlo-3tVv5E4EavaoJ5FX54UpWcbIAoe4xMCs3lCxUVT8wHM0-As41fKvCF2v58AKUkDrbJe1Srt-r-hd7S8wU6jwsdrlz7K8KmzgHwlxUE5FLedJeVdUK3e36HH6vgggDQKUzsbu3_y4_4yDbsPGmCb6QUvijQRXspN60vv3COem7BdT-zfZUD5xHYuMJJ4S94eL6EqHozCxDs63y0dwVJty760KmPUUq2g4RchPXdvHCnM-WNlEsinh8mQv--tttAUz7HXbAvuOXCq3s11D9b7WI7_8en4tmQlBK4pae2twss4f0DF6VaPDFGIBwMDEQ98JYhSU_eIpLC3zgHGC28FIMAjnT85lrbykmBoi3w63txB40l9SNh_f3bs7RFYpNZYPkD_67NsIW9Bam_B_hO9elE_aFGGcRLRrxKvOAjbHopHBVox55Tcs8JnMbtgfsB7wVmU9bRSqQNuDqldyRVDf81ZKBg52ghwdz1wICwHtoW1Hz9WcUvZktkjkh1nR3IQMmaNRRegZtWWHfdhKWkBTfpsSqMhgRgmgaEHZPhVki_QMl-4yiBHTiDA-iak-fWOsyBhPYAtVAr7RjbumQAd11qqOwS7OcqkRrzFXlrbI-iosp0K8c1pDrFiRZ-GjilySVTX_RVOls-dFS1CYVhOBz6GLby7q4ZQtAEJ1lGH70YkvbA8-DkfPNwUsAJU_Sl)
</Frame>

***

## 資料模型建議

整合時建議在商戶系統保存以下關聯（便於後續查詢、補扣、報表與客服）：

* `customer_id`：顧客識別
* `token_id`：付款令牌（代表已授權的扣款方式）
* `product_id`：訂閱產品（定價與週期）
* `subscription_id`：訂閱主體
* `subscription_order_id`：每次扣款的訂閱訂單（用於失敗重試與查核）

***

## Webhook 設計建議

建議將 Webhook 作為「狀態變更的主要來源」，並搭配查詢 API 作補單：

* Webhook 到達：更新訂閱 / 扣款狀態
* Webhook 未到 / 延遲：用查詢 API 追狀態並修正

<Warning>
  Webhook 資料請務必做簽名驗證（Signature Verification），避免依賴未驗證事件更新訂單或開通服務。
</Warning>

***

## 最佳實踐

<Note>
  建議使用「查詢 Subscription」API 定期同步訂閱狀態到 CRM / 後台系統，避免僅依賴單次 Webhook。
</Note>

<Tip>
  每次建立訂閱後，請確認已收到 Webhook，且訂閱狀態為 `ACTIVE` 或 `INCOMPLETE`，以確認建立成功並可追蹤後續扣款。
</Tip>

<Warning>
  請務必在資料庫中保存 `subscription_id` 與 `token_id`，以支援後續的扣款重試、取消訂閱、對賬與報表需求。
</Warning>

***

## 下一步

* 訂閱 API 參考文件
* Webhook 通知說明
* Payment Element SDK 整合指南
