UNPKG

better-auth-paystack

Version:

Production-ready Paystack billing plugin for Better Auth. Supports subscriptions, one-time payments, organization billing, secure webhooks and more

906 lines • 33 kB
import { AuthContext, BetterAuthPlugin, GenericEndpointContext, InferOptionSchema, Session, StrictEndpoint, User, defineErrorCodes } from "better-auth"; import { AuthMiddleware } from "better-auth/api"; import { PaystackCustomerClient, PaystackPlanClient, PaystackProductClient, PaystackSubscriptionClient, PaystackTransactionClient, PaystackWebhookEvent, TransactionChargeAuthorizationResponseData, TransactionVerifyResponseData } from "@alexasomba/paystack-node"; import { z } from "zod"; import { BetterAuthPluginDBSchema, DBFieldAttribute } from "better-auth/db"; import "better-auth/plugins/organization"; //#region src/schema.d.ts type PluginSchemaTable<TableName extends string, FieldName extends string> = Record<TableName, { fields: Record<FieldName, DBFieldAttribute>; disableMigration?: boolean; modelName?: string; }>; type TransactionsSchema = PluginSchemaTable<"paystackTransaction", "reference" | "paystackId" | "referenceId" | "userId" | "amount" | "currency" | "status" | "plan" | "product" | "metadata" | "createdAt" | "updatedAt">; type WebhookEventsSchema = PluginSchemaTable<"paystackWebhookEvent", "eventId" | "eventType" | "reference" | "payload" | "status" | "processedAt" | "createdAt" | "updatedAt">; type SubscriptionsSchema = PluginSchemaTable<"paystackSubscription", "plan" | "referenceId" | "userId" | "customerCode" | "subscriptionCode" | "transactionReference" | "planCode" | "status" | "periodStart" | "periodEnd" | "trialStart" | "trialEnd" | "cancelAtPeriodEnd" | "cancelAt" | "canceledAt" | "endedAt" | "billingInterval" | "groupId" | "seats" | "pendingPlan" | "createdAt" | "updatedAt">; type CustomerSchema = PluginSchemaTable<"paystackCustomer", "referenceType" | "referenceId" | "referenceKey" | "customerCode" | "email" | "createdAt" | "updatedAt">; type PaymentCredentialsSchema = PluginSchemaTable<"paystackPaymentCredential", "subscriptionId" | "authorizationCodeEncrypted" | "emailTokenEncrypted" | "createdAt" | "updatedAt">; type UserSchema = PluginSchemaTable<"user", never>; type OrganizationSchema = PluginSchemaTable<"organization", "email">; type ProductsSchema = PluginSchemaTable<"paystackProduct", "name" | "description" | "price" | "currency" | "quantity" | "unlimited" | "paystackId" | "slug" | "metadata" | "createdAt" | "updatedAt">; type PlansSchema = PluginSchemaTable<"paystackPlan", "name" | "description" | "amount" | "currency" | "interval" | "group" | "planCode" | "paystackId" | "metadata" | "createdAt" | "updatedAt">; type PaystackPluginSchema = SubscriptionsSchema & TransactionsSchema & CustomerSchema & PaymentCredentialsSchema & UserSchema & OrganizationSchema & ProductsSchema & PlansSchema & WebhookEventsSchema; declare const getSchema: (options: PaystackOptions) => BetterAuthPluginDBSchema; //#endregion //#region src/types.d.ts type PaystackCheckoutChannel = "card" | "bank" | "ussd" | "qr" | "mobile_money" | "bank_transfer" | "eft" | "apple_pay"; /** * Custom models for Paystack Plugin * These align with the database schema in src/schema.ts */ interface PaystackTransaction { id: string; reference: string; paystackId?: string; referenceId: string; userId: string; amount: number; currency: string; status: string; plan?: string | null; product?: string | null; metadata?: string | null; createdAt: Date; updatedAt: Date; } interface PaystackWebhookEventRecord { id: string; eventId: string; eventType: string; reference?: string | null; payload: string; status: string; processedAt?: Date | null; createdAt: Date; updatedAt: Date; } interface PaystackCustomer { id: string; referenceType: "user" | "organization"; referenceId: string; /** Internal unique key used to enforce referenceType/referenceId uniqueness. */ referenceKey: string; customerCode: string; email?: string | null; createdAt: Date; updatedAt: Date; } /** Public credential metadata. Plaintext and encrypted values are intentionally omitted. */ interface PaystackPaymentCredential { id: string; subscriptionId: string; createdAt: Date; updatedAt: Date; } interface PaystackProduct { id?: string; name: string; description?: string; price: number; currency: string; quantity?: number; unlimited?: boolean; paystackId?: string; slug?: string; metadata?: string | null; createdAt?: Date; updatedAt?: Date; } interface PaystackPlan { id?: string; name: string; description?: string; amount?: number; currency?: string; interval?: string; planCode?: string; paystackId?: string; seatAmount?: number; /** * Deprecated legacy alias for `seatAmount`. * If used, it must still be a numeric amount in the smallest currency unit. */ seatPriceId?: number | string; seatPlanCode?: string; invoiceLimit?: number; /** * Optional subscription group. A reference may hold one active or trialing * subscription per normalized group. Omitted values use the legacy default group. */ group?: string; freeTrial?: { days?: number; /** One-time tokenization amount in currency minor units; defaults to Paystack's documented minimum for NGN, GHS, ZAR, KES, and USD. */ tokenizationCharge?: number; onTrialStart?: (subscription: Subscription) => Promise<void>; onTrialEnd?: (subscription: Subscription) => Promise<void>; onTrialExpired?: (subscription: Subscription) => Promise<void>; }; limits?: Record<string, unknown>; features?: string[]; metadata?: string | null; createdAt?: Date; updatedAt?: Date; } /** * Paystack Webhook Payload structure */ type PaystackWebhookPayload = PaystackWebhookEvent; /** * Paystack SDK Result types */ type PaystackTransactionResponse = TransactionVerifyResponseData; type PaystackChargeAuthorizationResponse = TransactionChargeAuthorizationResponseData; type PaystackInitializeResult = { kind: "checkout"; url: string; reference: string; accessCode: string; redirect: true; trial?: { days: number; tokenizationCharge: { amount: number; currency: string; }; }; } | { kind: "scheduled"; status: "success"; message: string; scheduled: true; } | { kind: "prorated"; status: "success"; message: string; prorated: true; }; /** Validated invocation data; credentials and provider responses are deliberately excluded. */ interface PaystackSubscriptionOperation { actor: { id: string; }; referenceId: string; kind: "initialize" | "proration-initialize" | "proration-charge" | "local-change" | "schedule"; subscription: Subscription | null; intent: { plan: string; amount?: number; currency?: string; interval?: string; quantity?: number; groupId?: string | null; callbackURL?: string; trialStart?: Date; trialEnd?: Date; scheduleAtPeriodEnd?: boolean; cancelAtPeriodEnd?: boolean; }; } type PaystackSubscriptionOperationDecision = { kind: "proceed"; providerReference: string; context?: unknown; } | { kind: "replay"; result: PaystackInitializeResult; } | { kind: "block"; message?: string; }; type PaystackSubscriptionOperationPhase = "provider" | "persistence" | "completion"; /** Optional application-owned reservation/recovery boundary. No locks or ledger are provided by the SDK. */ interface PaystackSubscriptionOperationHooks { before: (operation: PaystackSubscriptionOperation, ctx: GenericEndpointContext) => Promise<PaystackSubscriptionOperationDecision>; after?: (operation: PaystackSubscriptionOperation & { providerReference: string; context?: unknown; result: PaystackInitializeResult; }, ctx: GenericEndpointContext) => Promise<void>; /** An error after dispatch does not establish that the provider did nothing. */ onError?: (operation: PaystackSubscriptionOperation & { providerReference: string; context?: unknown; phase: PaystackSubscriptionOperationPhase; error: unknown; }, ctx: GenericEndpointContext) => Promise<void>; } interface SubscriptionOptions { operationHooks?: PaystackSubscriptionOperationHooks; /** * Enable subscriptions */ enabled?: boolean; /** * Plans configuration */ plans: PaystackPlan[] | (() => Promise<PaystackPlan[]>); /** * Automatically sync quantity from local DB to Paystack (if seats are used) */ autoSyncQuantity?: boolean; /** * Handling of subscription cancellation * @default "at_period_end" */ cancelBehavior?: "at_period_end" | "immediately"; /** * Handlers for subscription events */ onSubscriptionComplete?: (data: { event: PaystackWebhookPayload; subscription: Subscription; plan: PaystackPlan; }, ctx: GenericEndpointContext) => Promise<void>; onSubscriptionCreated?: (data: { event: PaystackWebhookPayload; subscription: Subscription; plan: PaystackPlan; }, ctx: GenericEndpointContext) => Promise<void>; onSubscriptionUpdate?: (data: { event: PaystackWebhookPayload; subscription: Subscription; plan: PaystackPlan; }, ctx: GenericEndpointContext) => Promise<void>; onSubscriptionCancel?: (data: { event: PaystackWebhookPayload; subscription: Subscription; }, ctx: GenericEndpointContext) => Promise<void>; /** * Authorization handler for reference checks */ authorizeReference?: (data: { user: User; session: Session; referenceId: string; action: string; }, ctx: GenericEndpointContext) => Promise<boolean>; /** * Require email verification before subscription */ requireEmailVerification?: boolean; /** * Restrict checkout to specific Paystack channels for subscription flows. * Use `["card"]` to enforce card-only subscriptions. */ allowedPaymentChannels?: PaystackCheckoutChannel[]; } interface PaystackOptions<TPaystackClient extends PaystackClientLike = PaystackClientLike> { /** * Paystack Secret Key */ secretKey: string; /** * Key used to encrypt Paystack payment credentials with AES-256-GCM. * Recommended for production. `secretKey` is used as a compatibility fallback. */ credentialEncryptionKey?: string; /** * @deprecated Paystack signs webhooks with `secretKey`; this option is ignored. */ paystackWebhookSecret?: string; /** * Paystack Client Instance * If provided, will be used instead of creating a new one with secretKey */ paystackClient?: TPaystackClient; /** * Webhook configuration */ webhook?: { /** * @deprecated Paystack signs webhooks with `secretKey`; this option is ignored. */ secret?: string; /** * Whether to verify the request origin IP address * @default false */ verifyIP?: boolean; /** * List of trusted IP addresses for webhooks. * Defaults to official Paystack IPs if verifyIP is true and this is empty. */ trustedIPs?: string[]; }; /** * Subscription configuration */ subscription?: SubscriptionOptions; /** * Billing pattern * @default "native" */ billingPattern?: "native" | "local"; /** * Global event handler */ onEvent?: (event: PaystackWebhookEvent) => Promise<void>; /** * Organization billing configuration */ organization?: { enabled?: boolean; /** * Organization member roles allowed to manage billing when `subscription.authorizeReference` * is not provided. * * @default ["owner", "admin"] */ billingRoles?: string[]; getCustomerCreateParams?: (org: { id: string; name: string; email?: string | null; }, ctx: GenericEndpointContext) => Promise<Record<string, unknown>>; onCustomerCreate?: (data: { paystackCustomer: Record<string, unknown>; organization: unknown; }, ctx: GenericEndpointContext) => Promise<void>; }; /** * Products configuration */ products?: { products?: PaystackProduct[] | (() => Promise<PaystackProduct[]>); }; createCustomerOnSignUp?: boolean; onCustomerCreate?: (data: { paystackCustomer: Record<string, unknown>; user: unknown; }, ctx: GenericEndpointContext) => Promise<void>; /** * Custom database schema / model names */ schema?: InferOptionSchema<PaystackPluginSchema>; } interface PaystackSubscription { id: string; userId: string; organizationId?: string; plan: string; referenceId: string; customerCode?: string | null; subscriptionCode?: string | null; transactionReference?: string | null; planCode?: string | null; pendingPlan?: string | null; status: string; seats?: number | null; periodStart?: Date | null; periodEnd?: Date | null; cancelAtPeriodEnd: boolean; cancelAt?: Date | null; canceledAt?: Date | null; endedAt?: Date | null; billingInterval?: string | null; trialStart?: Date | null; trialEnd?: Date | null; groupId?: string | null; createdAt: Date; updatedAt: Date; } /** * @deprecated Use `PaystackSubscription`. These aliases are retained for source * compatibility while storage uses provider-local canonical field names. */ type Subscription = PaystackSubscription & { /** @deprecated Use `subscriptionCode`. */ paystackSubscriptionCode?: string | null; /** @deprecated Use `customerCode`. */ paystackCustomerCode?: string | null; /** @deprecated Use `planCode`. */ paystackPlanCode?: string | null; /** @deprecated Use `transactionReference`. */ paystackTransactionReference?: string | null; }; interface ChargeRecurringSubscriptionInput { subscriptionId: string; amount?: number; } interface ChargeRecurringSubscriptionResult { status: "success" | "failed"; data: PaystackChargeAuthorizationResponse; } interface PaystackSyncResult { status: "success"; count: number; } type AnyPaystackOptions = PaystackOptions<PaystackClientLike>; /** * Exact grouped SDK slices used by this plugin. * This deliberately matches the official SDK surface instead of a handwritten approximation. */ interface PaystackClientLike { transaction: Pick<PaystackTransactionClient, "initialize" | "verify" | "chargeAuthorization">; customer: Pick<PaystackCustomerClient, "create" | "update" | "fetch">; subscription: Pick<PaystackSubscriptionClient, "create" | "fetch" | "disable" | "enable" | "manageLink">; product: Pick<PaystackProductClient, "fetch" | "list">; plan: Pick<PaystackPlanClient, "list" | "create">; } //#endregion //#region src/billing-store.d.ts type BillingStoreAdapter = Pick<GenericEndpointContext["context"]["adapter"], "create" | "delete" | "deleteMany" | "findMany" | "findOne" | "update" | "updateMany">; //#endregion //#region src/routes.d.ts declare const paystackWebhook: <P extends string = "/webhook">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; metadata: { openapi: { operationId: string; }; scope: "server"; }; cloneRequest: true; disableBody: true; }, { received: boolean; }>; declare const initializeTransaction: <P extends string = "/initialize-transaction">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ plan: z.ZodOptional<z.ZodString>; product: z.ZodOptional<z.ZodString>; amount: z.ZodOptional<z.ZodNumber>; currency: z.ZodOptional<z.ZodString>; email: z.ZodOptional<z.ZodString>; metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; referenceId: z.ZodOptional<z.ZodString>; subscriptionId: z.ZodOptional<z.ZodString>; callbackURL: z.ZodOptional<z.ZodString>; quantity: z.ZodOptional<z.ZodNumber>; scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>; cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>; prorateAndCharge: z.ZodOptional<z.ZodBoolean>; confirmTrialTokenizationCharge: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>; use: AuthMiddleware[]; }, PaystackInitializeResult | undefined>; declare const createSubscription: <P extends string = "/create-subscription">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ plan: z.ZodOptional<z.ZodString>; product: z.ZodOptional<z.ZodString>; amount: z.ZodOptional<z.ZodNumber>; currency: z.ZodOptional<z.ZodString>; email: z.ZodOptional<z.ZodString>; metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; referenceId: z.ZodOptional<z.ZodString>; subscriptionId: z.ZodOptional<z.ZodString>; callbackURL: z.ZodOptional<z.ZodString>; quantity: z.ZodOptional<z.ZodNumber>; scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>; cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>; prorateAndCharge: z.ZodOptional<z.ZodBoolean>; confirmTrialTokenizationCharge: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>; use: AuthMiddleware[]; }, PaystackInitializeResult | undefined>; declare const upgradeSubscription: <P extends string = "/upgrade-subscription">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ plan: z.ZodOptional<z.ZodString>; product: z.ZodOptional<z.ZodString>; amount: z.ZodOptional<z.ZodNumber>; currency: z.ZodOptional<z.ZodString>; email: z.ZodOptional<z.ZodString>; metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; referenceId: z.ZodOptional<z.ZodString>; subscriptionId: z.ZodOptional<z.ZodString>; callbackURL: z.ZodOptional<z.ZodString>; quantity: z.ZodOptional<z.ZodNumber>; scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>; cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>; prorateAndCharge: z.ZodOptional<z.ZodBoolean>; confirmTrialTokenizationCharge: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>; use: AuthMiddleware[]; }, PaystackInitializeResult | undefined>; declare const cancelSubscription: <P extends string = "/cancel-subscription">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ referenceId: z.ZodOptional<z.ZodString>; subscriptionCode: z.ZodString; emailToken: z.ZodOptional<z.ZodString>; atPeriodEnd: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>; use: AuthMiddleware[]; }, { status: string; }>; declare const restoreSubscription: <P extends string = "/restore-subscription">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ referenceId: z.ZodOptional<z.ZodString>; subscriptionCode: z.ZodString; emailToken: z.ZodOptional<z.ZodString>; atPeriodEnd: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>; use: AuthMiddleware[]; }, { status: string; }>; declare const verifyTransaction: <P extends string = "/verify-transaction">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ reference: z.ZodString; }, z.core.$strip>; use: AuthMiddleware[]; }, { status: string; reference: string; data: { id: number; domain: string; status: string; reference: string; receipt_number: string | null; amount: number; message: string | null; gateway_response: string; channel: string; currency: string; ip_address: string | null; metadata: (string | Record<string, never> | number) | null; log: { start_time: number; time_spent: number; attempts: number; errors: number; success: boolean; mobile: boolean; input: unknown[]; history: { type: string; message: string; time: number; }[]; } | null; fees: number | null; fees_split: unknown; authorization: { authorization_code?: string; bin?: string | null; last4?: string; exp_month?: string; exp_year?: string; channel?: string; card_type?: string; bank?: string; country_code?: string; brand?: string; reusable?: boolean; signature?: string; account_name?: string | null; receiver_bank_account_number?: string | null; receiver_bank?: string | null; }; customer: { id: number; first_name: string | null; last_name: string | null; email: string; customer_code: string; phone: string | null; metadata: Record<string, never> | null; risk_action: string; international_format_phone?: string | null; }; plan: (string | Record<string, never>) | null; split: Record<string, never> | null; order_id: unknown; paidAt: string | null; createdAt: string; requested_amount: number; pos_transaction_data: unknown; source: unknown; fees_breakdown: unknown; connect: unknown; transaction_date: string; plan_object: { id?: number; name?: string; plan_code?: string; description?: unknown; amount?: number; interval?: string; send_invoices?: boolean; send_sms?: boolean; currency?: string; }; subaccount: Record<string, never> | null; }; }>; declare const listSubscriptions: <P extends string = "/list-subscriptions">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "GET"; query: z.ZodObject<{ referenceId: z.ZodOptional<z.ZodString>; }, z.core.$strip>; use: AuthMiddleware[]; }, { subscriptions: Subscription[]; }>; declare const listTransactions: <P extends string = "/list-transactions">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "GET"; query: z.ZodObject<{ referenceId: z.ZodOptional<z.ZodString>; }, z.core.$strip>; use: AuthMiddleware[]; }, { transactions: PaystackTransaction[]; }>; declare const disablePaystackSubscription: <P extends string = "/disable-subscription">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ referenceId: z.ZodOptional<z.ZodString>; subscriptionCode: z.ZodString; emailToken: z.ZodOptional<z.ZodString>; atPeriodEnd: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>; use: AuthMiddleware[]; }, { status: string; }>; declare const enablePaystackSubscription: <P extends string = "/enable-subscription">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "POST"; body: z.ZodObject<{ referenceId: z.ZodOptional<z.ZodString>; subscriptionCode: z.ZodString; emailToken: z.ZodOptional<z.ZodString>; atPeriodEnd: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>; use: AuthMiddleware[]; }, { status: string; }>; declare const getSubscriptionManageLink: <P extends string = "/subscription-manage-link">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "GET"; query: z.ZodObject<{ subscriptionCode: z.ZodString; }, z.core.$strip>; use: AuthMiddleware[]; }, { link: string | null; }>; declare const listProducts: <P extends string = "/list-products">(_options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "GET"; metadata: { openapi: { operationId: string; }; }; }, { products: PaystackProduct[]; }>; declare const listPlans: <P extends string = "/list-plans">(_options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "GET"; metadata: { scope: "server"; }; use: AuthMiddleware[]; }, { plans: PaystackPlan[]; }>; declare const getConfig: <P extends string = "/get-config">(options: AnyPaystackOptions, path?: P) => StrictEndpoint<P, { method: "GET"; metadata: { openapi: { operationId: string; }; }; }, { plans: PaystackPlan[]; products: PaystackProduct[]; }>; //#endregion //#region src/version.d.ts declare const PACKAGE_VERSION = "3.5.6"; //#endregion //#region src/metadata.d.ts type PaystackMetadata = Record<string, unknown>; interface CheckoutMetadataInput { referenceId: string; userId: string; plan?: string; groupId?: string | null; product?: string; extra?: PaystackMetadata; trial: { isTrial: boolean; requested: boolean; granted: boolean; deniedReason?: "already_used"; endsAt?: Date; }; } interface ProrationMetadataInput { subscriptionId: string; referenceId: string; newPlan: string; oldPlan: string; newSeatCount: number; remainingDays: number; } interface RenewalMetadataInput { subscriptionId: string; referenceId: string; } declare function parsePaystackMetadata(value: unknown): PaystackMetadata; declare function stringifyPaystackMetadata(value: unknown): string | undefined; declare function hasPaystackMetadata(value: unknown): boolean; declare function getMetadataString(metadata: PaystackMetadata, key: string): string | undefined; declare function getMetadataNumber(metadata: PaystackMetadata, key: string): number | undefined; declare function getMetadataBoolean(metadata: PaystackMetadata, key: string): boolean; declare function createCheckoutMetadata(input: CheckoutMetadataInput): PaystackMetadata; declare function createProrationMetadata(input: ProrationMetadataInput): PaystackMetadata; declare function createRenewalMetadata(input: RenewalMetadataInput): PaystackMetadata; //#endregion //#region src/operations.d.ts declare function syncPaystackProducts(ctx: GenericEndpointContext, options: AnyPaystackOptions): Promise<PaystackSyncResult>; declare function syncPaystackPlans(ctx: GenericEndpointContext, options: AnyPaystackOptions): Promise<PaystackSyncResult>; declare function chargeSubscriptionRenewal(ctx: GenericEndpointContext, options: AnyPaystackOptions, input: ChargeRecurringSubscriptionInput): Promise<ChargeRecurringSubscriptionResult>; //#endregion //#region src/limits.d.ts declare function getOrganizationEntitlements(ctx: GenericEndpointContext, organizationId: string, options: AnyPaystackOptions): Promise<{ limits: Record<string, number>; features: string[]; }>; //#endregion //#region src/reconciliation.d.ts type PaystackReconciliationSource = "webhook" | "queue" | "admin" | "server" | "browser"; interface ReconcilePaystackTransactionInput { reference: string; source?: PaystackReconciliationSource; referenceId?: string; actor?: { user: User; session: Session; }; throwOnError?: boolean; } interface PaystackReconciliationError { code: string; message: string; status?: number; } interface PaystackReconciliationSummary { transaction: { found: boolean; updated: boolean; referenceId?: string; previousStatus?: string; status?: string; }; subscription: { found: boolean; updated: boolean; id?: string; status?: string; prorationApplied: boolean; }; customer: { saved: boolean; referenceId?: string; customerCode?: string; model?: "user" | "organization"; }; product: { synced: boolean; name?: string; }; } interface ReconcilePaystackTransactionSuccess extends PaystackReconciliationSummary { ok: true; source: PaystackReconciliationSource; status: string; reference: string; data: PaystackTransactionResponse; error?: undefined; } interface ReconcilePaystackTransactionFailure extends PaystackReconciliationSummary { ok: false; source: PaystackReconciliationSource; status: string; reference: string; data: PaystackTransactionResponse | null; error: PaystackReconciliationError; } type ReconcilePaystackTransactionResult = ReconcilePaystackTransactionSuccess | ReconcilePaystackTransactionFailure; declare function reconcilePaystackTransaction(ctx: GenericEndpointContext, options: AnyPaystackOptions, input: ReconcilePaystackTransactionInput): Promise<ReconcilePaystackTransactionResult>; //#endregion //#region src/migrations.d.ts interface PaystackSchemaMigrationFailure { kind: "subscription" | "customer" | "credential"; id: string; message: string; } interface PaystackSchemaMigrationReport { status: "complete" | "partial"; subscriptions: { migrated: number; skipped: number; failed: number; }; customers: { migrated: number; skipped: number; failed: number; }; credentials: { migrated: number; skipped: number; failed: number; }; skipped: number; failures: PaystackSchemaMigrationFailure[]; } declare function migratePaystackSubscriptionSchema(ctx: GenericEndpointContext, options: AnyPaystackOptions): Promise<PaystackSchemaMigrationReport>; //#endregion //#region src/index.d.ts declare module "better-auth" { interface BetterAuthPluginRegistry<AuthOptions, Options> { paystack: { creator: typeof paystack; }; } } declare const INTERNAL_ERROR_CODES: ReturnType<typeof defineErrorCodes>; /** * Must stay a `type` rather than an `interface`. * * `BetterAuthPlugin["endpoints"]` is `{ [key: string]: Endpoint }`, and only object literal * *type aliases* get TypeScript's implicit index signature — interfaces do not. Declaring * this as `interface ... extends Record<string, Endpoint>` satisfies that constraint but * gives the plugin a real string index signature. Better Auth merges every plugin's * endpoints into `auth.api`, so that signature spreads across the whole API surface and, * under `noUncheckedIndexedAccess`, makes *every* `auth.api.*` call possibly `undefined`. */ type PaystackPluginEndpoints = { initializeTransaction: ReturnType<typeof initializeTransaction>; verifyTransaction: ReturnType<typeof verifyTransaction>; listSubscriptions: ReturnType<typeof listSubscriptions>; paystackWebhook: ReturnType<typeof paystackWebhook>; listTransactions: ReturnType<typeof listTransactions>; getConfig: ReturnType<typeof getConfig>; disableSubscription: ReturnType<typeof disablePaystackSubscription>; enableSubscription: ReturnType<typeof enablePaystackSubscription>; getSubscriptionManageLink: ReturnType<typeof getSubscriptionManageLink>; subscriptionManageLink: ReturnType<typeof getSubscriptionManageLink>; createSubscription: ReturnType<typeof createSubscription>; upgradeSubscription: ReturnType<typeof upgradeSubscription>; cancelSubscription: ReturnType<typeof cancelSubscription>; restoreSubscription: ReturnType<typeof restoreSubscription>; listProducts: ReturnType<typeof listProducts>; listPlans: ReturnType<typeof listPlans>; }; type PaystackHookHandler = (...args: unknown[]) => unknown; interface PaystackPluginInitResult { options: { databaseHooks: Record<string, unknown> & { organization: { create: { after: PaystackHookHandler; }; }; }; }; } type PaystackPluginInitContext = Pick<AuthContext, "hasPlugin" | "logger"> & { adapter: BillingStoreAdapter; }; type PaystackPluginInit = ((ctx: PaystackPluginInitContext) => PaystackPluginInitResult) & NonNullable<BetterAuthPlugin["init"]>; type PaystackPluginInstance<O extends AnyPaystackOptions> = Omit<BetterAuthPlugin, "id" | "version" | "endpoints" | "schema" | "init" | "$ERROR_CODES" | "options"> & { id: "paystack"; version: typeof PACKAGE_VERSION; endpoints: PaystackPluginEndpoints; schema: ReturnType<typeof getSchema>; init: PaystackPluginInit; $ERROR_CODES: typeof INTERNAL_ERROR_CODES; options: NoInfer<O>; }; declare const createPaystackPlugin: <TPaystackClient extends PaystackClientLike = PaystackClientLike, O extends PaystackOptions<TPaystackClient> = PaystackOptions<TPaystackClient>>(options: O) => PaystackPluginInstance<O>; declare const paystack: typeof createPaystackPlugin; type PaystackPlugin<TPaystackClient extends PaystackClientLike = PaystackClientLike, O extends PaystackOptions<TPaystackClient> = PaystackOptions<TPaystackClient>> = ReturnType<typeof paystack<TPaystackClient, O>>; //#endregion export { PaystackClientLike as A, PaystackSubscriptionOperationHooks as B, getMetadataNumber as C, stringifyPaystackMetadata as D, parsePaystackMetadata as E, PaystackPlan as F, PaystackWebhookEventRecord as G, PaystackSyncResult as H, PaystackProduct as I, Subscription as K, PaystackSubscription as L, PaystackInitializeResult as M, PaystackOptions as N, AnyPaystackOptions as O, PaystackPaymentCredential as P, PaystackSubscriptionOperation as R, getMetadataBoolean as S, hasPaystackMetadata as T, PaystackTransaction as U, PaystackSubscriptionOperationPhase as V, PaystackTransactionResponse as W, syncPaystackProducts as _, migratePaystackSubscriptionSchema as a, createProrationMetadata as b, PaystackReconciliationSummary as c, ReconcilePaystackTransactionResult as d, ReconcilePaystackTransactionSuccess as f, syncPaystackPlans as g, chargeSubscriptionRenewal as h, PaystackSchemaMigrationReport as i, PaystackCustomer as j, ChargeRecurringSubscriptionResult as k, ReconcilePaystackTransactionFailure as l, getOrganizationEntitlements as m, paystack as n, PaystackReconciliationError as o, reconcilePaystackTransaction as p, SubscriptionOptions as q, PaystackSchemaMigrationFailure as r, PaystackReconciliationSource as s, PaystackPlugin as t, ReconcilePaystackTransactionInput as u, PaystackMetadata as v, getMetadataString as w, createRenewalMetadata as x, createCheckoutMetadata as y, PaystackSubscriptionOperationDecision as z }; //# sourceMappingURL=index-CQ0SNlBx.d.mts.map