# 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: `aibot88/taiwan-invoice` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add aibot88/taiwan-invoice`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aibot88/taiwan-invoice/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Finance & Business
- Author: aibot88 (https://skillmd.com/u/aibot88)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/aibot88/taiwan-invoice

---


# 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](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 算法在資料庫中搜索相關資訊：

```bash
# 搜索加值中心
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)

根據需求自動推薦最適合的加值中心：

```bash
# 高交易量電商
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)

自動生成服務商專用代碼：

```bash
# 生成 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 助手持續參考：

```bash
# 初始化配置
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. 服務實作架構

創建服務時遵循以下結構：

```typescript
// 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. 金額計算邏輯

**含稅總額 → 未稅金額 + 稅額：**

```typescript
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 加密：**

```typescript
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 簽章：**

```typescript
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. 服務商綁定

**關鍵：開立發票時必須記錄使用的服務商，列印時才能正確調用**

```typescript
// 開立時儲存服務商
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. 列印回應處理

前端需根據回應類型處理：

```typescript
// 後端回應格式
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: 開立發票失敗，錯誤訊息不明確

**診斷步驟：**
1. 檢查 logger 輸出，查看 `raw` 欄位完整錯誤
2. 確認環境變數（測試/正式）是否正確
3. 驗證必填欄位是否完整

**綠界常見錯誤：**
- `10000006`: RelateNumber 重複 → 訂單編號已使用
- `10000016`: 金額計算錯誤 → 檢查 B2C/B2B 金額計算
- `10000019`: 打統編不可使用載具 → 移除 CarrierType

**速買配常見錯誤：**
- `-10066`: AllAmount 驗算錯誤 → 檢查是否傳入 TotalAmount
- `-10084`: orderid 格式錯誤 → 限制 30 字元
- `-10053`: 載具號碼錯誤 → 驗證手機條碼格式

**光貿常見錯誤：**
- `1002`: OrderId 已存在 → 使用唯一訂單編號
- `1007`: 金額計算錯誤 → 檢查 DetailVat 設定
- `1012`: 打統編發票不可使用載具或捐贈

### 問題 2: 列印時顯示「查詢不到該發票」

**解決方案：**
確認 `invoiceProvider` 欄位有正確儲存，列印時使用開立時的服務商。

```typescript
// 正確：使用發票記錄中的服務商
const service = record.invoiceProvider
    ? InvoiceServiceFactory.getService(record.invoiceProvider)
    : await InvoiceServiceFactory.getServiceForUser(userId)

// 錯誤：使用使用者當前預設服務商
const service = await InvoiceServiceFactory.getServiceForUser(userId)
```

### 問題 3: B2B 發票金額錯誤

**各服務商金額欄位：**

```typescript
// 綠界 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'`

**解決：**
```typescript
// 正確
if (printData.method === 'GET' && printData.url) {
    return { type: 'redirect', url: printData.url }
}

// 錯誤
return { type: 'form', url: printData.url, params: printData.params }
```

### 問題 5: 時間戳記逾時

**綠界錯誤 10000005：** 時間戳記超過 10 分鐘

**解決：**
```typescript
// 確保使用當前時間戳
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 碼數字）

## 新增服務商步驟

1. 在 `lib/services/` 建立 `{provider}-invoice-service.ts`
2. 實作 `InvoiceService` 介面的所有方法
3. 在 `InvoiceServiceFactory` 註冊新服務商
4. 在 `prisma/schema.prisma` 的 `InvoiceProvider` enum 新增選項
5. 執行 `prisma migrate` 或 `prisma db push`
6. 更新前端設定頁面（`app/settings/invoice/page.tsx`）
7. 撰寫單元測試

## 參考資料

詳細 API 規格請查看 `references/` 目錄：
- [綠界 ECPay API 規格](./references/ECPAY_API_REFERENCE.md)
- [速買配 SmilePay API 規格](./references/SMILEPAY_API_REFERENCE.md)
- [光貿 Amego API 規格](./references/AMEGO_API_REFERENCE.md)

---

最後更新：2026/01/29

