> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zendfi.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Types

> Type definitions, branded IDs, and enums for the ZendFi SDK.

# Types

The SDK provides full TypeScript type coverage for every API request and response. It also uses branded types to prevent accidentally mixing up different ID strings.

## Branded IDs

Branded types add compile-time safety to string IDs. A `PaymentId` cannot be accidentally passed where a `MerchantId` is expected, even though both are strings at runtime.

```typescript theme={null}
import { asPaymentId, asMerchantId } from '@zendfi/sdk';

const paymentId = asPaymentId('pay_test_abc123');
const merchantId = asMerchantId('merch_xyz789');

// These are type-safe at compile time
await zendfi.getPayment(paymentId);   // works
await zendfi.getPayment(merchantId);  // type error
```

### Available Branded Types

| Type                | Factory                 | Example           |
| ------------------- | ----------------------- | ----------------- |
| `PaymentId`         | `asPaymentId()`         | `pay_test_abc123` |
| `MerchantId`        | `asMerchantId()`        | `merch_xyz789`    |
| `InvoiceId`         | `asInvoiceId()`         | `inv_test_abc123` |
| `SubscriptionId`    | `asSubscriptionId()`    | `sub_abc123`      |
| `InstallmentPlanId` | `asInstallmentPlanId()` | `inst_abc123`     |
| `PaymentLinkCode`   | `asPaymentLinkCode()`   | `link_abc123`     |

### How Branding Works

```typescript theme={null}
type Brand<T, B> = T & { __brand: B };
type PaymentId = Brand<string, 'PaymentId'>;
```

The `__brand` property is a phantom type -- it exists only in the type system and has zero runtime overhead. You can still use branded IDs as regular strings:

```typescript theme={null}
const id = asPaymentId('pay_test_abc123');
console.log(id);           // "pay_test_abc123"
console.log(id.length);    // 17
console.log(`ID: ${id}`);  // "ID: pay_test_abc123"
```

***

## Enums

### Environment

```typescript theme={null}
type Environment = 'development' | 'staging' | 'production';
```

### ApiKeyMode

```typescript theme={null}
type ApiKeyMode = 'test' | 'live';
```

### Currency

```typescript theme={null}
type Currency = 'USD' | 'EUR' | 'GBP';
```

### PaymentToken

```typescript theme={null}
type PaymentToken = 'SOL' | 'USDC' | 'USDT';
```

### Status Types

```typescript theme={null}
type PaymentStatus = 'pending' | 'confirmed' | 'failed' | 'expired';
type SubscriptionStatus = 'active' | 'canceled' | 'past_due' | 'paused';
type InstallmentPlanStatus = 'active' | 'completed' | 'defaulted' | 'cancelled';
type InvoiceStatus = 'draft' | 'sent' | 'paid';
type SplitStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'refunded';
```

***

## Configuration

### ZendFiConfig

```typescript theme={null}
interface ZendFiConfig {
  apiKey?: string;
  baseURL?: string;
  environment?: Environment;
  mode?: ApiKeyMode;
  timeout?: number;        // default: 30000
  retries?: number;        // default: 3
  idempotencyEnabled?: boolean;  // default: true
  debug?: boolean;         // default: false
}
```

***

## Request Types

### CreatePaymentRequest

```typescript theme={null}
interface CreatePaymentRequest {
  amount: number;
  currency?: Currency;          // default: 'USD'
  token?: PaymentToken;         // default: 'USDC'
  description?: string;
  customer_email?: string;
  redirect_url?: string;
  metadata?: Record<string, any>;
  split_recipients?: SplitRecipient[];
}
```

### CustomerObject

Pre-filled customer info attached to a payment link at creation time. When present, the checkout page skips the email / customer-info collection step and shows a **"Continue to Pay"** button instead. The link is automatically forced to `max_uses = 1`.

```typescript theme={null}
interface CustomerObject {
  email: string;
  name?: string;
  phone?: string;
  company?: string;
  billing_address_line1?: string;
  billing_address_line2?: string;
  billing_city?: string;
  billing_state?: string;
  billing_postal_code?: string;
  billing_country?: string;    // ISO 3166-1 alpha-2
}
```

### CreatePaymentLinkRequest

```typescript theme={null}
interface CreatePaymentLinkRequest {
  amount: number;
  currency?: string;
  token?: string;
  description?: string;
  max_uses?: number;
  expires_at?: string;              // ISO 8601
  metadata?: Record<string, any>;
  onramp?: boolean;                 // enable fiat on-ramp
  amount_ngn?: number;              // NGN amount for exact conversion
  payer_service_charge?: boolean;   // add service charge to payer
  collect_customer_info?: boolean;  // show customer details form on checkout
  /**
   * Optional pre-filled customer.  When set:
   *   - checkout skips the info-collection step ("Continue to Pay" CTA)
   *   - max_uses is forced to 1 regardless of any supplied value
   *   - customer data is forwarded straight into the onramp / payment flow
   */
  customer?: CustomerObject;
  /**
   * Optional split recipients for this link. When present, all payment link features
   * (onramp, customer collection, etc.) are inherited by payments created from this link.
   */
  split_recipients?: SplitRecipient[];
}
```

### CreateSubscriptionPlanRequest

```typescript theme={null}
interface CreateSubscriptionPlanRequest {
  name: string;
  description?: string;
  amount: number;
  currency?: Currency;
  interval: 'daily' | 'weekly' | 'monthly' | 'yearly';
  interval_count?: number;      // default: 1
  trial_days?: number;          // default: 0
  metadata?: Record<string, any>;
}
```

### CreateSubscriptionRequest

```typescript theme={null}
interface CreateSubscriptionRequest {
  plan_id: string;
  customer_email: string;
  customer_wallet?: string;
  metadata?: Record<string, any>;
}
```

### CreateInstallmentPlanRequest

```typescript theme={null}
interface CreateInstallmentPlanRequest {
  customer_wallet: string;
  customer_email?: string;
  total_amount: number;
  installment_count: number;
  first_payment_date?: string;  // ISO 8601, default: tomorrow
  payment_frequency_days: number;
  description?: string;
  late_fee_amount?: number;
  grace_period_days?: number;   // default: 7
  metadata?: Record<string, any>;
}
```

### CreateInvoiceRequest

```typescript theme={null}
interface CreateInvoiceRequest {
  customer_email: string;
  customer_name?: string;
  amount: number;
  token?: PaymentToken;
  description: string;
  line_items?: InvoiceLineItem[];
  due_date?: string;                // ISO 8601
  metadata?: Record<string, any>;
  /** Enable PAJ onramp (NGN bank transfer) on the payment link generated when this invoice is sent. */
  onramp?: boolean;
  /** Max times the generated payment link can be used. Defaults to 1 (single-customer invoice). */
  payment_link_max_uses?: number;
  /** Show a customer-details form on the checkout page before the payment step. */
  collect_customer_info?: boolean;
  /** Original NGN amount for exact PAJ onramp conversion. */
  amount_ngn?: number;
  /** When true, the PAJ service charge is billed to the payer instead of the merchant. */
  payer_service_charge?: boolean;
}
```

### SplitRecipient

Discriminated union supporting two settlement paths: direct wallet transfers and bank account deposits via PAJ.

```typescript theme={null}
// Recipient type discriminator
type RecipientType = 'wallet' | 'bank_account';

// Base split configuration (shared across both types)
interface SplitRecipientBase {
  recipient_type: RecipientType;
  recipient_name?: string;
  percentage?: number;          // 0-100, sum of all percentages must equal 100
  fixed_amount_usd?: number;    // alternative to percentage (not summed)
  split_order?: number;         // processing order (default: 0)
}

// Wallet recipient: direct blockchain transfer
interface WalletSplitRecipient extends SplitRecipientBase {
  recipient_type: 'wallet';
  recipient_wallet?: string;      // Solana wallet address
  sub_account_id?: string;        // sub-account external_id or UUID
  recipient_sub_account?: string; // alias for sub_account_id
}

// Bank account recipient: PAJ offramp (USDC → NGN → bank deposit)
interface BankAccountSplitRecipient extends SplitRecipientBase {
  recipient_type: 'bank_account';
  recipient_account_name: string;   // Account holder name
  recipient_bank_account: string;   // Account number
  recipient_bank_id?: string;       // Bank identifier (PAJ id, bank code, or bank name)
  recipient_bank?: string;          // alias for recipient_bank_id
  bank_identifier?: string;         // alias for recipient_bank_id
  bank_code?: string;               // alias for recipient_bank_id
  recipient_email: string;          // For OTP verification
}

type SplitRecipient = WalletSplitRecipient | BankAccountSplitRecipient;
```

***

## Response Types

### Payment

```typescript theme={null}
interface Payment {
  id: string;
  merchant_id: string;
  amount_usd?: number;
  amount?: number;
  currency?: string;
  payment_token?: PaymentToken;
  status: PaymentStatus;
  customer_wallet?: string;
  customer_email?: string;
  description?: string;
  checkout_url?: string;
  payment_url?: string;
  qr_code?: string;
  expires_at: string;
  confirmed_at?: string;
  transaction_signature?: string;
  metadata?: Record<string, any>;
  /** Split recipient IDs and statuses (if splits were applied to this payment). */
  split_statuses?: Array<{
    split_id: string;
    recipient_type: 'wallet' | 'bank_account';
    amount_usd: number;
    status: 'pending' | 'processing' | 'completed' | 'failed';
    transaction_signature?: string;
  }>;
  created_at?: string;
  updated_at?: string;
}
```

### PaymentLink

```typescript theme={null}
interface PaymentLink {
  id: string;
  link_code: string;
  merchant_id: string;
  description?: string;
  amount: number;
  currency: string;
  token: string;
  payment_url: string;
  hosted_page_url: string;
  url: string;                  // alias for hosted_page_url
  max_uses?: number;
  uses_count: number;
  expires_at?: string;
  is_active: boolean;
  onramp: boolean;
  payer_service_charge: boolean;
  /** Whether the checkout page collects full customer details before payment. */
  collect_customer_info: boolean;
  /**
   * Present only on customer-scoped links (created with a `customer` object).
   * Forwarded to the checkout and onramp flow so no manual input is needed.
   */
  customer_data?: CustomerObject;
  /**
   * Split recipients associated with this payment link.
   * When split_recipients are present, all payments created from this link
   * will automatically apply the splits. Supports both wallet (direct blockchain transfer)
   * and bank_account (PAJ offramp: USDC → NGN → bank) recipient types.
   */
  split_recipients?: SplitRecipient[];
  metadata?: Record<string, any>;
  created_at: string;
  updated_at: string;
}
```

### Invoice

```typescript theme={null}
interface Invoice {
  id: string;
  invoice_number: string;
  customer_email: string;
  customer_name?: string;
  amount_usd: number;
  currency: string;
  token: PaymentToken;
  description: string;
  line_items: InvoiceLineItem[];
  status: InvoiceStatus;
  payment_url?: string;
  due_date?: string;
  sent_at?: string;
  paid_at?: string;
  created_at: string;
  /** Whether the payment link uses the PAJ onramp flow. */
  onramp: boolean;
  /** Max-use cap on the generated payment link. */
  payment_link_max_uses?: number;
  /** Whether the checkout page collects full customer details before payment. */
  collect_customer_info: boolean;
  /** Whether the payer bears the PAJ service charge. */
  payer_service_charge: boolean;
  /** Original NGN amount set by the merchant for exact PAJ conversion. */
  amount_ngn?: number;
}
```

### Webhook Types

```typescript theme={null}
type WebhookEvent =
  | 'payment.created'
  | 'payment.confirmed'
  | 'payment.failed'
  | 'payment.expired'
  | 'subscription.created'
  | 'subscription.activated'
  | 'subscription.canceled'
  | 'subscription.payment_failed'
  | 'split.completed'
  | 'split.failed'
  | 'installment.due'
  | 'installment.paid'
  | 'installment.late'
  | 'invoice.sent'
  | 'invoice.paid';

interface WebhookPayload {
  event: WebhookEvent;
  timestamp: string;
  merchant_id: string;
  data: Payment | Subscription;
}

interface VerifyWebhookRequest {
  payload: string | object;
  signature: string;
  secret: string;
}
```

### Pagination

```typescript theme={null}
interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    total_pages: number;
  };
}
```
