# Vanilla JS 整合指南 (/guides/frameworks/vanilla-js)





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

安裝 [#安裝]

<Tabs items="['CDN（推薦）', 'npm']">
  <Tab value="CDN（推薦）">
    直接在 HTML 中引入：

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

    SDK 會自動掛載到 `window.Recur`。
  </Tab>

  <Tab value="npm">
    ```bash
    npm install recur-tw
    ```

    ```javascript
    import { RecurCheckout } from 'recur-tw/vanilla';
    ```
  </Tab>
</Tabs>

快速開始 [#快速開始]

<Steps>
  <Step>
    初始化 SDK [#初始化-sdk]

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

  <Step>
    開啟結帳 [#開啟結帳]

    ```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);
        }
      });
    });
    ```
  </Step>
</Steps>

結帳模式 [#結帳模式]

Hosted Checkout（推薦） [#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`,
});
```

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

Modal 模式（彈窗） [#modal-模式彈窗]

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

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

```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 [#customer-portal]

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

<Steps>
  <Step>
    建立後端 API [#建立後端-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 });
    });
    ```
  </Step>

  <Step>
    前端呼叫 API [#前端呼叫-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;
    });
    ```
  </Step>
</Steps>

完整範例 [#完整範例]

```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？ [#q-如何處理-webhook]

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

Q: 如何查詢用戶的訂閱狀態？ [#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 影響怎麼辦？ [#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) - 完整的 API 文件
