import type { StorageAdapter, PaymentProviderAdapter, PayHooks, PayLogger, Authorizer, FeePolicy, Page, PageOptions, ResolveAccountInput, ResolveAccountResult, PayoutWebhookEvent } from "../interfaces.js"; import type { ID } from "../types.js"; import type { Operation } from "../schemas.js"; import type { PayoutRecipient } from "../schemas.js"; import type { ScheduledPayout, ScheduledPayoutExecution, ScheduledPayoutStatus } from "../schemas.js"; import type { OperationsDomain } from "./operations.js"; export interface CreatePayoutRecipientInput { provider: string; currency: string; details: Record; ownerId?: string; ownerType?: string; metadata?: Record; } export interface SendPayoutInput { walletId: ID; recipientId: ID; amountMinor: number; currency: string; narration?: string; idempotencyKey?: string; /** * Provider callback/webhook URL for async settlement notification. * Passed through to `InitPayoutInput.callbackUrl` so provider adapters * can include it on the transfer request without coupling to `metadata`. */ callbackUrl?: string; metadata?: Record; } export interface UpdatePayoutRecipientInput { metadata?: Record; } export interface ListPayoutsFilter { walletId?: string; recipientId?: string; statuses?: string[]; provider?: string; fromEffectiveAt?: Date; toEffectiveAt?: Date; } export interface ProcessPayoutWebhookInput { /** The provider name that sent this webhook. */ provider: string; rawBody: string | Buffer; headers: Record; } export interface ProcessPayoutWebhookResult { event: PayoutWebhookEvent; operationId: string | null; action: "settled" | "failed" | "already_finalized" | "not_found" | "pending"; } export interface CreateScheduledPayoutInput { walletId: ID; recipientId: ID; amountMinor: number; currency: string; narration?: string; /** One-time: fire at this timestamp then status → "completed". */ scheduledAt?: Date; /** Recurring: standard 5-field cron expression. Requires cronParser on client. */ cronExpression?: string; /** Auto-cancel when now >= endsAt on a fire tick. */ endsAt?: Date; /** * nextFireAt is auto-computed from cronExpression when cronParser is configured. * Must be supplied manually for recurring schedules without a cronParser. * For one-time schedules, defaults to scheduledAt. */ nextFireAt?: Date; /** Default: "pause". */ onFailure?: "pause" | "continue"; metadata?: Record; } export interface UpdateScheduledPayoutInput { amountMinor?: number; recipientId?: ID; cronExpression?: string; scheduledAt?: Date; nextFireAt?: Date; narration?: string; metadata?: Record; onFailure?: "pause" | "continue"; endsAt?: Date; } export interface ExecuteDueSchedulesResult { fired: number; succeeded: number; failed: number; failures: Array<{ scheduleId: string; error: string; }>; } export interface PayoutsDomain { /** * Register a new payout recipient with the specified provider. * Calls provider.createRecipient() and persists the result. * * @throws {PayoutProviderNotSupportedError} if provider lacks supportsRecipientRegistration * @throws {PayoutRecipientRegistrationError} if the provider call fails */ createRecipient(input: CreatePayoutRecipientInput): Promise; /** * Fetch a single recipient by ID. * @throws {PayoutRecipientNotFoundError} */ getRecipient(recipientId: ID): Promise; /** * List recipients for the tenant. */ listRecipients(page?: PageOptions, filter?: { provider?: string; currency?: string; ownerId?: string; ownerType?: string; includeArchived?: boolean; }): Promise>; /** * Update mutable fields on a recipient. * @throws {PayoutRecipientNotFoundError} * @throws {PayoutRecipientArchivedError} */ updateRecipient(recipientId: ID, patch: UpdatePayoutRecipientInput): Promise; /** * Soft-delete a recipient. Archived recipients cannot be used for new payouts. * @throws {PayoutRecipientNotFoundError} */ archiveRecipient(recipientId: ID): Promise; /** * Verify a bank account / mobile-money number without registering a recipient. * Read-only, stateless — does not persist anything. * * @throws {OperationNotSupportedError} if the provider does not implement resolveAccount */ resolveAccount(provider: string, input: Omit): Promise; /** * Dispatch a payout to a pre-registered recipient. * * Sequence: * 1. Idempotency check * 2. Load + validate wallet (exists, currency match) * 3. Load + validate recipient (exists, not archived, currency match) * 4. Locate provider adapter (must supportsPayout) * 5. Authorize: "payout.send" * 6. operations.withdraw() with _skipAuthorizer: true, finalityMode: "soft" * 7. Patch operation: recipientId, externalId placeholder * 8. provider.initPayout() — on throw: auto-fail operation, throw PayoutDispatchError * 9. Persist providerReference on operation.externalId * 10. If provider settles synchronously (status "completed"): settle operation * 11. hooks.onPayoutDispatched?.() * * @throws {WalletNotFoundError} * @throws {WalletArchivedError} * @throws {CurrencyMismatchError} * @throws {PayoutRecipientNotFoundError} * @throws {PayoutRecipientArchivedError} * @throws {PayoutRecipientMismatchError} * @throws {PayoutProviderNotSupportedError} * @throws {InsufficientFundsError} * @throws {PayoutDispatchError} */ send(input: SendPayoutInput): Promise; /** * List payout operations for the tenant, optionally filtered by recipient, * wallet, status, provider, or date range. */ listPayouts(page?: PageOptions, filter?: ListPayoutsFilter): Promise>; /** * Process an inbound payout webhook event from a provider. * Verifies the webhook, looks up the operation by externalId, and transitions * the operation status. * * @throws {WebhookVerificationError} if signature verification fails */ processWebhook(input: ProcessPayoutWebhookInput): Promise; /** * Poll-verify a payout's current status from the provider. * Transitions the operation to "completed" or "failed" if terminal. * * @throws {OperationNotFoundError} * @throws {OperationNotSupportedError} if the provider lacks verifyPayout */ verifyPayout(operationId: ID): Promise; /** * Create a scheduled payout (one-time or recurring). * @throws {PayoutRecipientNotFoundError} * @throws {PayoutRecipientArchivedError} */ createSchedule(input: CreateScheduledPayoutInput): Promise; /** * Fetch a single schedule by ID. * @throws {ScheduledPayoutNotFoundError} */ getSchedule(scheduleId: ID): Promise; /** * List schedules for the tenant with optional filters. */ listSchedules(page?: PageOptions, filter?: { walletId?: string; recipientId?: string; status?: ScheduledPayoutStatus; }): Promise>; /** * Update mutable fields on an active or paused schedule. * @throws {ScheduledPayoutNotFoundError} * @throws {ScheduledPayoutTerminalError} */ updateSchedule(scheduleId: ID, patch: UpdateScheduledPayoutInput): Promise; /** * Pause a schedule. Stops future firings until resumed. * @throws {ScheduledPayoutNotFoundError} * @throws {ScheduledPayoutTerminalError} */ pauseSchedule(scheduleId: ID): Promise; /** * Resume a paused schedule. * @throws {ScheduledPayoutNotFoundError} * @throws {ScheduleNotPausedError} */ resumeSchedule(scheduleId: ID): Promise; /** * Permanently cancel a schedule. * @throws {ScheduledPayoutNotFoundError} * @throws {ScheduledPayoutTerminalError} */ cancelSchedule(scheduleId: ID): Promise; /** * Scan for all due schedules and fire them. * Safe to call concurrently — atomic CAS prevents duplicate firings. */ executeDueSchedules(): Promise; /** * List execution history for a schedule. */ listScheduleExecutions(scheduleId: ID, page?: PageOptions): Promise>; } export interface CreatePayoutsDomainConfig { tenantId: ID; storage: StorageAdapter; providerMap: Map; operations: OperationsDomain; authorizer?: Authorizer; feePolicy?: FeePolicy; hooks?: PayHooks; logger?: PayLogger; cronParser?: (expression: string, after: Date) => Date; } export declare function createPayoutsDomain(config: CreatePayoutsDomainConfig): PayoutsDomain; //# sourceMappingURL=payouts.d.ts.map