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

# 收取第一筆付款

> 使用啟潤支付完成端到端收款的完整指南

本指南以 Node.js 後端為例，帶你完成完整的支付整合。相同的概念適用於任何程式語言。

## 選擇設定路徑

* 如果你想先手動建立產品並查看訂單，請使用控制台指南。
* 如果你的應用需要從服務端建立 Checkout Session，請使用 API 指南。

相關頁面：

* [產品控制台](/zh-Hant/dashboard/products)
* [訂單控制台](/zh-Hant/dashboard/orders)
* [快速開始](/zh-Hant/quickstart)
* [Webhook 總覽](/zh-Hant/webhooks/overview)

## 概覽

```mermaid theme={null}
sequenceDiagram
    participant Customer as 客戶
    participant YourServer as 你的伺服器
    participant KyrenPay as 啟潤支付 API
    participant Checkout as 收銀頁面

    Customer->>YourServer: 點擊「購買額度」
    YourServer->>KyrenPay: POST /v1/checkouts
    KyrenPay-->>YourServer: { url: "https://payment.kyren.io/checkout/cs_..." }
    YourServer-->>Customer: 重新導向到收銀 URL
    Customer->>Checkout: 完成付款
    Checkout->>KyrenPay: 支付確認
    KyrenPay->>YourServer: Webhook: order.paid
    YourServer-->>YourServer: 完成訂單
```

## 1. 搭建伺服器

安裝所需依賴：

```bash theme={null}
npm init -y
npm install express axios
```

建立伺服器檔案：

```javascript theme={null}
// server.js
const express = require('express');
const crypto = require('crypto');
const axios = require('axios');

const app = express();

const KYREN_API_KEY = process.env.KYREN_API_KEY;
const KYREN_WEBHOOK_SECRET = process.env.KYREN_WEBHOOK_SECRET;
const KYREN_BASE_URL = 'https://api.kyren.top';

app.use(express.json());
```

## 2. 建立產品（一次性操作）

你只需建立一次產品，可以透過 API 或控制台完成。

```javascript theme={null}
async function createProduct() {
  const response = await axios.post(`${KYREN_BASE_URL}/v1/products`, {
    name: '1000 AI Credits',
    description: '充值 1000 個 AI API 額度',
    price: '9.99',
    currency: 'USD',
    metadata: { credits: '1000' }
  }, {
    headers: { 'x-api-key': KYREN_API_KEY }
  });

  console.log('產品已建立:', response.data.data.id);
  return response.data.data;
}
```

## 3. 建立收銀介面

新增一個在客戶點擊「購買」時建立 Checkout Session 的介面：

```javascript theme={null}
app.post('/api/buy-credits', async (req, res) => {
  try {
    const response = await axios.post(`${KYREN_BASE_URL}/v1/checkouts`, {
      productId: 'prod_abc123',
      successUrl: 'https://yoursite.com/payment/success',
      cancelUrl: 'https://yoursite.com/payment/cancel',
      customerEmail: req.body.email,
      metadata: { userId: req.body.userId }
    }, {
      headers: { 'x-api-key': KYREN_API_KEY }
    });

    res.json({ checkoutUrl: response.data.data.url });
  } catch (error) {
    console.error('建立收銀失敗:', error.response?.data);
    res.status(500).json({ error: '建立收銀失敗' });
  }
});
```

## 4. 處理前端重新導向

在前端將客戶重新導向到收銀頁面：

```html theme={null}
<button id="buy-btn">購買 1000 額度 - $9.99</button>

<script>
document.getElementById('buy-btn').addEventListener('click', async () => {
  const response = await fetch('/api/buy-credits', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email: 'customer@example.com',
      userId: 'user_123'
    })
  });

  const { checkoutUrl } = await response.json();
  window.location.href = checkoutUrl;
});
</script>
```

## 5. 處理 Webhook

新增 Webhook 端點以接收支付通知：

```javascript theme={null}
function verifySignature(payload, signature, timestamp) {
  const data = `${timestamp}.${payload}`;
  const expected = 'sha256=' + crypto
    .createHmac('sha256', KYREN_WEBHOOK_SECRET)
    .update(data)
    .digest('hex');

  const currentTime = Date.now();
  if (Math.abs(currentTime - Number(timestamp)) > 5 * 60 * 1000) {
    return false;
  }

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

app.post('/webhooks/kyren',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-kyren-signature'];
    const timestamp = req.headers['x-kyren-timestamp'];
    const payload = req.body.toString();

    if (!verifySignature(payload, signature, timestamp)) {
      return res.status(400).send('Invalid signature');
    }

    const event = JSON.parse(payload);

    switch (event.type) {
      case 'order.paid':
        const { order_id, product_id, customer_email, amount, currency } = event.data;
        console.log(`收到付款: ${amount} ${currency}，來自 ${customer_email}`);
        // TODO: 完成訂單 — 為使用者帳戶充值額度
        break;
      default:
        console.log(`未處理的事件類型: ${event.type}`);
    }

    res.status(200).send('OK');
  }
);
```

## 6. 啟動伺服器

```javascript theme={null}
app.listen(3000, () => {
  console.log('伺服器執行在 http://localhost:3000');
});
```

執行：

```bash theme={null}
KYREN_API_KEY=kyren_live_xxx KYREN_WEBHOOK_SECRET=whsec_xxx node server.js
```

## 7. 測試流程

1. 如果啟潤已為你的商戶帳號發放 staging 憑證，或啟用了可測試的支付閘道，請使用這些憑證和啟潤提供的支付場景執行流程。
2. 如果你只有生產憑證，請在生產環境中使用低風險的真實付款驗證流程。
3. 確認客戶會被重新導向到啟潤支付收銀頁面。
4. 確認你的伺服器能收到付款 Webhook，並且履約邏輯能正確處理該事件。
5. 如果你使用真實付款進行驗證，請在需要時根據啟潤支援團隊的指引退款或做相應營運處理。

<Check>
  當 Checkout 建立、重新導向處理、Webhook 接收和履約邏輯都已透過啟潤為你帳號啟用的憑證和閘道驗證後，整合即可投入使用。
</Check>

## 下一步

<CardGroup cols={2}>
  <Card title="Webhook 事件" icon="bell" href="/zh-Hant/webhooks/events">
    查看所有可用的 Webhook 事件類型
  </Card>

  <Card title="程式碼範例" icon="code" href="/zh-Hant/guides/code-examples">
    查看其他語言的整合範例
  </Card>
</CardGroup>
