Embedded Checkout
在您的網站上嵌入支付表單,完全掌控 UI 體驗
Embedded Checkout
Embedded Checkout 讓您在自己的網站上嵌入支付表單,完全掌控使用者介面和體驗。
適合場景:
- 需要完全控制結帳 UI/UX
- 想讓結帳流程無縫融入您的網站
- 有前端開發資源
- 需要高度客製化
運作流程
1. 用戶點擊「訂閱」按鈕
↓
2. 前端呼叫您的後端 API
↓
3. 後端呼叫 POST /v1/checkouts 取得 SDK Token
↓
4. 前端使用 SDK Token 渲染支付表單
↓
5. 用戶填寫卡號並提交
↓
6. SDK 處理付款並回呼 onSuccess/onError安裝 SDK
npm install recur-twyarn add recur-twpnpm add recur-tw<script src="https://unpkg.com/recur-tw/dist/recur.umd.js"></script>React 整合
1. 設定 Provider
// app/providers.tsx
'use client';
import { RecurProvider } from 'recur-tw';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<RecurProvider publishableKey={process.env.NEXT_PUBLIC_RECUR_PUBLISHABLE_KEY!}>
{children}
</RecurProvider>
);
}2. 建立訂閱按鈕
// components/SubscribeButton.tsx
'use client';
import { useSubscribe } from 'recur-tw';
export function SubscribeButton({ productId }: { productId: string }) {
const { subscribe, isLoading, error } = useSubscribe({
onSuccess: (result) => {
console.log('訂閱成功!', result);
window.location.href = '/success';
},
onError: (error) => {
console.error('訂閱失敗:', error.message);
},
});
return (
<button
onClick={() => subscribe({
productId,
customerEmail: 'user@example.com',
customerName: '王小明',
})}
disabled={isLoading}
>
{isLoading ? '處理中...' : '立即訂閱'}
</button>
);
}Vanilla JavaScript 整合
<div id="checkout-container"></div>
<script src="https://unpkg.com/recur-tw/dist/recur.umd.js"></script>
<script>
const recur = RecurCheckout.init({
publishableKey: 'pk_test_xxx',
});
recur.createEmbeddedCheckout({
productId: 'prod_xxx',
container: '#checkout-container',
customerEmail: 'user@example.com',
onSuccess: (result) => {
console.log('成功!', result);
},
onError: (error) => {
alert('錯誤:' + error.message);
},
});
</script>本地開發
重要:Localhost 限制
PAYUNi 金流不支援 localhost 作為嵌入式結帳的 domain。這是因為 PAYUNi SDK 需要驗證請求來源的安全性。
解決方案
有三種方式可以在本地開發時測試 Embedded Checkout:
方案一:使用 Hosted Checkout(推薦)
在本地開發時使用 Hosted Checkout 模式。建立 Checkout Session 後會取得一個 url 欄位,直接導向該 URL 即可完成測試。
// 建立 Checkout Session
const response = await fetch('/api/create-checkout', {
method: 'POST',
body: JSON.stringify({ productId: 'prod_xxx' }),
});
const { url } = await response.json();
// 導向 Recur 託管的結帳頁面(在 localhost 也能正常運作)
window.location.href = url;Hosted Checkout 頁面位於 checkout.recur.tw,不受 localhost 限制。
方案二:使用 Tunneling 服務
使用 ngrok、Cloudflare Tunnel 或 localtunnel 等服務,將您的本地伺服器暴露到公開網路:
使用 ngrok:
# 安裝 ngrok
npm install -g ngrok
# 啟動 tunnel(假設您的開發伺服器在 port 5173)
ngrok http 5173ngrok 會提供一個公開 URL(如 https://abc123.ngrok.io),使用這個 URL 存取您的應用程式即可正常使用 Embedded Checkout。
使用 Cloudflare Tunnel:
# 安裝 cloudflared
brew install cloudflared
# 啟動 tunnel
cloudflared tunnel --url http://localhost:5173方案三:部署到 Staging 環境
將您的應用程式部署到有正式 domain 的 staging 環境進行測試:
- Vercel:
your-app.vercel.app - Netlify:
your-app.netlify.app - Railway:
your-app.up.railway.app
這些平台都提供免費的子網域,可以正常使用 Embedded Checkout。
錯誤訊息說明
當您在 localhost 使用 Embedded Checkout 時,可能會看到以下錯誤:
{
"error": {
"code": "localhost_not_supported",
"message": "Embedded checkout is not supported on localhost"
}
}這表示 PAYUNi 拒絕了來自 localhost 的請求。請使用上述三種解決方案之一。
測試模式
使用 pk_test_* 開頭的 Publishable Key 時,SDK 會自動進入測試模式。在測試模式下:
- 支付表單頂部會顯示「🧪 測試模式」提示
- 可以展開查看測試卡號
- 不會產生真實交易
測試卡號
| 卡號 | 說明 |
|---|---|
4147-6310-0000-0001 | VISA 測試卡(授權成功) |
3560-5110-0000-0001 | JCB 測試卡(授權成功) |
4147-6310-0000-0002 | VISA 測試卡(3D 驗證失敗) |
3560-5110-0000-0002 | JCB 測試卡(3D 驗證失敗) |
- 有效期:任意未過期日期
- CVV(背面末三碼):任意 3 位數
API 參考
POST /v1/checkouts
建立 Checkout 並取得 SDK Token。
請求參數:
| 參數 | 必填 | 說明 |
|---|---|---|
productId | ✅ | 商品 ID |
customerEmail | ❌ | 客戶 Email(結帳時必填) |
customerName | ❌ | 客戶姓名(結帳時必填) |
回應:
{
"checkout": {
"id": "pi_xxx",
"client_secret": "pi_xxx_secret_xxx",
"status": "requires_payment_method",
"amount": 990,
"currency": "TWD"
},
"product": {
"id": "prod_xxx",
"name": "Pro 方案",
"price": 990,
"interval": "month"
},
"sdk_token": "xxx",
"sdk_token_expires_at": "2024-01-01T12:30:00Z"
}下一步
- 錯誤處理 - 處理付款失敗與各種錯誤情況
- Webhook 整合 - 接收付款成功通知
- Customer Portal - 讓客戶管理自己的訂閱