taiwan-invoice-skill 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/taiwan-invoice/EXAMPLES.md +633 -0
- package/assets/taiwan-invoice/references/AMEGO_API_REFERENCE.md +1115 -0
- package/assets/taiwan-invoice/references/ECPAY_API_REFERENCE.md +647 -0
- package/assets/taiwan-invoice/references/SMILEPAY_API_REFERENCE.md +492 -0
- package/assets/taiwan-invoice/scripts/generate-invoice-service.py +192 -0
- package/assets/taiwan-invoice/scripts/test-invoice-amounts.py +129 -0
- package/assets/templates/base/skill-content.md +361 -0
- package/assets/templates/platforms/antigravity.json +21 -0
- package/assets/templates/platforms/claude.json +22 -0
- package/assets/templates/platforms/codebuddy.json +18 -0
- package/assets/templates/platforms/codex.json +21 -0
- package/assets/templates/platforms/continue.json +21 -0
- package/assets/templates/platforms/copilot.json +18 -0
- package/assets/templates/platforms/cursor.json +21 -0
- package/assets/templates/platforms/gemini.json +21 -0
- package/assets/templates/platforms/kiro.json +18 -0
- package/assets/templates/platforms/opencode.json +18 -0
- package/assets/templates/platforms/qoder.json +18 -0
- package/assets/templates/platforms/roocode.json +18 -0
- package/assets/templates/platforms/trae.json +18 -0
- package/assets/templates/platforms/windsurf.json +21 -0
- package/dist/index.js +15428 -0
- package/package.json +56 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
測試與驗證發票金額計算
|
|
4
|
+
|
|
5
|
+
使用方法:
|
|
6
|
+
python test-invoice-amounts.py
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def calculate_b2c_amounts(total_amount: int):
|
|
11
|
+
"""
|
|
12
|
+
B2C (二聯式) 金額計算
|
|
13
|
+
- 金額為含稅價
|
|
14
|
+
- TaxAmount = 0
|
|
15
|
+
"""
|
|
16
|
+
return {
|
|
17
|
+
'type': 'B2C',
|
|
18
|
+
'salesAmount': total_amount,
|
|
19
|
+
'taxAmount': 0,
|
|
20
|
+
'totalAmount': total_amount,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def calculate_b2b_amounts(total_amount: int):
|
|
25
|
+
"""
|
|
26
|
+
B2B (三聯式) 金額計算
|
|
27
|
+
- 需分拆未稅金額與稅額
|
|
28
|
+
- 稅率 5%
|
|
29
|
+
"""
|
|
30
|
+
tax_amount = round(total_amount - (total_amount / 1.05))
|
|
31
|
+
sales_amount = total_amount - tax_amount
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
'type': 'B2B',
|
|
35
|
+
'salesAmount': sales_amount,
|
|
36
|
+
'taxAmount': tax_amount,
|
|
37
|
+
'totalAmount': total_amount,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def verify_calculation(amounts: dict):
|
|
42
|
+
"""驗證計算是否正確"""
|
|
43
|
+
calculated_total = amounts['salesAmount'] + amounts['taxAmount']
|
|
44
|
+
is_valid = calculated_total == amounts['totalAmount']
|
|
45
|
+
|
|
46
|
+
return is_valid, calculated_total
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_amounts():
|
|
50
|
+
"""測試各種金額"""
|
|
51
|
+
test_cases = [
|
|
52
|
+
1050, # 標準金額
|
|
53
|
+
100, # 小金額
|
|
54
|
+
10000, # 大金額
|
|
55
|
+
9999, # 奇數金額
|
|
56
|
+
1, # 最小金額
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
print("=" * 60)
|
|
60
|
+
print("台灣電子發票金額計算測試")
|
|
61
|
+
print("=" * 60)
|
|
62
|
+
|
|
63
|
+
for amount in test_cases:
|
|
64
|
+
print(f"\n測試金額: NT$ {amount:,}")
|
|
65
|
+
print("-" * 60)
|
|
66
|
+
|
|
67
|
+
# B2C 計算
|
|
68
|
+
b2c = calculate_b2c_amounts(amount)
|
|
69
|
+
b2c_valid, b2c_total = verify_calculation(b2c)
|
|
70
|
+
|
|
71
|
+
print(f"\nB2C (二聯式):")
|
|
72
|
+
print(f" 銷售額 (含稅): {b2c['salesAmount']:,}")
|
|
73
|
+
print(f" 稅額: {b2c['taxAmount']:,}")
|
|
74
|
+
print(f" 總計: {b2c['totalAmount']:,}")
|
|
75
|
+
print(f" 驗證: {'[PASS]' if b2c_valid else '[FAIL]'}")
|
|
76
|
+
|
|
77
|
+
# B2B 計算
|
|
78
|
+
b2b = calculate_b2b_amounts(amount)
|
|
79
|
+
b2b_valid, b2b_total = verify_calculation(b2b)
|
|
80
|
+
|
|
81
|
+
print(f"\nB2B (三聯式):")
|
|
82
|
+
print(f" 銷售額 (未稅): {b2b['salesAmount']:,}")
|
|
83
|
+
print(f" 稅額: {b2b['taxAmount']:,}")
|
|
84
|
+
print(f" 總計: {b2b['totalAmount']:,}")
|
|
85
|
+
print(f" 驗證: {'[PASS]' if b2b_valid else '[FAIL]'}")
|
|
86
|
+
|
|
87
|
+
# 反推稅率
|
|
88
|
+
if b2b['salesAmount'] > 0:
|
|
89
|
+
tax_rate = (b2b['taxAmount'] / b2b['salesAmount']) * 100
|
|
90
|
+
print(f" 實際稅率: {tax_rate:.2f}%")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_product_items():
|
|
94
|
+
"""測試商品明細計算"""
|
|
95
|
+
print("\n" + "=" * 60)
|
|
96
|
+
print("商品明細計算測試")
|
|
97
|
+
print("=" * 60)
|
|
98
|
+
|
|
99
|
+
# 測試案例:購物車有多個商品
|
|
100
|
+
items = [
|
|
101
|
+
{'name': '商品A', 'price': 100, 'qty': 2}, # 200
|
|
102
|
+
{'name': '商品B', 'price': 150, 'qty': 1}, # 150
|
|
103
|
+
{'name': '商品C', 'price': 500, 'qty': 3}, # 1500
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
print("\n購物車商品:")
|
|
107
|
+
total = 0
|
|
108
|
+
for item in items:
|
|
109
|
+
amount = item['price'] * item['qty']
|
|
110
|
+
total += amount
|
|
111
|
+
print(f" {item['name']}: NT$ {item['price']:,} x {item['qty']} = NT$ {amount:,}")
|
|
112
|
+
|
|
113
|
+
print(f"\n 小計: NT$ {total:,}")
|
|
114
|
+
|
|
115
|
+
# B2B 分拆
|
|
116
|
+
b2b = calculate_b2b_amounts(total)
|
|
117
|
+
print(f"\nB2B 三聯式發票:")
|
|
118
|
+
print(f" 未稅金額: NT$ {b2b['salesAmount']:,}")
|
|
119
|
+
print(f" 稅額 (5%): NT$ {b2b['taxAmount']:,}")
|
|
120
|
+
print(f" 總計: NT$ {b2b['totalAmount']:,}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
test_amounts()
|
|
125
|
+
test_product_items()
|
|
126
|
+
|
|
127
|
+
print("\n" + "=" * 60)
|
|
128
|
+
print("[DONE] 測試完成")
|
|
129
|
+
print("=" * 60)
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# {{TITLE}}
|
|
2
|
+
|
|
3
|
+
> {{DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
## Quick Navigation
|
|
6
|
+
|
|
7
|
+
### Related Documents
|
|
8
|
+
When using this skill, refer to the API reference documents in the project:
|
|
9
|
+
- `references/ECPAY_API_REFERENCE.md` - ECPay API Specification
|
|
10
|
+
- `references/SMILEPAY_API_REFERENCE.md` - SmilePay API Specification
|
|
11
|
+
- `references/AMEGO_API_REFERENCE.md` - Amego API Specification
|
|
12
|
+
- [EXAMPLES.md](EXAMPLES.md) - Code Examples
|
|
13
|
+
|
|
14
|
+
### When to Use This Skill
|
|
15
|
+
- Developing e-invoice issuance functionality
|
|
16
|
+
- Integrating Taiwan E-Invoice provider APIs
|
|
17
|
+
- Implementing B2C or B2B invoices
|
|
18
|
+
- Handling invoice printing, void, and allowance
|
|
19
|
+
- Processing encryption/signatures (AES, MD5)
|
|
20
|
+
- Troubleshooting invoice API integration issues
|
|
21
|
+
|
|
22
|
+
## Invoice Types
|
|
23
|
+
|
|
24
|
+
### B2C Invoice (Two-Part)
|
|
25
|
+
- Buyer without tax ID
|
|
26
|
+
- `BuyerIdentifier` = `0000000000`
|
|
27
|
+
- Amount is **tax-inclusive**
|
|
28
|
+
- Can use carrier or donation
|
|
29
|
+
- Example: General consumer purchase
|
|
30
|
+
|
|
31
|
+
### B2B Invoice (Three-Part)
|
|
32
|
+
- Buyer has 8-digit tax ID
|
|
33
|
+
- `BuyerIdentifier` = actual tax ID (validate format)
|
|
34
|
+
- Amount is **pre-tax**, requires separate tax calculation
|
|
35
|
+
- **Cannot** use carrier or donation
|
|
36
|
+
- Example: Company purchase
|
|
37
|
+
|
|
38
|
+
## Provider Comparison
|
|
39
|
+
|
|
40
|
+
| Feature | ECPay | SmilePay | Amego |
|
|
41
|
+
|---------|-------|----------|-------|
|
|
42
|
+
| Test/Prod URL | Different URLs | Different URLs | **Same URL** |
|
|
43
|
+
| Authentication | AES encryption + HashKey/HashIV | Grvc + Verify_key | MD5 signature + App Key |
|
|
44
|
+
| Print Method | POST form submit | GET URL params | API returns PDF URL |
|
|
45
|
+
| B2B Amount Field | SalesAmount (pre-tax) | UnitTAX=N | DetailVat=0 |
|
|
46
|
+
| Transport Format | JSON (AES encrypted) | URL Parameters | JSON (URL Encoded) |
|
|
47
|
+
|
|
48
|
+
## Implementation Steps
|
|
49
|
+
|
|
50
|
+
### 1. Service Architecture
|
|
51
|
+
|
|
52
|
+
Create services following this structure:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// lib/services/invoice-provider.ts - Interface definition
|
|
56
|
+
export interface InvoiceService {
|
|
57
|
+
issueInvoice(userId: string, data: InvoiceIssueData): Promise<InvoiceIssueResponse>
|
|
58
|
+
voidInvoice(userId: string, invoiceNumber: string, reason: string): Promise<InvoiceVoidResponse>
|
|
59
|
+
printInvoice(userId: string, invoiceNumber: string): Promise<InvoicePrintResponse>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// lib/services/{provider}-invoice-service.ts - Provider implementation
|
|
63
|
+
export class ECPayInvoiceService implements InvoiceService {
|
|
64
|
+
private async encryptData(data: any, hashKey: string, hashIV: string): Promise<string> {
|
|
65
|
+
// AES-128-CBC encryption implementation
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async issueInvoice(userId: string, data: InvoiceIssueData) {
|
|
69
|
+
// 1. Get user settings
|
|
70
|
+
// 2. Prepare API data
|
|
71
|
+
// 3. Encrypt and sign
|
|
72
|
+
// 4. Send request
|
|
73
|
+
// 5. Decrypt response
|
|
74
|
+
// 6. Return standard format
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Amount Calculation
|
|
80
|
+
|
|
81
|
+
**Tax-inclusive total -> Pre-tax amount + Tax:**
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
function calculateInvoiceAmounts(totalAmount: number, isB2B: boolean) {
|
|
85
|
+
if (isB2B) {
|
|
86
|
+
// B2B: Need to split tax
|
|
87
|
+
const taxAmount = Math.round(totalAmount - (totalAmount / 1.05))
|
|
88
|
+
const salesAmount = totalAmount - taxAmount
|
|
89
|
+
return { salesAmount, taxAmount, totalAmount }
|
|
90
|
+
} else {
|
|
91
|
+
// B2C: Tax-inclusive total
|
|
92
|
+
return { salesAmount: totalAmount, taxAmount: 0, totalAmount }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Example
|
|
97
|
+
const amounts = calculateInvoiceAmounts(1050, true)
|
|
98
|
+
// { salesAmount: 1000, taxAmount: 50, totalAmount: 1050 }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. Encryption Implementation
|
|
102
|
+
|
|
103
|
+
**ECPay - AES Encryption:**
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import crypto from 'crypto'
|
|
107
|
+
|
|
108
|
+
function encryptECPay(data: object, hashKey: string, hashIV: string): string {
|
|
109
|
+
// 1. Convert JSON to string and URL encode
|
|
110
|
+
const jsonString = JSON.stringify(data)
|
|
111
|
+
const urlEncoded = encodeURIComponent(jsonString)
|
|
112
|
+
|
|
113
|
+
// 2. AES-128-CBC encryption
|
|
114
|
+
const cipher = crypto.createCipheriv('aes-128-cbc', hashKey, hashIV)
|
|
115
|
+
let encrypted = cipher.update(urlEncoded, 'utf8', 'base64')
|
|
116
|
+
encrypted += cipher.final('base64')
|
|
117
|
+
|
|
118
|
+
return encrypted
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function decryptECPay(encryptedData: string, hashKey: string, hashIV: string): object {
|
|
122
|
+
const decipher = crypto.createDecipheriv('aes-128-cbc', hashKey, hashIV)
|
|
123
|
+
let decrypted = decipher.update(encryptedData, 'base64', 'utf8')
|
|
124
|
+
decrypted += decipher.final('utf8')
|
|
125
|
+
|
|
126
|
+
const urlDecoded = decodeURIComponent(decrypted)
|
|
127
|
+
return JSON.parse(urlDecoded)
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Amego - MD5 Signature:**
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
function generateAmegoSign(data: object, time: number, appKey: string): string {
|
|
135
|
+
const dataString = JSON.stringify(data)
|
|
136
|
+
const signString = dataString + time + appKey
|
|
137
|
+
return crypto.createHash('md5').update(signString).digest('hex')
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 4. Provider Binding
|
|
142
|
+
|
|
143
|
+
**Key: When issuing invoice, must record the provider used so printing can call the correct one**
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// Save provider when issuing
|
|
147
|
+
await prisma.financialRecord.update({
|
|
148
|
+
where: { id: recordId },
|
|
149
|
+
data: {
|
|
150
|
+
invoiceNo: result.invoiceNumber,
|
|
151
|
+
invoiceProvider: actualProvider, // 'ECPAY' | 'SMILEPAY' | 'AMEGO'
|
|
152
|
+
invoiceRandomNum: result.randomNumber, // **Important**: needed for printing
|
|
153
|
+
invoiceDate: new Date(),
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Use the issuing provider when printing
|
|
158
|
+
const service = record.invoiceProvider
|
|
159
|
+
? InvoiceServiceFactory.getService(record.invoiceProvider)
|
|
160
|
+
: await InvoiceServiceFactory.getServiceForUser(userId)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 5. Print Response Handling
|
|
164
|
+
|
|
165
|
+
Frontend needs to handle based on response type:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// Backend response format
|
|
169
|
+
interface InvoicePrintResponse {
|
|
170
|
+
success: boolean
|
|
171
|
+
type?: 'html' | 'redirect' | 'form'
|
|
172
|
+
htmlContent?: string // ECPay
|
|
173
|
+
printUrl?: string // SmilePay/Amego
|
|
174
|
+
formUrl?: string
|
|
175
|
+
formParams?: Record<string, string>
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Frontend handling example
|
|
179
|
+
if (result.type === 'html') {
|
|
180
|
+
const win = window.open('', '_blank')
|
|
181
|
+
win.document.write(result.htmlContent)
|
|
182
|
+
} else if (result.type === 'redirect') {
|
|
183
|
+
window.open(result.url, '_blank')
|
|
184
|
+
} else if (result.type === 'form') {
|
|
185
|
+
// Dynamically create form submission
|
|
186
|
+
const form = document.createElement('form')
|
|
187
|
+
form.method = 'POST'
|
|
188
|
+
form.action = result.formUrl
|
|
189
|
+
form.target = '_blank'
|
|
190
|
+
// ... add parameters
|
|
191
|
+
form.submit()
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Common Issues
|
|
196
|
+
|
|
197
|
+
### Issue 1: Invoice issuance failed with unclear error
|
|
198
|
+
|
|
199
|
+
**Diagnostic steps:**
|
|
200
|
+
1. Check logger output for complete error in `raw` field
|
|
201
|
+
2. Confirm environment variables (test/prod) are correct
|
|
202
|
+
3. Verify required fields are complete
|
|
203
|
+
|
|
204
|
+
**ECPay common errors:**
|
|
205
|
+
- `10000006`: RelateNumber duplicate -> Order number already used
|
|
206
|
+
- `10000016`: Amount calculation error -> Check B2C/B2B calculation
|
|
207
|
+
- `10000019`: Cannot use carrier with tax ID -> Remove CarrierType
|
|
208
|
+
|
|
209
|
+
**SmilePay common errors:**
|
|
210
|
+
- `-10066`: AllAmount validation error -> Check if TotalAmount was sent
|
|
211
|
+
- `-10084`: orderid format error -> Limit to 30 characters
|
|
212
|
+
- `-10053`: Carrier number error -> Validate mobile barcode format
|
|
213
|
+
|
|
214
|
+
**Amego common errors:**
|
|
215
|
+
- `1002`: OrderId already exists -> Use unique order number
|
|
216
|
+
- `1007`: Amount calculation error -> Check DetailVat setting
|
|
217
|
+
- `1012`: B2B invoice cannot use carrier or donation
|
|
218
|
+
|
|
219
|
+
### Issue 2: Print shows "Invoice not found"
|
|
220
|
+
|
|
221
|
+
**Solution:**
|
|
222
|
+
Confirm `invoiceProvider` field is saved correctly, use the issuing provider when printing.
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Correct: Use provider from invoice record
|
|
226
|
+
const service = record.invoiceProvider
|
|
227
|
+
? InvoiceServiceFactory.getService(record.invoiceProvider)
|
|
228
|
+
: await InvoiceServiceFactory.getServiceForUser(userId)
|
|
229
|
+
|
|
230
|
+
// Incorrect: Use user's current default provider
|
|
231
|
+
const service = await InvoiceServiceFactory.getServiceForUser(userId)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Issue 3: B2B invoice amount error
|
|
235
|
+
|
|
236
|
+
**Amount fields by provider:**
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// ECPay
|
|
240
|
+
const b2bData = {
|
|
241
|
+
SalesAmount: 1000, // Pre-tax sales
|
|
242
|
+
TaxAmount: 50, // Tax
|
|
243
|
+
TotalAmount: 1050, // Total
|
|
244
|
+
ItemPrice: 100, // Item unit price (pre-tax)
|
|
245
|
+
ItemAmount: 1000, // Item subtotal (pre-tax)
|
|
246
|
+
ItemTax: 50 // Item tax
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// SmilePay
|
|
250
|
+
const b2bData = {
|
|
251
|
+
AllAmount: '1050', // Tax-inclusive total
|
|
252
|
+
SalesAmount: '1000', // Pre-tax sales (optional but recommended)
|
|
253
|
+
TaxAmount: '50', // Tax (optional)
|
|
254
|
+
UnitTAX: 'N', // **Important**: Unit price is pre-tax
|
|
255
|
+
UnitPrice: '100', // Item unit price (pre-tax)
|
|
256
|
+
Amount: '1000' // Item subtotal (pre-tax)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Amego
|
|
260
|
+
const b2bData = {
|
|
261
|
+
DetailVat: 0, // **Important**: 0=pre-tax
|
|
262
|
+
SalesAmount: 1000, // Pre-tax sales
|
|
263
|
+
TaxAmount: 50, // Tax
|
|
264
|
+
TotalAmount: 1050, // Total
|
|
265
|
+
ProductItem: [{
|
|
266
|
+
UnitPrice: 100, // Item unit price (pre-tax)
|
|
267
|
+
Amount: 1000 // Item subtotal (pre-tax)
|
|
268
|
+
}]
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Issue 4: SmilePay print blank
|
|
273
|
+
|
|
274
|
+
**Cause:** Using `type: 'form'` when response has `method: 'GET'`
|
|
275
|
+
|
|
276
|
+
**Solution:**
|
|
277
|
+
```typescript
|
|
278
|
+
// Correct
|
|
279
|
+
if (printData.method === 'GET' && printData.url) {
|
|
280
|
+
return { type: 'redirect', url: printData.url }
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Incorrect
|
|
284
|
+
return { type: 'form', url: printData.url, params: printData.params }
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Issue 5: Timestamp expired
|
|
288
|
+
|
|
289
|
+
**ECPay error 10000005:** Timestamp exceeds 10 minutes
|
|
290
|
+
|
|
291
|
+
**Solution:**
|
|
292
|
+
```typescript
|
|
293
|
+
// Ensure using current timestamp
|
|
294
|
+
const timestamp = Math.floor(Date.now() / 1000)
|
|
295
|
+
|
|
296
|
+
// Amego: tolerance is +/- 60 seconds
|
|
297
|
+
const time = Math.floor(Date.now() / 1000)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Test Accounts
|
|
301
|
+
|
|
302
|
+
### ECPay Test Environment
|
|
303
|
+
```
|
|
304
|
+
MerchantID: 2000132
|
|
305
|
+
HashKey: ejCk326UnaZWKisg
|
|
306
|
+
HashIV: q9jcZX8Ib9LM8wYk
|
|
307
|
+
URL: https://einvoice-stage.ecpay.com.tw
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### SmilePay Test Environment
|
|
311
|
+
```
|
|
312
|
+
Grvc: SEI1000034
|
|
313
|
+
Verify_key: 9D73935693EE0237FABA6AB744E48661
|
|
314
|
+
Test Tax ID: 80129529
|
|
315
|
+
URL: https://ssl.smse.com.tw/api_test/SPEinvoice_Storage.asp
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Amego Test Environment
|
|
319
|
+
```
|
|
320
|
+
Tax ID: 12345678
|
|
321
|
+
App Key: sHeq7t8G1wiQvhAuIM27
|
|
322
|
+
Admin: https://invoice.amego.tw/
|
|
323
|
+
Test Account: test@amego.tw
|
|
324
|
+
Test Password: 12345678
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Development Checklist
|
|
328
|
+
|
|
329
|
+
Use this checklist to ensure complete implementation:
|
|
330
|
+
|
|
331
|
+
- [ ] Implement `InvoiceService` interface
|
|
332
|
+
- [ ] Handle B2C / B2B amount calculation differences
|
|
333
|
+
- [ ] Implement encryption/signature mechanism (AES or MD5)
|
|
334
|
+
- [ ] Save `invoiceProvider` field
|
|
335
|
+
- [ ] Save `invoiceRandomNum` (needed for printing)
|
|
336
|
+
- [ ] Handle print response types (html/redirect/form)
|
|
337
|
+
- [ ] Implement error handling and logging
|
|
338
|
+
- [ ] Test environment verification
|
|
339
|
+
- [ ] Handle carrier and donation mutual exclusion
|
|
340
|
+
- [ ] Validate tax ID format (8-digit number)
|
|
341
|
+
|
|
342
|
+
## Adding New Provider
|
|
343
|
+
|
|
344
|
+
1. Create `{provider}-invoice-service.ts` in `lib/services/`
|
|
345
|
+
2. Implement all methods of `InvoiceService` interface
|
|
346
|
+
3. Register new provider in `InvoiceServiceFactory`
|
|
347
|
+
4. Add option to `InvoiceProvider` enum in `prisma/schema.prisma`
|
|
348
|
+
5. Run `prisma migrate` or `prisma db push`
|
|
349
|
+
6. Update frontend settings page (`app/settings/invoice/page.tsx`)
|
|
350
|
+
7. Write unit tests
|
|
351
|
+
|
|
352
|
+
## References
|
|
353
|
+
|
|
354
|
+
For detailed API specifications, see the `references/` directory:
|
|
355
|
+
- [ECPay API Specification](./references/ECPAY_API_REFERENCE.md)
|
|
356
|
+
- [SmilePay API Specification](./references/SMILEPAY_API_REFERENCE.md)
|
|
357
|
+
- [Amego API Specification](./references/AMEGO_API_REFERENCE.md)
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
Last updated: 2026/01/28
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "antigravity",
|
|
3
|
+
"displayName": "Google Antigravity",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".agent",
|
|
7
|
+
"skillPath": "skills/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": {
|
|
11
|
+
"name": "taiwan-invoice",
|
|
12
|
+
"description": "Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego. Provides B2C/B2B invoice issuance, void, allowance, query, and print functionality."
|
|
13
|
+
},
|
|
14
|
+
"sections": {
|
|
15
|
+
"examples": true,
|
|
16
|
+
"references": true,
|
|
17
|
+
"scripts": true
|
|
18
|
+
},
|
|
19
|
+
"title": "Taiwan E-Invoice Skill",
|
|
20
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "claude",
|
|
3
|
+
"displayName": "Claude Code",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".claude",
|
|
7
|
+
"skillPath": "skills/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": {
|
|
11
|
+
"name": "taiwan-invoice",
|
|
12
|
+
"description": "Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego. Provides B2C/B2B invoice issuance, void, allowance, query, and print functionality. Includes encryption implementations (AES-128-CBC, MD5), tax calculations, and complete API specifications.",
|
|
13
|
+
"user-invocable": "true"
|
|
14
|
+
},
|
|
15
|
+
"sections": {
|
|
16
|
+
"examples": true,
|
|
17
|
+
"references": true,
|
|
18
|
+
"scripts": true
|
|
19
|
+
},
|
|
20
|
+
"title": "Taiwan E-Invoice Skill",
|
|
21
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "codebuddy",
|
|
3
|
+
"displayName": "CodeBuddy",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".codebuddy",
|
|
7
|
+
"skillPath": "skills/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": null,
|
|
11
|
+
"sections": {
|
|
12
|
+
"examples": true,
|
|
13
|
+
"references": true,
|
|
14
|
+
"scripts": true
|
|
15
|
+
},
|
|
16
|
+
"title": "Taiwan E-Invoice Skill",
|
|
17
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "codex",
|
|
3
|
+
"displayName": "Codex CLI",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".codex",
|
|
7
|
+
"skillPath": "skills/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": {
|
|
11
|
+
"name": "taiwan-invoice",
|
|
12
|
+
"description": "Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego."
|
|
13
|
+
},
|
|
14
|
+
"sections": {
|
|
15
|
+
"examples": true,
|
|
16
|
+
"references": true,
|
|
17
|
+
"scripts": true
|
|
18
|
+
},
|
|
19
|
+
"title": "Taiwan E-Invoice Skill",
|
|
20
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "continue",
|
|
3
|
+
"displayName": "Continue",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".continue",
|
|
7
|
+
"skillPath": "skills/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": {
|
|
11
|
+
"name": "taiwan-invoice",
|
|
12
|
+
"description": "Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego."
|
|
13
|
+
},
|
|
14
|
+
"sections": {
|
|
15
|
+
"examples": true,
|
|
16
|
+
"references": true,
|
|
17
|
+
"scripts": true
|
|
18
|
+
},
|
|
19
|
+
"title": "Taiwan E-Invoice Skill",
|
|
20
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "copilot",
|
|
3
|
+
"displayName": "GitHub Copilot",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".github",
|
|
7
|
+
"skillPath": "prompts/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": null,
|
|
11
|
+
"sections": {
|
|
12
|
+
"examples": true,
|
|
13
|
+
"references": true,
|
|
14
|
+
"scripts": true
|
|
15
|
+
},
|
|
16
|
+
"title": "Taiwan E-Invoice Skill",
|
|
17
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "cursor",
|
|
3
|
+
"displayName": "Cursor",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".cursor",
|
|
7
|
+
"skillPath": "skills/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": {
|
|
11
|
+
"name": "taiwan-invoice",
|
|
12
|
+
"description": "Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego. Provides B2C/B2B invoice issuance, void, allowance, query, and print functionality. Includes encryption implementations (AES-128-CBC, MD5), tax calculations, and complete API specifications."
|
|
13
|
+
},
|
|
14
|
+
"sections": {
|
|
15
|
+
"examples": true,
|
|
16
|
+
"references": true,
|
|
17
|
+
"scripts": true
|
|
18
|
+
},
|
|
19
|
+
"title": "Taiwan E-Invoice Skill",
|
|
20
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "gemini",
|
|
3
|
+
"displayName": "Gemini CLI",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".gemini",
|
|
7
|
+
"skillPath": "skills/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": {
|
|
11
|
+
"name": "taiwan-invoice",
|
|
12
|
+
"description": "Taiwan E-Invoice API integration specialist for ECPay, SmilePay, and Amego."
|
|
13
|
+
},
|
|
14
|
+
"sections": {
|
|
15
|
+
"examples": true,
|
|
16
|
+
"references": true,
|
|
17
|
+
"scripts": true
|
|
18
|
+
},
|
|
19
|
+
"title": "Taiwan E-Invoice Skill",
|
|
20
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "kiro",
|
|
3
|
+
"displayName": "Kiro",
|
|
4
|
+
"installType": "full",
|
|
5
|
+
"folderStructure": {
|
|
6
|
+
"root": ".kiro",
|
|
7
|
+
"skillPath": "steering/taiwan-invoice",
|
|
8
|
+
"filename": "SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
"frontmatter": null,
|
|
11
|
+
"sections": {
|
|
12
|
+
"examples": true,
|
|
13
|
+
"references": true,
|
|
14
|
+
"scripts": true
|
|
15
|
+
},
|
|
16
|
+
"title": "Taiwan E-Invoice Skill",
|
|
17
|
+
"description": "Comprehensive guide for Taiwan E-Invoice API integration. Supports ECPay, SmilePay, and Amego providers with B2C/B2B invoice operations."
|
|
18
|
+
}
|