@paid-tw/einvoice-ezreceipt
v0.2.3
Published
ezReceipt 易發票 (COIMOTION API) adapter for @paid-tw/einvoice — order-centric REST e-invoices with token auth.
Maintainers
Readme
@paid-tw/einvoice-ezreceipt
English | 繁體中文
@paid-tw/einvoice 的
ezReceipt 易發票(底層為 COIMOTION 平台)轉接器。
與其他加密 form-post 的供應商不同,ezReceipt 是訂單導向的 REST + JSON API、採
token 認證。
pnpm add @paid-tw/einvoice @paid-tw/einvoice-ezreceiptimport { createEzreceiptProvider } from "@paid-tw/einvoice-ezreceipt";
const invoices = createEzreceiptProvider({
appCode: process.env.EZRECEIPT_APPCODE!, // x-deva-appcode(統編)
appKey: process.env.EZRECEIPT_APPKEY!, // x-deva-appkey
accName: process.env.EZRECEIPT_ACCNAME!, // 專用 API 帳號
password: process.env.EZRECEIPT_PASSWORD!, // 明文 —— 送出前在本地雜湊
mode: "TEST", // tryapi 主機;"PRODUCTION" → api 主機
});
await invoices.issue({ /* IssueInvoiceInput */ });認證(已自動處理)
每次呼叫都會帶 x-deva-appcode + x-deva-appkey;特權操作另需 x-deva-token。
client 會延遲登入(sha1(sha1(accName)+password),明文不離開程式)、快取 token,
並在遇到 -3 Invalid token 時自動重新登入一次。
⚠️ 請使用專用的 API 帳號。COIMOTION 每個帳號只允許一組有效 token,因此 API 登入會讓同帳號的後台網頁 session 失效(反之亦然)。請給整合程式自己的
accName。
運作方式(已於測試環境實機驗證)
| 項目 | 說明 |
| --- | --- |
| 傳輸 | POST JSON 至 {host}{endpoint};回應 { code, message, value }(code 0 為成功)。 |
| 開立 | all-in-one 的 eInvoice/invoice/issue —— 訂單由 prodList 隱式建立(只有 prodList 必要,order 選用)。 |
| 識別 | 操作以內部 invID / awID 為鍵(非發票號碼)。provider 會用 invoice/list 由發票號碼解析 invID,或你也可傳 providerOptions.invID(開立結果的 raw.id)省去查詢。 |
| 金額 | 稅額由平台計算(trCode 0 = 5%);prodList[].sales 為單價,incTax 依 priceMode。 |
各項操作
// 開立 —— B2C(會員載具)、B2B(統編)、捐贈、手機條碼、混合稅率…
const inv = await invoices.issue({
orderId: "ORDER_1",
buyer: { name: "買受人", email: "[email protected]" },
items: [{ description: "商品", quantity: 1, unitPrice: 100, amount: 100 }],
amount: { salesAmount: 100, taxAmount: 5, totalAmount: 105 },
taxType: "TAXABLE",
priceMode: "TAX_EXCLUSIVE",
carrier: { type: "MEMBER", code: "member_001" },
});
await invoices.query({ invoiceNumber: inv.invoiceNumber }); // 以發票號碼解析 invID
await invoices.void({ invoiceNumber: inv.invoiceNumber, reason: "客戶取消" });
const al = await invoices.allowance({
invoiceNumber: inv.invoiceNumber,
allowanceId: "A1",
items: [{ description: "商品", quantity: 1, unitPrice: 100, amount: 100 }],
amount: { salesAmount: 100, taxAmount: 5, totalAmount: 105 },
});
await invoices.voidAllowance({
invoiceNumber: inv.invoiceNumber,
allowanceNumber: al.allowanceNumber,
providerOptions: { awID: (al.raw as { awID: number }).awID },
});- B2B:傳入
buyer.ubn→ 對應到issueTo(不需載具)。 - 捐贈:傳入
donation.npoban→ carrierType 5。 - 混合稅率:設定逐項
taxType(應稅 / 零稅率 / 免稅)。
驗證策略:
issue刻意不套用共用 schema —— ezReceipt 允許用buyer.email帶會員 id(schema 的.email()會誤擋);void/allowance/voidAllowance/query則使用共用 schema,失敗丟出InvoiceError(codeVALIDATION)。
能力
ISSUE · VOID · ALLOWANCE · VOID_ALLOWANCE · QUERY · B2B · MIXED_TAX ·
CARRIER_VALIDATION(以 checkMobileCode / checkCharity 向財政部平台驗證手機條碼 / 愛心碼)。
未宣告:FOREIGN_CURRENCY(真正的境外電商 / carrierType 20 需要境外電商類型帳號 ——
一般帳號會回 1052)、SCHEDULED_ISSUE、QUERY_BY_ORDER_ID。
擴充方法
統一介面只涵蓋 5 個操作;createEzreceiptProvider 回傳的型別化 provider 另外直接暴露以下方法:
對帳 / 生命週期
listInvoices—— 條列已開立發票(對帳 / 報表)。revokeInvoice—— 註銷發票(有別於void作廢)。replyInvoice—— 回覆交換型(msgType=2)發票。
折讓輔助
getAllowanceQuota—— 查詢各品項尚餘可折讓額度。notifyAllowance—— 排程折讓事件 email 通知。notifyInvoice—— 排程發票事件 email 通知(收件人不可指定,見下)。
證明聯 / 列印
getInvoicePrintInfo—— 取得列印發票所需資料(JSON)。printInvoice—— 取得發票列印檔(PDF bytes)。format為必填,允許值1/2/11/12/21/22/25(實機驗證;漏帶回304)。printAllowance—— 取得折讓單列印檔(PDF bytes)。format:1熱感紙 /2A4。
財政部查詢
checkMobileCode—— 向財政部平台驗證手機條碼。checkCharity—— 向財政部平台驗證捐贈碼。lookupBusiness—— 以統編查詢機關資訊。
字軌管理
listInvoiceTracks—— 條列字軌分段。adjustInvoiceTrack—— 調整字軌分段起訖號。setDefaultTrack—— 設為預設字軌(有 / 無統編)。setInvoiceTrackStatus—— 開啟 / 關閉字軌分段。splitInvoiceTrack—— 字軌分段切分。updateInvoiceTrack—— 異動分段字軌(bizType / platform / memo)。listLogos—— 列出已上傳商標識別碼。uploadLogo—— 上傳商標圖檔。viewLogo—— 讀取商標圖檔(bytes)。setInvoiceTrackLogo—— 設定字軌印製發票的商標。
發票通知的收件人(實機驗證)
notifyInvoice 無法指定收件人。實機在 tryapi 驗證的行為:
- 通知端點忽略任何 per-send 收件人覆寫參數(
email/notifyEmail/to/receiverEmail… 皆無效);forceToBuyer只切換使用者的「通知買方」開關,不改地址。 - 發票的
notifyEmail(預期的通知對象)只在開立當下設定,透過issue的providerOptions.sendTo.email(開立後view可看到它成為notifyEmail)。 改用 body 頂層notifyEmail、或 MEMBER 載具的buyer.email,都不會設定它。 開立後沒有任何端點可以更改。 - ⚠️
tryapi沙盒會把所有通知信改寄到 API 帳號本身的註冊 email(無視notifyEmail/ 買方),因此「notifyEmail是否為真實投遞地址」無法在沙盒中確認。 - 要把已開立的發票補寄到任意地址:用
printInvoice取得證明聯 PDF,再由你自己的 mailer 寄出。
設定
| 選項 | 必填 | 說明 |
| --- | --- | --- |
| appCode | ✅ | x-deva-appcode(統一編號 / app code) |
| appKey | ✅ | x-deva-appkey |
| accName | ✅ | 登入帳號(專用 API 帳號) |
| password | ✅* | 明文登入密碼(*或改提供已取得的 token) |
| token | | 已取得的 access token,可跳過登入 |
| stID | | 合作廠商用的店家代號(x-deva-stid) |
| mode | | "TEST"(預設,tryapi)或 "PRODUCTION"(api) |
| validatePayload | | 在本地端驗證開立 payload(預設 true) |
| debug | | 選用的請求追蹤 logger(metadata:method/url/status/耗時/error,不含請求內容),預設 undefined |
補充:字軌「配號」(取得發票號碼區段)只能在後台操作 —— API 僅能管理既有字軌。
實機測試以 EZRECEIPT_LIVE=1 對專用 API 帳號執行。
授權條款
MIT
