taiwan-invoice
Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego. Use when developing invoice systems, implementing B2C/B2B invoice issuance, invoice printing, allowance creation, or working with Taiwan E-Invoice APIs. Handles encryption (AES, MD5), API requests, and service provider differences.
SKILL.md
| Name | taiwan-invoice |
| Description | Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego. Use when developing invoice systems, implementing B2C/B2B invoice issuance, invoice printing, allowance creation, or working with Taiwan E-Invoice APIs. Handles encryption (AES, MD5), API requests, and service provider differences. |
name: taiwan-invoice description: Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego. Use when developing invoice systems, implementing B2C/B2B invoice issuance, invoice printing, allowance creation, or working with Taiwan E-Invoice APIs. Handles encryption (AES, MD5), API requests, and service provider differences. user-invocable: true
Taiwan E-Invoice Development Skill
此技能涵蓋台灣電子發票 API 整合開發,包含綠界 (ECPay)、速買配 (SmilePay)、光貿 (Amego) 三家服務商。
快速導覽
相關文件
使用此技能時,請參考專案中的 API 規格文件:
references/ECPAY_API_REFERENCE.md- 綠界 API 規格references/SMILEPAY_API_REFERENCE.md- 速買配 API 規格references/AMEGO_API_REFERENCE.md- 光貿 API 規格- EXAMPLES.md - 程式碼範例集
智能工具
scripts/search.py- BM25 搜索引擎(查詢 API、錯誤碼、欄位映射)scripts/recommend.py- 加值中心推薦系統scripts/generate-invoice-service.py- 服務代碼生成器scripts/persist.py- 持久化配置工具(MASTER.md 生成)data/- CSV 數據檔(providers, operations, error-codes, field-mappings, tax-rules, troubleshooting, reasoning)
何時使用此技能
- 開發電子發票開立功能
- 整合台灣電子發票服務商 API
- 實作 B2C(二聯式)或 B2B(三聯式)發票
- 處理發票列印、作廢、折讓等功能
- 處理加密簽章(AES、MD5)
- 解決發票 API 整合問題
智能搜索與推薦
搜索引擎 (search.py)
使用 BM25 算法在資料庫中搜索相關資訊:
# 搜索加值中心
python scripts/search.py "ecpay" --domain provider
# 搜索錯誤碼
python scripts/search.py "10000016" --domain error
# 搜索欄位映射
python scripts/search.py "MerchantID" --domain field
# 搜索稅務規則
python scripts/search.py "B2B 稅額計算" --domain tax
# 搜索疑難排解
python scripts/search.py "列印空白" --domain troubleshoot
# JSON 輸出
python scripts/search.py "折讓" --format json
搜索域:
| 域 | 說明 | CSV 檔案 |
|---|---|---|
provider | 加值中心比較 | providers.csv |
operation | API 操作端點 | operations.csv |
error | 錯誤碼查詢 | error-codes.csv |
field | 欄位映射 | field-mappings.csv |
tax | 稅務計算規則 | tax-rules.csv |
troubleshoot | 疑難排解 | troubleshooting.csv |
reasoning | 推薦決策規則 | reasoning.csv |
推薦系統 (recommend.py)
根據需求自動推薦最適合的加值中心:
# 高交易量電商
python scripts/recommend.py "電商 高交易量 穩定"
# → 推薦 ECPay (市佔率高,穩定性佳)
# 快速整合需求
python scripts/recommend.py "簡單 快速 小型專案"
# → 推薦 SmilePay (整合最簡單)
# API 設計優先
python scripts/recommend.py "API 設計 MIG標準"
# → 推薦 Amego (MIG 4.0 最新標準)
# JSON 輸出
python scripts/recommend.py "穩定 文檔完整" --format json
推薦關鍵字:
- ECPay: 穩定、市佔、文檔、SDK、高交易量、電商
- SmilePay: 簡單、快速、小型、測試、無加密、便宜
- Amego: API、設計、新、MIG、標準
代碼生成器 (generate-invoice-service.py)
自動生成服務商專用代碼:
# 生成 TypeScript 服務
python scripts/generate-invoice-service.py ECPay --output ts
# 生成 Python 服務
python scripts/generate-invoice-service.py SmilePay --output py
# 輸出到檔案
python scripts/generate-invoice-service.py Amego --output ts > amego-service.ts
持久化配置 (persist.py)
將發票配置保存為 MASTER.md,供 AI 助手持續參考:
# 初始化配置
python scripts/persist.py init ECPay
python scripts/persist.py init SmilePay -p "MyProject"
# 顯示當前配置
python scripts/persist.py show
# 列出可用服務商
python scripts/persist.py list
# 強制覆蓋
python scripts/persist.py init Amego --force
生成結構:
invoice-config/
└── MASTER.md # 專案發票配置
├── 基本資訊
├── 服務商配置
├── API 端點
├── 發票類型設定
├── 環境變數建議
└── 開發檢查清單
發票類型
B2C 二聯式發票
- 買受人無統編
BuyerIdentifier=0000000000- 金額為含稅價
- 可使用載具或捐贈
- 示例:一般消費者購物
B2B 三聯式發票
- 買受人有 8 碼統編
BuyerIdentifier= 實際統編(需驗證格式)- 金額為未稅價,需另計稅額
- 不可使用載具或捐贈
- 示例:公司採購
各服務商特性比較
| 特性 | 綠界 ECPay | 速買配 SmilePay | 光貿 Amego |
|---|---|---|---|
| 測試/正式 URL | 不同 URL | 不同 URL | 相同 URL |
| 認證方式 | AES 加密 + HashKey/HashIV | Grvc + Verify_key | MD5 簽章 + App Key |
| 列印方式 | POST 表單提交 | GET URL 參數 | API 取得 PDF URL |
| B2B 金額欄位 | SalesAmount (未稅) | UnitTAX=N | DetailVat=0 |
| 傳輸格式 | JSON (AES 加密) | URL Parameters | JSON (URL Encode) |
開發實作步驟
1. 服務實作架構
創建服務時遵循以下結構:
// lib/services/invoice-provider.ts - 介面定義
export interface InvoiceService {
issueInvoice(userId: string, data: InvoiceIssueData): Promise<InvoiceIssueResponse>
voidInvoice(userId: string, invoiceNumber: string, reason: string): Promise<InvoiceVoidResponse>
printInvoice(userId: string, invoiceNumber: string): Promise<InvoicePrintResponse>
}
// lib/services/{provider}-invoice-service.ts - 各服務商實作
export class ECPayInvoiceService implements InvoiceService {
private async encryptData(data: any, hashKey: string, hashIV: string): Promise<string> {
// AES-128-CBC 加密實作
}
async issueInvoice(userId: string, data: InvoiceIssueData) {
// 1. 取得使用者設定
// 2. 準備 API 資料
// 3. 加密簽章
// 4. 發送請求
// 5. 解密回應
// 6. 回傳標準格式
}
}
2. 金額計算邏輯
含稅總額 → 未稅金額 + 稅額:
function calculateInvoiceAmounts(totalAmount: number, isB2B: boolean) {
if (isB2B) {
// B2B: 需分拆稅額
const taxAmount = Math.round(totalAmount - (totalAmount / 1.05))
const salesAmount = totalAmount - taxAmount
return { salesAmount, taxAmount, totalAmount }
} else {
// B2C: 含稅總額
return { salesAmount: totalAmount, taxAmount: 0, totalAmount }
}
}
// 範例
const amounts = calculateInvoiceAmounts(1050, true)
// { salesAmount: 1000, taxAmount: 50, totalAmount: 1050 }
3. 加密實作
綠界 (ECPay) - AES 加密:
import crypto from 'crypto'
function encryptECPay(data: object, hashKey: string, hashIV: string): string {
// 1. JSON 轉字串並 URL Encode
const jsonString = JSON.stringify(data)
const urlEncoded = encodeURIComponent(jsonString)
// 2. AES-128-CBC 加密
const cipher = crypto.createCipheriv('aes-128-cbc', hashKey, hashIV)
let encrypted = cipher.update(urlEncoded, 'utf8', 'base64')
encrypted += cipher.final('base64')
return encrypted
}
function decryptECPay(encryptedData: string, hashKey: string, hashIV: string): object {
const decipher = crypto.createDecipheriv('aes-128-cbc', hashKey, hashIV)
let decrypted = decipher.update(encryptedData, 'base64', 'utf8')
decrypted += decipher.final('utf8')
const urlDecoded = decodeURIComponent(decrypted)
return JSON.parse(urlDecoded)
}
光貿 (Amego) - MD5 簽章:
function generateAmegoSign(data: object, time: number, appKey: string): string {
const dataString = JSON.stringify(data)
const signString = dataString + time + appKey
return crypto.createHash('md5').update(signString).digest('hex')
}
4. 服務商綁定
關鍵:開立發票時必須記錄使用的服務商,列印時才能正確調用
// 開立時儲存服務商
await prisma.financialRecord.update({
where: { id: recordId },
data: {
invoiceNo: result.invoiceNumber,
invoiceProvider: actualProvider, // 'ECPAY' | 'SMILEPAY' | 'AMEGO'
invoiceRandomNum: result.randomNumber, // **重要**:列印時需要
invoiceDate: new Date(),
}
})
// 列印時使用開立時的服務商
const service = record.invoiceProvider
? InvoiceServiceFactory.getService(record.invoiceProvider)
: await InvoiceServiceFactory.getServiceForUser(userId)
5. 列印回應處理
前端需根據回應類型處理:
// 後端回應格式
interface InvoicePrintResponse {
success: boolean
type?: 'html' | 'redirect' | 'form'
htmlContent?: string // 綠界
printUrl?: string // 速買配/光貿
formUrl?: string
formParams?: Record<string, string>
}
// 前端處理範例
if (result.type === 'html') {
const win = window.open('', '_blank')
win.document.write(result.htmlContent)
} else if (result.type === 'redirect') {
window.open(result.url, '_blank')
} else if (result.type === 'form') {
// 動態建立表單提交
const form = document.createElement('form')
form.method = 'POST'
form.action = result.formUrl
form.target = '_blank'
// ... 添加參數
form.submit()
}
常見問題排除
問題 1: 開立發票失敗,錯誤訊息不明確
診斷步驟:
- 檢查 logger 輸出,查看
raw欄位完整錯誤 - 確認環境變數(測試/正式)是否正確
- 驗證必填欄位是否完整
綠界常見錯誤:
10000006: RelateNumber 重複 → 訂單編號已使用10000016: 金額計算錯誤 → 檢查 B2C/B2B 金額計算10000019: 打統編不可使用載具 → 移除 CarrierType
速買配常見錯誤:
-10066: AllAmount 驗算錯誤 → 檢查是否傳入 TotalAmount-10084: orderid 格式錯誤 → 限制 30 字元-10053: 載具號碼錯誤 → 驗證手機條碼格式
光貿常見錯誤:
1002: OrderId 已存在 → 使用唯一訂單編號1007: 金額計算錯誤 → 檢查 DetailVat 設定1012: 打統編發票不可使用載具或捐贈
問題 2: 列印時顯示「查詢不到該發票」
解決方案:
確認 invoiceProvider 欄位有正確儲存,列印時使用開立時的服務商。
// 正確:使用發票記錄中的服務商
const service = record.invoiceProvider
? InvoiceServiceFactory.getService(record.invoiceProvider)
: await InvoiceServiceFactory.getServiceForUser(userId)
// 錯誤:使用使用者當前預設服務商
const service = await InvoiceServiceFactory.getServiceForUser(userId)
問題 3: B2B 發票金額錯誤
各服務商金額欄位:
// 綠界 ECPay
const b2bData = {
SalesAmount: 1000, // 未稅銷售額
TaxAmount: 50, // 稅額
TotalAmount: 1050, // 總計
ItemPrice: 100, // 商品單價(未稅)
ItemAmount: 1000, // 商品小計(未稅)
ItemTax: 50 // 商品稅額
}
// 速買配 SmilePay
const b2bData = {
AllAmount: '1050', // 含稅總額
SalesAmount: '1000', // 未稅銷售額(選填,但建議填)
TaxAmount: '50', // 稅額(選填)
UnitTAX: 'N', // **重要**:單價未稅
UnitPrice: '100', // 商品單價(未稅)
Amount: '1000' // 商品小計(未稅)
}
// 光貿 Amego
const b2bData = {
DetailVat: 0, // **重要**:0=未稅
SalesAmount: 1000, // 未稅銷售額
TaxAmount: 50, // 稅額
TotalAmount: 1050, // 總計
ProductItem: [{
UnitPrice: 100, // 商品單價(未稅)
Amount: 1000 // 商品小計(未稅)
}]
}
問題 4: 速買配列印空白
原因: 回傳 method: 'GET' 時錯誤使用 type: 'form'
解決:
// 正確
if (printData.method === 'GET' && printData.url) {
return { type: 'redirect', url: printData.url }
}
// 錯誤
return { type: 'form', url: printData.url, params: printData.params }
問題 5: 時間戳記逾時
綠界錯誤 10000005: 時間戳記超過 10 分鐘
解決:
// 確保使用當前時間戳
const timestamp = Math.floor(Date.now() / 1000)
// 光貿:誤差容許 ±60 秒
const time = Math.floor(Date.now() / 1000)
測試帳號
綠界測試環境
MerchantID: 2000132
HashKey: ejCk326UnaZWKisg
HashIV: q9jcZX8Ib9LM8wYk
URL: https://einvoice-stage.ecpay.com.tw
速買配測試環境
Grvc: SEI1000034
Verify_key: 9D73935693EE0237FABA6AB744E48661
測試統編: 80129529
URL: https://ssl.smse.com.tw/api_test/SPEinvoice_Storage.asp
光貿測試環境
統編: 12345678
App Key: sHeq7t8G1wiQvhAuIM27
後台: https://invoice.amego.tw/
測試帳號: test@amego.tw
測試密碼: 12345678
開發檢查清單
使用此清單確保實作完整:
- 實作
InvoiceService介面 - 處理 B2C / B2B 金額計算差異
- 實作加密/簽章機制(AES 或 MD5)
- 儲存
invoiceProvider欄位 - 儲存
invoiceRandomNum(列印時需要) - 處理列印回應類型(html/redirect/form)
- 實作錯誤處理與 logger
- 測試環境驗證
- 處理載具與捐贈互斥邏輯
- 驗證統編格式(8 碼數字)
新增服務商步驟
- 在
lib/services/建立{provider}-invoice-service.ts - 實作
InvoiceService介面的所有方法 - 在
InvoiceServiceFactory註冊新服務商 - 在
prisma/schema.prisma的InvoiceProviderenum 新增選項 - 執行
prisma migrate或prisma db push - 更新前端設定頁面(
app/settings/invoice/page.tsx) - 撰寫單元測試
參考資料
詳細 API 規格請查看 references/ 目錄:
最後更新:2026/01/29