UNPKG

line-pay-core-v4

Version:

Core library for LINE Pay API V4 SDK - Provides shared utilities, base client, TypeScript types, and error handling for building LINE Pay integrations

216 lines (215 loc) 7 kB
import type { LinePayConfig } from './config/types'; /** * Base Response Format for all LINE Pay APIs * * All LINE Pay API responses follow this structure, with generic type `T` for the info field. * * @template T - Type of the info field containing API-specific response data * * @example * ```typescript * interface PaymentInfo { * transactionId: string * orderId: string * } * * type PaymentResponse = LinePayBaseResponse<PaymentInfo> * // { * // returnCode: '0000', * // returnMessage: 'Success', * // info: { transactionId: '...', orderId: '...' } * // } * ``` */ export interface LinePayBaseResponse<T = unknown> { /** * Return Code from LINE Pay API * * - `'0000'`: Success * - `'1xxx'`: Authentication/Authorization errors * - `'2xxx'`: Payment/Request errors * - `'9xxx'`: Internal server errors * * @see {@link https://pay.line.me/documents/online_v3_en.html#return-code} LINE Pay Return Codes */ returnCode: string; /** * Return Message description * * Human-readable error or success message in English. */ returnMessage: string; /** * Result Information * * Contains API-specific response data. Only present when `returnCode` is `'0000'`. * The structure varies depending on the API endpoint called. */ info?: T; } /** * LINE Pay Base Client * * Abstract base class for LINE Pay API integration (Online and Offline). * Provides core functionality for: * - API authentication with HMAC-SHA256 signatures * - HTTP request handling with timeout support * - Response parsing and error handling * - Configuration validation * * This class should be extended by specific API clients (e.g., `LinePayOnlineClient`). * * **Features:** * - ✅ Automatic signature generation for each request * - ✅ Timeout protection with AbortController * - ✅ Comprehensive error handling * - ✅ Type-safe response parsing * * @example * ```typescript * import { LinePayBaseClient } from 'line-pay-core-v4' * * class MyLinePayClient extends LinePayBaseClient { * async requestPayment(body: PaymentRequest) { * return this.sendRequest('POST', '/v3/payments/request', body) * } * } * * const client = new MyLinePayClient({ * channelId: process.env.LINE_PAY_CHANNEL_ID!, * channelSecret: process.env.LINE_PAY_CHANNEL_SECRET!, * env: 'sandbox', * timeout: 30000 * }) * ``` * * @see {@link https://pay.line.me/documents/online_v3_en.html} LINE Pay API Documentation */ export declare abstract class LinePayBaseClient { /** * LINE Pay Channel ID * @protected */ protected readonly channelId: string; /** * LINE Pay Channel Secret (encrypted/hashed in memory) * @protected */ protected readonly channelSecret: string; /** * BASE URL for LINE Pay API * @protected */ protected readonly baseUrl: string; /** * Request timeout in milliseconds * @protected */ protected readonly timeout: number; /** * Creates a new LinePayBaseClient instance * * Validates the configuration and sets up the client with the appropriate API base URL. * * @param config - LINE Pay configuration object * @throws {LinePayConfigError} If channelId or channelSecret is empty * @throws {LinePayConfigError} If timeout is not a positive number * * @example * ```typescript * const client = new MyLinePayClient({ * channelId: '1234567890', * channelSecret: 'abc123...', * env: 'sandbox', * timeout: 20000 * }) * ``` * * @example * ```typescript * // Using environment variables (recommended) * const client = new MyLinePayClient({ * channelId: process.env.LINE_PAY_CHANNEL_ID!, * channelSecret: process.env.LINE_PAY_CHANNEL_SECRET!, * env: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox' * }) * ``` */ constructor(config: LinePayConfig); /** * Sends an HTTP request to LINE Pay API with authentication * * Handles the complete request lifecycle: * 1. Generates HMAC-SHA256 signature * 2. Sets authentication headers * 3. Sends HTTP request with timeout * 4. Parses and validates response * 5. Throws appropriate errors on failure * * **Authentication Flow:** * - Generates unique nonce (UUID) * - Creates signature from: `secret + URI + queryString + body + nonce` * - Sets headers: `X-LINE-ChannelId`, `X-LINE-Authorization`, `X-LINE-Authorization-Nonce` * * **Error Handling:** * - Network errors → Propagated as-is * - Timeout → {@link LinePayTimeoutError} * - JSON parse error → {@link LinePayError} with code `PARSE_ERROR` * - HTTP error → {@link LinePayError} with LINE Pay error code * - Business error (returnCode !== '0000') → {@link LinePayError} * * @template T - Expected response type extending {@link LinePayBaseResponse} * @param method - HTTP method ('GET' or 'POST') * @param path - API endpoint path (e.g., '/v3/payments/request') * @param body - Optional request body (will be JSON stringified) * @param params - Optional query parameters * @param additionalHeaders - Optional additional HTTP headers to include in the request * @returns Promise resolving to typed LINE Pay response * @throws {LinePayTimeoutError} If request exceeds configured timeout * @throws {LinePayError} If API returns an error or response is invalid * @protected * * @example * ```typescript * // In a derived class * async requestPayment(requestBody: PaymentRequest) { * return this.sendRequest<PaymentResponse>( * 'POST', * '/v3/payments/request', * requestBody * ) * } * ``` * * @example * ```typescript * // GET request with query parameters * async getPaymentStatus(transactionId: string) { * return this.sendRequest<StatusResponse>( * 'GET', * `/v3/payments/${transactionId}`, * undefined, * { someParam: 'value' } * ) * } * ``` * * @example * ```typescript * // POST request with additional headers (e.g., for Offline API) * async makeOfflinePayment(body: PaymentRequest, deviceId: string) { * return this.sendRequest<PaymentResponse>( * 'POST', * '/v4/payments/oneTimeKeys/pay', * body, * undefined, * { * 'X-LINE-MerchantDeviceProfileId': deviceId, * 'X-LINE-MerchantDeviceType': 'POS' * } * ) * } * ``` */ protected sendRequest<T extends LinePayBaseResponse>(method: 'GET' | 'POST', path: string, body?: unknown, params?: Record<string, string>, additionalHeaders?: Record<string, string>): Promise<T>; }