| name | payment-development |
| description | Development guide for @rytass/payments base package (支付基底套件開發指南). Use when creating new payment adapters (新增支付 adapter), understanding base interfaces, or extending payment functionality. Covers PaymentGateway, Order, BindCardPaymentGateway interfaces and implementation patterns for Taiwan payment providers (台灣金流服務提供商). Keywords - payment adapter, 支付 adapter, gateway, order lifecycle, 訂單生命週期, card binding, 卡片綁定, credit card, 信用卡, virtual account, ATM 虛擬帳號, CVS payment, 超商代碼, event emitter, 事件監聽 |
Payment Development Guide
This skill provides guidance for developers working with the @rytass/payments base package, including creating new payment adapters.
Overview
The @rytass/payments package defines the core interfaces and types that all payment adapters must implement. It follows the adapter pattern to provide a unified API across different Taiwan payment providers.
Architecture
@rytass/payments (Base Package)
│
├── PaymentGateway<OCM, O> # Gateway interface
├── Order<OCM> # Order entity interface
├── BindCardPaymentGateway<...> # Card binding interface (optional)
├── Enums & Types # Shared types
└── Event System # EventEmitter-based callbacks
@rytass/payments-adapter-* # Provider implementations
│
├── [Provider]Payment # Implements PaymentGateway
├── [Provider]Order # Implements Order
└── [Provider]BindCard # Implements card binding (optional)
Installation
npm install @rytass/payments
Core Interfaces
PaymentItem
Item included in an order:
interface PaymentItem {
name: string;
unitPrice: number;
quantity: number;
}
PaymentGateway
The main interface that all adapters must implement:
interface PaymentGateway<
OCM extends OrderCommitMessage = OrderCommitMessage,
O extends Order<OCM> = Order<OCM>,
> {
emitter: EventEmitter;
prepare<N extends OCM>(input: InputFromOrderCommitMessage<N>): Promise<Order<N>>;
query<OO extends O>(id: string, options?: unknown): Promise<OO>;
}
PrepareOrderInput
Base input interface for orders:
interface PrepareOrderInput<I extends PaymentItem = PaymentItem> {
items: I[];
}
Order
The order entity interface:
interface Order<OCM extends OrderCommitMessage> extends PrepareOrderInput {
state: OrderState;
createdAt: Date | null;
committedAt: Date | null;
additionalInfo?: AdditionalInfo<OCM>;
asyncInfo?: AsyncOrderInformation<OCM>;
failedMessage: OrderFailMessage | null;
id: string;
items: PaymentItem[];
committable: boolean;
infoRetrieved<T extends OCM>(asyncInformation: AsyncOrderInformation<T>): void;
fail(code: string, message: string): void;
commit<T extends OCM>(message: T, additionalInfo?: AdditionalInfo<T>): void;
refund(amount?: number, options?: unknown): Promise<void>;
}
InputFromOrderCommitMessage
Input type for creating orders via prepare():
interface InputFromOrderCommitMessage<OCM extends OrderCommitMessage> extends PrepareOrderInput {
id?: string;
shopName?: string;
clientBackUrl?: string;
cardType?: CardType;
}
BindCardRequest
Card binding request interface:
interface BindCardRequest {
cardId: string | undefined;
memberId: string;
}
CheckoutWithBoundCardOptions
Options for checkout with bound card:
interface CheckoutWithBoundCardOptions {
cardId: string;
memberId: string;
items: PaymentItem[];
orderId?: string;
}
BindCardPaymentGateway (Optional)
For adapters supporting card binding:
interface BindCardPaymentGateway<
CM extends OrderCommitMessage = OrderCommitMessage,
R extends BindCardRequest = BindCardRequest,
O extends Order<CM> = Order<CM>,
> {
prepareBindCard(memberId: string): Promise<R>;
checkoutWithBoundCard(options: CheckoutWithBoundCardOptions): Promise<O>;
}
Quick Reference
Order Lifecycle
INITED
↓ prepare()
PRE_COMMIT (Created - form/URL ready)
↓ User completes payment
ASYNC_INFO_RETRIEVED (for ATM/CVS - virtual account/code ready)
↓ User pays at bank/CVS
COMMITTED (Payment successful)
↓ OR
FAILED (Payment failed)
↓ (optional)
REFUNDED (Refund processed)
Channel Enum (支付通道)
import { Channel } from '@rytass/payments';
enum Channel {
CREDIT_CARD = 'CREDIT_CARD',
WEB_ATM = 'WEB_ATM',
VIRTUAL_ACCOUNT = 'VIRTUAL_ACCOUNT',
CVS_KIOSK = 'CVS_KIOSK',
CVS_BARCODE = 'CVS_BARCODE',
APPLE_PAY = 'APPLE_PAY',
LINE_PAY = 'LINE_PAY',
}
Payment Channels
| Channel | Description | Commit Type |
|---|
CREDIT_CARD | Credit/Debit Card | Sync |
VIRTUAL_ACCOUNT | ATM Virtual Account | Async |
WEB_ATM | Online ATM | Async |
CVS_KIOSK | Convenience Store Code | Async |
CVS_BARCODE | Convenience Store Barcode | Async |
APPLE_PAY | Apple Pay | Sync |
LINE_PAY | LINE Pay | Sync |
OrderState Enum
enum OrderState {
INITED = 'INITED',
PRE_COMMIT = 'PRE_COMMIT',
ASYNC_INFO_RETRIEVED = 'ASYNC_INFO_RETRIEVED',
COMMITTED = 'COMMITTED',
FAILED = 'FAILED',
REFUNDED = 'REFUNDED',
}
| State | Description |
|---|
INITED | Order initialized |
PRE_COMMIT | Order created, awaiting payment |
ASYNC_INFO_RETRIEVED | Async payment info ready (ATM/CVS) |
COMMITTED | Payment successful |
FAILED | Payment failed |
REFUNDED | Order refunded |
OrderFailMessage
interface OrderFailMessage {
code: string;
message: string;
}
Card Types
import { CardType } from '@rytass/payments';
enum CardType {
VMJ = 'VMJ',
AE = 'AE',
}
CVS (便利商店)
import { CVS } from '@rytass/payments';
enum CVS {
FAMILY_MART = 'FAMILY_MART',
HILIFE = 'HILIFE',
OK_MART = 'OK_MART',
SEVEN_ELEVEN = 'SEVEN_ELEVEN',
}
CreditCardECI (3D 驗證結果)
import { CreditCardECI } from '@rytass/payments';
enum CreditCardECI {
MASTER_3D = '2',
MASTER_3D_PART = '1',
MASTER_3D_FAILED = '0',
VISA_AE_JCB_3D = '5',
VISA_AE_JCB_3D_PART = '6',
VISA_AE_JCB_3D_FAILED = '7',
}
PaymentPeriod (定期定額)
import { PaymentPeriod, PaymentPeriodType } from '@rytass/payments';
enum PaymentPeriodType {
DAY = 'DAY',
MONTH = 'MONTH',
YEAR = 'YEAR',
}
interface PaymentPeriod {
amountPerPeriod: number;
type: PaymentPeriodType;
frequency?: number;
times: number;
}
Payment Events
enum PaymentEvents {
SERVER_LISTENED = 'LISTENED',
ORDER_INFO_RETRIEVED = 'INFO_RETRIEVED',
ORDER_PRE_COMMIT = 'PRE_COMMIT',
ORDER_COMMITTED = 'COMMITTED',
ORDER_FAILED = 'FAILED',
CARD_BOUND = 'CARD_BOUND',
CARD_BINDING_FAILED = 'CARD_BINDING_FAILED',
}
| Event | When Triggered |
|---|
SERVER_LISTENED | Built-in server started |
ORDER_PRE_COMMIT | Order created (form/URL ready) |
ORDER_INFO_RETRIEVED | Async payment info ready |
ORDER_COMMITTED | Payment successful |
ORDER_FAILED | Payment failed |
CARD_BOUND | Card bound successfully |
CARD_BINDING_FAILED | Card binding failed |
Event Usage
import { PaymentEvents } from '@rytass/payments';
payment.emitter.on(PaymentEvents.ORDER_COMMITTED, (order) => {
console.log('Payment successful:', order.id);
});
payment.emitter.on(PaymentEvents.ORDER_FAILED, (order) => {
console.error('Payment failed:', order.failedMessage);
});
OrderCommitMessage Types
Base commit message and channel-specific types:
interface OrderCommitMessage {
id: string;
totalPrice: number;
committedAt: Date | null;
}
interface OrderCreditCardCommitMessage extends OrderCommitMessage {
type?: Channel.CREDIT_CARD;
id: string;
totalPrice: number;
committedAt: Date;
cardType?: CardType;
}
interface OrderVirtualAccountCommitMessage extends OrderCommitMessage {
type?: Channel.VIRTUAL_ACCOUNT;
id: string;
totalPrice: number;
committedAt: Date | null;
}
interface OrderCVSCommitMessage extends OrderCommitMessage {
type?: Channel.CVS_KIOSK;
id: string;
totalPrice: number;
committedAt: Date | null;
}
interface OrderBarcodeCommitMessage extends OrderCommitMessage {
type?: Channel.CVS_BARCODE;
id: string;
totalPrice: number;
committedAt: Date | null;
}
interface OrderApplePayCommitMessage extends OrderCommitMessage {
type?: Channel.APPLE_PAY;
id: string;
totalPrice: number;
committedAt: Date | null;
}
interface OrderLinePayCommitMessage extends OrderCommitMessage {
type?: Channel.LINE_PAY;
id: string;
totalPrice: number;
committedAt: Date | null;
}
interface OrderWebATMCommitMessage extends OrderCommitMessage {
type?: Channel.WEB_ATM;
id: string;
totalPrice: number;
committedAt: Date | null;
}
AsyncOrderInformation Type
Conditional type for async payment info based on commit message type:
type AsyncOrderInformation<OCM extends OrderCommitMessage> =
OCM extends OrderVirtualAccountCommitMessage
? VirtualAccountInfo
: OCM extends OrderCVSCommitMessage
? CVSInfo
: OCM extends OrderBarcodeCommitMessage
? BarcodeInfo
: never;
AdditionalInfo Type
Conditional type for additional payment info based on commit message type:
type AdditionalInfo<OCM extends OrderCommitMessage> =
OCM extends OrderCreditCardCommitMessage
? CreditCardAuthInfo
: OCM extends OrderVirtualAccountCommitMessage
? VirtualAccountPaymentInfo
: OCM extends OrderWebATMCommitMessage
? WebATMPaymentInfo
: OCM extends OrderCVSCommitMessage
? CVSPaymentInfo
: OCM extends OrderBarcodeCommitMessage
? BarcodeInfo
: OCM extends OrderApplePayCommitMessage
? undefined
: never;
Info Types
For async payment channels:
interface VirtualAccountInfo {
channel: Channel.VIRTUAL_ACCOUNT;
bankCode: string;
account: string;
expiredAt: Date;
}
interface CVSInfo {
channel: Channel.CVS_KIOSK;
paymentCode: string;
expiredAt: Date;
}
interface BarcodeInfo {
channel: Channel.CVS_BARCODE;
barcodes: [string, string, string];
expiredAt: Date;
}
For credit card authorization:
interface CreditCardAuthInfo {
channel: Channel.CREDIT_CARD;
processDate: Date;
authCode: string;
amount: number;
eci: CreditCardECI;
card4Number: string;
card6Number: string;
gwsr?: string;
xid?: string;
aetId?: string;
}
For WebATM payment:
interface WebATMPaymentInfo {
channel: Channel.WEB_ATM;
buyerAccountNumber: string;
buyerBankCode: string;
}
For Virtual Account payment (付款後):
interface VirtualAccountPaymentInfo {
channel: Channel.VIRTUAL_ACCOUNT;
buyerAccountNumber: string;
buyerBankCode: string;
}
For CVS payment:
interface CVSPaymentInfo {
channel: Channel.CVS_KIOSK;
cvsPayFrom: CVS;
}
Detailed Documentation
For complete interface specifications and implementation guide: