標題:LINE Pay API 整合技術指南
描述:完整說明如何整合 LINE Pay API 建立線上支付系統,包含 LIFF 環境整合與最新實作經驗
最後更新:2025-07-09
版本:v2.0
作者:rj0217
來源:LIFF + LINE Pay 實作專案
LINE Pay API 整合技術指南
概述
本指南說明如何整合 LINE Pay API 建立完整的線上支付系統。透過 LIFF (LINE Front-end Framework) 與 LINE Pay 的結合,可在 LINE 應用程式內提供無縫的支付體驗。
重要更新 (2025-07-09): 本指南已更新手機端 LIFF 環境跳轉解決方案及新域名整合經驗。
系統架構
技術堆疊
- 前端: HTML5 + JavaScript + LIFF SDK
- 後端: Node.js + Vercel Serverless Functions
- 支付: LINE Pay API v3
- 部署: Vercel
- 域名: 支援自訂域名整合
API 流程圖
用戶 → LIFF App → Request API → LINE Pay → 付款頁面
↓
成功頁面 ← Confirm API ← 重定向 ← 付款完成
前置準備
1. LINE Developers 設定
- 建立 LINE Login Channel
- 建立 LIFF App
- 取得 LIFF ID
- 設定 LIFF Endpoint URL(支援自訂域名)
2. LINE Pay 商家申請
- 申請 LINE Pay 商家帳號
- 取得 Channel ID 與 Channel Secret
- 設定 IP 白名單(Vercel: 76.76.21.0/24)
3. 開發環境
# 必要套件
npm install axios
核心 API 實作
1. 付款請求 (Request API)
端點
POST /v3/payments/request
請求範例
const requestBody = {
amount: 100,
currency: 'TWD',
orderId: 'ORDER_1234567890',
packages: [{
id: '1',
amount: 100,
name: '商品名稱',
products: [{
name: '商品名稱',
quantity: 1,
price: 100
}]
}],
redirectUrls: {
confirmUrl: 'https://yourdomain.com/api/pay/confirm',
cancelUrl: 'https://yourdomain.com/api/pay/cancel'
}
};
簽名計算
const crypto = require('crypto');
function generateSignature(channelSecret, uri, body, nonce) {
const signatureBase = channelSecret + uri + body + nonce;
return crypto
.createHmac('sha256', channelSecret)
.update(signatureBase, 'utf8')
.digest('base64');
}
2. 付款確認 (Confirm API)
端點
POST /v3/payments/{transactionId}/confirm
關鍵要點
- 必須在用戶付款後呼叫
- 金額必須與原始請求相同
- 需要相同的簽名驗證機制
- 確保從 URL 參數正確傳遞金額
請求範例
const confirmBody = {
amount: parseInt(req.query.amount), // 從 URL 參數取得
currency: 'TWD'
};
3. 查詢 API (選用)
端點
GET /v3/payments/requests/{transactionId}
用於查詢交易狀態,適合實作訂單追蹤功能。
LIFF 整合要點
1. SDK 載入
<script src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
2. 初始化時機(重要)
// ✅ 正確方式
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// ❌ 避免使用
window.onload = init; // 可能太晚執行
3. 登入流程
async function init() {
try {
await liff.init({ liffId: 'YOUR_LIFF_ID' });
if (liff.isLoggedIn()) {
const profile = await liff.getProfile();
// 使用 profile.userId, profile.displayName
} else {
liff.login();
}
} catch (error) {
console.error('LIFF 初始化失敗:', error);
}
}
4. 手機端付款跳轉(重要更新)
// ✅ 2025-07-09 更新:正確的跳轉方式
async function pay() {
try {
const response = await fetch('/api/pay/request', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount, productName })
});
const result = await response.json();
if (result.success) {
// 根據執行環境使用不同的跳轉方法
if (liff.isInClient()) {
// 在 LINE 客戶端內
liff.openWindow({
url: result.paymentUrl,
external: true
});
} else {
// 在外部瀏覽器
window.location.href = result.paymentUrl;
}
}
} catch (error) {
console.error('付款錯誤:', error);
}
}
域名整合要點(新增)
1. 自訂域名設定
當使用自訂域名(如 lajibro.com)時,需要注意:
-
Vercel 域名設定
- 在 Vercel Dashboard 添加自訂域名
- 設定正確的 DNS 記錄
- 注意 www 與非 www 的重定向設定
-
LIFF Endpoint URL 更新
- 在 LINE Developers Console 更新 LIFF Endpoint URL
- 確保使用與 Vercel 設定一致的域名(含或不含 www)
-
動態 URL 處理
const baseUrl = `https://${req.headers.host}`;
// 自動適應不同域名
2. 域名切換注意事項
- 確保 SSL 憑證正確設定
- 測試從舊域名到新域名的跳轉
- 驗證 API 端點在新域名下正常運作
安全性考量
1. Content Security Policy
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline' 'unsafe-eval'
https://static.line-scdn.net;">
注意: LIFF SDK 需要 'unsafe-eval' 權限
2. 簽名驗證
- 所有 API 請求都需要 HMAC-SHA256 簽名
- 使用 Channel Secret 作為密鑰
- Nonce 使用時間戳記確保唯一性
3. HTTPS 必要性
- LINE Pay 只接受 HTTPS 請求
- LIFF 應用必須使用 HTTPS
常見整合問題
問題 1: CORS 錯誤
解決方案: 使用後端 API 代理請求,不要從前端直接呼叫 LINE Pay
問題 2: 簽名驗證失敗
檢查項目:
- Channel Secret 是否正確
- 請求 body 的 JSON 字串化是否一致
- Nonce 是否為字串格式
問題 3: 金額錯誤
解決方案:
- 確保 Request 和 Confirm 的金額相同
- 使用 URL 參數傳遞金額到 confirm 頁面
問題 4: IP 未授權
解決方案: 在 LINE Pay 後台加入服務器 IP 白名單
問題 5: 手機端無法跳轉(新增)
解決方案:
- 使用
liff.isInClient()
判斷執行環境 - 在 LINE 內使用
liff.openWindow()
- 在外部瀏覽器使用
window.location.href
測試指南
1. 沙盒環境測試
- 使用
https://sandbox-api-pay.line.me
- 可使用測試信用卡號
- 交易不會實際扣款
2. 測試流程
- LIFF 登入測試
- 建立付款請求
- 完成付款流程
- 確認後台交易紀錄
- 測試手機端與電腦端的跳轉行為
3. 偵錯工具
// 加入偵錯日誌
console.log('Request Body:', JSON.stringify(requestBody));
console.log('Signature:', signature);
console.log('Response:', response.data);
console.log('Is in LINE client:', liff.isInClient());
生產環境部署
1. LINE Login Channel 發布(重要)
在部署到生產環境前,必須先將 LINE Login Channel 從開發狀態發布為正式版本:
- 登入 LINE Developers Console
- 選擇你的 LINE Login Channel
- 確認以下資訊已完整填寫:
- Channel name
- Channel description
- App types
- Email address
- 點擊「發布」(Publish) 按鈕
- 確認發布後,狀態會從「開發中」變更為「已發布」
注意: 未發布的 Channel 只有開發者和測試者可以使用,一般用戶將無法登入。
2. 環境變數設定
const channelId = process.env.LINE_PAY_CHANNEL_ID;
const channelSecret = process.env.LINE_PAY_CHANNEL_SECRET;
const apiUrl = process.env.NODE_ENV === 'production'
? 'https://api-pay.line.me'
: 'https://sandbox-api-pay.line.me';
3. 錯誤處理
try {
// API 呼叫
} catch (error) {
if (error.response) {
// LINE Pay 回傳的錯誤
console.error('API Error:', error.response.data);
} else {
// 網路或其他錯誤
console.error('System Error:', error.message);
}
}
4. 監控建議
- API 回應時間
- 付款成功率
- 錯誤類型分析
- 用戶行為追蹤
- 手機端與電腦端的使用比例
程式碼範例
完整的 Request Handler(更新版)
const axios = require('axios');
const crypto = require('crypto');
module.exports = async function handler(req, res) {
// CORS 設定
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') return res.status(200).end();
if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' });
try {
const { amount, productName } = req.body;
// 參數驗證
if (!amount || !productName) {
return res.status(400).json({
success: false,
error: '缺少必要參數'
});
}
// 設定值
const channelSecret = process.env.LINE_PAY_CHANNEL_SECRET;
const channelId = process.env.LINE_PAY_CHANNEL_ID;
const orderId = `ORDER_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
const baseUrl = `https://${req.headers.host}`; // 動態取得域名
// 建立請求 body
const requestBody = {
amount: parseInt(amount),
currency: 'TWD',
orderId: orderId,
packages: [{
id: '1',
amount: parseInt(amount),
name: productName,
products: [{
name: productName,
quantity: 1,
price: parseInt(amount)
}]
}],
redirectUrls: {
confirmUrl: `${baseUrl}/api/pay/confirm?orderId=${orderId}&amount=${amount}`,
cancelUrl: `${baseUrl}/api/pay/cancel?orderId=${orderId}`
}
};
// 簽名計算
const uri = '/v3/payments/request';
const nonce = Date.now().toString();
const bodyString = JSON.stringify(requestBody);
const signatureBase = channelSecret + uri + bodyString + nonce;
const signature = crypto
.createHmac('sha256', channelSecret)
.update(signatureBase, 'utf8')
.digest('base64');
// API 請求
const response = await axios.post(
`https://api-pay.line.me${uri}`, // 正式環境
requestBody,
{
headers: {
'Content-Type': 'application/json',
'X-LINE-ChannelId': channelId,
'X-LINE-Authorization-Nonce': nonce,
'X-LINE-Authorization': signature
},
timeout: 30000
}
);
// 回傳結果
if (response.data.returnCode === '0000') {
return res.json({
success: true,
orderId: orderId,
paymentUrl: response.data.info.paymentUrl.web,
transactionId: response.data.info.transactionId
});
} else {
throw new Error(response.data.returnMessage);
}
} catch (error) {
console.error('Payment error:', error.response?.data || error.message);
return res.status(500).json({
success: false,
error: error.response?.data?.returnMessage || error.message
});
}
};
地區差異注意事項
LINE Pay 服務可用性
根據 2025 年 7 月的資訊:
- 日本地區: LINE Pay 服務將於 2025 年 4 月 30 日終止
- 台灣地區: LINE Pay 服務正常運作中
- 其他地區: 請查詢當地 LINE Pay 服務狀態
建議定期關注 LINE Pay 官方公告以了解最新服務狀態。
參考資源
結論
LINE Pay API 整合需要注意許多細節,從簽名計算到錯誤處理都需要謹慎實作。透過本指南的說明與範例,開發者應能順利完成整合並建立穩定的支付系統。
記住關鍵要點:
- 正確的 LIFF 初始化時機
- 完整的 Confirm API 實作
- 妥善的錯誤處理
- 安全的簽名驗證
- 根據執行環境使用適當的跳轉方法
文檔迭代紀錄
協作夥伴戳記
- 2025/06/29 初版建立
- 2025/07/09 Claude 4 Opus - 更新手機端跳轉解決方案、新域名整合經驗、地區服務差異說明