---
title: Vanilla JS 整合指南
description: 使用純 JavaScript 整合 Recur 訂閱結帳功能
---

Vanilla JS SDK 適用於任何網站環境，包括靜態網站、WordPress、PHP、或任何可以執行 JavaScript 的環境。

## 安裝

  
    直接在 HTML 中引入：
    ```html
    <script src="https://unpkg.com/recur-tw@latest/dist/recur.umd.js"></script>
    ```

    SDK 會自動掛載到 `window.Recur`。
  
  
    ```bash
    npm install recur-tw
    ```

    ```javascript
    import { RecurCheckout } from 'recur-tw/vanilla';
    ```
  

## 快速開始

  
    ### 初始化 SDK

    ```javascript
    const recur = RecurCheckout.init({
      publishableKey: 'pk_test_your_publishable_key'
    });
    ```
  

  
    ### 開啟結帳

    ```javascript
    document.getElementById('subscribe-btn').addEventListener('click', async () => {
      await recur.checkout({
        productId: 'prod_xxx',  // 或使用 productSlug: 'pro-monthly'
        customerEmail: 'user@example.com',
        customerName: '王小明',
        mode: 'modal',  // 'modal' | 'redirect'
        onPaymentComplete: (result) => {
          console.log('訂閱成功！', result);
          // 導向成功頁面或顯示成功訊息
        },
        onError: (error) => {
          console.error('付款失敗：', error.message);
        }
      });
    });
    ```
  

## 結帳模式

### Hosted Checkout（推薦）

導向 Recur 託管的結帳頁面，最簡單且支援所有環境（包含 localhost）：

```javascript
await recur.redirectToCheckout({
  productId: 'prod_xxx',
  customerEmail: 'user@example.com',
  successUrl: `${window.location.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
  cancelUrl: `${window.location.origin}/cancel`,
});
```

`{CHECKOUT_SESSION_ID}` 會在導向時自動替換為實際的 session ID，你可以用它來查詢訂閱狀態。

### Modal 模式（彈窗）

在當前頁面彈出付款表單，用戶不離開你的網站：

**注意**：Modal 模式需要 PAYUNi 驗證你的網域，**在 localhost 環境下無法使用**。開發階段請使用上方的 Hosted Checkout。

```javascript
await recur.checkout({
  productId: 'prod_xxx',
  customerEmail: 'user@example.com',
  customerName: '王小明',
  mode: 'modal',
  onSuccess: (result) => {
    // Checkout session 建立成功（付款前）
    console.log('Checkout created:', result.checkout.id);
  },
  onPaymentComplete: (result) => {
    // 付款完成
    console.log('訂閱 ID:', result.id);
    console.log('狀態:', result.status);
  },
  onError: (error) => {
    alert('錯誤：' + error.message);
  },
  onClose: () => {
    console.log('用戶關閉了付款視窗');
  }
});
```

## 取得產品列表

動態顯示你的訂閱方案：

```javascript
const { products } = await recur.fetchProducts();

products.forEach(product => {
  console.log(product.id);             // prod_xxx
  console.log(product.name);           // "專業方案"
  console.log(product.price);          // 299
  console.log(product.interval);       // "month"
  console.log(product.interval_count); // 1
});
```

篩選特定類型的產品：

```javascript
// 只取得訂閱類型產品
const { products } = await recur.fetchProducts({ type: 'SUBSCRIPTION' });

// 只取得一次性付款產品
const { products } = await recur.fetchProducts({ type: 'ONE_TIME' });
```

## Customer Portal

讓客戶自助管理訂閱。由於 Portal 連結需要 Secret Key，你需要：

  
    ### 建立後端 API

    在你的後端（PHP、Node.js 等）建立一個 API：

    ```javascript
    // 後端範例（Node.js / Express）
    import { Recur } from 'recur-tw/server';

    const recur = new Recur(process.env.RECUR_SECRET_KEY);

    app.post('/api/portal', async (req, res) => {
      const { email } = req.body;

      const session = await recur.portal.sessions.create({
        email,
        returnUrl: 'https://yoursite.com/account',
      });

      res.json({ url: session.url });
    });
    ```
  

  
    ### 前端呼叫 API

    ```javascript
    document.getElementById('manage-btn').addEventListener('click', async () => {
      const response = await fetch('/api/portal', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: 'user@example.com' })
      });

      const { url } = await response.json();
      window.location.href = url;
    });
    ```
  

## 完整範例

```html
<!DOCTYPE html>
<html>
<head>
  <title>訂閱方案</title>
  <script src="https://unpkg.com/recur-tw@latest/dist/recur.umd.js"></script>
</head>
<body>
  <h1>選擇你的方案</h1>
  <div id="plans"></div>

  <script>
    const recur = RecurCheckout.init({
      publishableKey: 'pk_test_xxx'
    });

    async function loadPlans() {
      const { products } = await recur.fetchProducts({ type: 'SUBSCRIPTION' });
      const container = document.getElementById('plans');

      products.forEach(product => {
        const card = document.createElement('div');
        card.innerHTML = `
          <h3>${product.name}</h3>
          <p>NT$ ${product.price} / ${product.interval === 'month' ? '月' : '年'}</p>
          <button onclick="subscribe('${product.id}')">訂閱</button>
        `;
        container.appendChild(card);
      });
    }

    async function subscribe(productId) {
      const email = prompt('請輸入你的 Email：');
      const name = prompt('請輸入你的姓名：');

      if (!email || !name) return;

      await recur.checkout({
        productId,
        customerEmail: email,
        customerName: name,
        mode: 'modal',
        onPaymentComplete: (result) => {
          alert('訂閱成功！感謝你的支持。');
          window.location.reload();
        },
        onError: (error) => {
          alert('訂閱失敗：' + error.message);
        }
      });
    }

    loadPlans();
  </script>
</body>
</html>
```

## 常見問題

### Q: 如何處理 Webhook？

Webhook 需要後端來接收。請參考 [Webhook 整合指南](/guides/webhooks)。

### Q: 如何查詢用戶的訂閱狀態？

這需要使用 Server SDK（Secret Key），必須在後端執行：

```javascript
// 後端
import { Recur } from 'recur-tw/server';
const recur = new Recur(process.env.RECUR_SECRET_KEY);

const { allowed } = await recur.entitlements.check({
  product: 'pro-plan',
  customer: { email: 'user@example.com' },
});
console.log(allowed); // true or false
```

### Q: Modal 模式下樣式被網站 CSS 影響怎麼辦？

Recur SDK 已針對常見的 CSS 衝突做了處理。如果仍有問題，請確保你使用的是最新版本：

```html
<script src="https://unpkg.com/recur-tw@latest/dist/recur.umd.js"></script>
```

## 下一步

- [Webhook 整合](/guides/webhooks) - 接收訂閱事件通知
- [Customer Portal](/guides/portal) - 讓客戶自助管理訂閱
- [API 參考](/api-reference) - 完整的 API 文件
