跳至主要内容

標題: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 設定

  1. 建立 LINE Login Channel
  2. 建立 LIFF App
  3. 取得 LIFF ID
  4. 設定 LIFF Endpoint URL(支援自訂域名)

2. LINE Pay 商家申請

  1. 申請 LINE Pay 商家帳號
  2. 取得 Channel ID 與 Channel Secret
  3. 設定 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)時,需要注意:

  1. Vercel 域名設定

    • 在 Vercel Dashboard 添加自訂域名
    • 設定正確的 DNS 記錄
    • 注意 www 與非 www 的重定向設定
  2. LIFF Endpoint URL 更新

    • 在 LINE Developers Console 更新 LIFF Endpoint URL
    • 確保使用與 Vercel 設定一致的域名(含或不含 www)
  3. 動態 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. 測試流程

  1. LIFF 登入測試
  2. 建立付款請求
  3. 完成付款流程
  4. 確認後台交易紀錄
  5. 測試手機端與電腦端的跳轉行為

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 從開發狀態發布為正式版本:

  1. 登入 LINE Developers Console
  2. 選擇你的 LINE Login Channel
  3. 確認以下資訊已完整填寫:
    • Channel name
    • Channel description
    • App types
    • Email address
  4. 點擊「發布」(Publish) 按鈕
  5. 確認發布後,狀態會從「開發中」變更為「已發布」

注意: 未發布的 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 整合需要注意許多細節,從簽名計算到錯誤處理都需要謹慎實作。透過本指南的說明與範例,開發者應能順利完成整合並建立穩定的支付系統。

記住關鍵要點:

  1. 正確的 LIFF 初始化時機
  2. 完整的 Confirm API 實作
  3. 妥善的錯誤處理
  4. 安全的簽名驗證
  5. 根據執行環境使用適當的跳轉方法

文檔迭代紀錄

協作夥伴戳記

  1. 2025/06/29 初版建立
  2. 2025/07/09 Claude 4 Opus - 更新手機端跳轉解決方案、新域名整合經驗、地區服務差異說明