name: ezpay-invoice description: > ezPay 電子發票(藍新金流 NewebPay 旗下品牌)API 完整技術參考,對應官方文件 EZP_INVI_1.2.1「電子發票技術串接手冊(標準版)」(簡單行動支付股份有限公司, 2021-04-01)。台灣電子發票服務商,涵蓋發票全生命週期:開立發票 (即時 / 等待觸發 / 預約自動,B2C / B2B,invoice_issue)、觸發開立發票 (invoice_touch_issue)、作廢發票(invoice_invalid)、開立折讓 (allowance_issue)、觸發確認 / 取消折讓(allowance_touch_issue)、 作廢折讓(allowanceInvalid)、查詢發票(invoice_search)。包含 AES-256-CBC 加密 PostData_(HashKey 32 bytes / HashIV 16 bytes / PKCS#7 padding / hex 輸出)、回應 CheckCode SHA256 驗證機制、載具類別 (手機條碼 0 / 自然人憑證 1 / ezPay 電子發票載具 2)、捐贈碼 LoveCode、 課稅別 TaxType、零稅率報關標記、含稅 / 未稅金額計算邏輯、完整錯誤碼表、 測試與正式環境 endpoint。Use this skill whenever code or tasks involve ezPay、ezpay、藍新電子發票、電子發票、cinv.ezpay.com.tw、inv.ezpay.com.tw、 開立發票、發票作廢、折讓單、開立折讓、作廢折讓、查詢發票、PostData_、 MerchantID_、CheckCode、HashKey、HashIV、CarrierType、載具、LoveCode、 捐贈碼、TaxType、課稅別、invoice_issue、invoice_touch_issue、 invoice_invalid、allowance_issue、allowance_touch_issue、allowanceInvalid、 invoice_search、e-invoice integration、台灣電子發票串接,或在 zenbu-site apps/api-gateway/src/commerce/ 下整合 ezPay 電子發票開立。 本 SKILL 為唯一官方 API reference 來源——不要再去翻 ezpay.com.tw 官網。
ezPay 電子發票 API
ezPay 電子發票加值服務平台由簡單行動支付股份有限公司(藍新金流 NewebPay
集團旗下品牌)提供,串接台灣財政部電子發票。本文件對應官方手冊
EZP_INVI_1.2.1「電子發票技術串接手冊(標準版)」(2021-04-01),適用範圍為
開立、作廢、折讓、作廢折讓、查詢發票。所有 API 採 HTTP POST + 標準
Form Post(application/x-www-form-urlencoded),編碼 UTF-8,本平台以
Web Service 方式回應 JSON 或 String。本專案 zenbu-site 為 NestJS 11 + TypeORM
0.3 後端,金流 / 第三方整合慣例位於 apps/api-gateway/src/commerce/。
文件範圍說明:EZP_INVI_1.2.1「標準版」只涵蓋上述 7 支 API。 任務中可能提及的「發票列表」「中獎發票查詢」「發票通知」「發票檔案下載」 「熱感應機列印」等功能不在此標準版文件內——本 SKILL 不臆造這些端點。 若需要請另查 ezPay 其他版本文件或洽 ezPay 客服。
環境與認證
測試平台:https://cinv.ezpay.com.tw/ (申請測試會員、建立測試商店、取得 Hash Key/IV)
正式平台:https://inv.ezpay.com.tw/ (申請正式會員、建立商店、取得 Hash Key/IV)
- 測試與正式是不同網域(不像某些服務商共用 URL)。測試走
cinv.ezpay.com.tw, 正式走inv.ezpay.com.tw。切換環境需同時換「網域 + 商店代號 + HashKey + HashIV」。 - 串接前必須在對應平台:① 申請會員並建立商店、取得商店 API 串接金鑰 HashKey 與 HashIV;② 等平台審核啟用電子發票;③ 在【管理設定/發票字軌號碼設定】 新增發票字軌號碼,才能開始開立。
- 發票資料可登入平台於【銷項發票作業/銷項發票查詢】查看。
串接認證的兩個層級
| 層級 | 機制 |
|---|---|
| 商店識別 | POST body 帶 MerchantID_(商店代號,明文,不加密) |
| 資料加密 | POST body 帶 PostData_(業務參數用 AES-256-CBC 加密後的 hex 字串) |
注意底線:
MerchantID_與PostData_兩個傳輸參數名稱結尾都有底線_。 漏掉底線是常見錯誤。
PostData_ 加密規則(AES-256-CBC,最容易出錯)
除 MerchantID_ 外,所有業務欄位先組成 query string,再用 AES 加密:
演算法:AES-256-CBC
金鑰 :HashKey(商店專屬,長度 32 bytes)
IV :HashIV(商店專屬,長度 16 bytes)
Padding:PKCS#7(先補 padding,加密時用 ZERO_PADDING 模式 → 等同自行 PKCS#7)
輸出 :加密結果轉小寫 hex 字串
加密三步驟:
- 業務參數組成 query string(
http_build_query風格,值需 url encode)。 - 對該字串補 PKCS#7 padding(blocksize = 32)。
- 用 HashKey + HashIV 做 AES-256-CBC 加密(zero padding 模式,因 padding 已自行
補好),結果
bin2hex轉小寫 hex → 即PostData_的值。
關鍵陷阱:官方 PHP 範例
openssl_encrypt(addpadding($str), 'AES-256-CBC', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv)—— 先自行addpadding補 PKCS#7,再用OPENSSL_ZERO_PADDING(告訴 openssl 不要再補)。若直接用 openssl 預設 padding 又自行補一次,會多補一個 block 導致解密錯誤(KEY10002)。
NestJS / TypeScript 加密函式與完整呼叫流程見 references/integration.md。
CheckCode 回應驗證(SHA256)
PostData_ 是 request 端加密;CheckCode 是 response 端驗證——平台回傳的
CheckCode 讓你驗證回應確實來自 ezPay。計算規則(見 references/concepts.md):
1. 取回應 5 個欄位 InvoiceTransNo / MerchantID / MerchantOrderNo / RandomNum / TotalAmt
2. 依參數名稱 A~Z 排序後用 & 串聯成 query string
3. 前後加上 HashIV 與 HashKey:
"HashIV=<IV>&" + <排序後字串> + "&HashKey=<Key>"
4. 整串做 SHA256,轉大寫 → 與回應的 CheckCode 比對
API 端點總覽
每支 API 測試 / 正式網域不同,路徑相同。所有 API 的 POST body 都是
MerchantID_ + PostData_ 兩欄。
| 功能 | 路徑(接在網域後) | PostData_ Version | 說明 |
|---|---|---|---|
| 開立發票 | /Api/invoice_issue |
1.5 |
即時 / 等待觸發 / 預約自動開立 |
| 觸發開立發票 | /Api/invoice_touch_issue |
1.0 |
觸發「等待觸發 / 預約自動」的發票立即開出 |
| 作廢發票 | /Api/invoice_invalid |
1.0 |
作廢已開立發票 |
| 開立折讓 | /Api/allowance_issue |
1.3 |
對已開立發票開折讓單 |
| 觸發確認 / 取消折讓 | /Api/allowance_touch_issue |
1.0 |
確認(C)或取消(D)未確認的折讓 |
| 作廢折讓 | /Api/allowanceInvalid |
1.0 |
作廢已確認的折讓 |
| 查詢發票 | /Api/invoice_search |
1.3 |
查單張發票完整內容(含明細) |
Version 欄位不可亂填:每支 API 的
PostData_內含Version欄位有固定值 (見上表)。填錯版本可能回INV10017(版本不支援混合稅率)等錯誤。
完整逐欄位 request / response 參數表見 references/api-reference.md。
三種開立發票方式
開立發票 invoice_issue 的 Status 欄位決定開立時機:
| Status | 方式 | 行為 |
|---|---|---|
1 |
即時開立發票 | 傳參數後立即開立,回應帶 InvoiceNumber 發票號碼 |
0 |
等待觸發開立發票 | 平台暫存發票資料,回應不帶 InvoiceNumber;需另呼叫 invoice_touch_issue 才開出 |
3 |
預約自動開立發票 | 須帶 CreateStatusTime 預計開立日期,平台於該日自動開立;提前開立可呼叫 invoice_touch_issue |
- 只有
Status=1即時開立,invoice_issue回應才會帶InvoiceNumber。 Status=0與Status=3的發票,發票號碼要等invoice_touch_issue觸發後(或預約日到)才會產生。- 觸發開立用
InvoiceTransNo(開立時回傳的 ezPay 電子發票開立序號)來指定要觸發哪張。
課稅別、零稅率與金額計算
TaxType 課稅別
開立發票 TaxType(發票層級):
| 值 | 意義 |
|---|---|
1 |
應稅 |
2 |
零稅率 |
3 |
免稅 |
9 |
混合應稅與免稅或零稅率(限 Category=B2C) |
商品層級 ItemTaxType(僅 TaxType=9 時必填):1=應稅、2=零稅率、3=免稅,
多項商品以 | 分隔。
稅率 TaxRate
- 應稅:一般稅率帶
5;特種稅率帶實際稅率數字(不含 %,例 18% 帶18)。 - 零稅率 / 免稅:
TaxRate帶0。
零稅率報關標記 CustomsClearance
TaxType=2(零稅率)時必須帶 CustomsClearance:1=非經海關出口、2=經海關出口。
含稅 / 未稅金額(順序敏感)
核心規則由 Category 決定 ItemPrice / ItemAmt 是含稅或未稅:
| Category | ItemPrice / ItemAmt 金額性質 |
|---|---|
B2B |
未稅金額 |
B2C |
含稅金額 |
開立發票需自行算出並傳入 Amt(銷售額未稅)、TaxAmt(稅額)、TotalAmt(含稅總額):
TotalAmt = Amt + TaxAmt # 平台檢核:銷售額 + 稅額 = 發票金額
ItemAmt = ItemCount * ItemPrice # 平台檢核:數量 × 單價 = 小計
# TaxType=9 混合稅率時:
Amt = AmtSales + AmtZero + AmtFree # Amt 為三者合計
平台金額檢核範圍(依文件「其他說明」):
- 開立發票只檢核「商品小計 = 商品數量 × 商品單價」與「發票金額 = 銷售額 + 稅額」。
- 開立折讓只檢核「折讓總金額 = 折讓商品小計 + 折讓商品稅額」。
- 發票金額計算方式請務必與公司財會人員確認——發票資料關係公司稅務。
金額計算的完整範例見 references/api-reference.md「金額計算」章節。
載具與捐贈
開立 B2C 發票(Category=B2C)時,依財政部規定須讓買受人擇一:存載具、捐贈、
或索取紙本。
載具 CarrierType / CarrierNum
| CarrierType | 載具種類 | CarrierNum 規則 |
|---|---|---|
0 |
手機條碼載具 | 第 1 碼 / + 7 碼大寫英數(39 字元集 0-9A-Z+-.) |
1 |
自然人憑證條碼載具 | 2 碼大寫英字 + 14 碼數字 |
2 |
ezPay 電子發票載具 | 任意可識別買受人代號(e-mail / 手機 / 會員編號);BuyerEmail 變必填 |
CarrierType有值時CarrierNum必填,且LoveCode必為空(載具與捐贈互斥)。CarrierNum須用rawurlencode()encode,且值前後不得含空白。- ezPay 電子發票載具(
CarrierType=2)不需事先申請,由賣方統編 + 買受人代號 組成載具號碼;同一統編下相同代號視為同一買受人。
捐贈碼 LoveCode
LoveCode:3~7 碼純數字,受贈單位的愛心碼(財政部清單內有效碼)。- 僅
Category=B2C適用;有值時CarrierType必為空。
索取紙本 PrintFlag
PrintFlag=Y索取紙本(營業人可於平台列印);PrintFlag=N不索取。Category=B2B→PrintFlag必填Y。Category=B2C且CarrierType、LoveCode皆空 →PrintFlag必填Y。PrintFlag=Y時回應才會帶BarCode/QRcodeL/QRcodeR。
回應格式
回應格式由 PostData_ 內的 RespondType 決定:
| RespondType | 回應形態 |
|---|---|
JSON |
最外層 { Status, Message, Result },Result 為 JSON 字串需再 parse |
String |
url-encoded query string,結尾固定 EndStr=##(用於判斷資料完整) |
Status=SUCCESS代表成功;失敗時Status為錯誤代碼(見references/error-codes.md)。- JSON 模式:成功業務資料在
Result欄位(是字串,需JSON.parse再取)。 - String 模式:所有欄位平鋪在 query string,必須驗證
EndStr=##確保傳輸完整。
重要:開立發票回
Status=SUCCESS只代表「ezPay 平台收件並處理成功」, 不代表已上傳財政部成功。上傳財政部是非同步流程——平台每日 01:00 起上傳 前一日資料,06:00 起更新上傳結果。真正上傳狀態要靠invoice_search查UploadStatus欄位(0未上傳 /1已上傳成功 /2上傳中 /3上傳失敗 /4上傳逾時)。
重送冪等性
開立發票 MerchantOrderNo(自訂編號)同一商店內不可重覆。若用完全相同的
PostData_ 重送,平台會回傳原本那張發票的 Result(Status=SUCCESS),不會重複
開立——但若 MerchantOrderNo 重覆而其他欄位不同,會回 LIB10003(自訂編號重覆)。
作廢與折讓的業務規則
作廢發票(invoice_invalid)
- 作廢期限:於奇數月 14 日前,可作廢前兩個月開立的發票(例:7/14 前可作廢
5/1–6/30 開立的發票)。超期回
LIB10008。 - 不可作廢的情況:
- 已開過折讓的發票無法作廢(須先作廢折讓)→
LIB10007。 - 已開立但尚未上傳財政部的發票無法作廢 →
LIB10009。 - 上傳失敗的發票不得作廢 →
INV70002。 - 已作廢過 →
LIB10005。
- 已開過折讓的發票無法作廢(須先作廢折讓)→
折讓流程(兩段式)
開立折讓 allowance_issue 的 Status 欄位:
| Status | 意義 |
|---|---|
0 |
開立折讓後不立即確認——折讓資料僅記錄於平台,未上傳財政部 |
1 |
開立折讓後立即確認——平台隔日自動上傳財政部 |
Status=0的折讓,營業人與買受人確認後,需再呼叫allowance_touch_issue:AllowanceStatus=C→ 確認折讓(隔日上傳財政部)。AllowanceStatus=D→ 取消折讓(折讓狀態變更為取消)。- 已確認的折讓無法再取消。
- 作廢折讓
allowanceInvalid只能作廢已確認的折讓。
折讓金額(ItemTaxAmt 邏輯)
折讓商品單價 ItemPrice 可為未稅或含稅:
ItemPrice為未稅金額 →ItemTaxAmt(折讓商品稅額)= 小計 × 稅率。ItemPrice為含稅金額 →ItemTaxAmt = 0(申報時無法扣抵該項營業稅額)。- 平台檢核:
TotalAmt(折讓總金額)= 折讓商品小計合計 + 折讓商品稅額合計。 - 請與會計人員確認採未稅或含稅。
整合工作流程(典型電商)
1. 結帳完成 → 後端組業務參數(ItemName/Count/Unit/Price/Amt 多項用 | 分隔)
2. 算 Amt / TaxAmt / TotalAmt;決定 Category(B2B/B2C)、載具 / 捐贈 / 紙本(互斥)
3. 組 query string → PKCS#7 padding → AES-256-CBC 加密 → hex → PostData_
4. POST {MerchantID_, PostData_} 到 /Api/invoice_issue
5. 回 Status=SUCCESS → parse Result → 存 InvoiceTransNo / InvoiceNumber / RandomNum
6. 驗證回應 CheckCode(SHA256)確認來源
7. (非同步)需要時呼叫 /Api/invoice_search 查 UploadStatus 確認財政部上傳結果
8. 退貨 → /Api/allowance_issue 開折讓(必要時 /Api/allowance_touch_issue 確認)
9. 整張取消 → /Api/invoice_invalid 作廢(須在期限內、未開過折讓、已上傳財政部)
NestJS service pattern、AES 加密實作、輪詢策略見 references/integration.md。
禁止事項與常見陷阱
- ❌ 不要漏掉傳輸參數的底線——是
MerchantID_與PostData_,不是MerchantID/PostData。 - ❌ 不要混淆測試 / 正式網域——測試
cinv.ezpay.com.tw、正式inv.ezpay.com.tw。 - ❌ 不要弄錯 AES padding——先自行補 PKCS#7(blocksize 32),加密時用 zero padding 模式;
重複補或不補都會
KEY10002(資料解密錯誤)。 - ❌ 不要填錯
Version——每支 API 的PostData_內Version有固定值(issue 1.5 / touch 1.0 / invalid 1.0 / allowance_issue 1.3 / allowance_touch 1.0 / allowanceInvalid 1.0 / search 1.3)。 - ❌ B2C 與 B2B 的
ItemPrice/ItemAmt金額性質不同——B2C 含稅、B2B 未稅,弄反金額會錯。 - ❌ 載具與捐贈互斥——
CarrierType有值時LoveCode必空,反之亦然。 - ❌
Category=B2C且選紙本時不可帶載具 / 捐贈;PrintFlag在 B2B 或 B2C 無載具無捐贈時必填Y。 - ❌ 不要假設
Status=SUCCESS= 財政部上傳成功——要查invoice_search的UploadStatus。 - ❌
MerchantOrderNo同商店不可重覆——重覆且內容不同回LIB10003。 - ❌ 不要對已開折讓 / 未上傳財政部 / 跨期的發票作廢(各有專屬錯誤碼)。
- ❌ String 回應模式必須驗
EndStr=##,否則無法確保資料完整。 - ❌
CarrierNum須rawurlencode,前後不得有空白。
Reference 檔案導覽
| 檔案 | 內容 |
|---|---|
references/api-reference.md |
7 支 API 逐欄位 request / response 參數表、JSON / String 回應範例、金額計算範例、查詢發票回應的完整欄位(含 ItemDetail、UploadStatus、InvoiceStatus、QRCode) |
references/error-codes.md |
完整錯誤碼對照表(KEY / INV / LIB / IAI / NOR 系列)+ 各 API 適用情境 |
references/concepts.md |
AES-256-CBC 加密細節、CheckCode SHA256 驗證、載具規則、捐贈碼、課稅別、發票生命週期、財政部上傳機制、台灣電子發票背景知識 |
references/integration.md |
NestJS / TypeScript 整合:AES 加解密函式、CheckCode 驗證函式、EzpayInvoiceService pattern、ConfigService、錯誤處理、zenbu-site commerce 對齊建議 |