skills/rytass/utils/invoice-development

invoice-development

SKILL.md

Invoice Development Guide

This skill provides guidance for developers working with the @rytass/invoice base package, including creating new invoice adapters.

Overview

The @rytass/invoice package defines the core interfaces and types that all invoice adapters must implement. It follows the adapter pattern to provide a unified API across different Taiwan e-invoice providers.

Architecture

@rytass/invoice (Base Package)
    ├── Invoice<Item>              # Invoice entity interface
    ├── InvoiceGateway<...>        # Gateway interface
    ├── InvoiceAllowance<Item>     # Allowance interface
    ├── Enums & Types              # Shared types
    └── Utility Functions          # Helpers

@rytass/invoice-adapter-*         # Provider implementations
    ├── [Provider]InvoiceGateway   # Implements InvoiceGateway
    ├── [Provider]Invoice          # Implements Invoice
    └── [Provider]Allowance        # Implements InvoiceAllowance

Installation

npm install @rytass/invoice

Core Interfaces

InvoiceGateway

The main interface that all adapters must implement:

interface InvoiceGateway<
  Item extends PaymentItem = PaymentItem,
  I extends Invoice<Item> = Invoice<Item>,
  QueryOptions = unknown,
> {
  issue(options: InvoiceIssueOptions<Item>): Promise<I>;
  void(invoice: Invoice<PaymentItem>, options: InvoiceVoidOptions): Promise<Invoice<PaymentItem>>;
  allowance(invoice: Invoice<PaymentItem>, allowanceItems: InvoicePaymentItem[], options?: InvoiceAllowanceOptions): Promise<Invoice<PaymentItem>>;
  invalidAllowance(allowance: InvoiceAllowance<PaymentItem>): Promise<Invoice<PaymentItem>>;
  query(options: QueryOptions): Promise<I>;
  isMobileBarcodeValid(code: string): Promise<boolean>;
  isLoveCodeValid(code: string): Promise<boolean>;
}

Invoice

The invoice entity interface:

interface Invoice<Item extends PaymentItem> {
  readonly invoiceNumber: string;
  readonly issuedOn: Date;
  readonly allowances: InvoiceAllowance<PaymentItem>[];
  readonly issuedAmount: number;
  readonly randomCode: string;
  readonly items: Item[];
  state: InvoiceState;
  nowAmount: number;
  voidOn: Date | null;
  setVoid: () => void;
  awardType?: InvoiceAwardType;
}

InvoiceAllowance

The allowance entity interface:

interface InvoiceAllowance<Item extends PaymentItem> {
  readonly allowanceNumber: string;
  readonly allowancePrice: number;
  readonly allowancedOn: Date;
  readonly remainingAmount: number;
  readonly items: Item[];
  readonly parentInvoice: Invoice<Item>;
  status: InvoiceAllowanceState;
  invalidOn: Date | null;
  invalid: () => void;
}

Quick Reference

Supporting Types

// Issue Options
interface InvoiceIssueOptions<Item extends PaymentItem = PaymentItem> {
  items: InvoicePaymentItem<Item>[];
  vatNumber?: string;
  carrier?: InvoiceCarrier;
  customsMark?: CustomsMark;
}

// Payment Item with Tax Type
type InvoicePaymentItem<Item extends PaymentItem = PaymentItem> = Item & {
  taxType?: Omit<TaxType, TaxType.MIXED>;
};

// Void Options
interface InvoiceVoidOptions {
  reason: string;
}

// Allowance Options
interface InvoiceAllowanceOptions {
  taxType?: Omit<TaxType, TaxType.MIXED | TaxType.SPECIAL>;
}

// Query Options
interface BaseInvoiceQueryOptions {
  orderId?: string;
  invoiceNumber?: string;
  [key: string]: unknown;
}
type InvoiceQueryOptions<T = BaseInvoiceQueryOptions> = T;

// Carrier Types (Union)
type InvoiceCarrier =
  | InvoicePrintCarrier
  | InvoiceMobileCarrier
  | InvoiceMoicaCarrier
  | InvoiceLoveCodeCarrier
  | InvoiceMemberCarrier
  | InvoicePlatformCarrier;

// Tax Types (Union)
type InvoiceTax = CommonTax | SpecialTax;

interface CommonTax {
  type: Exclude<TaxType, TaxType.SPECIAL>;
}

interface SpecialTax {
  type: TaxType.SPECIAL;
  taxCode: SpecialTaxCode;
}

Enums

Enum Values
TaxType TAXED, TAX_FREE, ZERO_TAX, SPECIAL, MIXED
InvoiceState INITED, ISSUED, VOID, ALLOWANCED
InvoiceAllowanceState INITED, ISSUED, INVALID
InvoiceCarrierType PRINT, MOBILE, MOICA, LOVE_CODE, MEMBER, PLATFORM
CustomsMark YES, NO
SpecialTaxCode TEA, CLUB, BANK_SELF, INSURANCE, BANK_COMMON, BANK_SELF_SALES_BEFORE_103, BANK_SELF_SALES_AFTER_103, FREE
InvoiceAwardType TWO_HUNDRED, FIVE_HUNDRED, EIGHT_HUNDRED, ONE_THOUSAND, FOUR_THOUSAND, TEN_THOUSAND, FORTY_THOUSAND, TWO_HUNDRED_THOUSAND, ONE_MILLION, TWO_MILLION, TEN_MILLION, CLOUD_TWO_THOUSAND

InvoiceAwardType (發票中獎類型)

import { InvoiceAwardType } from '@rytass/invoice';

enum InvoiceAwardType {
  TWO_HUNDRED = 6,          // 200 元
  FIVE_HUNDRED = 11,        // 500 元
  EIGHT_HUNDRED = 12,       // 800 元
  ONE_THOUSAND = 5,         // 1,000 元
  FOUR_THOUSAND = 4,        // 4,000 元
  TEN_THOUSAND = 3,         // 10,000 元
  FORTY_THOUSAND = 2,       // 40,000 元
  TWO_HUNDRED_THOUSAND = 1, // 200,000 元
  ONE_MILLION = 10,         // 1,000,000 元
  TWO_MILLION = 7,          // 2,000,000 元
  TEN_MILLION = 8,          // 10,000,000 元
  CLOUD_TWO_THOUSAND = 9,   // 雲端發票專屬 2,000 元
}

SpecialTaxCode (特殊稅種代碼)

TaxType.SPECIAL 時需指定特殊稅種代碼:

import { TaxType, SpecialTaxCode } from '@rytass/invoice';

enum SpecialTaxCode {
  TEA = 1,                      // 茶葉
  CLUB = 2,                     // 娛樂業
  BANK_SELF = 3,                // 銀行業(本業)
  INSURANCE = 4,                // 保險業
  BANK_COMMON = 5,              // 銀行業(一般)
  BANK_SELF_SALES_BEFORE_103 = 6, // 銀行業本業銷售額(103年前)
  BANK_SELF_SALES_AFTER_103 = 7,  // 銀行業本業銷售額(103年後)
  FREE = 8,                     // 免稅
}

// 使用 SpecialTaxCode
const invoice = await gateway.issue({
  orderId: 'ORDER-001',
  items: [...],
  tax: {
    type: TaxType.SPECIAL,
    taxCode: SpecialTaxCode.BANK_COMMON,
  },
});

Utility Functions

Function Description
isValidVATNumber(input) Validate Taiwan VAT number
getTaxTypeFromItems(items) Determine TaxType from items

Carrier Helpers

import { InvoiceCarriers } from '@rytass/invoice';

InvoiceCarriers.PRINT;              // 列印發票
InvoiceCarriers.MEMBER;             // 會員載具
InvoiceCarriers.PLATFORM;           // 平台載具
InvoiceCarriers.MOBILE('/ABC+123'); // 手機條碼
InvoiceCarriers.MOICA('...');       // 自然人憑證
InvoiceCarriers.LOVE_CODE('168001'); // 愛心碼捐贈

Deprecated Functions

import { verifyVatNumber, isValidVATNumber } from '@rytass/invoice';

// ⚠️ Deprecated - 請改用 isValidVATNumber
verifyVatNumber('12345678'); // 會顯示 deprecation warning

// ✅ 建議使用
isValidVATNumber('12345678');

Detailed Documentation

For complete interface specifications and implementation guide:

Weekly Installs
6
Repository
rytass/utils
GitHub Stars
6
First Seen
Feb 5, 2026
Installed on
amp6
github-copilot6
replit6
codex6
kimi-cli6
gemini-cli6