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.
@@ -0,0 +1,492 @@
1
+ # SmilePay 電子發票 API 完整技術規格
2
+
3
+ > 來源:SmilePay 官方文件
4
+ > 注意:本 API 可接受 **POST** 與 **GET** 兩種傳輸方式
5
+ > 編碼:僅提供 UTF-8 編碼
6
+
7
+ ---
8
+
9
+ ## 目錄
10
+ 1. [測試環境資料](#測試環境資料)
11
+ 2. [開立發票](#1-開立發票-issue-invoice)
12
+ 3. [開立折讓單](#2-開立折讓單-issue-allowance)
13
+ 4. [作廢/註銷功能](#3-作廢註銷功能-voidcancel)
14
+ 5. [列印發票](#4-列印發票-print-invoice)
15
+
16
+ ---
17
+
18
+ ## 測試環境資料
19
+ - **電子發票帳號 (Grvc)**:`SEI1000034`
20
+ - **驗證碼 (Verify_key)**:`9D73935693EE0237FABA6AB744E48661`
21
+ - **測試統編**:`80129529`
22
+
23
+ ---
24
+
25
+ ## 1. 開立發票 (Issue Invoice)
26
+
27
+ ### 環境資訊
28
+ - **正式環境**:`https://ssl.smse.com.tw/api/SPEinvoice_Storage.asp`
29
+ - **測試環境**:`https://ssl.smse.com.tw/api_test/SPEinvoice_Storage.asp`
30
+
31
+ ### 欄位參數
32
+ > 符號說明:O:必要、▲:非必要、X:不用填
33
+ > 請注意大小寫
34
+ > API主要為四大部分:使用者參數 \ 發票資訊 \ 商品明細 \ 買受人資訊
35
+
36
+ #### A. 使用者參數
37
+ | 參數名稱 | 名稱 | B2C | B2B | 格式 | 說明 |
38
+ | :--- | :--- | :--- | :--- | :--- | :--- |
39
+ | `Grvc` | 電子發票帳號 | O | O | 由速買配提供 | SEI1000034 |
40
+ | `Verify_key` | 驗證碼 | O | O | 由速買配提供 | 9D73935693EE0237FABA6AB744E48661 |
41
+
42
+ #### B. 發票資訊
43
+ | 參數名稱 | 名稱 | B2C | B2B | 格式 | 說明 |
44
+ | :--- | :--- | :--- | :--- | :--- | :--- |
45
+ | `InvoiceNumber` | 發票號碼 | ▲ | ▲ | 英文(2)+數字(8)共10碼<br>不可有符號 | 營業人自行管理字軌時使用<br>如需了解,請與速買配聯繫 |
46
+ | `RandomNumber` | 隨機碼 | ▲ | ▲ | 4字元(數字) | 同上 |
47
+ | `InvoiceDate` | 開立發票日期 | O | O | YYYY/MM/DD | B2C發票僅能開立48小時之內<br>B2B發票僅能開立168小時之內 |
48
+ | `InvoiceTime` | 開立發票時間 | O | O | HH:MM:SS | |
49
+ | `TrackSystemID` | 自訂字軌系統代號 | ▲ | ▲ | 中文/英文/數字 | 營業人在【字軌管理】設定,帶入後可指定使用該組字軌 |
50
+ | `Intype` | 發票稅率類型 | O | O | 07/08 | **07**:一般稅額計算之電子發票<br>(TaxType允許1/2/3/9)<br>**08**:特種稅額計算之電子發票<br>(TaxType允許2/3/4/9) |
51
+ | `TaxType` | 課稅別 | O | O | 1/2/3/4/9 | **1**:應稅<br>**2**:零稅率<br>**3**:免稅<br>**4**:應稅(特種稅率)<br>**9**:混合應稅與免稅(限收銀機發票無法分辨時使用) |
52
+ | `TaxRate` | 稅率 | ▲ | ▲ | 允許範圍:0.00~1.00 | 必須以小數格式傳入<br>當發票為【特種稅額】時輸入才有效<br>【Intype=08】與【TaxType=4/9】<br>根據行業別,傳入對應的稅率小數值(依國稅局公告為主):<br>0.25 (特種飲食業,稅率 25%)<br>0.15 (特種飲食業,稅率 15%)<br>0.01 (查定課徵,稅率 1%)<br>0.001 (農產品相關,稅率 0.1%) |
53
+ | `BuyerRemark` | 買受人註記 | ▲ | ▲ | 1/2/3/4 | 可保留空白<br>**1**:得抵扣之進貨及費<br>**2**:得抵扣之固定資<br>**3**:不得抵扣之進貨及費用<br>**4**:不得抵扣之固定資口 |
54
+ | `CustomsClearanceMark` | 通關方式註記 | ▲ | ▲ | 1/2 | **若為零稅率發票,此為必填欄位**<br>**1**:非經海關出口<br>**2**:經海關出口 |
55
+ | `GroupMark` | 彙開註記 | ▲ | ▲ | Y | 可保留空白<br>如為彙開發票再填入 |
56
+ | `BondedAreaConfirm` | 買受人簽署適用<br>零稅率註記 | X | ▲ | 1/2/3/4 | 可保留空白<br>**1**:買受人為保稅區營業人<br>**2**:買受人為遠洋漁業營業人<br>**3**:買受人為自由貿易港區營業人<br>**4**:其他 |
57
+ | `ZeroTaxRateReason` | 零稅率原因 | ▲ | ▲ | 71~79 | **若為零稅率發票,此為必填欄位**<br>參照加值型及非加值型營業稅法第 7 條:<br>**71**:第一款 外銷貨物<br>**72**:第二款 與外銷有關之勞務,或在國內提供而在國外使用之勞務<br>**73**:第三款 依法設立之免稅商店銷售與過境或出境旅客之貨物<br>**74**:第四款 銷售與保稅區營業人供營運之貨物或勞務<br>**75**:第五款 國際間之運輸。但外國運輸事業在中華民國境內經營國際運輸業務者,應以各該國對中華民國國際運輸事業予以相等待遇或免徵類似稅捐者為限<br>**76**:第六款 國際運輸用之船舶、航空器及遠洋漁船<br>**77**:第七款 銷售與國際運輸用之船舶、航空器及遠洋漁船所使用之貨物或修繕勞務<br>**78**:第八款 保稅區營業人銷售與課稅區營業人未輸往課稅區而直接出口之貨物<br>**79**:第九款 保稅區營業人銷售與課稅區營業人存入自由港區事業或海關管理之保稅倉庫、物流中心以供外銷之貨物 |
58
+ | `MainRemark` | 總備註 | ▲ | ▲ | 200字元 | 呈現在A4、A5紙張格式 |
59
+ | `RelateNumber` | 相關號碼 | ▲ | ▲ | 20字元 | |
60
+ | `DonateMark` | 捐贈 | O | O | 1/0 | **1**:捐贈<br>**0**:不捐贈<br>有Buyer_id時,必須為0<br>捐贈時載具類型(CarrierType)不可填入 |
61
+ | `LoveKey` | 愛心碼 | ▲ | X | | DonateMark為1,此處不可為空 |
62
+ | `Visa_Last4` | 信用卡末四碼 | ▲ | ▲ | 4字元 | 如為刷卡交易,請填入卡號末四碼 |
63
+ | `data_id` | 自訂發票編號 | ▲ | ▲ | 50字元 | 如該號碼已開立過發票,將無法重複開立,除非發票作廢<br>檢查範圍為相同期別的發票 |
64
+ | `orderid` | 自訂號碼 | ▲ | ▲ | 30字元 | 營業人自訂使用 |
65
+ | `PosSystemID` | 營業人自定義系統代號 | ▲ | ▲ | 20字元(英文/數字) | 營業人自訂的系統代碼,用以區分不同開立來源 |
66
+ | `Certificate_Remark` | 發票證明聯備註 | ▲ | ▲ | 34字元 | 呈現在熱感紙證明聯與A4、A5紙張格式 |
67
+
68
+ #### C. 商品明細
69
+ > **注意**:除總金額(AllAmount)/單價含稅(UnitTAX)參數外均以【 `|` 】(半形)符號區隔,並依照商品明細排列,各項總數必須相同
70
+
71
+ | 參數名稱 | 名稱 | B2C | B2B | 格式 | 說明 |
72
+ | :--- | :--- | :--- | :--- | :--- | :--- |
73
+ | `Description` | 商品明細 | O | O | 商品1\|商品2 | 請勿填入符號,每項品名最多256個字 |
74
+ | `Quantity` | 數量明細 | O | O | 數量1\|數量2 | 純數字,必須大於0 |
75
+ | `UnitPrice` | 單價明細 | O | O | 單價1\|單價2 | 純數字,可以小於0<br>透過單價含稅(UnitTAX)指定金額含稅或未稅價 |
76
+ | `Unit` | 單位明細 | ▲ | ▲ | 單位1\|單位2 | 請勿填入符號,每項單位最多6個字 |
77
+ | `ProductTaxType` | 商品稅率明細 | ▲ | ▲ | 稅率1\|稅率2 | 課稅別(TaxType)【9:混合稅率】時必填<br>**1**:應稅<br>**3**:免稅 |
78
+ | `Remark` | 商品備註明細 | ▲ | ▲ | 備註1\|備註2 | 請勿填入符號,每項內容最多40個字 |
79
+ | `Amount` | 各明細總額 | O | O | 金額1\|金額2 | 由每項【數量*單價】計算<br>純數字,可以小於0 |
80
+ | `AllAmount` | 總金額(含稅) | O | O | 整數,不可小於0 | 由各明細總額(Amount)合計<br>**金額必須含稅** |
81
+ | `SalesAmount` | 應稅銷售額 | ▲ | ▲ | 整數,不可小於0 | 課稅別(TaxType)【9:混合稅率】B2C/B2B發票<br>營業人必須提供含稅銷售額<br>【1:應稅】B2B發票填入未稅銷售額 |
82
+ | `FreeTaxSalesAmount` | 免稅銷售額 | ▲ | X | 整數,不可小於0 | 僅在課稅別(TaxType)為【9:混合稅率】<br>營業人必須提供銷售額金額 |
83
+ | `ZeroTaxSalesAmount` | 零稅率銷售額 | ▲ | X | 整數,不可小於0 | |
84
+ | `UnitTAX` | 單價含稅 | X | ▲ | Y/N | 商品單價是否含稅<br>**Y**:含稅金額(預設)<br>**N**:未稅金額 |
85
+ | `TaxAmount` | 稅金 | X | ▲ | 整數,不可小於0 | 僅在B2B發票時才會生效<br>營業人自行計算發票稅金 |
86
+
87
+ #### D. 買受人資訊
88
+ | 參數名稱 | 名稱 | B2C | B2B | 格式 | 說明 |
89
+ | :--- | :--- | :--- | :--- | :--- | :--- |
90
+ | `Buyer_id` | 買受人統編 | X | O | | **有值:開立B2B發票**<br>**空值:開立B2C發票** |
91
+ | `CompanyName` | 買受人公司名稱 | X | ▲ | 30字元 | 請勿填入符號 |
92
+ | `Name` | 買受人姓名 | ▲ | X | 30字元 | 請勿填入符號 |
93
+ | `Phone` | 電話 | ▲ | ▲ | 0900123456 | 純數字,請勿填入符號 |
94
+ | `Facsimile` | 傳真 | X | ▲ | | 純數字,請勿填入符號 |
95
+ | `Email` | 信箱 | ▲ | ▲ | 80字元 | 多組信箱請用分號(;)分隔 |
96
+ | `Address` | 地址 | ▲ | ▲ | 100字元 | |
97
+ | `CarrierType` | 載具類型 | ▲ | X | | 速買配載具: `EJ0113`<br>手機條碼: `3J0002`<br>自然人憑證: `CQ0001` |
98
+ | `CarrierID` | 載具ID明碼 | ▲ | X | | 當載具類型(CarrierType)有值時,此處不可為空<br>使用速買配載具(EJ0113)可透過Email/Phone進行載具註冊,此處能保留空白 |
99
+ | `CarrierID2` | 載具ID暗碼 | ▲ | X | | 當載具類型(CarrierType)有值時,此處不可為空<br>使用速買配載具(EJ0113)可透過Email/Phone進行載具註冊,此處能保留空白 |
100
+
101
+ #### 開立規則與各欄位規則整理
102
+ | | **買受人** | **個人** | **公司** |
103
+ | :--- | :--- | :--- | :--- |
104
+ | **捐贈** | 捐贈 | 填1 | X |
105
+ | | 愛心碼 | O | X |
106
+ | **載具** | 載具類型 | O | X |
107
+ | | 載具ID | O | X |
108
+ | **統編發票** | 統一編號 | X | O |
109
+
110
+ #### 發票範例 (測試區)
111
+
112
+ **B2C發票**:
113
+ ```
114
+ https://ssl.smse.com.tw/api_test/SPEinvoice_Storage.asp?Grvc=SEI1000034&Verify_key=9D73935693EE0237FABA6AB744E48661&Name=速買配&Phone=0900000000&Email=Test@testmailserver.net&Intype=07&TaxType=1&LoveKey=&DonateMark=0&Description=商品1|商品2&Quantity=5|8&UnitPrice=10|15&Unit=顆|條&Amount=50|120&ALLAmount=170&InvoiceDate=2026/1/26&InvoiceTime=15:33:33
115
+ ```
116
+
117
+ **B2B發票**:
118
+ ```
119
+ https://ssl.smse.com.tw/api_test/SPEinvoice_Storage.asp?Grvc=SEI1000034&Verify_key=9D73935693EE0237FABA6AB744E48661&CompanyName=速買配&Phone=0900000000&Email=Test@testmailserver.net&Intype=07&TaxType=1&LoveKey=&DonateMark=0&Description=商品1|商品2&Quantity=5|8&UnitPrice=10|15&Unit=顆|條&Amount=50|120&ALLAmount=170&InvoiceDate=2026/1/26&InvoiceTime=15:33:33&Buyer_id=80129529
120
+ ```
121
+
122
+ **B2G發票** (統編不可空白,商品金額必須含稅 UnitTAX=Y,發票類型必須帶入 B2B Einvoice_Type=B2B):
123
+ ```
124
+ https://ssl.smse.com.tw/api_test/SPEinvoice_Storage.asp?Grvc=SEI1000034&Verify_key=9D73935693EE0237FABA6AB744E48661&CompanyName=速買配&Phone=0900000000&Email=Test@testmailserver.net&Intype=07&TaxType=1&LoveKey=&DonateMark=0&Description=商品&Quantity=1&UnitPrice=100&Unit=顆&Amount=100&ALLAmount=100&InvoiceDate=2026/1/26&InvoiceTime=15:33:33&Buyer_id=80129529&UnitTAX=Y&Einvoice_Type=B2B
125
+ ```
126
+
127
+ ### 回應值
128
+ 格式:XML
129
+ ```xml
130
+ <SmilePayEinvoice>
131
+ <Status>0</Status>
132
+ <Desc></Desc>
133
+ <Grvc>SEI1000002</Grvc>
134
+ <orderno>order20171231</orderno>
135
+ <data_id>inid00001</data_id>
136
+ <InvoiceNumber>YY00000000</InvoiceNumber>
137
+ <RandomNumber>1234</RandomNumber>
138
+ <InvoiceDate>2017/12/31</InvoiceDate>
139
+ <InvoiceTime>23:59:59</InvoiceTime>
140
+ <InvoiceType>B2C</InvoiceType>
141
+ <CarrierID></CarrierID>
142
+ </SmilePayEinvoice>
143
+ ```
144
+
145
+ #### Xml Tag 說明
146
+ | 參數 | 名稱 | 說明 |
147
+ | :--- | :--- | :--- |
148
+ | `Status` | 狀態碼 | 回應值請參考下表 |
149
+ | `Desc` | 詳細原因 | 回應值請參考下表 |
150
+ | `orderno` | 自訂號碼 | |
151
+ | `data_id` | 自訂發票編號 | |
152
+ | `InvoiceNumber` | 發票號碼 | 實際發票開立號碼 |
153
+ | `RandomNumber` | 隨機碼 | |
154
+ | `InvoiceDate` | 開立日期 | |
155
+ | `InvoiceTime` | 開立時間 | |
156
+ | `InvoiceType` | 發票類型 | **B2C**:無統編,一般的銷售發票<br>**B2C2B**:有統編,可接受發票作廢<br>**B2B**:有統編,無法註銷<br>當買受人簽署適用零稅率註記(BondedAreaConfirm)有輸入值時給予此 |
157
+ | `CarrierID` | 載具ID | 如申請速買配載具則會回應載具號碼 |
158
+
159
+ #### 回應代號說明 (Status)
160
+ | 代號 | 說明 | 代號 | 說明 |
161
+ | :--- | :--- | :--- | :--- |
162
+ | `0` | 開立成功 | `-1001` | 商家帳號缺少參數 |
163
+ | `-10011` | 查無商家帳號 | `-10012` | 尚未開放B2B功能 |
164
+ | `-10013` | 尚未開放B2C功能 | `-10021` | 統一編號(Buyer_id)格式錯誤 |
165
+ | `-10022` | 統一編號不可捐贈(DonateMark)必須為0 | `-10023` | 統一編號(Buyer_id)內容錯誤 |
166
+ | `-10024` | 統一編號(Buyer_id)不可使用其他載具(CarrierType) | `-10025` | 缺少公司名稱(CompanyName) |
167
+ | `-10031` | 缺少開立日期(InvoiceDate、InvoiceTime) | `-10032` | 日期格式(InvoiceDate、InvoiceTime)錯誤 |
168
+ | `-10033` | B2C開立需再48hr內 | `-10034` | B2B開立需再168hr內 |
169
+ | `-10041` | 發票類別(Intype)錯誤 | `-10042` | 買受人註記欄(BuyerRemark)錯誤 |
170
+ | `-10043` | 通關方式註記(CustomsClearanceMark) | `-10044` | 捐贈註記(DonateMark)錯誤 |
171
+ | `-10045` | 愛心碼(LoveKey)空白 | `-10046` | 愛心碼伺服器異常 |
172
+ | `-10047` | 查無此愛心碼(LoveKey) | `-10048` | 課稅別(TaxType)錯誤 |
173
+ | `-10049` | 買受人簽署適用零稅率註記(BondedAreaConfirm)錯誤 | `-100410` | 總備註(MainRemark)錯誤 |
174
+ | `-100411` | 相關號碼(RelateNumber)錯誤 | `-100412` | 零稅率原因(ZeroTaxRateReason)錯誤 |
175
+ | `-10051` | 手機號碼(Phone)格式錯誤 | `-10052` | 載具號碼(CarrierID)錯誤 |
176
+ | `-10053` | 查無載具號碼(CarrierID) | `-10054` | 缺少建立載具參數,Email/Phone參數 |
177
+ | `-10055` | 建立載具失敗 | `-10056` | 查無手機條碼(CarrierID) |
178
+ | `-10057` | 自然人憑證(CarrierID)格式錯誤 | `-10058` | 載具類型(CarrierType)非允許使用的 |
179
+ | `-10061` | 商品各項目數量不符 | `-10062` | 內容長度不正確(單一品項)<br>品名(Description):256個字,不可空白<br>單位(Unit):6個字,可空白<br>商品備註(Remark):40個字,可空白 |
180
+ | `-10063` | 商品數量(Quantity)內容錯誤 | `-10064` | 商品金額(UnitPrice、Amount)內容錯誤 |
181
+ | `-10065` | 商品小計(UnitPrice、Amount)驗算錯誤 | `-10066` | 商品總金額(AllAmount)驗算錯誤 |
182
+ | `-10067` | 商品與總金額(ALLAmount)不符合規定 | `-10068` | 混合稅率銷售額明細(SalesAmount、FreeTaxSalesAmount)內容錯誤 |
183
+ | `-10069` | 稅金(TaxAmount)與未稅銷售額(SalesAmount)驗算錯誤 | `-100610` | 稅率(TaxRate)內容錯誤 |
184
+ | `-100611` | 產品稅率(ProductTaxType)內容誤 | `-10071` | 無可用字軌 |
185
+ | `-10072` | 自訂發票編號 (data_id)重複 | `-10073` | 營業人自定義系統代號(PosSystemID)格式錯誤 |
186
+ | `-10081` | 信用卡末四碼(Visa_Last4)格式錯誤 | `-10082` | 發票證明聯備註(Certificate_Remark)格式錯誤 |
187
+ | `-10083` | 自訂發票編號(data_id)格式錯誤 | `-10084` | 自訂號碼(orderid)格式錯誤 |
188
+ | `-2001` | (InvoiceNumber)格式錯誤 | `-2002` | (RandomNumber)格式錯誤 |
189
+ | `-2003` | (InvoiceNumber)不可重複 | | |
190
+
191
+ ---
192
+
193
+ ## 2. 開立折讓單 (Issue Allowance)
194
+
195
+ ### 環境資訊
196
+ - **正式環境**:`https://ssl.smse.com.tw/api/SPEinvoice_Storage_Allowance.asp`
197
+ - **測試環境**:`https://ssl.smse.com.tw/api_test/SPEinvoice_Storage_Allowance.asp`
198
+
199
+ ### 欄位參數
200
+ > 符號說明:O:必要、▲:非必要、X:不用填
201
+
202
+ #### A. 使用者參數
203
+ | 參數名稱 | 名稱 | 格式 | 說明 |
204
+ | :--- | :--- | :--- | :--- |
205
+ | `Grvc` | 電子發票帳號 | | 由速買配提供 |
206
+ | `Verify_key` | 驗證碼 | | 由速買配提供 |
207
+
208
+ #### B. 折讓單資訊
209
+ | 參數名稱 | 名稱 | 格式 | 說明 |
210
+ | :--- | :--- | :--- | :--- |
211
+ | `InvoiceNumber` | 發票號碼 | | 需折讓的發票號碼 |
212
+ | `InvoiceDate` | 發票日期 | | 發票號碼日期 |
213
+ | `AllowanceNumber` | 折讓單號碼 | 15字元(英/數混合)<br>不可填入符號 | 可保留空白<br>速買配會自動產生 |
214
+ | `AllowanceDate` | 折讓日期 | YYYY-MM-DD | 可保留空白 |
215
+ | `AllowanceType` | 折讓類型 | 1/2 | **1**:買方開立折讓單<br>**2**:賣方開立折讓單(預設) |
216
+
217
+ #### C. 折讓明細
218
+ > 均以【 `|` 】(半形)符號區隔,並依照折讓明細排列,各項總數必須相同
219
+
220
+ | 參數名稱 | 名稱 | 格式 | 說明 |
221
+ | :--- | :--- | :--- | :--- |
222
+ | `Description` | 商品明細 | 商品1\|商品2 | 請勿填入符號 |
223
+ | `Quantity` | 數量明細 | 數量1\|數量2 | 純數字,必須大於0 |
224
+ | `UnitPrice` | 單價明細(未稅) | 單價1\|單價2 | 純數字,可以小於0 |
225
+ | `Unit` | 單位明細 | 單位1\|單位2 | 可保留空白<br>請勿填入符號 |
226
+ | `Amount` | 各明細總額(未稅) | 金額1\|金額2 | 由每項【數量 * 單價(未稅) 】計算<br>純數字,可以小於0 |
227
+ | `Tax` | 稅金 | 稅金1\|稅金2 | 營業人需自行計算稅金<br>純數字 |
228
+ | `TaxType` | 課稅別 | 課稅別1\|課稅別2 | **1**:應稅<br>**2**:零稅率<br>**3**:免稅<br>**4**:應稅(特種稅率) |
229
+
230
+ ### 回應值
231
+ ```xml
232
+ <SmilePayEinvoice>
233
+ <Status>0</Status>
234
+ <Desc></Desc>
235
+ <Grvc>SEI1000002</Grvc>
236
+ <InvoiceNumber>YY00000000</InvoiceNumber>
237
+ <AllowanceNumber>YY00000000</AllowanceNumber>
238
+ </SmilePayEinvoice>
239
+ ```
240
+
241
+ #### Xml Tag 說明
242
+ | 參數 | 名稱 | 說明 |
243
+ | :--- | :--- | :--- |
244
+ | `Status` | 狀態碼 | 回應值請參考下表 |
245
+ | `Desc` | 詳細原因 | 回應值請參考下表 |
246
+ | `Grvc` | 商家代號 | |
247
+ | `InvoiceNumber` | 發票號碼 | |
248
+ | `AllowanceNumber` | 折讓單號碼 | |
249
+
250
+ #### 回應代號說明
251
+ | 代號 | 說明 |
252
+ | :--- | :--- |
253
+ | `-1001` | 商家帳號缺少參數 |
254
+ | `-10011` | 查無商家帳號 |
255
+ | `-1002` | 發票號碼(InvoiceNumber)錯誤 |
256
+ | `-10021` | 商品不可空白 |
257
+ | `-10022` | 商品各項目數量不符 |
258
+ | `-10023` | 商品明細(Description)參數異常 |
259
+ | `-10024` | 數量明細(Quantity)參數異常 |
260
+ | `-10025` | 單價明細(UnitPrice)金額異常 |
261
+ | `-10026` | 稅金明細(TaxType)參數異常 |
262
+ | `-10027` | 稅率明細(Tax)參數異常 |
263
+ | `-10028` | 折讓日期(AllowanceDate)參數異常 |
264
+ | `-1003` | 查無此筆發票 |
265
+ | `-10031` | 超過可折讓金額 |
266
+ | `-10032` | 折讓單號碼(AllowanceNumber)不可重複 |
267
+
268
+ ---
269
+
270
+ ## 3. 作廢/註銷功能 (Void/Cancel)
271
+
272
+ ### 環境資訊
273
+ - **正式環境**:`https://ssl.smse.com.tw/api/SPEinvoice_Storage_Modify.asp`
274
+ - **測試環境**:`https://ssl.smse.com.tw/api_test/SPEinvoice_Storage_Modify.asp`
275
+
276
+ ### 欄位參數
277
+ > 符號說明:O:必要、▲:非必要、X:無法使用
278
+ > API主要為兩大部分:使用者參數 \ 相關欄位
279
+
280
+ #### A. 使用者參數
281
+ | 參數名稱 | 名稱 | 格式 | 說明 |
282
+ | :--- | :--- | :--- | :--- |
283
+ | `Grvc` | 商家代號 | | 由速買配提供 |
284
+ | `Verify_key` | 驗證碼 | | 由速買配提供 |
285
+
286
+ #### B. 相關欄位
287
+ | 參數名稱 | 名稱 | 格式 | 說明 |
288
+ | :--- | :--- | :--- | :--- |
289
+ | `InvoiceNumber` | 發票號碼 | | 填入需處理的發票號碼 |
290
+ | `InvoiceDate` | 發票日期 | | 該筆發票日期 |
291
+ | `AllowanceNumber` | 折讓單號碼 | | 填入需處理的折讓單號碼 |
292
+ | `AllowanceDate` | 折讓單日期 | | 該筆折讓單日期 |
293
+ | `types` | 服務類型 | | **Cancel**:作廢發票<br>**Void**:註銷發票<br>**CancelAllowance**:作廢折讓單<br>**StopProcessing**:取消執行<br>※取消執行說明:<br>停止作廢/註銷作業並返回先前狀態,<br>如果大平台已接收,將無法執行。<br>限發票才能使用 |
294
+ | `CancelReason` | 作廢原因 | 20字元 | 作廢發票/折讓單實際原因 |
295
+ | `ReturnTaxDocumentNumber` | 專案作廢核准文號 | 60字元 | 可保留空白<br>如有【專案作廢核准文號】請填入 |
296
+ | `VoidReason` | 註銷原因 | 20字元 | 註銷發票實際原因 |
297
+ | `Remark` | 備註 | 200字元 | |
298
+
299
+ #### 處理規則與各欄位規則整理
300
+ | | **作廢發票** | **註銷發票** | **作廢折讓單** | **取消執行** |
301
+ | :--- | :--- | :--- | :--- | :--- |
302
+ | 發票號碼 | O | O | X | O |
303
+ | 發票日期 | O | O | X | O |
304
+ | 折讓單號碼 | X | X | O | X |
305
+ | 折讓單日期 | X | X | O | X |
306
+ | 作廢原因 | O | X | O | X |
307
+ | 專案作廢核准文號 | ▲ | X | X | X |
308
+ | 註銷原因 | X | O | X | X |
309
+ | 備註 | ▲ | ▲ | ▲ | X |
310
+
311
+ ### 回應值
312
+ ```xml
313
+ <SmilePayEinvoice>
314
+ <Status>0</Status>
315
+ <Desc></Desc>
316
+ <Types></Types>
317
+ <Grvc>SEI1000002</Grvc>
318
+ <InvoiceNumber>YY00000000</InvoiceNumber>
319
+ <AllowanceNumber>SMEE000000000000</AllowanceNumber>
320
+ <CancelDate>2017/12/31</CancelDate>
321
+ <CancelTime>23:59:59</CancelTime>
322
+ <VoidDate>2017/12/31</VoidDate>
323
+ <VoidTime>23:59:59</VoidTime>
324
+ <RejectDate>2017/12/31</RejectDate>
325
+ <RejectTime>23:59:59</RejectTime>
326
+ </SmilePayEinvoice>
327
+ ```
328
+
329
+ #### Xml Tag 說明
330
+ | 參數 | 名稱 | 說明 |
331
+ | :--- | :--- | :--- |
332
+ | `Status` | 狀態碼 | 回應值請參考下表 |
333
+ | `Desc` | 詳細原因 | 回應值請參考下表 |
334
+ | `Nowstatus` | 物流狀態 | 僅在-2008時,才提供 |
335
+ | `Types` | 服務類型 | |
336
+ | `Grvc` | 商家代號 | |
337
+ | `InvoiceNumber` | 發票號碼 | |
338
+ | `AllowanceNumber` | 折讓單號碼 | |
339
+ | `CancelDate` | 發票作廢/折讓單作廢日期 | 僅在types=Cancel/CancelAllowance才回應 |
340
+ | `CancelTime` | 發票作廢/折讓單作廢時間 | 僅在types=Cancel/CancelAllowance才回應 |
341
+ | `VoidDate` | 發票註銷日期 | 僅在types=Void才回應 |
342
+ | `VoidTime` | 發票註銷時間 | 僅在types=Void才回應 |
343
+
344
+ #### 回應代號說明
345
+ | 代號 | 說明 |
346
+ | :--- | :--- |
347
+ | `-1000` | 商家帳號缺少參數 |
348
+ | `-1001` | 查無商家帳號 |
349
+ | `-1002` | 服務類型錯誤 |
350
+ | `-2001` | 缺少發票號碼(InvoiceNumber)或作廢原因(CancelReason) |
351
+ | `-2002` | 作廢原因(CancelReason)超過字數 |
352
+ | `-2003` | 專案作廢核准文號(ReturnTaxDocumentNumber)超過字數 |
353
+ | `-2004` | 備註(Remark)超過字數 |
354
+ | `-2005` | 缺少發票號碼(InvoiceNumber)或註銷原因(VoidReason) |
355
+ | `-2006` | 註銷原因(VoidReason)超過字數 |
356
+ | `-2007` | 缺少折讓單號碼(AllowanceNumber)或作廢原因(CancelReason) |
357
+ | `-2008` | 發票目前狀態不允許執行該動作 |
358
+ | `-2009` | 發票有折讓紀錄不允許執行該動作 |
359
+ | `-2010` | 查無該筆發票/折讓單 |
360
+
361
+ ---
362
+
363
+ ## 4. 列印發票 (Print Invoice)
364
+
365
+ ### API 路徑與使用說明
366
+ 使用者可以通過 POST 或 GET 方法將請求發送到相應的 API 端點,即可開啟發票列印畫面。
367
+
368
+ | 說明 | 環境 | API位置 |
369
+ | :--- | :--- | :--- |
370
+ | **網頁模式**<br>(瀏覽器列印對話框)<br>版型:A4/A5/證明聯 | 正式環境 | `https://einvoice.smilepay.net/einvoice/SmilePayCarrier/InvoiceDetails.php` |
371
+ | | 測試環境 | `https://einvoice.smilepay.net/einvoice_test/SmilePayCarrier/InvoiceDetails.php` |
372
+ | **EPSON IP列印**<br>版型:證明聯 | 正式環境 | `https://einvoice.smilepay.net/einvoice/Invoice_Print/Invoice_Print_EPSON.php` |
373
+ | | 測試環境 | `https://einvoice.smilepay.net/einvoice_test/Invoice_Print/Invoice_Print_EPSON.php` |
374
+
375
+ ### 欄位參數
376
+ > 請注意大小寫
377
+
378
+ | 參數 | 名稱 | 格式 | 說明 |
379
+ | :--- | :--- | :--- | :--- |
380
+ | `Grvc` | 電子發票帳號 | 由速買配提供 | SEI1000034 |
381
+ | `Verify_key` | 驗證碼 | 由速買配提供 | 9D73935693EE0237FABA6AB744E48661 |
382
+ | `InNumber` | 發票號碼 | 英文(2)+數字(8)共10碼 | |
383
+ | `InvoiceDate` | 發票日期 | YYYY/MM/DD | |
384
+ | `RaNumber` | 發票認證碼 | 數字 | **B2C發票**:隨機碼<br>**B2B發票**:買受人統編 |
385
+ | `DetailPrint` | 呈現交易明細聯 | Y/不帶入 | 是否出現交易明細聯 |
386
+ | `AutoPrint` | 自動列印 | Y/不帶入 | 開啟網頁後自動執行列印 |
387
+
388
+ ### 範例 (測試區)
389
+
390
+ **B2C發票**:
391
+ ```
392
+ https://einvoice.smilepay.net/einvoice_test/SmilePayCarrier/InvoiceDetails.php?Grvc=SEI1000034&Verify_key=9D73935693EE0237FABA6AB744E48661&InNumber=HG00631928&InvoiceDate=2024/11/06&RaNumber=7572
393
+ ```
394
+
395
+ **B2B發票**:
396
+ ```
397
+ https://einvoice.smilepay.net/einvoice_test/SmilePayCarrier/InvoiceDetails.php?Grvc=SEI1000034&Verify_key=9D73935693EE0237FABA6AB744E48661&InNumber=HG00631929&InvoiceDate=2024/11/06&RaNumber=80129529
398
+ ```
399
+
400
+ ---
401
+
402
+ ## 開發筆記 (踩坑紀錄)
403
+
404
+ > 以下是實際整合過程中遇到的問題與解決方案
405
+
406
+ ### 1. 測試環境 vs 正式環境
407
+
408
+ **問題**:使用正式環境 URL 時顯示「尚未開放B2C功能」(-10013)
409
+
410
+ **原因**:需要在設定中啟用沙盒模式 (`isProd: false`)
411
+
412
+ **URL 對照**:
413
+ | 環境 | 開立發票 | 列印發票 |
414
+ |------|---------|---------|
415
+ | 測試 | `https://ssl.smse.com.tw/api_test/SPEinvoice_Storage.asp` | `https://einvoice.smilepay.net/einvoice_test/SmilePayCarrier/InvoiceDetails.php` |
416
+ | 正式 | `https://ssl.smse.com.tw/api/SPEinvoice_Storage.asp` | `https://einvoice.smilepay.net/einvoice/SmilePayCarrier/InvoiceDetails.php` |
417
+
418
+ ### 2. AllAmount 金額驗算錯誤 (-10066)
419
+
420
+ **問題**:開發票時 `AllAmount: "0"`,但金額應該是 21000
421
+
422
+ **原因**:程式碼用 `data.TotalAmount`,但 B2C 時 TotalAmount 未設定
423
+
424
+ **解決**:
425
+ ```typescript
426
+ // B2B: TotalAmount = 含稅總額
427
+ // B2C: SalesAmount = 含稅總額
428
+ AllAmount: String(data.TotalAmount || data.SalesAmount || '0')
429
+ ```
430
+
431
+ ### 3. orderid 格式錯誤 (-10084)
432
+
433
+ **問題**:`orderid` 超過 30 字元限制
434
+
435
+ **原因**:訂單編號格式 `INV-{recordId}-{timestamp}` 產生 43 字元
436
+
437
+ **解決**:
438
+ ```typescript
439
+ orderid: data.OrderId.slice(0, 30) // 限制 30 字元
440
+ data_id: data.OrderId // 50 字元,用來防重複
441
+ ```
442
+
443
+ ### 4. 列印發票彈窗空白
444
+
445
+ **問題**:列印發票時彈窗空白
446
+
447
+ **原因**:SmilePay 用 GET 方法,參數在 URL query string,但系統錯誤地用 POST 表單提交
448
+
449
+ **解決**:
450
+ ```typescript
451
+ // print-invoice route 判斷 method
452
+ if (printData.method === 'GET' && printData.url) {
453
+ return jsonResponse({ type: 'redirect', url: printData.url })
454
+ }
455
+ ```
456
+
457
+ ### 5. 列印 B2C 發票需要隨機碼
458
+
459
+ **參數 RaNumber**:
460
+ - **B2C 發票**:使用 `RandomNumber`(開立時回傳)
461
+ - **B2B 發票**:使用買方統編
462
+
463
+ **重要**:開立發票成功後必須儲存 `randomNumber` 到 `invoiceRandomNum` 欄位
464
+
465
+ ### 6. 金額計算
466
+
467
+ **B2C (二聯式)**:
468
+ - `AllAmount` = 含稅總額 (= SalesAmount)
469
+ - `UnitTAX` = 'Y' (單價含稅)
470
+
471
+ **B2B (三聯式)**:
472
+ - `AllAmount` = 含稅總額 (= TotalAmount)
473
+ - `SalesAmount` = 未稅金額
474
+ - `TaxAmount` = 稅額
475
+ - `UnitTAX` = 'N' (單價未稅)
476
+
477
+ ### 7. 實作檔案
478
+
479
+ - **服務實作**:`lib/services/smilepay-invoice-service.ts`
480
+ - **API 路由**:`app/api/v1/financials/[id]/issue-invoice/route.ts`
481
+
482
+ ---
483
+
484
+ ## 相關文件
485
+
486
+ - [綠界 API 規格](./ECPAY_API_REFERENCE.md)
487
+ - [光貿 Amego API 規格](./AMEGO_API_REFERENCE.md)
488
+ - [發票開立流程](./INVOICE_FLOW.md)
489
+
490
+ ---
491
+
492
+ 最後更新:2026/01/28 + 實作筆記
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 快速生成新的發票服務商實作模板
4
+
5
+ 使用方法:
6
+ python generate-invoice-service.py NewProvider
7
+
8
+ 這會生成:
9
+ - lib/services/newprovider-invoice-service.ts
10
+ - 包含所有必要的介面實作
11
+ """
12
+
13
+ import sys
14
+ import os
15
+ from pathlib import Path
16
+
17
+
18
+ TEMPLATE = """import crypto from 'crypto'
19
+ import { InvoiceService, InvoiceIssueData, InvoiceIssueResponse, InvoiceVoidResponse, InvoicePrintResponse } from './invoice-provider'
20
+ import { prisma } from '@/lib/prisma'
21
+
22
+ export class {ClassName}InvoiceService implements InvoiceService {{
23
+ private API_BASE_URL: string
24
+ private TEST_API_URL = 'https://test.{provider}.com/api'
25
+ private PROD_API_URL = 'https://api.{provider}.com'
26
+
27
+ constructor(isProd: boolean = false) {{
28
+ this.API_BASE_URL = isProd ? this.PROD_API_URL : this.TEST_API_URL
29
+ }}
30
+
31
+ /**
32
+ * 取得使用者的發票設定
33
+ */
34
+ private async getUserInvoiceSettings(userId: string) {{
35
+ const settings = await prisma.invoiceSettings.findUnique({{
36
+ where: {{ userId }},
37
+ }})
38
+
39
+ if (!settings || !settings.{provider}ApiKey) {{
40
+ throw new Error('{ProviderName} 發票設定不完整')
41
+ }}
42
+
43
+ return settings
44
+ }}
45
+
46
+ /**
47
+ * 開立發票
48
+ */
49
+ async issueInvoice(userId: string, data: InvoiceIssueData): Promise<InvoiceIssueResponse> {{
50
+ try {{
51
+ const settings = await this.getUserInvoiceSettings(userId)
52
+ const isB2B = data.IsB2B === true
53
+
54
+ // TODO: 實作 API 請求邏輯
55
+ // 1. 準備資料
56
+ // 2. 加密/簽章
57
+ // 3. 發送請求
58
+ // 4. 解析回應
59
+
60
+ const apiData = {{
61
+ // TODO: 填入 API 參數
62
+ }}
63
+
64
+ const response = await fetch(`${{this.API_BASE_URL}}/issue`, {{
65
+ method: 'POST',
66
+ headers: {{
67
+ 'Content-Type': 'application/json',
68
+ }},
69
+ body: JSON.stringify(apiData),
70
+ }})
71
+
72
+ const result = await response.json()
73
+
74
+ return {{
75
+ success: result.code === 0,
76
+ code: result.code,
77
+ msg: result.message,
78
+ invoiceNumber: result.invoice_number,
79
+ randomNumber: result.random_number,
80
+ raw: result,
81
+ }}
82
+ }} catch (error) {{
83
+ console.error('[{ProviderName}] 開立發票失敗:', error)
84
+ throw error
85
+ }}
86
+ }}
87
+
88
+ /**
89
+ * 作廢發票
90
+ */
91
+ async voidInvoice(userId: string, invoiceNumber: string, reason: string): Promise<InvoiceVoidResponse> {{
92
+ try {{
93
+ const settings = await this.getUserInvoiceSettings(userId)
94
+
95
+ // TODO: 實作作廢邏輯
96
+
97
+ return {{
98
+ success: true,
99
+ msg: '發票作廢成功',
100
+ }}
101
+ }} catch (error) {{
102
+ console.error('[{ProviderName}] 作廢發票失敗:', error)
103
+ throw error
104
+ }}
105
+ }}
106
+
107
+ /**
108
+ * 列印發票
109
+ */
110
+ async printInvoice(userId: string, invoiceNumber: string): Promise<InvoicePrintResponse> {{
111
+ try {{
112
+ const settings = await this.getUserInvoiceSettings(userId)
113
+
114
+ // TODO: 實作列印邏輯
115
+ // 根據服務商特性回傳不同類型:
116
+ // - type: 'html' - 回傳 HTML 內容
117
+ // - type: 'redirect' - 回傳 URL 跳轉
118
+ // - type: 'form' - 回傳表單資料
119
+
120
+ return {{
121
+ success: true,
122
+ type: 'redirect',
123
+ printUrl: `${{this.API_BASE_URL}}/print?invoice=${{invoiceNumber}}`,
124
+ }}
125
+ }} catch (error) {{
126
+ console.error('[{ProviderName}] 列印發票失敗:', error)
127
+ throw error
128
+ }}
129
+ }}
130
+
131
+ /**
132
+ * 輔助方法:計算金額
133
+ */
134
+ private calculateAmounts(totalAmount: number, isB2B: boolean) {{
135
+ if (isB2B) {{
136
+ const taxAmount = Math.round(totalAmount - (totalAmount / 1.05))
137
+ const salesAmount = totalAmount - taxAmount
138
+ return {{ salesAmount, taxAmount, totalAmount }}
139
+ }} else {{
140
+ return {{ salesAmount: totalAmount, taxAmount: 0, totalAmount }}
141
+ }}
142
+ }}
143
+
144
+ /**
145
+ * 輔助方法:加密/簽章
146
+ */
147
+ private generateSignature(data: any, secret: string): string {{
148
+ // TODO: 實作加密/簽章邏輯
149
+ // 範例: MD5
150
+ const signString = JSON.stringify(data) + secret
151
+ return crypto.createHash('md5').update(signString).digest('hex')
152
+ }}
153
+ }}
154
+ """
155
+
156
+
157
+ def generate_service(provider_name: str):
158
+ """生成服務檔案"""
159
+ # 轉換名稱格式
160
+ class_name = provider_name.capitalize()
161
+ provider_lower = provider_name.lower()
162
+
163
+ # 生成內容
164
+ content = TEMPLATE.format(
165
+ ClassName=class_name,
166
+ ProviderName=provider_name,
167
+ provider=provider_lower,
168
+ )
169
+
170
+ # 輸出檔案
171
+ output_file = f"{provider_lower}-invoice-service.ts"
172
+
173
+ with open(output_file, 'w', encoding='utf-8') as f:
174
+ f.write(content)
175
+
176
+ print(f"[OK] 已生成服務檔案: {output_file}")
177
+ print(f"\n接下來的步驟:")
178
+ print(f"1. 編輯 {output_file},實作 API 邏輯")
179
+ print(f"2. 在 InvoiceServiceFactory 註冊服務")
180
+ print(f"3. 在 Prisma schema 新增 InvoiceProvider enum")
181
+ print(f"4. 執行 prisma migrate 或 db push")
182
+ print(f"5. 更新前端設定頁面")
183
+
184
+
185
+ if __name__ == "__main__":
186
+ if len(sys.argv) < 2:
187
+ print("使用方法: python generate-invoice-service.py <ProviderName>")
188
+ print("範例: python generate-invoice-service.py NewProvider")
189
+ sys.exit(1)
190
+
191
+ provider = sys.argv[1]
192
+ generate_service(provider)