@alexasomba/better-auth-paystack 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/paystack-sdk.ts","../src/utils.ts","../src/middleware.ts","../src/limits.ts","../src/routes.ts","../src/schema.ts","../src/operations.ts","../src/index.ts"],"sourcesContent":["import { APIError } from \"better-auth/api\";\nimport { PaystackResponse } from \"@alexasomba/paystack-node\";\nimport type { PaystackClientLike } from \"./types\";\n\n/**\n * Interface for checking if a result is a PaystackResponse from the SDK v1.9.1+\n */\nfunction IsPaystackResponse(value: unknown): value is PaystackResponse<unknown> {\n return value instanceof PaystackResponse;\n}\n\n/**\n * Unwraps a Paystack SDK result, extracting the data or throwing an APIError if the request failed.\n * Leverages the native .unwrap() method in SDK v1.9.1+ if available.\n */\nexport function unwrapSdkResult<T = unknown>(result: unknown): T {\n if (IsPaystackResponse(result)) {\n try {\n return result.unwrap() as T;\n } catch (e: unknown) {\n throw new APIError(\"BAD_REQUEST\", {\n message: (e as Error)?.message ?? \"Paystack API error\",\n });\n }\n }\n\n // Fallback for custom or legacy structures (e.g. from mocks in tests)\n let current = result;\n\n // Handle nested { data: { data: ... } } or { status: true, data: ... }\n while (current !== null && current !== undefined && typeof current === \"object\") {\n const body = current as Record<string, unknown>;\n\n // Check for Paystack Error shape\n if (body.status === false) {\n throw new APIError(\"BAD_REQUEST\", {\n message: (body.message as string | undefined) ?? \"Paystack API error\",\n });\n }\n\n // Stop if we have found the actual transaction/subscription payload properties\n if (\"authorization_url\" in body || \"reference\" in body || \"customer_code\" in body) {\n break;\n }\n\n // If there's a data property, unwrap it and continue checking\n if (\n \"data\" in body &&\n body.data !== undefined &&\n body.data !== null &&\n typeof body.data === \"object\"\n ) {\n current = body.data;\n continue;\n }\n break;\n }\n\n return current as T;\n}\n\n/**\n * Returns the operations object from a Paystack client.\n * For v1.9.1+, the client itself uses the grouped structure.\n */\nexport function getPaystackOps(client?: PaystackClientLike): PaystackClientLike | undefined {\n return client;\n}\n","import type { GenericEndpointContext } from \"better-auth\";\n\nimport type {\n AnyPaystackOptions,\n PaystackClientLike,\n PaystackPlan,\n PaystackProduct,\n Subscription,\n PaystackProductResponse,\n} from \"./types\";\nimport { unwrapSdkResult } from \"./paystack-sdk\";\n\nexport async function getPlans(\n subscriptionOptions: AnyPaystackOptions[\"subscription\"],\n): Promise<PaystackPlan[]> {\n if (subscriptionOptions?.enabled === true) {\n return typeof subscriptionOptions.plans === \"function\"\n ? subscriptionOptions.plans()\n : subscriptionOptions.plans;\n }\n throw new Error(\"Subscriptions are not enabled in the Paystack options.\");\n}\n\nexport const getPlan: (\n options: AnyPaystackOptions,\n planId: string,\n) => Promise<PaystackPlan | null> = async (options, planId) => {\n if (options.subscription?.enabled === true) {\n const plans = await getPlans(options.subscription);\n return plans.find((plan) => plan.name === planId) ?? null;\n }\n return null;\n};\n\nexport async function getPlanByName(\n options: AnyPaystackOptions,\n name: string,\n): Promise<PaystackPlan | null> {\n if (typeof name !== \"string\" || name.trim() === \"\") {\n return null;\n }\n if (options.subscription?.enabled === true) {\n const plans = await getPlans(options.subscription);\n const normalizedName = name.toLowerCase();\n return (\n plans.find(\n (plan) => typeof plan.name === \"string\" && plan.name.toLowerCase() === normalizedName,\n ) ?? null\n );\n }\n return null;\n}\n\nexport async function getPlanByPriceId(\n options: AnyPaystackOptions,\n priceId: string,\n): Promise<PaystackPlan | null> {\n if (options.subscription?.enabled === true) {\n const plans = await getPlans(options.subscription);\n return plans.find((plan) => plan.name === priceId) ?? null;\n }\n return null;\n}\n\nexport async function getProducts(\n productOptions: AnyPaystackOptions[\"products\"],\n): Promise<PaystackProduct[]> {\n if (productOptions?.products) {\n return typeof productOptions.products === \"function\"\n ? await productOptions.products()\n : productOptions.products;\n }\n return [];\n}\n\nexport async function getProductByName(\n options: AnyPaystackOptions,\n name: string,\n): Promise<PaystackProduct | null> {\n return await getProducts(options.products).then((products) =>\n products !== undefined && products !== null\n ? (products.find((product) => product.name.toLowerCase() === name.toLowerCase()) ?? null)\n : null,\n );\n}\n\nexport function getNextPeriodEnd(startDate: Date, interval: string): Date {\n const date = new Date(startDate);\n switch (interval) {\n case \"daily\":\n date.setDate(date.getDate() + 1);\n break;\n case \"weekly\":\n date.setDate(date.getDate() + 7);\n break;\n case \"monthly\":\n date.setMonth(date.getMonth() + 1);\n break;\n case \"quarterly\":\n date.setMonth(date.getMonth() + 3);\n break;\n case \"biannually\":\n date.setMonth(date.getMonth() + 6);\n break;\n case \"annually\":\n date.setFullYear(date.getFullYear() + 1);\n break;\n default:\n // Default to monthly if unknown\n date.setMonth(date.getMonth() + 1);\n }\n return date;\n}\n\n/**\n * Validates if the amount meets Paystack's minimum transaction requirements.\n * Amounts should be in the smallest currency unit (e.g., kobo, cents).\n */\nexport function validateMinAmount(amount: number, currency: string): boolean {\n const minAmounts: Record<string, number> = {\n NGN: 5000, // 50.00\n GHS: 10, // 0.10\n ZAR: 100, // 1.00\n KES: 300, // 3.00\n USD: 200, // 2.00\n XOF: 100, // 1.00\n };\n const min = minAmounts[currency.toUpperCase()];\n return min !== undefined ? amount >= min : true;\n}\n\nexport async function syncProductQuantityFromPaystack(\n ctx: GenericEndpointContext,\n productName: string,\n paystackClient: PaystackClientLike,\n): Promise<void> {\n // Find the local product record (by name or slug)\n let localProduct = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"name\", value: productName }],\n });\n\n localProduct ??= await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"slug\", value: productName.toLowerCase().replace(/\\s+/g, \"-\") }],\n });\n\n if (\n localProduct?.paystackId === undefined ||\n localProduct.paystackId === null ||\n localProduct.paystackId === \"\"\n ) {\n // No local record with a Paystack ID - fall back to local decrement\n if (\n localProduct?.id !== undefined &&\n localProduct.unlimited !== true &&\n typeof localProduct.quantity === \"number\" &&\n localProduct.quantity > 0\n ) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: { quantity: localProduct.quantity - 1, updatedAt: new Date() },\n where: [{ field: \"id\", value: localProduct.id }],\n });\n }\n return;\n }\n\n // Fetch the latest quantity from Paystack\n try {\n const raw = await paystackClient.product?.fetch(localProduct.paystackId);\n const sdkRes = unwrapSdkResult<PaystackProductResponse>(raw);\n const remoteQuantity = sdkRes?.quantity;\n\n if (remoteQuantity !== undefined && localProduct.id !== undefined) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: { quantity: remoteQuantity, updatedAt: new Date() },\n where: [{ field: \"id\", value: localProduct.id }],\n });\n }\n } catch {\n // If API call fails, fall back to local decrement\n if (\n localProduct?.id !== undefined &&\n localProduct.unlimited !== true &&\n typeof localProduct.quantity === \"number\" &&\n localProduct.quantity > 0\n ) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: { quantity: localProduct.quantity - 1, updatedAt: new Date() },\n where: [{ field: \"id\", value: localProduct.id }],\n });\n }\n }\n}\n\nexport async function decrementProductQuantity(\n ctx: GenericEndpointContext,\n productName: string,\n): Promise<void> {\n let product = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"name\", value: productName }],\n });\n\n product ??= await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"slug\", value: productName.toLowerCase().replace(/\\s+/g, \"-\") }],\n });\n\n if (product !== undefined && product !== null) {\n if (\n product.unlimited !== true &&\n typeof product.quantity === \"number\" &&\n product.quantity > 0 &&\n product.id !== undefined\n ) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: {\n quantity: product.quantity - 1,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: product.id }],\n });\n }\n }\n}\n\nexport async function syncSubscriptionSeats(\n ctx: GenericEndpointContext,\n organizationId: string,\n options: AnyPaystackOptions,\n): Promise<void> {\n if (options.subscription?.enabled !== true) return;\n\n const adapter = ctx.context.adapter;\n const subscription = await adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: organizationId }],\n });\n\n if (\n subscription?.paystackSubscriptionCode === undefined ||\n subscription.paystackSubscriptionCode === null ||\n subscription.paystackSubscriptionCode === \"\"\n )\n return;\n if (subscription === null || subscription === undefined) return;\n const plan = await getPlanByName(options, subscription.plan);\n if (plan === null || plan === undefined) return;\n if (plan.seatAmount === undefined) return;\n\n const members = await adapter.findMany({\n model: \"member\",\n where: [{ field: \"organizationId\", value: organizationId }],\n });\n\n const quantity = members.length;\n let totalAmount = plan.amount ?? 0;\n\n if (\n plan.seatAmount !== undefined &&\n plan.seatAmount !== null &&\n typeof plan.seatAmount === \"number\"\n ) {\n totalAmount += quantity * plan.seatAmount;\n }\n\n try {\n const client = options.paystackClient;\n if (client === undefined || client === null) return;\n\n // Paystack subscription update doesn't natively support quantity in the same way as Stripe\n // but we can update the amount or the plan.\n const raw = await client.subscription?.update(subscription.paystackSubscriptionCode, {\n body: { amount: totalAmount },\n });\n unwrapSdkResult(raw);\n\n // Update local DB to reflect current seat count\n await adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: subscription.id }],\n update: {\n seats: quantity,\n updatedAt: new Date(),\n },\n });\n } catch (e: unknown) {\n const log = ctx.context.logger;\n if (log !== undefined && log !== null) {\n log.error(\"Failed to sync subscription seats with Paystack\", e);\n }\n }\n}\n","import { createAuthMiddleware, type AuthMiddleware } from \"@better-auth/core/api\";\nimport { logger } from \"better-auth\";\nimport { APIError } from \"better-auth/api\";\n\nimport type { PaystackOptions, Session, User } from \"./types\";\n\nexport const referenceMiddleware = (\n options: PaystackOptions,\n action:\n | \"initialize-transaction\"\n | \"verify-transaction\"\n | \"list-subscriptions\"\n | \"list-transactions\"\n | \"disable-subscription\"\n | \"enable-subscription\"\n | \"get-subscription-manage-link\",\n): AuthMiddleware =>\n createAuthMiddleware(async (ctx) => {\n const session = ctx.context.session as {\n user: User;\n session: Session;\n } | null;\n\n if (session === null || session === undefined) {\n throw new APIError(\"UNAUTHORIZED\");\n }\n const body = (ctx.body ?? {}) as Record<string, unknown>;\n const query = (ctx.query ?? {}) as Record<string, unknown>;\n const referenceId =\n (body.referenceId as string | undefined) ??\n (query.referenceId as string | undefined) ??\n session.user.id;\n\n const subscriptionOptions = options.subscription;\n\n if (referenceId === session.user.id) {\n return {\n referenceId,\n };\n }\n\n // 1. Try custom authorization first if provided\n if (\n subscriptionOptions?.enabled === true &&\n \"authorizeReference\" in subscriptionOptions &&\n typeof subscriptionOptions.authorizeReference === \"function\"\n ) {\n const authorized = await subscriptionOptions.authorizeReference(\n {\n user: session.user,\n session: session.session,\n referenceId,\n action,\n },\n ctx,\n );\n if (authorized === true) {\n return {\n referenceId,\n };\n }\n // If explicit authorizeReference returns false, do we fail immediately?\n // Usually yes, but maybe we fallback to org check?\n // Let's assume authorizeReference overrides everything.\n throw new APIError(\"UNAUTHORIZED\");\n }\n\n // 2. Fallback: Organization Check\n if (options.organization?.enabled === true) {\n // Check if referenceId indicates an organization the user is a member of\n const member = await ctx.context.adapter.findOne({\n model: \"member\",\n where: [\n { field: \"userId\", value: session.user.id },\n { field: \"organizationId\", value: referenceId },\n ],\n });\n\n if (member !== null && member !== undefined) {\n logger.debug(\"DEBUG MIDDLEWARE MEMBER FOUND:\", member);\n // User is a member of the organization.\n // We could check roles here, but for now allow any member.\n return {\n referenceId,\n };\n }\n }\n\n logger.error(\n `Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your paystack plugin config and matches no organization membership.`,\n );\n throw new APIError(\"BAD_REQUEST\", {\n message:\n \"Passing referenceId isn't allowed without subscription.authorizeReference or valid organization membership.\",\n });\n });\n","import type { GenericEndpointContext } from \"better-auth\";\nimport { APIError } from \"better-auth/api\";\n\nimport type { Subscription } from \"./types\";\n\nexport const getOrganizationSubscription = async (\n ctx: GenericEndpointContext,\n organizationId: string,\n): Promise<Subscription | null> => {\n const subscription = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: organizationId }],\n });\n return subscription;\n};\n\nexport const checkSeatLimit = async (\n ctx: GenericEndpointContext,\n organizationId: string,\n seatsToAdd = 1,\n): Promise<boolean> => {\n const subscription = await getOrganizationSubscription(ctx, organizationId);\n\n if (subscription?.seats === null) {\n return true; // No explicit seat limit found\n }\n\n const members = await ctx.context.adapter.findMany({\n model: \"member\",\n where: [{ field: \"organizationId\", value: organizationId }],\n });\n\n if (!subscription) {\n return true; // No subscription, no specific limit enforcement here (or maybe allow depending on config)\n }\n\n if (members.length + seatsToAdd > subscription.seats) {\n throw new APIError(\"FORBIDDEN\", {\n message: `Organization member limit reached. Used: ${members.length}, Max: ${subscription.seats}`,\n });\n }\n\n return true;\n};\n\nexport const checkTeamLimit = async (\n ctx: GenericEndpointContext,\n organizationId: string,\n maxTeams: number,\n): Promise<boolean> => {\n const teams = await ctx.context.adapter.findMany({\n model: \"team\",\n where: [{ field: \"organizationId\", value: organizationId }],\n });\n\n if (teams.length >= maxTeams) {\n throw new APIError(\"FORBIDDEN\", {\n message: `Organization team limit reached. Max teams: ${maxTeams}`,\n });\n }\n\n return true;\n};\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { defineErrorCodes } from \"@better-auth/core/utils/error-codes\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { APIError, getSessionFromCtx, originCheck, sessionMiddleware } from \"better-auth/api\";\n/* oxlint-disable no-restricted-imports */\nimport { z } from \"zod\";\nimport type { components } from \"@alexasomba/paystack-node\";\nimport type {\n GenericEndpointContext,\n MiddlewareInputContext,\n MiddlewareOptions,\n RawError,\n StrictEndpoint,\n} from \"better-auth\";\n\nimport type {\n InputPaystackProduct,\n PaystackTransaction,\n AnyPaystackOptions,\n PaystackProduct,\n Subscription,\n Member,\n PaystackOrganization,\n PaystackPlan,\n PaystackWebhookPayload,\n PaystackTransactionResponse,\n User,\n PaystackUser,\n} from \"./types\";\nimport {\n syncProductQuantityFromPaystack,\n getPlanByName,\n getPlans,\n getProductByName,\n getProducts,\n validateMinAmount,\n getNextPeriodEnd,\n} from \"./utils\";\nimport { referenceMiddleware } from \"./middleware\";\nimport { getPaystackOps, unwrapSdkResult } from \"./paystack-sdk\";\nimport { getOrganizationSubscription } from \"./limits\";\n\nconst PAYSTACK_ERROR_CODES: {\n SUBSCRIPTION_NOT_FOUND: RawError<\"SUBSCRIPTION_NOT_FOUND\">;\n SUBSCRIPTION_PLAN_NOT_FOUND: RawError<\"SUBSCRIPTION_PLAN_NOT_FOUND\">;\n UNABLE_TO_CREATE_CUSTOMER: RawError<\"UNABLE_TO_CREATE_CUSTOMER\">;\n FAILED_TO_INITIALIZE_TRANSACTION: RawError<\"FAILED_TO_INITIALIZE_TRANSACTION\">;\n FAILED_TO_VERIFY_TRANSACTION: RawError<\"FAILED_TO_VERIFY_TRANSACTION\">;\n FAILED_TO_DISABLE_SUBSCRIPTION: RawError<\"FAILED_TO_DISABLE_SUBSCRIPTION\">;\n FAILED_TO_ENABLE_SUBSCRIPTION: RawError<\"FAILED_TO_ENABLE_SUBSCRIPTION\">;\n EMAIL_VERIFICATION_REQUIRED: RawError<\"EMAIL_VERIFICATION_REQUIRED\">;\n} = defineErrorCodes({\n SUBSCRIPTION_NOT_FOUND: \"Subscription not found\",\n SUBSCRIPTION_PLAN_NOT_FOUND: \"Subscription plan not found\",\n UNABLE_TO_CREATE_CUSTOMER: \"Unable to create customer\",\n FAILED_TO_INITIALIZE_TRANSACTION: \"Failed to initialize transaction\",\n FAILED_TO_VERIFY_TRANSACTION: \"Failed to verify transaction\",\n FAILED_TO_DISABLE_SUBSCRIPTION: \"Failed to disable subscription\",\n FAILED_TO_ENABLE_SUBSCRIPTION: \"Failed to enable subscription\",\n EMAIL_VERIFICATION_REQUIRED: \"Email verification is required before you can subscribe to a plan\",\n});\n\nasync function hmacSha512Hex(secret: string, message: string): Promise<string> {\n const encoder = new TextEncoder();\n const keyData = encoder.encode(secret);\n const msgData = encoder.encode(message);\n\n const crypto = globalThis.crypto;\n if (crypto !== undefined && crypto !== null && \"subtle\" in crypto) {\n const subtle = crypto.subtle;\n const key = await subtle.importKey(\"raw\", keyData, { name: \"HMAC\", hash: \"SHA-512\" }, false, [\n \"sign\",\n ]);\n const signature = await subtle.sign(\"HMAC\", key, msgData);\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n }\n\n const { createHmac } = await import(\"node:crypto\");\n return createHmac(\"sha512\", secret).update(message).digest(\"hex\");\n}\n\nexport const paystackWebhook = <P extends string = \"/webhook\">(\n options: AnyPaystackOptions,\n path: P = \"/webhook\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n metadata: {\n openapi: {\n operationId: string;\n };\n scope: \"server\";\n };\n cloneRequest: true;\n disableBody: true;\n },\n {\n received: boolean;\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n metadata: {\n ...HIDE_METADATA,\n openapi: {\n operationId: \"handlePaystackWebhook\",\n },\n },\n cloneRequest: true,\n disableBody: true,\n },\n async (ctx) => {\n const request =\n (ctx as unknown as { requestClone?: Request }).requestClone ??\n (ctx as { request: Request }).request;\n if (request === undefined || request === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Request object is missing from context\",\n });\n }\n const payload = await request.text();\n const headers =\n (ctx as GenericEndpointContext & { headers?: Headers }).headers ??\n (ctx.request as unknown as { headers: Headers })?.headers;\n const signature = headers?.get(\"x-paystack-signature\") as string | null | undefined;\n\n if (options.webhook?.verifyIP === true) {\n const trustedIPs = options.webhook.trustedIPs ?? [\n \"52.31.139.75\",\n \"52.49.173.169\",\n \"52.214.14.220\",\n ];\n const clientIP =\n headers?.get(\"x-forwarded-for\")?.split(\",\")[0]?.trim() ??\n headers?.get(\"x-real-ip\") ??\n (ctx.request as unknown as { ip?: string }).ip;\n\n if (\n clientIP !== undefined &&\n clientIP !== null &&\n trustedIPs.includes(clientIP) === false\n ) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: `Forbidden IP: ${clientIP}`,\n status: 401,\n });\n }\n }\n\n if (signature === undefined || signature === null || signature === \"\") {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Missing x-paystack-signature header\",\n status: 401,\n });\n }\n\n const webhookSecret = options.webhook?.secret ?? options.secretKey;\n const expected = await hmacSha512Hex(webhookSecret, payload);\n if (expected !== signature) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid Paystack webhook signature\",\n status: 401,\n });\n }\n\n const event = JSON.parse(payload) as PaystackWebhookPayload;\n const eventName = event.event;\n const data = event.data;\n\n // Core Transaction Status Sync (Applies to both one-time and recurring)\n if (eventName === \"charge.success\") {\n const reference = (data as { reference?: string | null })?.reference;\n const paystackIdRaw = (data as { id?: number | string | null })?.id;\n const paystackId =\n paystackIdRaw !== undefined && paystackIdRaw !== null ? String(paystackIdRaw) : undefined;\n\n if (reference !== undefined && reference !== null && reference !== \"\") {\n try {\n await ctx.context.adapter.update({\n model: \"paystackTransaction\",\n update: {\n status: \"success\",\n paystackId,\n updatedAt: new Date(),\n },\n where: [{ field: \"reference\", value: reference }],\n });\n } catch (e) {\n ctx.context.logger.warn(\"Failed to update transaction status for charge.success\", e);\n }\n\n // Sync product quantity from Paystack after successful charge\n try {\n const transaction = await ctx.context.adapter.findOne<PaystackTransaction>({\n model: \"paystackTransaction\",\n where: [{ field: \"reference\", value: reference }],\n });\n if (\n transaction !== undefined &&\n transaction !== null &&\n transaction.product !== undefined &&\n transaction.product !== null &&\n transaction.product !== \"\"\n ) {\n if (options.paystackClient !== undefined && options.paystackClient !== null) {\n await syncProductQuantityFromPaystack(\n ctx,\n transaction.product,\n options.paystackClient,\n );\n }\n }\n } catch (e) {\n ctx.context.logger.warn(\"Failed to sync product quantity\", e);\n }\n }\n }\n\n if ((eventName as string) === \"charge.failure\") {\n const reference = (data as { reference?: string })?.reference;\n if (reference !== undefined && reference !== null && reference !== \"\") {\n try {\n await ctx.context.adapter.update({\n model: \"paystackTransaction\",\n update: {\n status: \"failed\",\n updatedAt: new Date(),\n },\n where: [{ field: \"reference\", value: reference }],\n });\n } catch (e) {\n ctx.context.logger.warn(\"Failed to update transaction status for charge.failure\", e);\n }\n }\n }\n\n // Best-effort local state sync for subscription lifecycle.\n if (options.subscription?.enabled === true) {\n try {\n if (eventName === \"subscription.create\") {\n const subscriptionData =\n data as unknown as components[\"schemas\"][\"SubscriptionListResponseArray\"];\n const subscriptionCode = subscriptionData.subscription_code ?? \"\";\n const customerCode = (\n subscriptionData.customer as { customer_code?: string | null } | undefined\n )?.customer_code;\n const planCode = (subscriptionData.plan as { plan_code?: string | null } | undefined)\n ?.plan_code;\n\n const metadataVal = (subscriptionData as unknown as { metadata?: unknown }).metadata;\n let metadata: unknown = metadataVal;\n if (typeof metadata === \"string\") {\n try {\n metadata = JSON.parse(metadata);\n } catch {\n // ignore\n }\n }\n\n const metadataObj =\n metadata !== undefined && metadata !== null && typeof metadata === \"object\"\n ? (metadata as Record<string, unknown>)\n : {};\n const referenceIdFromMetadata =\n typeof metadataObj.referenceId === \"string\" ? metadataObj.referenceId : undefined;\n let planNameFromMetadata =\n typeof metadataObj.plan === \"string\" ? metadataObj.plan : undefined;\n if (typeof planNameFromMetadata === \"string\") {\n planNameFromMetadata = planNameFromMetadata.toLowerCase();\n }\n\n const plans = await getPlans(options.subscription);\n const planFromCode =\n planCode !== undefined && planCode !== null && planCode !== \"\"\n ? plans.find((p) => p.planCode === planCode)\n : undefined;\n const planPart = planFromCode?.name ?? planNameFromMetadata;\n const planName =\n planPart !== undefined && planPart !== null && planPart !== \"\"\n ? planPart.toLowerCase()\n : undefined;\n\n if (\n subscriptionCode !== undefined &&\n subscriptionCode !== null &&\n subscriptionCode !== \"\"\n ) {\n const where: { field: string; value: string | number | boolean | null }[] = [];\n if (\n referenceIdFromMetadata !== undefined &&\n referenceIdFromMetadata !== null &&\n referenceIdFromMetadata !== \"\"\n ) {\n where.push({ field: \"referenceId\", value: referenceIdFromMetadata });\n } else if (\n customerCode !== undefined &&\n customerCode !== null &&\n customerCode !== \"\"\n ) {\n where.push({ field: \"paystackCustomerCode\", value: customerCode });\n }\n if (planName !== undefined && planName !== null && planName !== \"\") {\n where.push({ field: \"plan\", value: planName });\n }\n\n if (where.length > 0) {\n const matches = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: where as unknown as {\n field: string;\n value: string | number | boolean | null;\n }[],\n });\n const subscription = matches?.[0];\n if (subscription !== undefined && subscription !== null) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n paystackSubscriptionCode: subscriptionCode,\n status: \"active\",\n updatedAt: new Date(),\n periodEnd:\n subscriptionData.next_payment_date !== undefined &&\n subscriptionData.next_payment_date !== null\n ? new Date(subscriptionData.next_payment_date)\n : undefined,\n },\n where: [{ field: \"id\", value: subscription.id }],\n });\n\n const plan =\n planFromCode ??\n (planName !== undefined && planName !== null && planName !== \"\"\n ? await getPlanByName(options, planName)\n : undefined);\n if (plan !== undefined && plan !== null) {\n await options.subscription.onSubscriptionComplete?.(\n {\n event,\n subscription: {\n ...subscription,\n paystackSubscriptionCode: subscriptionCode,\n status: \"active\",\n },\n plan,\n },\n ctx as GenericEndpointContext,\n );\n await options.subscription.onSubscriptionCreated?.(\n {\n event,\n subscription: {\n ...subscription,\n paystackSubscriptionCode: subscriptionCode,\n status: \"active\",\n },\n plan,\n },\n ctx as GenericEndpointContext,\n );\n }\n }\n }\n }\n }\n\n if (eventName === \"subscription.disable\" || eventName === \"subscription.not_renew\") {\n const subscriptionData =\n data as unknown as components[\"schemas\"][\"SubscriptionListResponseArray\"];\n const subscriptionCode = subscriptionData.subscription_code ?? \"\";\n if (subscriptionCode !== \"\") {\n const existing = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n let newStatus = \"canceled\";\n const nextPaymentDate = subscriptionData.next_payment_date;\n const periodEnd =\n nextPaymentDate !== undefined && nextPaymentDate !== null && nextPaymentDate !== \"\"\n ? new Date(nextPaymentDate)\n : existing?.periodEnd !== undefined && existing.periodEnd !== null\n ? new Date(existing.periodEnd)\n : undefined;\n\n if (periodEnd !== undefined && periodEnd.getTime() > Date.now()) {\n newStatus = \"active\";\n }\n\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: newStatus,\n cancelAtPeriodEnd: true,\n ...(periodEnd ? { periodEnd } : {}),\n updatedAt: new Date(),\n },\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (existing) {\n await options.subscription.onSubscriptionCancel?.(\n { event, subscription: { ...existing, status: \"canceled\" } },\n ctx as GenericEndpointContext,\n );\n }\n }\n }\n\n // Handle plan changes on renewal\n if (eventName === \"charge.success\" || eventName === \"invoice.update\") {\n const subData = (data as { subscription?: { subscription_code?: string | null } })\n ?.subscription;\n const subscriptionCodeRaw =\n subData?.subscription_code ??\n (data as { subscription_code?: string | null })?.subscription_code;\n const subscriptionCode =\n subscriptionCodeRaw !== undefined &&\n subscriptionCodeRaw !== null &&\n subscriptionCodeRaw !== \"\"\n ? subscriptionCodeRaw\n : undefined;\n\n if (subscriptionCode !== undefined) {\n const existingSub = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (\n existingSub !== undefined &&\n existingSub !== null &&\n existingSub.pendingPlan !== undefined &&\n existingSub.pendingPlan !== null &&\n existingSub.pendingPlan !== \"\"\n ) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n plan: existingSub.pendingPlan,\n pendingPlan: null,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: existingSub.id }],\n });\n }\n }\n }\n } catch (_e: unknown) {\n ctx.context.logger.error(\"Failed to sync Paystack webhook event\", _e);\n }\n }\n\n await options.onEvent?.(event);\n return ctx.json({ received: true });\n },\n );\n};\n\nconst initializeTransactionBodySchema = z.object({\n plan: z.string().optional(),\n product: z.string().optional(),\n amount: z.number().int().positive().optional(),\n currency: z.string().optional(),\n email: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n referenceId: z.string().optional(),\n callbackURL: z.string().optional(),\n quantity: z.number().int().positive().optional(),\n scheduleAtPeriodEnd: z.boolean().optional(),\n cancelAtPeriodEnd: z.boolean().optional(),\n prorateAndCharge: z.boolean().optional(),\n});\n\nexport const initializeTransaction = <P extends string = \"/initialize-transaction\">(\n options: AnyPaystackOptions,\n path: P = \"/initialize-transaction\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n plan: z.ZodOptional<z.ZodString>;\n product: z.ZodOptional<z.ZodString>;\n amount: z.ZodOptional<z.ZodNumber>;\n currency: z.ZodOptional<z.ZodString>;\n email: z.ZodOptional<z.ZodString>;\n metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;\n referenceId: z.ZodOptional<z.ZodString>;\n callbackURL: z.ZodOptional<z.ZodString>;\n quantity: z.ZodOptional<z.ZodNumber>;\n scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n prorateAndCharge: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n> => {\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"initialize-transaction\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n body: initializeTransactionBodySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n const {\n plan: planName,\n product: productName,\n amount: bodyAmount,\n currency,\n email,\n metadata: extraMetadata,\n callbackURL,\n quantity,\n scheduleAtPeriodEnd,\n cancelAtPeriodEnd,\n prorateAndCharge,\n } = ctx.body;\n\n // 1. Validate Callback URL validation (same as before)\n if (callbackURL !== undefined && callbackURL !== null && callbackURL !== \"\") {\n const checkTrusted = () => {\n try {\n if (callbackURL.startsWith(\"/\")) return true;\n const baseUrl =\n ((ctx.context as Record<string, unknown>)?.baseURL as string | undefined) ??\n (ctx.request as unknown as { url?: string })?.url ??\n \"\";\n if (baseUrl === \"\") return false;\n const baseOrigin = new URL(baseUrl).origin;\n return new URL(callbackURL).origin === baseOrigin;\n } catch {\n return false;\n }\n };\n if (checkTrusted() === false) {\n throw new APIError(\"FORBIDDEN\", {\n message: \"callbackURL is not a trusted origin.\",\n status: 403,\n });\n }\n }\n\n // 2. Get User & Session\n const session = await getSessionFromCtx(ctx);\n if (session === undefined || session === null) throw new APIError(\"UNAUTHORIZED\");\n const user = session.user;\n\n // 3. Email Verification Check (only if subscription options enforce it)\n if (\n subscriptionOptions?.enabled === true &&\n subscriptionOptions.requireEmailVerification === true &&\n user.emailVerified !== true\n ) {\n throw new APIError(\"BAD_REQUEST\", {\n code: \"EMAIL_VERIFICATION_REQUIRED\",\n message: PAYSTACK_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED.message,\n });\n }\n\n // 4. Determine Payment Mode: Subscription (Plan) vs Product vs One-Time (Amount)\n let plan: PaystackPlan | undefined;\n let product: PaystackProduct | InputPaystackProduct | undefined;\n\n if (planName !== undefined && planName !== null && planName !== \"\") {\n if (subscriptionOptions?.enabled !== true) {\n throw new APIError(\"BAD_REQUEST\", { message: \"Subscriptions are not enabled.\" });\n }\n plan = (await getPlanByName(options, planName)) ?? undefined;\n if (plan === undefined || plan === null) {\n try {\n // Fallback: Check database for synced plans when that model exists.\n const nativePlan = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"name\", value: planName }],\n });\n if (nativePlan !== undefined && nativePlan !== null) {\n plan = nativePlan;\n } else {\n const nativePlanByCode = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"planCode\", value: planName }],\n });\n plan = nativePlanByCode ?? undefined;\n }\n } catch {\n plan = undefined;\n }\n }\n if (plan === undefined || plan === null) {\n throw new APIError(\"BAD_REQUEST\", {\n code: \"SUBSCRIPTION_PLAN_NOT_FOUND\",\n message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND.message,\n status: 400,\n });\n }\n } else if (productName !== undefined && productName !== null && productName !== \"\") {\n if (typeof productName === \"string\") {\n product = (await getProductByName(options, productName)) ?? undefined;\n // Fallback: Check database for synced products\n product ??=\n (await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"name\", value: productName }],\n })) ?? undefined;\n }\n if (product === undefined || product === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: `Product '${productName}' not found.`,\n status: 400,\n });\n }\n } else if (bodyAmount === undefined || bodyAmount === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Either 'plan', 'product', or 'amount' is required to initialize a transaction.\",\n status: 400,\n });\n }\n\n let amount =\n bodyAmount ??\n (product as PaystackProduct)?.price ??\n (product as InputPaystackProduct)?.amount;\n const finalCurrency =\n currency ??\n (product as PaystackProduct)?.currency ??\n (product as InputPaystackProduct)?.currency ??\n plan?.currency ??\n \"NGN\";\n\n const referenceIdFromCtx = (ctx.context as Record<string, unknown>).referenceId as\n | string\n | undefined;\n const referenceId =\n ctx.body.referenceId ?? referenceIdFromCtx ?? (session.user as { id: string }).id;\n\n // Handle scheduleAtPeriodEnd for existing subscriptions\n if (plan !== undefined && scheduleAtPeriodEnd === true) {\n const existingSub = await getOrganizationSubscription(ctx, referenceId);\n if (existingSub?.status === \"active\") {\n await ctx.context.adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: existingSub.id }],\n update: {\n pendingPlan: plan.name,\n updatedAt: new Date(),\n },\n });\n return ctx.json({\n status: \"success\",\n message: \"Plan change scheduled at period end.\",\n scheduled: true,\n });\n }\n }\n\n // Handle cancelAtPeriodEnd for existing subscriptions\n if (cancelAtPeriodEnd === true) {\n const existingSub = await getOrganizationSubscription(ctx, referenceId);\n if (existingSub?.status === \"active\") {\n await ctx.context.adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: existingSub.id }],\n update: {\n cancelAtPeriodEnd: true,\n updatedAt: new Date(),\n },\n });\n\n return ctx.json({\n status: \"success\",\n message: \"Subscription cancellation scheduled at period end.\",\n scheduled: true,\n });\n }\n }\n\n // Calculate final amount considering seats if applicable\n if (\n plan !== undefined &&\n (plan.seatAmount !== undefined ||\n (plan as unknown as Record<string, unknown>).seatPriceId !== undefined)\n ) {\n const members = await ctx.context.adapter.findMany<Member>({\n model: \"member\",\n where: [{ field: \"organizationId\", value: referenceId }],\n });\n const seatCount = members.length > 0 ? members.length : 1;\n const quantityToUse = quantity ?? seatCount;\n\n amount =\n (plan.amount ?? 0) +\n quantityToUse *\n (plan.seatAmount ??\n ((plan as unknown as Record<string, unknown>).seatPriceId as number) ??\n 0);\n }\n\n let url: string | undefined;\n let reference: string | undefined;\n let accessCode: string | undefined;\n\n // Check trial eligibility - prevent trial abuse\n let trialStart: Date | undefined;\n let trialEnd: Date | undefined;\n if (plan?.freeTrial?.days !== undefined && plan.freeTrial.days > 0) {\n // Check if user/referenceId has ever had a trial\n const previousTrials = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: referenceId }],\n });\n const hadTrial = previousTrials?.some(\n (sub: Subscription) =>\n (sub.trialStart !== undefined && sub.trialStart !== null) ||\n (sub.trialEnd !== undefined && sub.trialEnd !== null) ||\n sub.status === \"trialing\",\n );\n\n if (hadTrial === false) {\n trialStart = new Date();\n trialEnd = new Date();\n trialEnd.setDate(trialEnd.getDate() + plan.freeTrial.days);\n }\n }\n\n try {\n // Determine Customer Email & Code (Organization support)\n let targetEmail = email ?? user.email;\n let paystackCustomerCode = (user as PaystackUser).paystackCustomerCode;\n\n if (\n options.organization?.enabled === true &&\n referenceId !== undefined &&\n referenceId !== null &&\n referenceId !== user.id\n ) {\n const org = await ctx.context.adapter.findOne({\n model: \"organization\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (org !== undefined && org !== null) {\n const paystackOrg = org as PaystackOrganization;\n if (\n paystackOrg.paystackCustomerCode !== undefined &&\n paystackOrg.paystackCustomerCode !== null &&\n paystackOrg.paystackCustomerCode !== \"\"\n ) {\n paystackCustomerCode = paystackOrg.paystackCustomerCode;\n }\n const orgWithEmail = org as { email?: string | null };\n if (\n orgWithEmail.email !== undefined &&\n orgWithEmail.email !== null &&\n orgWithEmail.email !== \"\"\n ) {\n targetEmail = orgWithEmail.email;\n } else {\n // Fallback: Use Organization Owner Email\n const ownerMember = await ctx.context.adapter.findOne({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: referenceId },\n { field: \"role\", value: \"owner\" },\n ],\n });\n\n if (ownerMember !== undefined && ownerMember !== null) {\n const ownerUser = (await ctx.context.adapter.findOne({\n model: \"user\",\n where: [{ field: \"id\", value: (ownerMember as Member).userId }],\n })) as User | null;\n\n if (\n ownerUser !== undefined &&\n ownerUser !== null &&\n ownerUser.email !== undefined &&\n ownerUser.email !== null &&\n ownerUser.email !== \"\"\n ) {\n targetEmail = ownerUser.email;\n }\n }\n }\n }\n }\n\n // Construct Metadata\n const metadata = JSON.stringify({\n referenceId,\n userId: user.id,\n plan: plan !== undefined ? plan.name.toLowerCase() : undefined, // Undefined for one-time\n product: product !== undefined ? product.name.toLowerCase() : undefined,\n isTrial: trialStart !== undefined,\n trialEnd: trialEnd !== undefined ? trialEnd.toISOString() : undefined,\n ...extraMetadata,\n });\n\n const initBody: {\n email: string;\n callback_url?: string;\n metadata: string;\n currency: string;\n quantity?: number;\n amount?: number;\n plan?: string;\n [key: string]: unknown;\n } = {\n email: targetEmail,\n callback_url: callbackURL ?? undefined,\n metadata,\n // If plan/product exists, use its currency; otherwise fallback to provided or default\n currency: finalCurrency,\n quantity,\n };\n\n // Sync/Update Customer: ensure email matches if code exists\n if (\n paystackCustomerCode !== undefined &&\n paystackCustomerCode !== null &&\n paystackCustomerCode !== \"\"\n ) {\n try {\n const ops = getPaystackOps(options.paystackClient);\n // Only update if email is present\n if (ops !== undefined && ops !== null && initBody.email !== \"\") {\n await ops.customer?.update(paystackCustomerCode, {\n body: { email: initBody.email },\n });\n }\n } catch (_e: unknown) {\n // Ignore sync errors\n }\n }\n\n // Handle prorateAndCharge for existing active subscriptions\n if (plan !== undefined && prorateAndCharge === true) {\n const existingSub = await getOrganizationSubscription(ctx, referenceId);\n if (\n existingSub?.status === \"active\" &&\n existingSub.paystackAuthorizationCode !== undefined &&\n existingSub.paystackAuthorizationCode !== null &&\n existingSub.paystackAuthorizationCode !== \"\" &&\n existingSub.paystackSubscriptionCode !== undefined &&\n existingSub.paystackSubscriptionCode !== null &&\n existingSub.paystackSubscriptionCode !== \"\"\n ) {\n if (\n existingSub.periodEnd !== undefined &&\n existingSub.periodEnd !== null &&\n existingSub.periodStart !== undefined &&\n existingSub.periodStart !== null\n ) {\n // 1. Calculate remaining days\n const now = new Date();\n const periodEndLocal = new Date(existingSub.periodEnd);\n const periodStartLocal = new Date(existingSub.periodStart);\n\n const totalDays = Math.max(\n 1,\n Math.ceil(\n (periodEndLocal.getTime() - periodStartLocal.getTime()) / (1000 * 60 * 60 * 24),\n ),\n );\n const remainingDays = Math.max(\n 0,\n Math.ceil((periodEndLocal.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)),\n );\n\n // 2. Fetch old plan/amount\n let oldAmount = 0;\n if (existingSub.plan !== \"\") {\n const oldPlan =\n (await getPlanByName(options, existingSub.plan)) ??\n (await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"name\", value: existingSub.plan }],\n })) ??\n undefined;\n if (oldPlan !== undefined && oldPlan !== null) {\n const oldSeatCount = existingSub.seats;\n oldAmount =\n (oldPlan.amount ?? 0) +\n oldSeatCount *\n (oldPlan.seatAmount ??\n ((oldPlan as unknown as Record<string, unknown>).seatPriceId as number) ??\n 0);\n }\n }\n\n // 3. Calculate new total amount\n let membersCount = 1;\n if (\n plan.seatAmount !== undefined ||\n (plan as unknown as Record<string, unknown>).seatPriceId !== undefined\n ) {\n const members = await ctx.context.adapter.findMany<Member>({\n model: \"member\",\n where: [{ field: \"organizationId\", value: referenceId }],\n });\n membersCount = members.length > 0 ? members.length : 1;\n }\n const newSeatCount = quantity ?? existingSub.seats ?? membersCount;\n const newAmount =\n (plan.amount ?? 0) +\n newSeatCount *\n (plan.seatAmount ??\n ((plan as unknown as Record<string, unknown>).seatPriceId as number) ??\n 0);\n\n // 4. Calculate Difference & Charge\n const costDifference = newAmount - oldAmount;\n if (costDifference > 0 && remainingDays > 0) {\n const proratedAmount = Math.round((costDifference / totalDays) * remainingDays);\n // Ensure minimum Paystack charge limit is met (50 NGN -> 5000)\n if (proratedAmount >= 5000) {\n const ops = getPaystackOps(options.paystackClient);\n if (ops === undefined || ops === null) {\n ctx.context.logger.error(\"Paystack client not configured for proration charge\");\n return;\n }\n const chargeResRaw = await ops.transaction?.chargeAuthorization({\n body: {\n email: targetEmail,\n amount: proratedAmount,\n authorization_code: existingSub.paystackAuthorizationCode,\n reference: `upg_${existingSub.id}_${Date.now()}_${Math.random().toString(36).substring(7)}`,\n metadata: {\n type: \"proration\",\n referenceId,\n newPlan: plan.name,\n oldPlan: existingSub.plan,\n remainingDays,\n },\n },\n });\n const sdkRes = unwrapSdkResult<PaystackTransactionResponse>(chargeResRaw);\n\n if (sdkRes?.status !== \"success\") {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Failed to process prorated charge via saved authorization.\",\n });\n }\n }\n }\n\n // 5. Update Subscription Future Cycle in Paystack\n const ops = getPaystackOps(options.paystackClient);\n if (ops !== undefined && ops !== null) {\n await ops.subscription?.update(existingSub.paystackSubscriptionCode, {\n body: {\n amount: newAmount,\n plan: plan.planCode,\n },\n });\n }\n\n // 6. Update Local DB\n await ctx.context.adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: existingSub.id }],\n update: {\n plan: plan.name,\n seats: newSeatCount,\n updatedAt: new Date(),\n },\n });\n\n return ctx.json({\n status: \"success\",\n message: \"Subscription successfully upgraded with prorated charge.\",\n prorated: true,\n });\n }\n }\n }\n\n if (plan !== undefined) {\n // Subscription Flow\n if (trialStart !== undefined) {\n // Trial Flow: Authorize card with minimum amount, don't start sub yet\n initBody.amount = 5000; // 50 NGN (minimum allowed)\n } else {\n // Standard Flow\n initBody.plan = plan.planCode;\n // SDK might use different field names, but keeping DX consistency\n (initBody as Record<string, unknown>).invoice_limit = plan.invoiceLimit;\n\n let finalAmount: number;\n if (amount !== undefined && amount !== null) {\n finalAmount = amount;\n initBody.quantity = 1;\n } else {\n finalAmount = (plan.amount ?? 0) * (quantity ?? 1);\n }\n initBody.amount = Math.max(Math.round(finalAmount), 5000);\n }\n } else {\n // One-Time Payment Flow\n if (amount === undefined || amount === null)\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Amount is required for one-time payments\",\n });\n initBody.amount = Math.round(amount);\n }\n\n const initRaw = await paystack?.transaction?.initialize({\n body: initBody,\n });\n const sdkRes =\n unwrapSdkResult<components[\"schemas\"][\"TransactionInitializeResponse\"][\"data\"]>(initRaw);\n\n url = sdkRes?.authorization_url;\n reference = sdkRes?.reference;\n accessCode = sdkRes?.access_code;\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to initialize Paystack transaction\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_INITIALIZE_TRANSACTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_INITIALIZE_TRANSACTION\",\n message: errorMessage,\n });\n }\n\n // 6. Record Transaction & Subscription\n await ctx.context.adapter.create({\n model: \"paystackTransaction\",\n data: {\n reference: reference ?? \"\",\n referenceId,\n userId: user.id,\n amount: amount ?? 0,\n currency: plan?.currency ?? currency ?? \"NGN\",\n status: \"pending\",\n plan: plan !== undefined ? plan.name.toLowerCase() : undefined,\n product: product !== undefined ? product.name.toLowerCase() : undefined,\n metadata:\n extraMetadata !== undefined && Object.keys(extraMetadata).length > 0\n ? JSON.stringify(extraMetadata)\n : undefined,\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n });\n\n if (plan !== undefined) {\n let storedCustomerCode = (user as PaystackUser).paystackCustomerCode;\n if (options.organization?.enabled === true && referenceId !== user.id) {\n const org = await ctx.context.adapter.findOne({\n model: \"organization\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (org !== undefined && org !== null) {\n const paystackOrg = org as PaystackOrganization;\n if (\n paystackOrg.paystackCustomerCode !== undefined &&\n paystackOrg.paystackCustomerCode !== null &&\n paystackOrg.paystackCustomerCode !== \"\"\n ) {\n storedCustomerCode = paystackOrg.paystackCustomerCode;\n }\n }\n }\n\n const newSubscription = await ctx.context.adapter.create<Subscription>({\n model: \"subscription\",\n data: {\n plan: plan.name.toLowerCase(),\n referenceId,\n userId: user.id,\n paystackCustomerCode: storedCustomerCode ?? \"\",\n paystackSubscriptionCode: \"\",\n paystackPlanCode: plan.planCode,\n paystackAuthorizationCode: \"\",\n paystackTransactionReference: reference ?? \"\",\n status: trialStart !== undefined ? \"trialing\" : \"incomplete\",\n seats: quantity ?? 1,\n periodStart: new Date(),\n periodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // Default 30 days\n cancelAtPeriodEnd: false,\n trialStart,\n trialEnd,\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n });\n\n // Call trial start hook if trial was granted\n if (\n trialStart !== undefined &&\n newSubscription !== undefined &&\n newSubscription !== null &&\n plan.freeTrial?.onTrialStart !== undefined\n ) {\n await plan.freeTrial.onTrialStart(newSubscription);\n }\n }\n\n return ctx.json({\n url,\n reference,\n accessCode,\n redirect: true,\n });\n },\n );\n};\n\n// Aliases for Client DX Parity\nexport const createSubscription = <P extends string = \"/create-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/create-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n plan: z.ZodOptional<z.ZodString>;\n product: z.ZodOptional<z.ZodString>;\n amount: z.ZodOptional<z.ZodNumber>;\n currency: z.ZodOptional<z.ZodString>;\n email: z.ZodOptional<z.ZodString>;\n metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;\n referenceId: z.ZodOptional<z.ZodString>;\n callbackURL: z.ZodOptional<z.ZodString>;\n quantity: z.ZodOptional<z.ZodNumber>;\n scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n prorateAndCharge: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n> => initializeTransaction(options, path);\n\nexport const upgradeSubscription = <P extends string = \"/upgrade-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/upgrade-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n plan: z.ZodOptional<z.ZodString>;\n product: z.ZodOptional<z.ZodString>;\n amount: z.ZodOptional<z.ZodNumber>;\n currency: z.ZodOptional<z.ZodString>;\n email: z.ZodOptional<z.ZodString>;\n metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;\n referenceId: z.ZodOptional<z.ZodString>;\n callbackURL: z.ZodOptional<z.ZodString>;\n quantity: z.ZodOptional<z.ZodNumber>;\n scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n prorateAndCharge: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n> => initializeTransaction(options, path);\n\nexport const cancelSubscription = <P extends string = \"/cancel-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/cancel-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => disablePaystackSubscription(options, path);\n\nexport const restoreSubscription = <P extends string = \"/restore-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/restore-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => enablePaystackSubscription(options, path);\n\nexport const verifyTransaction = <P extends string = \"/verify-transaction\">(\n options: AnyPaystackOptions,\n path: P = \"/verify-transaction\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n reference: z.ZodString;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n reference: string;\n data: {\n id: number;\n domain: string;\n status: string;\n reference: string;\n receipt_number: string | null;\n amount: number;\n message: string | null;\n gateway_response: string;\n channel: string;\n currency: string;\n ip_address: string | null;\n metadata: (string | Record<string, never> | number) | null;\n log: {\n start_time: number;\n time_spent: number;\n attempts: number;\n errors: number;\n success: boolean;\n mobile: boolean;\n input: unknown[];\n history: {\n type: string;\n message: string;\n time: number;\n }[];\n } | null;\n fees: number | null;\n fees_split: unknown;\n authorization: {\n authorization_code?: string;\n bin?: string | null;\n last4?: string;\n exp_month?: string;\n exp_year?: string;\n channel?: string;\n card_type?: string;\n bank?: string;\n country_code?: string;\n brand?: string;\n reusable?: boolean;\n signature?: string;\n account_name?: string | null;\n receiver_bank_account_number?: string | null;\n receiver_bank?: string | null;\n };\n customer: {\n id: number;\n first_name: string | null;\n last_name: string | null;\n email: string;\n customer_code: string;\n phone: string | null;\n metadata: Record<string, never> | null;\n risk_action: string;\n international_format_phone?: string | null;\n };\n plan: (string | Record<string, never>) | null;\n split: Record<string, never> | null;\n order_id: unknown;\n paidAt: string | null;\n createdAt: string;\n requested_amount: number;\n pos_transaction_data: unknown;\n source: unknown;\n fees_breakdown: unknown;\n connect: unknown;\n transaction_date: string;\n plan_object: {\n id?: number;\n name?: string;\n plan_code?: string;\n description?: unknown;\n amount?: number;\n interval?: string;\n send_invoices?: boolean;\n send_sms?: boolean;\n currency?: string;\n };\n subaccount: Record<string, never> | null;\n };\n }\n> => {\n const verifyBodySchema = z.object({\n reference: z.string(),\n });\n\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"verify-transaction\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n body: verifyBodySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n let data: PaystackTransactionResponse | undefined;\n\n try {\n const verifyRaw = await paystack?.transaction?.verify(ctx.body.reference);\n // unwrapSdkResult might return the data field or the whole body depending on its impl.\n // But with PaystackResponse and ours, it should give us the 'data' part for success.\n data = unwrapSdkResult<PaystackTransactionResponse>(verifyRaw);\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to verify Paystack transaction\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_VERIFY_TRANSACTION\",\n message: errorMessage,\n });\n }\n\n if (data === undefined || data === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Failed to fetch transaction data from Paystack.\",\n });\n }\n\n const status = data.status ?? \"failed\";\n const reference = data.reference ?? ctx.body.reference;\n const paystackIdRaw = data.id;\n const paystackId =\n paystackIdRaw !== undefined && paystackIdRaw !== null ? String(paystackIdRaw) : undefined;\n const authorizationCode = (data.authorization as { authorization_code?: string | null })\n ?.authorization_code;\n\n if (status === \"success\") {\n const session = await getSessionFromCtx(ctx);\n\n // Get the local transaction record to know the intended referenceId (Org or User)\n const txRecord = await ctx.context.adapter.findOne<\n PaystackTransaction & { referenceId?: string }\n >({\n model: \"paystackTransaction\",\n where: [{ field: \"reference\", value: reference }],\n });\n\n // Trust the referenceId from the record, fallback to session user if missing\n const referenceId =\n txRecord !== undefined &&\n txRecord !== null &&\n txRecord.referenceId !== undefined &&\n txRecord.referenceId !== null &&\n txRecord.referenceId !== \"\"\n ? txRecord.referenceId\n : session !== undefined && session !== null\n ? session.user.id\n : undefined;\n\n // Authorization check: ensure the current user has access to this referenceId\n if (\n session !== undefined &&\n session !== null &&\n referenceId !== undefined &&\n referenceId !== null &&\n referenceId !== \"\" &&\n referenceId !== session.user.id\n ) {\n const authRef = subscriptionOptions?.authorizeReference;\n let authorized = false;\n if (authRef !== undefined && authRef !== null) {\n authorized = await authRef(\n {\n user: session.user,\n session: session.session,\n referenceId,\n action: \"verify-transaction\",\n },\n ctx as GenericEndpointContext,\n );\n }\n if (authorized === false && options.organization?.enabled === true) {\n const member = await ctx.context.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"userId\", value: session.user.id },\n { field: \"organizationId\", value: referenceId },\n ],\n });\n if (member !== undefined && member !== null) authorized = true;\n }\n\n if (authorized === false) {\n throw new APIError(\"UNAUTHORIZED\");\n }\n }\n\n try {\n await ctx.context.adapter.update({\n model: \"paystackTransaction\",\n update: {\n status: \"success\",\n paystackId,\n amount: data.amount,\n currency: data.currency,\n updatedAt: new Date(),\n },\n where: [{ field: \"reference\", value: reference }],\n });\n\n const paystackCustomerCodeFromPaystack = data.customer?.customer_code;\n if (\n paystackCustomerCodeFromPaystack !== undefined &&\n paystackCustomerCodeFromPaystack !== null &&\n paystackCustomerCodeFromPaystack !== \"\" &&\n referenceId !== undefined &&\n referenceId !== null &&\n referenceId !== \"\"\n ) {\n let isOrg =\n options.organization?.enabled === true &&\n typeof referenceId === \"string\" &&\n referenceId.startsWith(\"org_\");\n if (isOrg === false && options.organization?.enabled === true) {\n const org = await ctx.context.adapter.findOne({\n model: \"organization\",\n where: [{ field: \"id\", value: referenceId }],\n });\n isOrg = org !== undefined && org !== null;\n }\n\n if (isOrg) {\n await ctx.context.adapter.update({\n model: \"organization\",\n update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },\n where: [{ field: \"id\", value: referenceId }],\n });\n } else {\n await ctx.context.adapter.update({\n model: \"user\",\n update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },\n where: [{ field: \"id\", value: referenceId }],\n });\n }\n }\n\n // Decrement product quantity if applicable\n const transaction = await ctx.context.adapter.findOne<PaystackTransaction>({\n model: \"paystackTransaction\",\n where: [{ field: \"reference\", value: reference }],\n });\n if (\n transaction !== undefined &&\n transaction !== null &&\n transaction.product !== undefined &&\n transaction.product !== null &&\n transaction.product !== \"\" &&\n options.paystackClient !== undefined &&\n options.paystackClient !== null\n ) {\n await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);\n }\n\n // Check for trial activation\n let isTrial = false;\n let trialEnd: string | undefined;\n let targetPlan: string | undefined;\n\n if (data.metadata !== undefined && data.metadata !== null && data.metadata !== \"\") {\n const meta = (\n typeof data.metadata === \"string\" ? JSON.parse(data.metadata) : data.metadata\n ) as Record<string, unknown>;\n isTrial = meta.isTrial === true || meta.isTrial === \"true\";\n trialEnd = meta.trialEnd as string | undefined;\n targetPlan = meta.plan as string | undefined;\n }\n\n let paystackSubscriptionCode: string | undefined;\n\n if (isTrial && targetPlan !== undefined && trialEnd !== undefined) {\n // Trial Flow: Create subscription with future start date using auth code\n const email = data.customer?.email;\n\n const plans = await getPlans(subscriptionOptions);\n const planConfig = plans.find(\n (p) => p.name.toLowerCase() === targetPlan?.toLowerCase(),\n );\n\n // For local plans (no planCode), generate a local subscription code\n if (\n planConfig !== undefined &&\n planConfig !== null &&\n (planConfig.planCode === undefined ||\n planConfig.planCode === null ||\n planConfig.planCode === \"\")\n ) {\n paystackSubscriptionCode = `LOC_${reference}`;\n }\n\n if (\n authorizationCode !== undefined &&\n authorizationCode !== null &&\n email !== undefined &&\n email !== null &&\n email !== \"\" &&\n planConfig?.planCode !== undefined &&\n planConfig.planCode !== null &&\n planConfig.planCode !== \"\"\n ) {\n const subResRaw = await paystack?.subscription?.create({\n body: {\n customer: email,\n plan: planConfig.planCode,\n authorization: authorizationCode,\n start_date: trialEnd,\n },\n });\n const subRes =\n unwrapSdkResult<components[\"schemas\"][\"SubscriptionListResponseArray\"]>(subResRaw);\n paystackSubscriptionCode = subRes?.subscription_code;\n }\n } else if (isTrial === false) {\n const planCodeFromPaystack = (data as { plan?: { plan_code?: string | null } }).plan\n ?.plan_code;\n if (\n planCodeFromPaystack === undefined ||\n planCodeFromPaystack === null ||\n planCodeFromPaystack === \"\"\n ) {\n // Local Plan\n paystackSubscriptionCode = `LOC_${reference}`;\n } else {\n // Native Paystack subscription (if created during charge)\n paystackSubscriptionCode =\n (data as { subscription?: { subscription_code?: string | null } }).subscription\n ?.subscription_code ?? undefined;\n }\n }\n\n const existingSubs = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackTransactionReference\", value: reference }],\n });\n\n const targetSub = existingSubs?.find(\n (s) =>\n referenceId === undefined ||\n referenceId === null ||\n referenceId === \"\" ||\n s.referenceId === referenceId,\n );\n\n let updatedSubscription: Subscription | null = null;\n if (targetSub !== undefined && targetSub !== null) {\n updatedSubscription = await ctx.context.adapter.update<Subscription>({\n model: \"subscription\",\n update: {\n status: isTrial ? \"trialing\" : \"active\",\n periodStart: new Date(),\n updatedAt: new Date(),\n ...(isTrial && trialEnd !== undefined\n ? {\n trialStart: new Date(),\n trialEnd: new Date(trialEnd),\n periodEnd: new Date(trialEnd),\n }\n : {}),\n ...(paystackSubscriptionCode !== undefined ? { paystackSubscriptionCode } : {}),\n ...(authorizationCode !== undefined && authorizationCode !== null\n ? { paystackAuthorizationCode: authorizationCode }\n : {}),\n },\n where: [{ field: \"id\", value: targetSub.id }],\n });\n }\n\n if (\n updatedSubscription !== undefined &&\n updatedSubscription !== null &&\n subscriptionOptions?.onSubscriptionComplete !== undefined\n ) {\n const plans = await getPlans(subscriptionOptions);\n const plan = plans.find(\n (p) => p.name.toLowerCase() === updatedSubscription.plan.toLowerCase(),\n );\n if (plan !== undefined) {\n await subscriptionOptions.onSubscriptionComplete(\n {\n event: data as unknown as PaystackWebhookPayload,\n subscription: updatedSubscription,\n plan,\n },\n ctx as GenericEndpointContext,\n );\n }\n }\n } catch (e: unknown) {\n ctx.context.logger.error(\n \"Failed to update transaction/subscription after verification\",\n e,\n );\n }\n }\n\n return ctx.json({ status, reference, data });\n },\n );\n};\n\nexport const listSubscriptions = <P extends string = \"/list-subscriptions\">(\n options: AnyPaystackOptions,\n path: P = \"/list-subscriptions\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n query: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n subscriptions: Subscription[];\n }\n> => {\n const listQuerySchema = z.object({\n referenceId: z.string().optional(),\n });\n\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"list-subscriptions\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n query: listQuerySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n if (subscriptionOptions?.enabled !== true) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Subscriptions are not enabled in the Paystack options.\",\n });\n }\n const session = await getSessionFromCtx(ctx);\n if (session === undefined || session === null) throw new APIError(\"UNAUTHORIZED\");\n const referenceIdPart = (ctx.context as Record<string, unknown>).referenceId as\n | string\n | undefined;\n const queryRefId = ctx.query?.referenceId;\n const referenceId = referenceIdPart ?? queryRefId ?? (session.user as { id: string }).id;\n const res = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: referenceId }],\n });\n return ctx.json({ subscriptions: res });\n },\n );\n};\n\nexport const listTransactions = <P extends string = \"/list-transactions\">(\n options: AnyPaystackOptions,\n path: P = \"/list-transactions\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n query: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n transactions: PaystackTransaction[];\n }\n> => {\n const listQuerySchema = z.object({\n referenceId: z.string().optional(),\n });\n\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"list-transactions\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n query: listQuerySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n if (session === undefined || session === null) throw new APIError(\"UNAUTHORIZED\");\n const referenceIdPart = (ctx.context as Record<string, unknown>).referenceId as\n | string\n | undefined;\n const queryRefId = ctx.query?.referenceId;\n const referenceId = referenceIdPart ?? queryRefId ?? (session.user as { id: string }).id;\n const res = await ctx.context.adapter.findMany<PaystackTransaction>({\n model: \"paystackTransaction\",\n where: [{ field: \"referenceId\", value: referenceId }],\n });\n // Sort by createdAt desc locally.\n const sorted = res.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return ctx.json({ transactions: sorted });\n },\n );\n};\n\nconst enableDisableBodySchema = z.object({\n referenceId: z.string().optional(),\n subscriptionCode: z.string(),\n emailToken: z.string().optional(),\n atPeriodEnd: z.boolean().optional(),\n});\n\nfunction decodeBase64UrlToString(value: string): string {\n const normalized = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = normalized + \"===\".slice((normalized.length + 3) % 4);\n const binaryString = atob(padded);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\nfunction tryGetEmailTokenFromSubscriptionManageLink(link: string): string | undefined {\n try {\n const url = new URL(link);\n const subscriptionToken = url.searchParams.get(\"subscription_token\");\n if (subscriptionToken === undefined || subscriptionToken === null || subscriptionToken === \"\")\n return undefined;\n const parts = subscriptionToken.split(\".\");\n if (parts.length < 2) return undefined;\n const payloadJson = decodeBase64UrlToString(parts[1]);\n const payload = JSON.parse(payloadJson) as Record<string, unknown>;\n return typeof payload.email_token === \"string\" ? payload.email_token : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport const disablePaystackSubscription = <P extends string = \"/disable-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/disable-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => {\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"disable-subscription\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n { method: \"POST\", body: enableDisableBodySchema, use: useMiddlewares },\n async (ctx) => {\n const { subscriptionCode, atPeriodEnd } = ctx.body;\n const paystack = getPaystackOps(options.paystackClient);\n try {\n if (subscriptionCode.startsWith(\"LOC_\") || subscriptionCode.startsWith(\"sub_local_\")) {\n const sub = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (sub) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: atPeriodEnd === false ? \"canceled\" : \"active\",\n cancelAtPeriodEnd: atPeriodEnd !== false,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: sub.id }],\n });\n return ctx.json({ status: \"success\" });\n }\n throw new APIError(\"BAD_REQUEST\", { message: \"Subscription not found\" });\n }\n\n let emailToken = ctx.body.emailToken;\n let nextPaymentDate: string | undefined;\n\n try {\n const raw = await paystack?.subscription?.fetch(subscriptionCode);\n const fetchRes =\n unwrapSdkResult<components[\"schemas\"][\"SubscriptionListResponseArray\"]>(raw);\n\n if (fetchRes !== undefined && fetchRes !== null) {\n emailToken ??= fetchRes.email_token ?? undefined;\n nextPaymentDate = fetchRes.next_payment_date ?? undefined;\n }\n } catch {\n // ignore fetch failure\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n try {\n const raw = await paystack?.subscription?.manageLink(subscriptionCode);\n const linkRes = unwrapSdkResult<{ link: string }>(raw);\n const link = linkRes?.link;\n if (link !== undefined && link !== null && link !== \"\") {\n emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);\n }\n } catch {\n // ignore\n }\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n throw new Error(\"Could not retrieve email_token for subscription disable.\");\n }\n\n await paystack?.subscription?.disable({\n body: { code: subscriptionCode, token: emailToken },\n });\n\n const periodEnd =\n nextPaymentDate !== undefined && nextPaymentDate !== null && nextPaymentDate !== \"\"\n ? new Date(nextPaymentDate)\n : undefined;\n\n const sub = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (sub !== undefined && sub !== null) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: atPeriodEnd === false ? \"canceled\" : \"active\",\n cancelAtPeriodEnd: atPeriodEnd !== false,\n periodEnd,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: sub.id }],\n });\n } else {\n ctx.context.logger.warn(\n `Could not find subscription with code ${subscriptionCode} to disable`,\n );\n }\n\n return ctx.json({ status: \"success\" });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to disable subscription\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_DISABLE_SUBSCRIPTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_DISABLE_SUBSCRIPTION\",\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const enablePaystackSubscription = <P extends string = \"/enable-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/enable-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => {\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"enable-subscription\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n { method: \"POST\", body: enableDisableBodySchema, use: useMiddlewares },\n async (ctx) => {\n const { subscriptionCode } = ctx.body;\n const paystack = getPaystackOps(options.paystackClient);\n try {\n let emailToken = ctx.body.emailToken;\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n try {\n const raw = await paystack?.subscription?.fetch(subscriptionCode);\n const fetchRes =\n unwrapSdkResult<components[\"schemas\"][\"SubscriptionListResponseArray\"]>(raw);\n if (fetchRes !== undefined && fetchRes !== null) {\n emailToken = fetchRes.email_token ?? undefined;\n }\n } catch {\n // ignore\n }\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n try {\n const raw = await paystack?.subscription?.manageLink(subscriptionCode);\n const linkRes = unwrapSdkResult<{ link: string }>(raw);\n const link = linkRes?.link;\n if (link !== undefined && link !== null && link !== \"\") {\n emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);\n }\n } catch {\n // ignore\n }\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Could not retrieve email_token for subscription enable.\",\n });\n }\n\n await paystack?.subscription?.enable({\n body: { code: subscriptionCode, token: emailToken },\n });\n\n // Update local status immediately\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: \"active\",\n updatedAt: new Date(),\n },\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n return ctx.json({ status: \"success\" });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to enable subscription\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_ENABLE_SUBSCRIPTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_ENABLE_SUBSCRIPTION\",\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const getSubscriptionManageLink = <P extends string = \"/subscription-manage-link\">(\n options: AnyPaystackOptions,\n path: P = \"/subscription-manage-link\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n query: z.ZodObject<\n {\n subscriptionCode: z.ZodString;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n link: string | null;\n }\n> => {\n const manageLinkQuerySchema = z.object({\n subscriptionCode: z.string(),\n });\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [\n sessionMiddleware,\n originCheck,\n referenceMiddleware(options, \"get-subscription-manage-link\"),\n ]\n : [sessionMiddleware, originCheck];\n\n const handler = async (ctx: GenericEndpointContext) => {\n const { subscriptionCode } = ctx.query;\n\n if (\n (subscriptionCode as string).startsWith(\"LOC_\") ||\n (subscriptionCode as string).startsWith(\"sub_local_\")\n ) {\n return ctx.json({ link: null, message: \"Local subscriptions cannot be managed on Paystack\" });\n }\n\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.subscription?.manageLink(subscriptionCode as string);\n const res = unwrapSdkResult<{ link: string }>(raw);\n return ctx.json({ link: res?.link || null });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to get subscription manage link\", error);\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to get subscription manage link\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n };\n\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n query: manageLinkQuerySchema,\n use: useMiddlewares,\n },\n handler,\n );\n};\n\nexport const syncProducts = <P extends string = \"/sync-products\">(\n options: AnyPaystackOptions,\n path: P = \"/sync-products\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n metadata: {\n scope: \"server\";\n };\n disableBody: true;\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n | {\n products: never[];\n }\n | {\n status: string;\n count: number;\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n metadata: { ...HIDE_METADATA },\n disableBody: true,\n use: [sessionMiddleware],\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.product?.list({});\n const productsData =\n unwrapSdkResult<components[\"schemas\"][\"ProductListsResponseArray\"][]>(raw);\n\n if (!Array.isArray(productsData)) {\n return ctx.json({ products: [] });\n }\n\n for (const product of productsData) {\n const paystackId = String(product.id);\n const existing = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const productFields = {\n name: product.name ?? \"\",\n description: product.description ?? \"\",\n price: product.price ?? 0,\n currency: product.currency ?? \"\",\n quantity: product.quantity ?? 0,\n unlimited:\n product.unlimited !== undefined &&\n product.unlimited !== null &&\n product.unlimited !== false,\n paystackId,\n slug:\n (product as unknown as { slug?: string }).slug ??\n product.name?.toLowerCase().replace(/\\s+/g, \"-\") ??\n \"\",\n metadata:\n (product as unknown as { metadata?: unknown }).metadata !== undefined &&\n (product as unknown as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((product as unknown as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: productFields,\n where: [{ field: \"id\", value: String(existing.id) }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackProduct\",\n data: { ...productFields, createdAt: new Date() },\n });\n }\n }\n\n return ctx.json({ status: \"success\", count: productsData.length });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync products\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync products\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const listProducts = <P extends string = \"/list-products\">(\n _options: AnyPaystackOptions,\n path: P = \"/list-products\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n products: PaystackProduct[];\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n metadata: {\n openapi: {\n operationId: \"listPaystackProducts\",\n },\n },\n },\n async (ctx) => {\n const res = await ctx.context.adapter.findMany<PaystackProduct>({\n model: \"paystackProduct\",\n });\n const sorted = res.sort((a, b) => a.name.localeCompare(b.name));\n return ctx.json({ products: sorted });\n },\n );\n};\n\nexport const syncPlans = <P extends string = \"/sync-plans\">(\n options: AnyPaystackOptions,\n path: P = \"/sync-plans\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n metadata: {\n scope: \"server\";\n };\n disableBody: true;\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n {\n status: string;\n count: number;\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n metadata: { ...HIDE_METADATA },\n disableBody: true,\n use: [sessionMiddleware],\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.plan?.list();\n const plansData = unwrapSdkResult<components[\"schemas\"][\"PlanListResponseArray\"][]>(raw);\n\n if (!Array.isArray(plansData)) {\n return ctx.json({ status: \"success\", count: 0 });\n }\n\n for (const plan of plansData) {\n const paystackId = String(plan.id);\n const existing = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const planData = {\n name: plan.name ?? \"\",\n description: plan.description ?? \"\",\n amount: plan.amount ?? 0,\n currency: plan.currency ?? \"\",\n interval: plan.interval ?? \"\",\n planCode: plan.plan_code ?? \"\",\n paystackId,\n metadata:\n (plan as unknown as { metadata?: unknown }).metadata !== undefined &&\n (plan as unknown as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((plan as unknown as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackPlan\",\n update: planData,\n where: [{ field: \"id\", value: existing.id! }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackPlan\",\n data: { ...planData, createdAt: new Date() },\n });\n }\n }\n\n return ctx.json({ status: \"success\", count: plansData.length });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync plans\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync plans\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const listPlans = <P extends string = \"/list-plans\">(\n _options: AnyPaystackOptions,\n path: P = \"/list-plans\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n metadata: {\n scope: \"server\";\n };\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n {\n plans: PaystackPlan[];\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n metadata: { ...HIDE_METADATA },\n use: [sessionMiddleware],\n },\n async (ctx) => {\n try {\n const plans = await ctx.context.adapter.findMany<PaystackPlan>({\n model: \"paystackPlan\",\n });\n return ctx.json({ plans });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to list plans\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to list plans\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const getConfig = <P extends string = \"/get-config\">(\n options: AnyPaystackOptions,\n path: P = \"/get-config\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n plans: PaystackPlan[];\n products: PaystackProduct[];\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n metadata: {\n openapi: {\n operationId: \"getPaystackConfig\",\n },\n },\n },\n async (ctx: GenericEndpointContext) => {\n const plans =\n options.subscription?.enabled === true ? await getPlans(options.subscription) : [];\n const products = await getProducts(options.products);\n return ctx.json({ plans, products });\n },\n );\n};\n\nexport { PAYSTACK_ERROR_CODES };\n\nexport const chargeRecurringSubscription = <P extends string = \"/charge-recurring-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/charge-recurring-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n subscriptionId: z.ZodString;\n amount: z.ZodOptional<z.ZodNumber>;\n },\n z.core.$strip\n >;\n },\n {\n status: string;\n data: {\n id: number;\n domain: string;\n status: string;\n reference: string;\n receipt_number: string | null;\n amount: number;\n message: string | null;\n gateway_response: string;\n channel: string;\n currency: string;\n ip_address: string | null;\n metadata: (string | Record<string, never> | number) | null;\n log: {\n start_time: number;\n time_spent: number;\n attempts: number;\n errors: number;\n success: boolean;\n mobile: boolean;\n input: unknown[];\n history: {\n type: string;\n message: string;\n time: number;\n }[];\n } | null;\n fees: number | null;\n fees_split: unknown;\n authorization: {\n authorization_code?: string;\n bin?: string | null;\n last4?: string;\n exp_month?: string;\n exp_year?: string;\n channel?: string;\n card_type?: string;\n bank?: string;\n country_code?: string;\n brand?: string;\n reusable?: boolean;\n signature?: string;\n account_name?: string | null;\n receiver_bank_account_number?: string | null;\n receiver_bank?: string | null;\n };\n customer: {\n id: number;\n first_name: string | null;\n last_name: string | null;\n email: string;\n customer_code: string;\n phone: string | null;\n metadata: Record<string, never> | null;\n risk_action: string;\n international_format_phone?: string | null;\n };\n plan: (string | Record<string, never>) | null;\n split: Record<string, never> | null;\n order_id: unknown;\n paidAt: string | null;\n createdAt: string;\n requested_amount: number;\n pos_transaction_data: unknown;\n source: unknown;\n fees_breakdown: unknown;\n connect: unknown;\n transaction_date: string;\n plan_object: {\n id?: number;\n name?: string;\n plan_code?: string;\n description?: unknown;\n amount?: number;\n interval?: string;\n send_invoices?: boolean;\n send_sms?: boolean;\n currency?: string;\n };\n subaccount: Record<string, never> | null;\n };\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n body: z.object({\n subscriptionId: z.string(),\n amount: z.number().optional(),\n }),\n },\n async (ctx) => {\n const { subscriptionId, amount: bodyAmount } = ctx.body;\n const subscription = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"id\", value: subscriptionId }],\n });\n\n if (subscription === undefined || subscription === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Subscription not found\" });\n }\n\n if (\n subscription.paystackAuthorizationCode === undefined ||\n subscription.paystackAuthorizationCode === null ||\n subscription.paystackAuthorizationCode === \"\"\n ) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"No authorization code found for this subscription\",\n });\n }\n\n const plans = await getPlans(options.subscription);\n const plan = plans.find((p) => p.name.toLowerCase() === subscription.plan.toLowerCase());\n\n if (plan === undefined || plan === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Plan not found\" });\n }\n\n const amount = bodyAmount ?? plan.amount;\n if (amount === undefined || amount === null) {\n throw new APIError(\"BAD_REQUEST\", { message: \"Plan amount is not defined\" });\n }\n\n let email: string | undefined;\n const referenceId = subscription.referenceId;\n if (referenceId !== undefined && referenceId !== null && referenceId !== \"\") {\n const user = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (user !== undefined && user !== null) {\n email = user.email;\n } else if (options.organization?.enabled === true) {\n const ownerMember = await ctx.context.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: referenceId },\n { field: \"role\", value: \"owner\" },\n ],\n });\n if (ownerMember !== undefined && ownerMember !== null) {\n const ownerUser = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: ownerMember.userId }],\n });\n email = ownerUser?.email;\n }\n }\n }\n\n if (email === undefined || email === null || email === \"\") {\n throw new APIError(\"NOT_FOUND\", { message: \"User email not found\" });\n }\n\n const finalCurrency = plan.currency ?? \"NGN\";\n if (!validateMinAmount(amount, finalCurrency)) {\n throw new APIError(\"BAD_REQUEST\", {\n message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,\n status: 400,\n });\n }\n\n const paystack = getPaystackOps(options.paystackClient);\n const chargeResRaw = await paystack?.transaction?.chargeAuthorization({\n body: {\n email,\n amount,\n authorization_code: subscription.paystackAuthorizationCode,\n reference: `rec_${subscription.id}_${Date.now()}`,\n metadata: {\n subscriptionId,\n referenceId,\n },\n },\n });\n\n const chargeData = unwrapSdkResult<PaystackTransactionResponse>(chargeResRaw);\n\n if (chargeData?.status === \"success\" && chargeData.reference !== undefined) {\n const now = new Date();\n const nextPeriodEnd = getNextPeriodEnd(now, plan.interval ?? \"monthly\");\n\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n periodStart: now,\n periodEnd: nextPeriodEnd,\n updatedAt: now,\n paystackTransactionReference: chargeData.reference,\n },\n where: [{ field: \"id\", value: subscription.id }],\n });\n\n return ctx.json({ status: \"success\", data: chargeData });\n }\n\n return ctx.json({ status: \"failed\", data: chargeData }, { status: 400 });\n },\n );\n};\n","import type { BetterAuthPluginDBSchema } from \"@better-auth/core/db\";\nimport { mergeSchema } from \"better-auth/db\";\n\nimport type { PaystackOptions } from \"./types\";\n\nexport const transactions: BetterAuthPluginDBSchema = {\n paystackTransaction: {\n fields: {\n reference: {\n type: \"string\",\n required: true,\n unique: true,\n },\n paystackId: {\n type: \"string\",\n required: false,\n },\n referenceId: {\n type: \"string\",\n required: true,\n index: true,\n },\n userId: {\n type: \"string\",\n required: true,\n index: true,\n },\n amount: {\n type: \"number\",\n required: true,\n },\n currency: {\n type: \"string\",\n required: true,\n },\n status: {\n type: \"string\",\n required: true,\n },\n plan: {\n type: \"string\",\n required: false,\n },\n product: {\n type: \"string\",\n required: false,\n },\n metadata: {\n type: \"string\",\n required: false,\n },\n createdAt: {\n type: \"date\",\n required: true,\n },\n updatedAt: {\n type: \"date\",\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const subscriptions: BetterAuthPluginDBSchema = {\n subscription: {\n fields: {\n plan: {\n type: \"string\",\n required: true,\n index: true,\n },\n referenceId: {\n type: \"string\",\n required: true,\n index: true,\n },\n paystackCustomerCode: {\n type: \"string\",\n required: false,\n index: true,\n },\n paystackSubscriptionCode: {\n type: \"string\",\n required: false,\n unique: true,\n },\n paystackTransactionReference: {\n type: \"string\",\n required: false,\n index: true,\n },\n paystackAuthorizationCode: {\n type: \"string\",\n required: false,\n },\n paystackEmailToken: {\n type: \"string\",\n required: false,\n },\n status: {\n type: \"string\",\n defaultValue: \"incomplete\",\n },\n periodStart: {\n type: \"date\",\n required: false,\n },\n periodEnd: {\n type: \"date\",\n required: false,\n },\n trialStart: {\n type: \"date\",\n required: false,\n },\n trialEnd: {\n type: \"date\",\n required: false,\n },\n cancelAtPeriodEnd: {\n type: \"boolean\",\n required: false,\n defaultValue: false,\n },\n groupId: {\n type: \"string\",\n required: false,\n },\n seats: {\n type: \"number\",\n required: false,\n },\n pendingPlan: {\n type: \"string\",\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const user: BetterAuthPluginDBSchema = {\n user: {\n fields: {\n paystackCustomerCode: {\n type: \"string\",\n required: false,\n index: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const organization: BetterAuthPluginDBSchema = {\n organization: {\n fields: {\n paystackCustomerCode: {\n type: \"string\",\n required: false,\n index: true,\n },\n email: {\n type: \"string\",\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const products: BetterAuthPluginDBSchema = {\n paystackProduct: {\n fields: {\n name: {\n type: \"string\",\n required: true,\n },\n description: {\n type: \"string\",\n required: false,\n },\n price: {\n type: \"number\",\n required: true,\n },\n currency: {\n type: \"string\",\n required: true,\n },\n quantity: {\n type: \"number\",\n required: false,\n defaultValue: 0,\n },\n unlimited: {\n type: \"boolean\",\n required: false,\n defaultValue: true,\n },\n paystackId: {\n type: \"string\",\n required: false,\n unique: true,\n },\n slug: {\n type: \"string\",\n required: true,\n unique: true,\n },\n metadata: {\n type: \"string\",\n required: false,\n },\n createdAt: {\n type: \"date\",\n required: true,\n },\n updatedAt: {\n type: \"date\",\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const plans: BetterAuthPluginDBSchema = {\n paystackPlan: {\n fields: {\n name: {\n type: \"string\",\n required: true,\n },\n description: {\n type: \"string\",\n required: false,\n },\n amount: {\n type: \"number\",\n required: true,\n },\n currency: {\n type: \"string\",\n required: true,\n },\n interval: {\n type: \"string\",\n required: true,\n },\n planCode: {\n type: \"string\",\n required: true,\n unique: true,\n },\n paystackId: {\n type: \"string\",\n required: true,\n unique: true,\n },\n metadata: {\n type: \"string\",\n required: false,\n },\n createdAt: {\n type: \"date\",\n required: true,\n },\n updatedAt: {\n type: \"date\",\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = (options: PaystackOptions): BetterAuthPluginDBSchema => {\n let baseSchema: BetterAuthPluginDBSchema;\n\n if (options.subscription?.enabled === true) {\n baseSchema = {\n ...subscriptions,\n ...transactions,\n ...user,\n ...products,\n ...plans,\n };\n } else {\n baseSchema = {\n ...user,\n ...transactions,\n ...products,\n ...plans,\n };\n }\n\n // Add organization schema if organization support is enabled\n if (options.organization?.enabled === true) {\n baseSchema = {\n ...baseSchema,\n ...organization,\n };\n }\n\n if (\n options.schema !== undefined &&\n options.subscription?.enabled !== true &&\n \"subscription\" in options.schema\n ) {\n const { subscription: _subscription, ...restSchema } = options.schema;\n return mergeSchema(baseSchema, restSchema);\n }\n\n return mergeSchema(baseSchema, options.schema);\n};\n","import { APIError } from \"better-auth/api\";\nimport type { GenericEndpointContext } from \"better-auth\";\nimport type { components } from \"@alexasomba/paystack-node\";\n\nimport { getPaystackOps, unwrapSdkResult } from \"./paystack-sdk\";\nimport { getNextPeriodEnd, getPlans, validateMinAmount } from \"./utils\";\nimport type {\n AnyPaystackOptions,\n ChargeRecurringSubscriptionInput,\n ChargeRecurringSubscriptionResult,\n Member,\n PaystackPlan,\n PaystackProduct,\n PaystackSyncResult,\n PaystackTransactionResponse,\n Subscription,\n User,\n} from \"./types\";\n\nexport async function syncPaystackProducts(\n ctx: GenericEndpointContext,\n options: AnyPaystackOptions,\n): Promise<PaystackSyncResult> {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.product?.list({});\n const productsData = unwrapSdkResult<components[\"schemas\"][\"ProductListsResponseArray\"][]>(raw);\n\n if (!Array.isArray(productsData)) {\n return { status: \"success\", count: 0 };\n }\n\n for (const product of productsData) {\n const paystackId = String(product.id);\n const existing = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const productFields = {\n name: product.name ?? \"\",\n description: product.description ?? \"\",\n price: product.price ?? 0,\n currency: product.currency ?? \"\",\n quantity: product.quantity ?? 0,\n unlimited:\n product.unlimited !== undefined &&\n product.unlimited !== null &&\n product.unlimited !== false,\n paystackId,\n slug:\n (product as { slug?: string }).slug ??\n product.name?.toLowerCase().replace(/\\s+/g, \"-\") ??\n \"\",\n metadata:\n (product as { metadata?: unknown }).metadata !== undefined &&\n (product as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((product as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: productFields,\n where: [{ field: \"id\", value: String(existing.id) }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackProduct\",\n data: { ...productFields, createdAt: new Date() },\n });\n }\n }\n\n return { status: \"success\", count: productsData.length };\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync products\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync products\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n}\n\nexport async function syncPaystackPlans(\n ctx: GenericEndpointContext,\n options: AnyPaystackOptions,\n): Promise<PaystackSyncResult> {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.plan?.list();\n const plansData = unwrapSdkResult<components[\"schemas\"][\"PlanListResponseArray\"][]>(raw);\n\n if (!Array.isArray(plansData)) {\n return { status: \"success\", count: 0 };\n }\n\n for (const plan of plansData) {\n const paystackId = String(plan.id);\n const existing = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const planData = {\n name: plan.name ?? \"\",\n description: plan.description ?? \"\",\n amount: plan.amount ?? 0,\n currency: plan.currency ?? \"\",\n interval: plan.interval ?? \"\",\n planCode: plan.plan_code ?? \"\",\n paystackId,\n metadata:\n (plan as { metadata?: unknown }).metadata !== undefined &&\n (plan as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((plan as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackPlan\",\n update: planData,\n where: [{ field: \"id\", value: existing.id! }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackPlan\",\n data: { ...planData, createdAt: new Date() },\n });\n }\n }\n\n return { status: \"success\", count: plansData.length };\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync plans\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync plans\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n}\n\nexport async function chargeSubscriptionRenewal(\n ctx: GenericEndpointContext,\n options: AnyPaystackOptions,\n input: ChargeRecurringSubscriptionInput,\n): Promise<ChargeRecurringSubscriptionResult> {\n const { subscriptionId, amount: bodyAmount } = input;\n const subscription = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"id\", value: subscriptionId }],\n });\n\n if (subscription === undefined || subscription === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Subscription not found\" });\n }\n\n if (\n subscription.paystackAuthorizationCode === undefined ||\n subscription.paystackAuthorizationCode === null ||\n subscription.paystackAuthorizationCode === \"\"\n ) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"No authorization code found for this subscription\",\n });\n }\n\n const plans = await getPlans(options.subscription);\n const plan = plans.find(\n (candidate) => candidate.name.toLowerCase() === subscription.plan.toLowerCase(),\n );\n\n if (plan === undefined || plan === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Plan not found\" });\n }\n\n const amount = bodyAmount ?? plan.amount;\n if (amount === undefined || amount === null) {\n throw new APIError(\"BAD_REQUEST\", { message: \"Plan amount is not defined\" });\n }\n\n let email: string | undefined;\n const referenceId = subscription.referenceId;\n if (referenceId !== undefined && referenceId !== null && referenceId !== \"\") {\n const user = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (user !== undefined && user !== null) {\n email = user.email;\n } else if (options.organization?.enabled === true) {\n const ownerMember = await ctx.context.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: referenceId },\n { field: \"role\", value: \"owner\" },\n ],\n });\n if (ownerMember !== undefined && ownerMember !== null) {\n const ownerUser = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: ownerMember.userId }],\n });\n email = ownerUser?.email;\n }\n }\n }\n\n if (email === undefined || email === null || email === \"\") {\n throw new APIError(\"NOT_FOUND\", { message: \"User email not found\" });\n }\n\n const finalCurrency = plan.currency ?? \"NGN\";\n if (!validateMinAmount(amount, finalCurrency)) {\n throw new APIError(\"BAD_REQUEST\", {\n message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,\n status: 400,\n });\n }\n\n const paystack = getPaystackOps(options.paystackClient);\n const chargeResRaw = await paystack?.transaction?.chargeAuthorization({\n body: {\n email,\n amount,\n authorization_code: subscription.paystackAuthorizationCode,\n reference: `rec_${subscription.id}_${Date.now()}`,\n metadata: {\n subscriptionId,\n referenceId,\n },\n },\n });\n\n const chargeData = unwrapSdkResult<PaystackTransactionResponse>(chargeResRaw);\n if (chargeData?.status === \"success\" && chargeData.reference !== undefined) {\n const now = new Date();\n const nextPeriodEnd = getNextPeriodEnd(now, plan.interval ?? \"monthly\");\n\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n periodStart: now,\n periodEnd: nextPeriodEnd,\n updatedAt: now,\n paystackTransactionReference: chargeData.reference,\n },\n where: [{ field: \"id\", value: subscription.id }],\n });\n\n return { status: \"success\", data: chargeData };\n }\n\n return { status: \"failed\", data: chargeData };\n}\n","import { defineErrorCodes } from \"@better-auth/core/utils/error-codes\";\nimport type {\n AuthContext,\n BetterAuthPlugin,\n BetterAuthPluginDBSchema,\n GenericEndpointContext,\n MiddlewareInputContext,\n MiddlewareOptions,\n RawError,\n StrictEndpoint,\n ZodBoolean,\n ZodNumber,\n ZodObject,\n ZodOptional,\n ZodRecord,\n ZodString,\n ZodUnknown,\n} from \"better-auth\";\nimport { defu } from \"defu\";\n\nimport {\n disablePaystackSubscription,\n enablePaystackSubscription,\n initializeTransaction,\n listSubscriptions,\n listTransactions,\n paystackWebhook,\n verifyTransaction,\n getConfig,\n getSubscriptionManageLink,\n PAYSTACK_ERROR_CODES,\n createSubscription,\n upgradeSubscription,\n cancelSubscription,\n restoreSubscription,\n listProducts,\n listPlans,\n} from \"./routes\";\nimport { getSchema } from \"./schema\";\nimport { checkSeatLimit, checkTeamLimit, getOrganizationSubscription } from \"./limits\";\nimport { getPlanByName, syncSubscriptionSeats } from \"./utils\";\nimport type {\n PaystackClientLike,\n PaystackOptions,\n PaystackPlan,\n Subscription,\n SubscriptionOptions,\n PaystackProduct,\n PaystackCustomerResponse,\n Member,\n AnyPaystackOptions,\n User,\n PaystackTransaction,\n} from \"./types\";\nimport { getPaystackOps, unwrapSdkResult } from \"./paystack-sdk\";\nimport type { $strip } from \"zod/v4/core\";\n\ndeclare module \"@better-auth/core\" {\n interface BetterAuthPluginRegistry<AuthOptions, Options> {\n paystack: {\n creator: typeof paystack;\n };\n }\n}\n\nconst INTERNAL_ERROR_CODES = defineErrorCodes(\n Object.fromEntries(\n Object.entries(PAYSTACK_ERROR_CODES).map(([key, value]) => [\n key,\n typeof value === \"string\" ? value : (value as { message: string }).message,\n ]),\n ),\n);\n\nexport const paystack = <\n TPaystackClient extends PaystackClientLike = PaystackClientLike,\n O extends PaystackOptions<TPaystackClient> = PaystackOptions<TPaystackClient>,\n>(\n options: O,\n): {\n id: \"paystack\";\n endpoints: {\n initializeTransaction: StrictEndpoint<\n \"/paystack/initialize-transaction\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n plan: ZodOptional<ZodString>;\n product: ZodOptional<ZodString>;\n amount: ZodOptional<ZodNumber>;\n currency: ZodOptional<ZodString>;\n email: ZodOptional<ZodString>;\n metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;\n referenceId: ZodOptional<ZodString>;\n callbackURL: ZodOptional<ZodString>;\n quantity: ZodOptional<ZodNumber>;\n scheduleAtPeriodEnd: ZodOptional<ZodBoolean>;\n cancelAtPeriodEnd: ZodOptional<ZodBoolean>;\n prorateAndCharge: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n >;\n verifyTransaction: StrictEndpoint<\n \"/paystack/verify-transaction\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n reference: ZodString;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n reference: string;\n data: {\n id: number;\n domain: string;\n status: string;\n reference: string;\n receipt_number: string | null;\n amount: number;\n message: string | null;\n gateway_response: string;\n channel: string;\n currency: string;\n ip_address: string | null;\n metadata: (string | Record<string, never> | number) | null;\n log: {\n start_time: number;\n time_spent: number;\n attempts: number;\n errors: number;\n success: boolean;\n mobile: boolean;\n input: unknown[];\n history: {\n type: string;\n message: string;\n time: number;\n }[];\n } | null;\n fees: number | null;\n fees_split: unknown;\n authorization: {\n authorization_code?: string;\n bin?: string | null;\n last4?: string;\n exp_month?: string;\n exp_year?: string;\n channel?: string;\n card_type?: string;\n bank?: string;\n country_code?: string;\n brand?: string;\n reusable?: boolean;\n signature?: string;\n account_name?: string | null;\n receiver_bank_account_number?: string | null;\n receiver_bank?: string | null;\n };\n customer: {\n id: number;\n first_name: string | null;\n last_name: string | null;\n email: string;\n customer_code: string;\n phone: string | null;\n metadata: Record<string, never> | null;\n risk_action: string;\n international_format_phone?: string | null;\n };\n plan: (string | Record<string, never>) | null;\n split: Record<string, never> | null;\n order_id: unknown;\n paidAt: string | null;\n createdAt: string;\n requested_amount: number;\n pos_transaction_data: unknown;\n source: unknown;\n fees_breakdown: unknown;\n connect: unknown;\n transaction_date: string;\n plan_object: {\n id?: number;\n name?: string;\n plan_code?: string;\n description?: unknown;\n amount?: number;\n interval?: string;\n send_invoices?: boolean;\n send_sms?: boolean;\n currency?: string;\n };\n subaccount: Record<string, never> | null;\n };\n }\n >;\n listSubscriptions: StrictEndpoint<\n \"/paystack/list-subscriptions\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n subscriptions: Subscription[];\n }\n >;\n paystackWebhook: StrictEndpoint<\n \"/paystack/webhook\",\n {\n method: \"POST\";\n metadata: {\n openapi: {\n operationId: string;\n };\n scope: \"server\";\n };\n cloneRequest: true;\n disableBody: true;\n },\n {\n received: boolean;\n }\n >;\n listTransactions: StrictEndpoint<\n \"/paystack/list-transactions\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n transactions: PaystackTransaction[];\n }\n >;\n getConfig: StrictEndpoint<\n \"/paystack/config\",\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n plans: PaystackPlan[];\n products: PaystackProduct[];\n }\n >;\n disableSubscription: StrictEndpoint<\n \"/paystack/disable-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n enableSubscription: StrictEndpoint<\n \"/paystack/enable-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n getSubscriptionManageLink: StrictEndpoint<\n \"/paystack/subscription-manage-link\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n subscriptionCode: ZodString;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n link: string | null;\n }\n >;\n subscriptionManageLink: StrictEndpoint<\n \"/paystack/subscription/manage-link\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n subscriptionCode: ZodString;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n link: string | null;\n }\n >;\n createSubscription: StrictEndpoint<\n \"/paystack/create-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n plan: ZodOptional<ZodString>;\n product: ZodOptional<ZodString>;\n amount: ZodOptional<ZodNumber>;\n currency: ZodOptional<ZodString>;\n email: ZodOptional<ZodString>;\n metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;\n referenceId: ZodOptional<ZodString>;\n callbackURL: ZodOptional<ZodString>;\n quantity: ZodOptional<ZodNumber>;\n scheduleAtPeriodEnd: ZodOptional<ZodBoolean>;\n cancelAtPeriodEnd: ZodOptional<ZodBoolean>;\n prorateAndCharge: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n >;\n upgradeSubscription: StrictEndpoint<\n \"/paystack/upgrade-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n plan: ZodOptional<ZodString>;\n product: ZodOptional<ZodString>;\n amount: ZodOptional<ZodNumber>;\n currency: ZodOptional<ZodString>;\n email: ZodOptional<ZodString>;\n metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;\n referenceId: ZodOptional<ZodString>;\n callbackURL: ZodOptional<ZodString>;\n quantity: ZodOptional<ZodNumber>;\n scheduleAtPeriodEnd: ZodOptional<ZodBoolean>;\n cancelAtPeriodEnd: ZodOptional<ZodBoolean>;\n prorateAndCharge: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n >;\n cancelSubscription: StrictEndpoint<\n \"/paystack/cancel-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n restoreSubscription: StrictEndpoint<\n \"/paystack/restore-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n listProducts: StrictEndpoint<\n \"/paystack/list-products\",\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n products: PaystackProduct[];\n }\n >;\n listPlans: StrictEndpoint<\n \"/paystack/list-plans\",\n {\n method: \"GET\";\n metadata: {\n scope: \"server\";\n };\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n {\n plans: PaystackPlan[];\n }\n >;\n };\n schema: BetterAuthPluginDBSchema;\n init: (ctx: AuthContext) => {\n options: {\n databaseHooks: {\n user: {\n create: {\n after(\n user: { id: string; email?: string | null; name?: string | null },\n hookCtx?: GenericEndpointContext | null,\n ): Promise<void>;\n };\n };\n organization:\n | {\n create: {\n after(\n org: { id: string; name: string; email?: string | null },\n hookCtx: GenericEndpointContext | null,\n ): Promise<void>;\n };\n }\n | undefined;\n };\n member: {\n create: {\n before: (\n member: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n after: (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n delete: {\n after: (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n };\n invitation: {\n create: {\n before: (\n invitation: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n after: (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n delete: {\n after: (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n };\n team: {\n create: {\n before: (\n team: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n };\n };\n };\n $ERROR_CODES: Record<string, RawError<string>>;\n options: NoInfer<O>;\n} => {\n const routeOptions = options as unknown as AnyPaystackOptions;\n return {\n id: \"paystack\",\n endpoints: {\n initializeTransaction: initializeTransaction(\n routeOptions,\n \"/paystack/initialize-transaction\",\n ),\n verifyTransaction: verifyTransaction(routeOptions, \"/paystack/verify-transaction\"),\n listSubscriptions: listSubscriptions(routeOptions, \"/paystack/list-subscriptions\"),\n paystackWebhook: paystackWebhook(routeOptions, \"/paystack/webhook\"),\n listTransactions: listTransactions(routeOptions, \"/paystack/list-transactions\"),\n getConfig: getConfig(routeOptions, \"/paystack/config\"),\n disableSubscription: disablePaystackSubscription(\n routeOptions,\n \"/paystack/disable-subscription\",\n ),\n enableSubscription: enablePaystackSubscription(routeOptions, \"/paystack/enable-subscription\"),\n getSubscriptionManageLink: getSubscriptionManageLink(\n routeOptions,\n \"/paystack/subscription-manage-link\",\n ),\n subscriptionManageLink: getSubscriptionManageLink(\n routeOptions,\n \"/paystack/subscription/manage-link\",\n ),\n createSubscription: createSubscription(routeOptions, \"/paystack/create-subscription\"),\n upgradeSubscription: upgradeSubscription(routeOptions, \"/paystack/upgrade-subscription\"),\n cancelSubscription: cancelSubscription(routeOptions, \"/paystack/cancel-subscription\"),\n restoreSubscription: restoreSubscription(routeOptions, \"/paystack/restore-subscription\"),\n listProducts: listProducts(routeOptions, \"/paystack/list-products\"),\n listPlans: listPlans(routeOptions, \"/paystack/list-plans\"),\n },\n schema: getSchema(options),\n init: (ctx: AuthContext) => {\n return {\n options: {\n databaseHooks: {\n user: {\n create: {\n async after(\n user: { id: string; email?: string | null; name?: string | null },\n hookCtx?: GenericEndpointContext | null,\n ) {\n if (\n !hookCtx ||\n options.createCustomerOnSignUp !== true ||\n user.email === null ||\n user.email === undefined ||\n user.email === \"\"\n )\n return;\n\n try {\n const paystackOps = getPaystackOps(\n options.paystackClient as PaystackClientLike,\n );\n if (!paystackOps) return;\n const raw =\n (await paystackOps.customer?.create({\n body: {\n email: user.email,\n first_name: user.name ?? undefined,\n metadata: {\n userId: user.id,\n },\n },\n })) ??\n (await Promise.reject(new Error(\"Paystack client missing customer ops\")));\n const sdkRes = unwrapSdkResult<PaystackCustomerResponse>(raw);\n const customerCode = sdkRes?.customer_code;\n\n if (\n customerCode !== undefined &&\n customerCode !== null &&\n customerCode !== \"\"\n ) {\n await ctx.adapter.update({\n model: \"user\",\n where: [{ field: \"id\", value: user.id }],\n update: {\n paystackCustomerCode: customerCode,\n },\n });\n\n if (typeof options.onCustomerCreate === \"function\") {\n await options.onCustomerCreate(\n {\n paystackCustomer: sdkRes,\n user: {\n ...(user as User),\n paystackCustomerCode: customerCode,\n },\n },\n hookCtx,\n );\n }\n }\n } catch (error: unknown) {\n ctx.logger.error(\"Failed to create Paystack customer for user\", error);\n }\n },\n },\n },\n organization:\n options.organization?.enabled === true\n ? {\n create: {\n async after(\n org: { id: string; name: string; email?: string | null },\n hookCtx: GenericEndpointContext | null,\n ) {\n try {\n const extraCreateParams =\n typeof options.organization?.getCustomerCreateParams === \"function\"\n ? await (\n options.organization.getCustomerCreateParams as (\n org: Record<string, unknown>,\n hookCtx: GenericEndpointContext,\n ) => Promise<Record<string, unknown>>\n )(org as Record<string, unknown>, hookCtx!)\n : {};\n\n let targetEmail = org.email;\n if (targetEmail === undefined || targetEmail === null) {\n const ownerMember = await ctx.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: org.id },\n { field: \"role\", value: \"owner\" },\n ],\n });\n if (ownerMember !== null && ownerMember !== undefined) {\n const ownerUser = await ctx.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: ownerMember.userId }],\n });\n targetEmail = ownerUser?.email;\n }\n }\n\n if (targetEmail === undefined || targetEmail === null) return;\n\n const params = defu(\n {\n email: targetEmail,\n first_name: org.name,\n metadata: { organizationId: org.id },\n },\n extraCreateParams,\n );\n const paystackOps = getPaystackOps(\n options.paystackClient as PaystackClientLike,\n );\n if (!paystackOps) return;\n const raw =\n (await paystackOps.customer?.create({\n body: params as Record<string, unknown>,\n })) ??\n (await Promise.reject(\n new Error(\"Paystack client missing customer ops\"),\n ));\n const sdkRes = unwrapSdkResult<PaystackCustomerResponse>(raw);\n const customerCode = sdkRes?.customer_code as string | undefined;\n\n if (\n customerCode !== undefined &&\n customerCode !== null &&\n customerCode !== \"\" &&\n sdkRes !== undefined &&\n sdkRes !== null\n ) {\n await (\n ctx.internalAdapter as unknown as {\n updateOrganization: (\n id: string,\n data: Record<string, unknown>,\n ) => Promise<void>;\n }\n ).updateOrganization(org.id, {\n paystackCustomerCode: customerCode,\n });\n\n if (typeof options.organization?.onCustomerCreate === \"function\") {\n await options.organization.onCustomerCreate(\n {\n paystackCustomer: sdkRes,\n organization: {\n ...org,\n paystackCustomerCode: customerCode,\n },\n },\n hookCtx!,\n );\n }\n }\n } catch (error: unknown) {\n ctx.logger.error(\n \"Failed to create Paystack customer for organization\",\n error,\n );\n }\n },\n },\n }\n : undefined,\n },\n member: {\n create: {\n before: async (\n member: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n member.organizationId &&\n ctx !== null &&\n ctx !== undefined\n ) {\n await checkSeatLimit(ctx, member.organizationId);\n }\n },\n after: async (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof member?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, member.organizationId, routeOptions);\n }\n },\n },\n delete: {\n after: async (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof member?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, member.organizationId, routeOptions);\n }\n },\n },\n },\n invitation: {\n create: {\n before: async (\n invitation: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n invitation.organizationId &&\n ctx !== null &&\n ctx !== undefined\n ) {\n await checkSeatLimit(ctx, invitation.organizationId);\n }\n },\n after: async (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof invitation?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, invitation.organizationId, routeOptions);\n }\n },\n },\n delete: {\n after: async (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof invitation?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, invitation.organizationId, routeOptions);\n }\n },\n },\n },\n team: {\n create: {\n before: async (\n team: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (options.subscription?.enabled === true && team.organizationId && ctx) {\n const subscription = await getOrganizationSubscription(ctx, team.organizationId);\n if (subscription !== null && subscription !== undefined) {\n const plan = await getPlanByName(routeOptions, subscription.plan);\n const limits = plan?.limits;\n const maxTeams = limits?.teams as number | undefined;\n\n if (typeof maxTeams === \"number\") {\n await checkTeamLimit(ctx, team.organizationId, maxTeams);\n }\n }\n }\n },\n },\n },\n },\n };\n },\n $ERROR_CODES: INTERNAL_ERROR_CODES,\n options: options as NoInfer<O>,\n } satisfies BetterAuthPlugin;\n};\n\nexport type PaystackPlugin<\n TPaystackClient extends PaystackClientLike = PaystackClientLike,\n O extends PaystackOptions<TPaystackClient> = PaystackOptions<TPaystackClient>,\n> = ReturnType<typeof paystack<TPaystackClient, O>>;\n\nexport { chargeSubscriptionRenewal, syncPaystackPlans, syncPaystackProducts } from \"./operations\";\nexport type { Subscription, SubscriptionOptions, PaystackPlan, PaystackOptions, PaystackProduct };\n"],"mappings":"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pF,MAAM,WAAW,MAAM,YAAY,QAAQ,SAAS;AACpD,SAAO,IAAI,KAAK;GAAE;GAAO;GAAU,CAAC;GAEvC;;;;ACngFH,MAAa,eAAyC,EACpD,qBAAqB,EACnB,QAAQ;CACN,WAAW;EACT,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,YAAY;EACV,MAAM;EACN,UAAU;EACX;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,QAAQ;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,QAAQ;EACN,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,QAAQ;EACN,MAAM;EACN,UAAU;EACX;CACD,MAAM;EACJ,MAAM;EACN,UAAU;EACX;CACD,SAAS;EACP,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACF,EACF,EACF;AAED,MAAa,gBAA0C,EACrD,cAAc,EACZ,QAAQ;CACN,MAAM;EACJ,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,sBAAsB;EACpB,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,0BAA0B;EACxB,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,8BAA8B;EAC5B,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,2BAA2B;EACzB,MAAM;EACN,UAAU;EACX;CACD,oBAAoB;EAClB,MAAM;EACN,UAAU;EACX;CACD,QAAQ;EACN,MAAM;EACN,cAAc;EACf;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACD,YAAY;EACV,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,mBAAmB;EACjB,MAAM;EACN,UAAU;EACV,cAAc;EACf;CACD,SAAS;EACP,MAAM;EACN,UAAU;EACX;CACD,OAAO;EACL,MAAM;EACN,UAAU;EACX;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACX;CACF,EACF,EACF;AAED,MAAa,OAAiC,EAC5C,MAAM,EACJ,QAAQ,EACN,sBAAsB;CACpB,MAAM;CACN,UAAU;CACV,OAAO;CACR,EACF,EACF,EACF;AAED,MAAa,eAAyC,EACpD,cAAc,EACZ,QAAQ;CACN,sBAAsB;EACpB,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,OAAO;EACL,MAAM;EACN,UAAU;EACX;CACF,EACF,EACF;AAED,MAAa,WAAqC,EAChD,iBAAiB,EACf,QAAQ;CACN,MAAM;EACJ,MAAM;EACN,UAAU;EACX;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACX;CACD,OAAO;EACL,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACV,cAAc;EACf;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACV,cAAc;EACf;CACD,YAAY;EACV,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,MAAM;EACJ,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACF,EACF,EACF;AAED,MAAa,QAAkC,EAC7C,cAAc,EACZ,QAAQ;CACN,MAAM;EACJ,MAAM;EACN,UAAU;EACX;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACX;CACD,QAAQ;EACN,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,YAAY;EACV,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EAC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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/paystack-sdk.ts","../src/utils.ts","../src/middleware.ts","../src/limits.ts","../src/routes.ts","../src/schema.ts","../src/operations.ts","../src/index.ts"],"sourcesContent":["import { APIError } from \"better-auth/api\";\nimport { PaystackResponse } from \"@alexasomba/paystack-node\";\nimport type { PaystackClientLike } from \"./types\";\n\n/**\n * Interface for checking if a result is a PaystackResponse from the SDK v1.9.1+\n */\nfunction IsPaystackResponse(value: unknown): value is PaystackResponse<unknown> {\n return value instanceof PaystackResponse;\n}\n\n/**\n * Unwraps a Paystack SDK result, extracting the data or throwing an APIError if the request failed.\n * Leverages the native .unwrap() method in SDK v1.9.1+ if available.\n */\nexport function unwrapSdkResult<T = unknown>(result: unknown): T {\n if (IsPaystackResponse(result)) {\n try {\n return result.unwrap() as T;\n } catch (e: unknown) {\n throw new APIError(\"BAD_REQUEST\", {\n message: (e as Error)?.message ?? \"Paystack API error\",\n });\n }\n }\n\n // Fallback for custom or legacy structures (e.g. from mocks in tests)\n let current = result;\n\n // Handle nested { data: { data: ... } } or { status: true, data: ... }\n while (current !== null && current !== undefined && typeof current === \"object\") {\n const body = current as Record<string, unknown>;\n\n // Check for Paystack Error shape\n if (body.status === false) {\n throw new APIError(\"BAD_REQUEST\", {\n message: (body.message as string | undefined) ?? \"Paystack API error\",\n });\n }\n\n // Stop if we have found the actual transaction/subscription payload properties\n if (\"authorization_url\" in body || \"reference\" in body || \"customer_code\" in body) {\n break;\n }\n\n // If there's a data property, unwrap it and continue checking\n if (\n \"data\" in body &&\n body.data !== undefined &&\n body.data !== null &&\n typeof body.data === \"object\"\n ) {\n current = body.data;\n continue;\n }\n break;\n }\n\n return current as T;\n}\n\n/**\n * Returns the operations object from a Paystack client.\n * For v1.9.1+, the client itself uses the grouped structure.\n */\nexport function getPaystackOps(client?: PaystackClientLike): PaystackClientLike | undefined {\n return client;\n}\n","import type { GenericEndpointContext } from \"better-auth\";\n\nimport type {\n AnyPaystackOptions,\n PaystackClientLike,\n PaystackPlan,\n PaystackProduct,\n Subscription,\n PaystackProductResponse,\n} from \"./types\";\nimport { unwrapSdkResult } from \"./paystack-sdk\";\n\nexport function getPlanSeatAmount(plan: PaystackPlan): number | undefined {\n if (plan.seatAmount !== undefined) {\n if (typeof plan.seatAmount === \"number\" && Number.isFinite(plan.seatAmount)) {\n return plan.seatAmount;\n }\n throw new Error(`Invalid seatAmount for plan '${plan.name}'. Expected a finite number.`);\n }\n\n if (plan.seatPriceId === undefined || plan.seatPriceId === null || plan.seatPriceId === \"\") {\n return undefined;\n }\n\n const parsed = typeof plan.seatPriceId === \"string\" ? Number(plan.seatPriceId) : plan.seatPriceId;\n if (typeof parsed === \"number\" && Number.isFinite(parsed)) {\n return parsed;\n }\n\n throw new Error(\n `Invalid seatPriceId for plan '${plan.name}'. Expected a numeric amount in the smallest currency unit.`,\n );\n}\n\nexport function calculatePlanAmount(plan: PaystackPlan, quantity: number): number {\n return (plan.amount ?? 0) + quantity * (getPlanSeatAmount(plan) ?? 0);\n}\n\nexport function isLocalSubscriptionCode(subscriptionCode: string | undefined | null): boolean {\n return (\n typeof subscriptionCode === \"string\" &&\n (subscriptionCode.startsWith(\"LOC_\") || subscriptionCode.startsWith(\"sub_local_\"))\n );\n}\n\nexport function isLocallyManagedSubscription(\n subscription: Pick<Subscription, \"paystackSubscriptionCode\" | \"paystackPlanCode\">,\n): boolean {\n if (isLocalSubscriptionCode(subscription.paystackSubscriptionCode)) {\n return true;\n }\n\n if (\n typeof subscription.paystackSubscriptionCode === \"string\" &&\n subscription.paystackSubscriptionCode !== \"\"\n ) {\n return false;\n }\n\n return (\n subscription.paystackPlanCode === undefined ||\n subscription.paystackPlanCode === null ||\n subscription.paystackPlanCode === \"\"\n );\n}\n\nexport function assertLocallyManagedSubscription(\n subscription: Pick<Subscription, \"paystackSubscriptionCode\" | \"paystackPlanCode\">,\n action: string,\n): void {\n if (!isLocallyManagedSubscription(subscription)) {\n throw new Error(\n `Paystack-managed subscriptions do not support ${action}. Use local billing for seat-based or prorated subscription changes.`,\n );\n }\n}\n\nexport async function getPlans(\n subscriptionOptions: AnyPaystackOptions[\"subscription\"],\n): Promise<PaystackPlan[]> {\n if (subscriptionOptions?.enabled === true) {\n return typeof subscriptionOptions.plans === \"function\"\n ? subscriptionOptions.plans()\n : subscriptionOptions.plans;\n }\n throw new Error(\"Subscriptions are not enabled in the Paystack options.\");\n}\n\nexport const getPlan: (\n options: AnyPaystackOptions,\n planId: string,\n) => Promise<PaystackPlan | null> = async (options, planId) => {\n if (options.subscription?.enabled === true) {\n const plans = await getPlans(options.subscription);\n return plans.find((plan) => plan.name === planId) ?? null;\n }\n return null;\n};\n\nexport async function getPlanByName(\n options: AnyPaystackOptions,\n name: string,\n): Promise<PaystackPlan | null> {\n if (typeof name !== \"string\" || name.trim() === \"\") {\n return null;\n }\n if (options.subscription?.enabled === true) {\n const plans = await getPlans(options.subscription);\n const normalizedName = name.toLowerCase();\n return (\n plans.find(\n (plan) => typeof plan.name === \"string\" && plan.name.toLowerCase() === normalizedName,\n ) ?? null\n );\n }\n return null;\n}\n\nexport async function getPlanByPriceId(\n options: AnyPaystackOptions,\n priceId: string,\n): Promise<PaystackPlan | null> {\n if (options.subscription?.enabled === true) {\n const plans = await getPlans(options.subscription);\n return plans.find((plan) => plan.name === priceId) ?? null;\n }\n return null;\n}\n\nexport async function getProducts(\n productOptions: AnyPaystackOptions[\"products\"],\n): Promise<PaystackProduct[]> {\n if (productOptions?.products) {\n return typeof productOptions.products === \"function\"\n ? await productOptions.products()\n : productOptions.products;\n }\n return [];\n}\n\nexport async function getProductByName(\n options: AnyPaystackOptions,\n name: string,\n): Promise<PaystackProduct | null> {\n return await getProducts(options.products).then((products) =>\n products !== undefined && products !== null\n ? (products.find((product) => product.name.toLowerCase() === name.toLowerCase()) ?? null)\n : null,\n );\n}\n\nexport function getNextPeriodEnd(startDate: Date, interval: string): Date {\n const date = new Date(startDate);\n switch (interval) {\n case \"daily\":\n date.setDate(date.getDate() + 1);\n break;\n case \"weekly\":\n date.setDate(date.getDate() + 7);\n break;\n case \"monthly\":\n date.setMonth(date.getMonth() + 1);\n break;\n case \"quarterly\":\n date.setMonth(date.getMonth() + 3);\n break;\n case \"biannually\":\n date.setMonth(date.getMonth() + 6);\n break;\n case \"annually\":\n date.setFullYear(date.getFullYear() + 1);\n break;\n default:\n // Default to monthly if unknown\n date.setMonth(date.getMonth() + 1);\n }\n return date;\n}\n\n/**\n * Validates if the amount meets Paystack's minimum transaction requirements.\n * Amounts should be in the smallest currency unit (e.g., kobo, cents).\n */\nexport function validateMinAmount(amount: number, currency: string): boolean {\n const minAmounts: Record<string, number> = {\n NGN: 5000, // 50.00\n GHS: 10, // 0.10\n ZAR: 100, // 1.00\n KES: 300, // 3.00\n USD: 200, // 2.00\n XOF: 100, // 1.00\n };\n const min = minAmounts[currency.toUpperCase()];\n return min !== undefined ? amount >= min : true;\n}\n\nexport async function syncProductQuantityFromPaystack(\n ctx: GenericEndpointContext,\n productName: string,\n paystackClient: PaystackClientLike,\n): Promise<void> {\n // Find the local product record (by name or slug)\n let localProduct = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"name\", value: productName }],\n });\n\n localProduct ??= await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"slug\", value: productName.toLowerCase().replace(/\\s+/g, \"-\") }],\n });\n\n if (\n localProduct?.paystackId === undefined ||\n localProduct.paystackId === null ||\n localProduct.paystackId === \"\"\n ) {\n // No local record with a Paystack ID - fall back to local decrement\n if (\n localProduct?.id !== undefined &&\n localProduct.unlimited !== true &&\n typeof localProduct.quantity === \"number\" &&\n localProduct.quantity > 0\n ) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: { quantity: localProduct.quantity - 1, updatedAt: new Date() },\n where: [{ field: \"id\", value: localProduct.id }],\n });\n }\n return;\n }\n\n // Fetch the latest quantity from Paystack\n try {\n const paystackProductId = Number(localProduct.paystackId);\n if (!Number.isFinite(paystackProductId)) {\n return;\n }\n const raw = await paystackClient.product?.fetch(paystackProductId);\n const sdkRes = unwrapSdkResult<PaystackProductResponse>(raw);\n const remoteQuantity = sdkRes?.quantity;\n\n if (remoteQuantity !== undefined && localProduct.id !== undefined) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: { quantity: remoteQuantity, updatedAt: new Date() },\n where: [{ field: \"id\", value: localProduct.id }],\n });\n }\n } catch {\n // If API call fails, fall back to local decrement\n if (\n localProduct?.id !== undefined &&\n localProduct.unlimited !== true &&\n typeof localProduct.quantity === \"number\" &&\n localProduct.quantity > 0\n ) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: { quantity: localProduct.quantity - 1, updatedAt: new Date() },\n where: [{ field: \"id\", value: localProduct.id }],\n });\n }\n }\n}\n\nexport async function decrementProductQuantity(\n ctx: GenericEndpointContext,\n productName: string,\n): Promise<void> {\n let product = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"name\", value: productName }],\n });\n\n product ??= await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"slug\", value: productName.toLowerCase().replace(/\\s+/g, \"-\") }],\n });\n\n if (product !== undefined && product !== null) {\n if (\n product.unlimited !== true &&\n typeof product.quantity === \"number\" &&\n product.quantity > 0 &&\n product.id !== undefined\n ) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: {\n quantity: product.quantity - 1,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: product.id }],\n });\n }\n }\n}\n\nexport async function syncSubscriptionSeats(\n ctx: GenericEndpointContext,\n organizationId: string,\n options: AnyPaystackOptions,\n): Promise<void> {\n if (options.subscription?.enabled !== true) return;\n\n const adapter = ctx.context.adapter;\n const subscription = await adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: organizationId }],\n });\n\n if (\n subscription?.paystackSubscriptionCode === undefined ||\n subscription.paystackSubscriptionCode === null ||\n subscription.paystackSubscriptionCode === \"\"\n )\n return;\n if (subscription === null || subscription === undefined) return;\n const plan = await getPlanByName(options, subscription.plan);\n if (plan === null || plan === undefined) return;\n const seatAmount = getPlanSeatAmount(plan);\n if (seatAmount === undefined) return;\n\n const members = await adapter.findMany({\n model: \"member\",\n where: [{ field: \"organizationId\", value: organizationId }],\n });\n\n const quantity = members.length;\n\n try {\n assertLocallyManagedSubscription(subscription, \"automatic seat sync\");\n\n // Locally managed subscriptions renew via saved authorizations, so seat count lives in our DB.\n await adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: subscription.id }],\n update: {\n seats: quantity,\n updatedAt: new Date(),\n },\n });\n } catch (e: unknown) {\n const log = ctx.context.logger;\n if (log !== undefined && log !== null) {\n log.error(\"Failed to sync subscription seats\", e);\n }\n }\n}\n","import { createAuthMiddleware, type AuthMiddleware } from \"@better-auth/core/api\";\nimport { logger } from \"better-auth\";\nimport { APIError } from \"better-auth/api\";\n\nimport type { PaystackOptions, Session, User } from \"./types\";\n\nexport const referenceMiddleware = (\n options: PaystackOptions,\n action:\n | \"initialize-transaction\"\n | \"verify-transaction\"\n | \"list-subscriptions\"\n | \"list-transactions\"\n | \"disable-subscription\"\n | \"enable-subscription\"\n | \"get-subscription-manage-link\",\n): AuthMiddleware =>\n createAuthMiddleware(async (ctx) => {\n const session = ctx.context.session as {\n user: User;\n session: Session;\n } | null;\n\n if (session === null || session === undefined) {\n throw new APIError(\"UNAUTHORIZED\");\n }\n const body = (ctx.body ?? {}) as Record<string, unknown>;\n const query = (ctx.query ?? {}) as Record<string, unknown>;\n const referenceId =\n (body.referenceId as string | undefined) ??\n (query.referenceId as string | undefined) ??\n session.user.id;\n\n const subscriptionOptions = options.subscription;\n\n if (referenceId === session.user.id) {\n return {\n referenceId,\n };\n }\n\n // 1. Try custom authorization first if provided\n if (\n subscriptionOptions?.enabled === true &&\n \"authorizeReference\" in subscriptionOptions &&\n typeof subscriptionOptions.authorizeReference === \"function\"\n ) {\n const authorized = await subscriptionOptions.authorizeReference(\n {\n user: session.user,\n session: session.session,\n referenceId,\n action,\n },\n ctx,\n );\n if (authorized === true) {\n return {\n referenceId,\n };\n }\n // If explicit authorizeReference returns false, do we fail immediately?\n // Usually yes, but maybe we fallback to org check?\n // Let's assume authorizeReference overrides everything.\n throw new APIError(\"UNAUTHORIZED\");\n }\n\n // 2. Fallback: Organization Check\n if (options.organization?.enabled === true) {\n // Check if referenceId indicates an organization the user is a member of\n const member = await ctx.context.adapter.findOne({\n model: \"member\",\n where: [\n { field: \"userId\", value: session.user.id },\n { field: \"organizationId\", value: referenceId },\n ],\n });\n\n if (member !== null && member !== undefined) {\n logger.debug(\"DEBUG MIDDLEWARE MEMBER FOUND:\", member);\n // User is a member of the organization.\n // We could check roles here, but for now allow any member.\n return {\n referenceId,\n };\n }\n }\n\n logger.error(\n `Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your paystack plugin config and matches no organization membership.`,\n );\n throw new APIError(\"BAD_REQUEST\", {\n message:\n \"Passing referenceId isn't allowed without subscription.authorizeReference or valid organization membership.\",\n });\n });\n","import type { GenericEndpointContext } from \"better-auth\";\nimport { APIError } from \"better-auth/api\";\n\nimport type { Subscription } from \"./types\";\n\nexport const getOrganizationSubscription = async (\n ctx: GenericEndpointContext,\n organizationId: string,\n): Promise<Subscription | null> => {\n const subscription = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: organizationId }],\n });\n return subscription;\n};\n\nexport const checkSeatLimit = async (\n ctx: GenericEndpointContext,\n organizationId: string,\n seatsToAdd = 1,\n): Promise<boolean> => {\n const subscription = await getOrganizationSubscription(ctx, organizationId);\n\n if (subscription?.seats === null) {\n return true; // No explicit seat limit found\n }\n\n const members = await ctx.context.adapter.findMany({\n model: \"member\",\n where: [{ field: \"organizationId\", value: organizationId }],\n });\n\n if (!subscription) {\n return true; // No subscription, no specific limit enforcement here (or maybe allow depending on config)\n }\n\n if (members.length + seatsToAdd > subscription.seats) {\n throw new APIError(\"FORBIDDEN\", {\n message: `Organization member limit reached. Used: ${members.length}, Max: ${subscription.seats}`,\n });\n }\n\n return true;\n};\n\nexport const checkTeamLimit = async (\n ctx: GenericEndpointContext,\n organizationId: string,\n maxTeams: number,\n): Promise<boolean> => {\n const teams = await ctx.context.adapter.findMany({\n model: \"team\",\n where: [{ field: \"organizationId\", value: organizationId }],\n });\n\n if (teams.length >= maxTeams) {\n throw new APIError(\"FORBIDDEN\", {\n message: `Organization team limit reached. Max teams: ${maxTeams}`,\n });\n }\n\n return true;\n};\n","import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { defineErrorCodes } from \"@better-auth/core/utils/error-codes\";\nimport { HIDE_METADATA } from \"better-auth\";\nimport { APIError, getSessionFromCtx, originCheck, sessionMiddleware } from \"better-auth/api\";\n/* oxlint-disable no-restricted-imports */\nimport { z } from \"zod\";\nimport type { components } from \"@alexasomba/paystack-node\";\nimport type {\n GenericEndpointContext,\n MiddlewareInputContext,\n MiddlewareOptions,\n RawError,\n StrictEndpoint,\n} from \"better-auth\";\n\nimport type {\n InputPaystackProduct,\n PaystackTransaction,\n AnyPaystackOptions,\n PaystackProduct,\n Subscription,\n Member,\n PaystackOrganization,\n PaystackPlan,\n PaystackWebhookPayload,\n PaystackTransactionResponse,\n User,\n PaystackUser,\n PaystackCheckoutChannel,\n} from \"./types\";\nimport {\n syncProductQuantityFromPaystack,\n getPlanByName,\n getPlans,\n getProductByName,\n getProducts,\n validateMinAmount,\n getNextPeriodEnd,\n getPlanSeatAmount,\n calculatePlanAmount,\n assertLocallyManagedSubscription,\n isLocalSubscriptionCode,\n} from \"./utils\";\nimport { referenceMiddleware } from \"./middleware\";\nimport { getPaystackOps, unwrapSdkResult } from \"./paystack-sdk\";\nimport { getOrganizationSubscription } from \"./limits\";\n\nconst PAYSTACK_ERROR_CODES: {\n SUBSCRIPTION_NOT_FOUND: RawError<\"SUBSCRIPTION_NOT_FOUND\">;\n SUBSCRIPTION_PLAN_NOT_FOUND: RawError<\"SUBSCRIPTION_PLAN_NOT_FOUND\">;\n UNABLE_TO_CREATE_CUSTOMER: RawError<\"UNABLE_TO_CREATE_CUSTOMER\">;\n FAILED_TO_INITIALIZE_TRANSACTION: RawError<\"FAILED_TO_INITIALIZE_TRANSACTION\">;\n FAILED_TO_VERIFY_TRANSACTION: RawError<\"FAILED_TO_VERIFY_TRANSACTION\">;\n FAILED_TO_DISABLE_SUBSCRIPTION: RawError<\"FAILED_TO_DISABLE_SUBSCRIPTION\">;\n FAILED_TO_ENABLE_SUBSCRIPTION: RawError<\"FAILED_TO_ENABLE_SUBSCRIPTION\">;\n EMAIL_VERIFICATION_REQUIRED: RawError<\"EMAIL_VERIFICATION_REQUIRED\">;\n SUBSCRIPTION_PAYMENT_CHANNEL_NOT_ALLOWED: RawError<\"SUBSCRIPTION_PAYMENT_CHANNEL_NOT_ALLOWED\">;\n} = defineErrorCodes({\n SUBSCRIPTION_NOT_FOUND: \"Subscription not found\",\n SUBSCRIPTION_PLAN_NOT_FOUND: \"Subscription plan not found\",\n UNABLE_TO_CREATE_CUSTOMER: \"Unable to create customer\",\n FAILED_TO_INITIALIZE_TRANSACTION: \"Failed to initialize transaction\",\n FAILED_TO_VERIFY_TRANSACTION: \"Failed to verify transaction\",\n FAILED_TO_DISABLE_SUBSCRIPTION: \"Failed to disable subscription\",\n FAILED_TO_ENABLE_SUBSCRIPTION: \"Failed to enable subscription\",\n EMAIL_VERIFICATION_REQUIRED: \"Email verification is required before you can subscribe to a plan\",\n SUBSCRIPTION_PAYMENT_CHANNEL_NOT_ALLOWED:\n \"This subscription only supports specific payment channels\",\n});\n\nfunction getAllowedSubscriptionChannels(\n options: AnyPaystackOptions,\n): PaystackCheckoutChannel[] | undefined {\n const channels = options.subscription?.allowedPaymentChannels;\n return Array.isArray(channels) && channels.length > 0 ? channels : undefined;\n}\n\nfunction isAllowedSubscriptionChannel(\n channel: string | null | undefined,\n allowedChannels: readonly PaystackCheckoutChannel[] | undefined,\n): boolean {\n if (allowedChannels === undefined) return true;\n return channel !== undefined && channel !== null && allowedChannels.includes(channel as never);\n}\n\nasync function hmacSha512Hex(secret: string, message: string): Promise<string> {\n const encoder = new TextEncoder();\n const keyData = encoder.encode(secret);\n const msgData = encoder.encode(message);\n\n const crypto = globalThis.crypto;\n if (crypto !== undefined && crypto !== null && \"subtle\" in crypto) {\n const subtle = crypto.subtle;\n const key = await subtle.importKey(\"raw\", keyData, { name: \"HMAC\", hash: \"SHA-512\" }, false, [\n \"sign\",\n ]);\n const signature = await subtle.sign(\"HMAC\", key, msgData);\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n }\n\n const { createHmac } = await import(\"node:crypto\");\n return createHmac(\"sha512\", secret).update(message).digest(\"hex\");\n}\n\nexport const paystackWebhook = <P extends string = \"/webhook\">(\n options: AnyPaystackOptions,\n path: P = \"/webhook\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n metadata: {\n openapi: {\n operationId: string;\n };\n scope: \"server\";\n };\n cloneRequest: true;\n disableBody: true;\n },\n {\n received: boolean;\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n metadata: {\n ...HIDE_METADATA,\n openapi: {\n operationId: \"handlePaystackWebhook\",\n },\n },\n cloneRequest: true,\n disableBody: true,\n },\n async (ctx) => {\n const request =\n (ctx as unknown as { requestClone?: Request }).requestClone ??\n (ctx as { request: Request }).request;\n if (request === undefined || request === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Request object is missing from context\",\n });\n }\n const payload = await request.text();\n const headers =\n (ctx as GenericEndpointContext & { headers?: Headers }).headers ??\n (ctx.request as unknown as { headers: Headers })?.headers;\n const signature = headers?.get(\"x-paystack-signature\") as string | null | undefined;\n\n if (options.webhook?.verifyIP === true) {\n const trustedIPs = options.webhook.trustedIPs ?? [\n \"52.31.139.75\",\n \"52.49.173.169\",\n \"52.214.14.220\",\n ];\n const clientIP =\n headers?.get(\"x-forwarded-for\")?.split(\",\")[0]?.trim() ??\n headers?.get(\"x-real-ip\") ??\n (ctx.request as unknown as { ip?: string }).ip;\n\n if (\n clientIP !== undefined &&\n clientIP !== null &&\n trustedIPs.includes(clientIP) === false\n ) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: `Forbidden IP: ${clientIP}`,\n status: 401,\n });\n }\n }\n\n if (signature === undefined || signature === null || signature === \"\") {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Missing x-paystack-signature header\",\n status: 401,\n });\n }\n\n const webhookSecret =\n options.webhook?.secret ?? options.paystackWebhookSecret ?? options.secretKey;\n const expected = await hmacSha512Hex(webhookSecret, payload);\n if (expected !== signature) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid Paystack webhook signature\",\n status: 401,\n });\n }\n\n const event = JSON.parse(payload) as PaystackWebhookPayload;\n const eventName = event.event;\n const data = event.data;\n\n // Core Transaction Status Sync (Applies to both one-time and recurring)\n if (eventName === \"charge.success\") {\n const reference = (data as { reference?: string | null })?.reference;\n const paystackIdRaw = (data as { id?: number | string | null })?.id;\n const paystackId =\n paystackIdRaw !== undefined && paystackIdRaw !== null ? String(paystackIdRaw) : undefined;\n\n if (reference !== undefined && reference !== null && reference !== \"\") {\n try {\n await ctx.context.adapter.update({\n model: \"paystackTransaction\",\n update: {\n status: \"success\",\n paystackId,\n updatedAt: new Date(),\n },\n where: [{ field: \"reference\", value: reference }],\n });\n } catch (e) {\n ctx.context.logger.warn(\"Failed to update transaction status for charge.success\", e);\n }\n\n // Sync product quantity from Paystack after successful charge\n try {\n const transaction = await ctx.context.adapter.findOne<PaystackTransaction>({\n model: \"paystackTransaction\",\n where: [{ field: \"reference\", value: reference }],\n });\n if (\n transaction !== undefined &&\n transaction !== null &&\n transaction.product !== undefined &&\n transaction.product !== null &&\n transaction.product !== \"\"\n ) {\n if (options.paystackClient !== undefined && options.paystackClient !== null) {\n await syncProductQuantityFromPaystack(\n ctx,\n transaction.product,\n options.paystackClient,\n );\n }\n }\n } catch (e) {\n ctx.context.logger.warn(\"Failed to sync product quantity\", e);\n }\n }\n }\n\n if ((eventName as string) === \"charge.failure\") {\n const reference = (data as { reference?: string })?.reference;\n if (reference !== undefined && reference !== null && reference !== \"\") {\n try {\n await ctx.context.adapter.update({\n model: \"paystackTransaction\",\n update: {\n status: \"failed\",\n updatedAt: new Date(),\n },\n where: [{ field: \"reference\", value: reference }],\n });\n } catch (e) {\n ctx.context.logger.warn(\"Failed to update transaction status for charge.failure\", e);\n }\n }\n }\n\n // Best-effort local state sync for subscription lifecycle.\n if (options.subscription?.enabled === true) {\n try {\n if (eventName === \"subscription.create\") {\n const subscriptionData =\n data as unknown as components[\"schemas\"][\"SubscriptionListResponseArray\"];\n const subscriptionCode = subscriptionData.subscription_code ?? \"\";\n const customerCode = (\n subscriptionData.customer as { customer_code?: string | null } | undefined\n )?.customer_code;\n const planCode = (subscriptionData.plan as { plan_code?: string | null } | undefined)\n ?.plan_code;\n\n const metadataVal = (subscriptionData as unknown as { metadata?: unknown }).metadata;\n let metadata: unknown = metadataVal;\n if (typeof metadata === \"string\") {\n try {\n metadata = JSON.parse(metadata);\n } catch {\n // ignore\n }\n }\n\n const metadataObj =\n metadata !== undefined && metadata !== null && typeof metadata === \"object\"\n ? (metadata as Record<string, unknown>)\n : {};\n const referenceIdFromMetadata =\n typeof metadataObj.referenceId === \"string\" ? metadataObj.referenceId : undefined;\n let planNameFromMetadata =\n typeof metadataObj.plan === \"string\" ? metadataObj.plan : undefined;\n if (typeof planNameFromMetadata === \"string\") {\n planNameFromMetadata = planNameFromMetadata.toLowerCase();\n }\n\n const plans = await getPlans(options.subscription);\n const planFromCode =\n planCode !== undefined && planCode !== null && planCode !== \"\"\n ? plans.find((p) => p.planCode === planCode)\n : undefined;\n const planPart = planFromCode?.name ?? planNameFromMetadata;\n const planName =\n planPart !== undefined && planPart !== null && planPart !== \"\"\n ? planPart.toLowerCase()\n : undefined;\n\n if (\n subscriptionCode !== undefined &&\n subscriptionCode !== null &&\n subscriptionCode !== \"\"\n ) {\n const where: { field: string; value: string | number | boolean | null }[] = [];\n if (\n referenceIdFromMetadata !== undefined &&\n referenceIdFromMetadata !== null &&\n referenceIdFromMetadata !== \"\"\n ) {\n where.push({ field: \"referenceId\", value: referenceIdFromMetadata });\n } else if (\n customerCode !== undefined &&\n customerCode !== null &&\n customerCode !== \"\"\n ) {\n where.push({ field: \"paystackCustomerCode\", value: customerCode });\n }\n if (planName !== undefined && planName !== null && planName !== \"\") {\n where.push({ field: \"plan\", value: planName });\n }\n\n if (where.length > 0) {\n const matches = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: where as unknown as {\n field: string;\n value: string | number | boolean | null;\n }[],\n });\n const subscription = matches?.[0];\n if (subscription !== undefined && subscription !== null) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n paystackSubscriptionCode: subscriptionCode,\n status: \"active\",\n updatedAt: new Date(),\n periodEnd:\n subscriptionData.next_payment_date !== undefined &&\n subscriptionData.next_payment_date !== null\n ? new Date(subscriptionData.next_payment_date)\n : undefined,\n },\n where: [{ field: \"id\", value: subscription.id }],\n });\n\n const plan =\n planFromCode ??\n (planName !== undefined && planName !== null && planName !== \"\"\n ? await getPlanByName(options, planName)\n : undefined);\n if (plan !== undefined && plan !== null) {\n await options.subscription.onSubscriptionComplete?.(\n {\n event,\n subscription: {\n ...subscription,\n paystackSubscriptionCode: subscriptionCode,\n status: \"active\",\n },\n plan,\n },\n ctx as GenericEndpointContext,\n );\n await options.subscription.onSubscriptionCreated?.(\n {\n event,\n subscription: {\n ...subscription,\n paystackSubscriptionCode: subscriptionCode,\n status: \"active\",\n },\n plan,\n },\n ctx as GenericEndpointContext,\n );\n }\n }\n }\n }\n }\n\n if (eventName === \"subscription.disable\" || eventName === \"subscription.not_renew\") {\n const subscriptionData =\n data as unknown as components[\"schemas\"][\"SubscriptionListResponseArray\"];\n const subscriptionCode = subscriptionData.subscription_code ?? \"\";\n if (subscriptionCode !== \"\") {\n const existing = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n let newStatus = \"canceled\";\n const nextPaymentDate = subscriptionData.next_payment_date;\n const periodEnd =\n nextPaymentDate !== undefined && nextPaymentDate !== null && nextPaymentDate !== \"\"\n ? new Date(nextPaymentDate)\n : existing?.periodEnd !== undefined && existing.periodEnd !== null\n ? new Date(existing.periodEnd)\n : undefined;\n\n if (periodEnd !== undefined && periodEnd.getTime() > Date.now()) {\n newStatus = \"active\";\n }\n\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: newStatus,\n cancelAtPeriodEnd: true,\n ...(periodEnd ? { periodEnd } : {}),\n updatedAt: new Date(),\n },\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (existing !== null && existing !== undefined) {\n await options.subscription.onSubscriptionCancel?.(\n { event, subscription: { ...existing, status: \"canceled\" } as Subscription },\n ctx as GenericEndpointContext,\n );\n }\n }\n }\n\n // Handle plan changes on renewal\n if (eventName === \"charge.success\" || eventName === \"invoice.update\") {\n const subData = (data as { subscription?: { subscription_code?: string | null } })\n ?.subscription;\n const subscriptionCodeRaw =\n subData?.subscription_code ??\n (data as { subscription_code?: string | null })?.subscription_code;\n const subscriptionCode =\n subscriptionCodeRaw !== undefined &&\n subscriptionCodeRaw !== null &&\n subscriptionCodeRaw !== \"\"\n ? subscriptionCodeRaw\n : undefined;\n\n if (subscriptionCode !== undefined) {\n const existingSub = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (\n existingSub !== undefined &&\n existingSub !== null &&\n existingSub.pendingPlan !== undefined &&\n existingSub.pendingPlan !== null &&\n existingSub.pendingPlan !== \"\"\n ) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n plan: existingSub.pendingPlan,\n pendingPlan: null,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: existingSub.id }],\n });\n }\n }\n }\n } catch (_e: unknown) {\n ctx.context.logger.error(\"Failed to sync Paystack webhook event\", _e);\n }\n }\n\n await options.onEvent?.(event);\n return ctx.json({ received: true });\n },\n );\n};\n\nconst initializeTransactionBodySchema = z.object({\n plan: z.string().optional(),\n product: z.string().optional(),\n amount: z.number().int().positive().optional(),\n currency: z.string().optional(),\n email: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n referenceId: z.string().optional(),\n callbackURL: z.string().optional(),\n quantity: z.number().int().positive().optional(),\n scheduleAtPeriodEnd: z.boolean().optional(),\n cancelAtPeriodEnd: z.boolean().optional(),\n prorateAndCharge: z.boolean().optional(),\n});\n\nexport const initializeTransaction = <P extends string = \"/initialize-transaction\">(\n options: AnyPaystackOptions,\n path: P = \"/initialize-transaction\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n plan: z.ZodOptional<z.ZodString>;\n product: z.ZodOptional<z.ZodString>;\n amount: z.ZodOptional<z.ZodNumber>;\n currency: z.ZodOptional<z.ZodString>;\n email: z.ZodOptional<z.ZodString>;\n metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;\n referenceId: z.ZodOptional<z.ZodString>;\n callbackURL: z.ZodOptional<z.ZodString>;\n quantity: z.ZodOptional<z.ZodNumber>;\n scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n prorateAndCharge: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n> => {\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"initialize-transaction\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n body: initializeTransactionBodySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n const {\n plan: planName,\n product: productName,\n amount: bodyAmount,\n currency,\n email,\n metadata: extraMetadata,\n callbackURL,\n quantity,\n scheduleAtPeriodEnd,\n cancelAtPeriodEnd,\n prorateAndCharge,\n } = ctx.body;\n\n // 1. Validate Callback URL validation (same as before)\n if (callbackURL !== undefined && callbackURL !== null && callbackURL !== \"\") {\n const checkTrusted = () => {\n try {\n if ((callbackURL as string | undefined)?.startsWith(\"/\") === true) return true;\n const baseUrl =\n ((ctx.context as Record<string, unknown>)?.baseURL as string | undefined) ??\n (ctx.request as unknown as { url?: string })?.url ??\n \"\";\n if (baseUrl === \"\") return false;\n const baseOrigin = new URL(baseUrl).origin;\n return new URL(callbackURL).origin === baseOrigin;\n } catch {\n return false;\n }\n };\n if (checkTrusted() === false) {\n throw new APIError(\"FORBIDDEN\", {\n message: \"callbackURL is not a trusted origin.\",\n status: 403,\n });\n }\n }\n\n // 2. Get User & Session\n const session = await getSessionFromCtx(ctx);\n if (session === undefined || session === null) throw new APIError(\"UNAUTHORIZED\");\n const user = session.user;\n\n // 3. Email Verification Check (only if subscription options enforce it)\n if (\n subscriptionOptions?.enabled === true &&\n subscriptionOptions.requireEmailVerification === true &&\n user.emailVerified !== true\n ) {\n throw new APIError(\"BAD_REQUEST\", {\n code: \"EMAIL_VERIFICATION_REQUIRED\",\n message: PAYSTACK_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED.message,\n });\n }\n\n // 4. Determine Payment Mode: Subscription (Plan) vs Product vs One-Time (Amount)\n let plan: PaystackPlan | undefined;\n let product: PaystackProduct | InputPaystackProduct | undefined;\n\n if (planName !== undefined && planName !== null && planName !== \"\") {\n if (subscriptionOptions?.enabled !== true) {\n throw new APIError(\"BAD_REQUEST\", { message: \"Subscriptions are not enabled.\" });\n }\n plan = (await getPlanByName(options, planName)) ?? undefined;\n if (plan === undefined || plan === null) {\n try {\n // Fallback: Check database for synced plans when that model exists.\n const nativePlan = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"name\", value: planName }],\n });\n if (nativePlan !== undefined && nativePlan !== null) {\n plan = nativePlan;\n } else {\n const nativePlanByCode = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"planCode\", value: planName }],\n });\n plan = nativePlanByCode ?? undefined;\n }\n } catch {\n plan = undefined;\n }\n }\n if (plan === undefined || plan === null) {\n throw new APIError(\"BAD_REQUEST\", {\n code: \"SUBSCRIPTION_PLAN_NOT_FOUND\",\n message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND.message,\n status: 400,\n });\n }\n } else if (productName !== undefined && productName !== null && productName !== \"\") {\n if (typeof productName === \"string\") {\n product = (await getProductByName(options, productName)) ?? undefined;\n // Fallback: Check database for synced products\n product ??=\n (await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"name\", value: productName }],\n })) ?? undefined;\n }\n if (product === undefined || product === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: `Product '${productName}' not found.`,\n status: 400,\n });\n }\n } else if (bodyAmount === undefined || bodyAmount === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Either 'plan', 'product', or 'amount' is required to initialize a transaction.\",\n status: 400,\n });\n }\n\n let amount =\n bodyAmount ??\n (product as PaystackProduct)?.price ??\n (product as InputPaystackProduct)?.amount;\n const finalCurrency =\n currency ??\n (product as PaystackProduct)?.currency ??\n (product as InputPaystackProduct)?.currency ??\n plan?.currency ??\n \"NGN\";\n\n const referenceIdFromCtx = (ctx.context as Record<string, unknown>).referenceId as\n | string\n | undefined;\n const referenceId =\n ctx.body.referenceId ?? referenceIdFromCtx ?? (session.user as { id: string }).id;\n\n // Handle scheduleAtPeriodEnd for existing subscriptions\n if (plan !== undefined && scheduleAtPeriodEnd === true) {\n const existingSub = await getOrganizationSubscription(ctx, referenceId);\n if (existingSub?.status === \"active\") {\n await ctx.context.adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: existingSub.id }],\n update: {\n pendingPlan: plan.name,\n updatedAt: new Date(),\n },\n });\n return ctx.json({\n status: \"success\",\n message: \"Plan change scheduled at period end.\",\n scheduled: true,\n });\n }\n }\n\n // Handle cancelAtPeriodEnd for existing subscriptions\n if (cancelAtPeriodEnd === true) {\n const existingSub = await getOrganizationSubscription(ctx, referenceId);\n if (existingSub?.status === \"active\") {\n await ctx.context.adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: existingSub.id }],\n update: {\n cancelAtPeriodEnd: true,\n updatedAt: new Date(),\n },\n });\n\n return ctx.json({\n status: \"success\",\n message: \"Subscription cancellation scheduled at period end.\",\n scheduled: true,\n });\n }\n }\n\n // Calculate final amount considering seats if applicable\n if (plan !== undefined) {\n try {\n if (getPlanSeatAmount(plan) !== undefined) {\n const members = await ctx.context.adapter.findMany<Member>({\n model: \"member\",\n where: [{ field: \"organizationId\", value: referenceId }],\n });\n const seatCount = members.length > 0 ? members.length : 1;\n const quantityToUse = quantity ?? seatCount;\n amount = calculatePlanAmount(plan, quantityToUse);\n }\n } catch (error: unknown) {\n throw new APIError(\"BAD_REQUEST\", {\n message:\n error instanceof Error ? error.message : \"Invalid seat configuration for plan.\",\n });\n }\n }\n\n let url: string | undefined;\n let reference: string | undefined;\n let accessCode: string | undefined;\n\n // Check trial eligibility - prevent trial abuse\n let trialStart: Date | undefined;\n let trialEnd: Date | undefined;\n const requestedTrialDays =\n plan?.freeTrial?.days !== undefined && plan.freeTrial.days > 0 ? plan.freeTrial.days : 0;\n const trialRequested = requestedTrialDays > 0;\n let trialGranted = false;\n let trialDeniedReason: \"already_used\" | undefined;\n if (trialRequested) {\n // Check if user/referenceId has ever had a trial\n const previousTrials = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: referenceId }],\n });\n const hadTrial = previousTrials?.some(\n (sub: Subscription) =>\n (sub.trialStart !== undefined && sub.trialStart !== null) ||\n (sub.trialEnd !== undefined && sub.trialEnd !== null) ||\n sub.status === \"trialing\",\n );\n\n if (hadTrial === false) {\n trialStart = new Date();\n trialEnd = new Date();\n trialEnd.setDate(trialEnd.getDate() + requestedTrialDays);\n trialGranted = true;\n } else {\n trialDeniedReason = \"already_used\";\n }\n }\n\n try {\n // Determine Customer Email & Code (Organization support)\n let targetEmail = email ?? user.email;\n\n if (\n options.organization?.enabled === true &&\n referenceId !== undefined &&\n referenceId !== null &&\n referenceId !== user.id\n ) {\n const org = await ctx.context.adapter.findOne({\n model: \"organization\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (org !== undefined && org !== null) {\n const orgWithEmail = org as { email?: string | null };\n if (\n orgWithEmail.email !== undefined &&\n orgWithEmail.email !== null &&\n orgWithEmail.email !== \"\"\n ) {\n targetEmail = orgWithEmail.email;\n } else {\n // Fallback: Use Organization Owner Email\n const ownerMember = await ctx.context.adapter.findOne({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: referenceId },\n { field: \"role\", value: \"owner\" },\n ],\n });\n\n if (ownerMember !== undefined && ownerMember !== null) {\n const ownerUser = (await ctx.context.adapter.findOne({\n model: \"user\",\n where: [{ field: \"id\", value: (ownerMember as Member).userId }],\n })) as User | null;\n\n if (\n ownerUser !== undefined &&\n ownerUser !== null &&\n ownerUser.email !== undefined &&\n ownerUser.email !== null &&\n ownerUser.email !== \"\"\n ) {\n targetEmail = ownerUser.email;\n }\n }\n }\n }\n }\n\n const allowedSubscriptionChannels = plan\n ? getAllowedSubscriptionChannels(options)\n : undefined;\n\n // Construct Metadata\n const metadata = JSON.stringify({\n referenceId,\n userId: user.id,\n plan: plan !== undefined ? plan.name.toLowerCase() : undefined, // Undefined for one-time\n product: product !== undefined ? product.name.toLowerCase() : undefined,\n ...extraMetadata,\n isTrial: trialStart !== undefined,\n trialRequested,\n trialGranted,\n trialDeniedReason,\n trialEnd: trialEnd !== undefined ? trialEnd.toISOString() : undefined,\n });\n\n const initBody: {\n email: string;\n callback_url?: string;\n metadata: string;\n currency: string;\n quantity?: number;\n amount?: number;\n plan?: string;\n channels?: PaystackCheckoutChannel[];\n [key: string]: unknown;\n } = {\n email: targetEmail,\n callback_url: callbackURL ?? undefined,\n metadata,\n // If plan/product exists, use its currency; otherwise fallback to provided or default\n currency: finalCurrency,\n quantity,\n };\n\n if (allowedSubscriptionChannels !== undefined) {\n initBody.channels = allowedSubscriptionChannels;\n }\n\n // Handle prorateAndCharge for existing active subscriptions\n if (plan !== undefined && prorateAndCharge === true) {\n const existingSub = await getOrganizationSubscription(ctx, referenceId);\n if (\n existingSub?.status === \"active\" &&\n existingSub.paystackSubscriptionCode !== undefined &&\n existingSub.paystackSubscriptionCode !== null &&\n existingSub.paystackSubscriptionCode !== \"\"\n ) {\n if (\n existingSub.periodEnd !== undefined &&\n existingSub.periodEnd !== null &&\n existingSub.periodStart !== undefined &&\n existingSub.periodStart !== null\n ) {\n // 1. Calculate remaining days\n const now = new Date();\n const periodEndLocal = new Date(existingSub.periodEnd);\n const periodStartLocal = new Date(existingSub.periodStart);\n\n const totalDays = Math.max(\n 1,\n Math.ceil(\n (periodEndLocal.getTime() - periodStartLocal.getTime()) / (1000 * 60 * 60 * 24),\n ),\n );\n const remainingDays = Math.max(\n 0,\n Math.ceil((periodEndLocal.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)),\n );\n\n // 2. Fetch old plan/amount\n let oldAmount = 0;\n if (existingSub.plan !== \"\") {\n const oldPlan =\n (await getPlanByName(options, existingSub.plan)) ??\n (await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"name\", value: existingSub.plan }],\n })) ??\n undefined;\n if (oldPlan !== undefined && oldPlan !== null) {\n const oldSeatCount = existingSub.seats;\n oldAmount = calculatePlanAmount(oldPlan, oldSeatCount);\n }\n }\n\n // 3. Calculate new total amount\n let membersCount = 1;\n let newSeatCount = quantity ?? existingSub.seats ?? membersCount;\n let newAmount: number;\n try {\n assertLocallyManagedSubscription(existingSub, \"plan or seat changes\");\n if (getPlanSeatAmount(plan) !== undefined) {\n const members = await ctx.context.adapter.findMany<Member>({\n model: \"member\",\n where: [{ field: \"organizationId\", value: referenceId }],\n });\n membersCount = members.length > 0 ? members.length : 1;\n }\n newSeatCount = quantity ?? existingSub.seats ?? membersCount;\n newAmount = calculatePlanAmount(plan, newSeatCount);\n } catch (error: unknown) {\n throw new APIError(\"BAD_REQUEST\", {\n message:\n error instanceof Error ? error.message : \"Invalid seat configuration for plan.\",\n });\n }\n\n // 4. Calculate Difference & Charge\n const costDifference = newAmount - oldAmount;\n const prorationMetadata = {\n type: \"proration\",\n subscriptionId: existingSub.id,\n referenceId,\n newPlan: plan.name.toLowerCase(),\n oldPlan: existingSub.plan,\n newSeatCount,\n remainingDays,\n };\n let completedProrationReference: string | undefined;\n\n if (costDifference > 0 && remainingDays > 0) {\n const proratedAmount = Math.round((costDifference / totalDays) * remainingDays);\n if (proratedAmount < 5000) {\n throw new APIError(\"BAD_REQUEST\", {\n message:\n \"Prorated upgrade amount is below Paystack's minimum charge. Schedule the change for period end instead.\",\n status: 400,\n });\n }\n\n const ops = getPaystackOps(options.paystackClient);\n if (ops === undefined || ops === null) {\n ctx.context.logger.error(\"Paystack client not configured for proration charge\");\n return;\n }\n\n if (\n existingSub.paystackAuthorizationCode !== undefined &&\n existingSub.paystackAuthorizationCode !== null &&\n existingSub.paystackAuthorizationCode !== \"\"\n ) {\n const chargeResRaw = await ops.transaction?.chargeAuthorization({\n body: {\n email: targetEmail,\n amount: proratedAmount,\n authorization_code: existingSub.paystackAuthorizationCode,\n reference: `upg_${existingSub.id}_${Date.now()}_${Math.random().toString(36).substring(7)}`,\n metadata: JSON.stringify(prorationMetadata),\n },\n });\n const sdkRes = unwrapSdkResult<PaystackTransactionResponse>(chargeResRaw);\n\n if (sdkRes?.status !== \"success\") {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Failed to process prorated charge via saved authorization.\",\n });\n }\n\n await ctx.context.adapter.create({\n model: \"paystackTransaction\",\n data: {\n reference: sdkRes.reference ?? \"\",\n paystackId:\n sdkRes.id !== undefined && sdkRes.id !== null\n ? String(sdkRes.id)\n : undefined,\n referenceId,\n userId: user.id,\n amount: sdkRes.amount ?? proratedAmount,\n currency: sdkRes.currency ?? finalCurrency,\n status: \"success\",\n plan: plan.name.toLowerCase(),\n metadata: JSON.stringify(prorationMetadata),\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n });\n completedProrationReference = sdkRes.reference ?? undefined;\n } else {\n const initRaw = await ops.transaction?.initialize({\n body: {\n email: targetEmail,\n amount: proratedAmount,\n currency: finalCurrency,\n callback_url: callbackURL ?? undefined,\n metadata: JSON.stringify(prorationMetadata),\n ...(allowedSubscriptionChannels !== undefined\n ? { channels: allowedSubscriptionChannels }\n : {}),\n } as components[\"schemas\"][\"TransactionInitialize\"],\n });\n const initRes =\n unwrapSdkResult<components[\"schemas\"][\"TransactionInitializeResponse\"][\"data\"]>(\n initRaw,\n );\n\n await ctx.context.adapter.create({\n model: \"paystackTransaction\",\n data: {\n reference: initRes?.reference ?? \"\",\n referenceId,\n userId: user.id,\n amount: proratedAmount,\n currency: finalCurrency,\n status: \"pending\",\n plan: plan.name.toLowerCase(),\n metadata: JSON.stringify(prorationMetadata),\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n });\n\n return ctx.json({\n url: initRes?.authorization_url,\n reference: initRes?.reference,\n accessCode: initRes?.access_code,\n redirect: true,\n });\n }\n }\n\n // 5. Update Local DB for locally managed subscriptions.\n await ctx.context.adapter.update({\n model: \"subscription\",\n where: [{ field: \"id\", value: existingSub.id }],\n update: {\n plan: plan.name,\n seats: newSeatCount,\n ...(completedProrationReference !== undefined\n ? { paystackTransactionReference: completedProrationReference }\n : {}),\n updatedAt: new Date(),\n },\n });\n\n return ctx.json({\n status: \"success\",\n message: \"Subscription successfully upgraded with prorated charge.\",\n prorated: true,\n });\n }\n }\n }\n\n if (plan !== undefined) {\n // Subscription Flow\n if (trialStart !== undefined) {\n // Trial Flow: Authorize card with minimum amount, don't start sub yet\n initBody.amount = 5000; // 50 NGN (minimum allowed)\n } else {\n // Standard Flow\n initBody.plan = plan.planCode;\n // SDK might use different field names, but keeping DX consistency\n (initBody as Record<string, unknown>).invoice_limit = plan.invoiceLimit;\n\n let finalAmount: number;\n if (amount !== undefined && amount !== null) {\n finalAmount = amount;\n initBody.quantity = 1;\n } else {\n finalAmount = (plan.amount ?? 0) * (quantity ?? 1);\n }\n initBody.amount = Math.max(Math.round(finalAmount), 5000);\n }\n } else {\n // One-Time Payment Flow\n if (amount === undefined || amount === null)\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Amount is required for one-time payments\",\n });\n initBody.amount = Math.round(amount);\n }\n\n const initRaw = await paystack?.transaction?.initialize({\n body: initBody as components[\"schemas\"][\"TransactionInitialize\"],\n });\n const sdkRes =\n unwrapSdkResult<components[\"schemas\"][\"TransactionInitializeResponse\"][\"data\"]>(initRaw);\n\n url = sdkRes?.authorization_url;\n reference = sdkRes?.reference;\n accessCode = sdkRes?.access_code;\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to initialize Paystack transaction\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_INITIALIZE_TRANSACTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_INITIALIZE_TRANSACTION\",\n message: errorMessage,\n });\n }\n\n // 6. Record Transaction & Subscription\n await ctx.context.adapter.create({\n model: \"paystackTransaction\",\n data: {\n reference: reference ?? \"\",\n referenceId,\n userId: user.id,\n amount: amount ?? 0,\n currency: plan?.currency ?? currency ?? \"NGN\",\n status: \"pending\",\n plan: plan !== undefined ? plan.name.toLowerCase() : undefined,\n product: product !== undefined ? product.name.toLowerCase() : undefined,\n metadata:\n extraMetadata !== undefined && Object.keys(extraMetadata).length > 0\n ? JSON.stringify(extraMetadata)\n : undefined,\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n });\n\n if (plan !== undefined) {\n let storedCustomerCode = (user as PaystackUser).paystackCustomerCode;\n if (options.organization?.enabled === true && referenceId !== user.id) {\n const org = await ctx.context.adapter.findOne({\n model: \"organization\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (org !== undefined && org !== null) {\n const paystackOrg = org as PaystackOrganization;\n if (\n paystackOrg.paystackCustomerCode !== undefined &&\n paystackOrg.paystackCustomerCode !== null &&\n paystackOrg.paystackCustomerCode !== \"\"\n ) {\n storedCustomerCode = paystackOrg.paystackCustomerCode;\n }\n }\n }\n\n const newSubscription = await ctx.context.adapter.create<Subscription>({\n model: \"subscription\",\n data: {\n plan: plan.name.toLowerCase(),\n referenceId,\n userId: user.id,\n paystackCustomerCode: storedCustomerCode ?? \"\",\n paystackSubscriptionCode: \"\",\n paystackPlanCode: plan.planCode,\n paystackAuthorizationCode: \"\",\n paystackTransactionReference: reference ?? \"\",\n status: trialStart !== undefined ? \"trialing\" : \"incomplete\",\n seats: quantity ?? 1,\n periodStart: new Date(),\n periodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // Default 30 days\n cancelAtPeriodEnd: false,\n trialStart,\n trialEnd,\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n });\n\n // Call trial start hook if trial was granted\n if (\n trialStart !== undefined &&\n newSubscription !== undefined &&\n newSubscription !== null &&\n plan.freeTrial?.onTrialStart !== undefined\n ) {\n await plan.freeTrial.onTrialStart(newSubscription);\n }\n }\n\n return ctx.json({\n url,\n reference,\n accessCode,\n redirect: true,\n });\n },\n );\n};\n\n// Aliases for Client DX Parity\nexport const createSubscription = <P extends string = \"/create-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/create-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n plan: z.ZodOptional<z.ZodString>;\n product: z.ZodOptional<z.ZodString>;\n amount: z.ZodOptional<z.ZodNumber>;\n currency: z.ZodOptional<z.ZodString>;\n email: z.ZodOptional<z.ZodString>;\n metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;\n referenceId: z.ZodOptional<z.ZodString>;\n callbackURL: z.ZodOptional<z.ZodString>;\n quantity: z.ZodOptional<z.ZodNumber>;\n scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n prorateAndCharge: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n> => initializeTransaction(options, path);\n\nexport const upgradeSubscription = <P extends string = \"/upgrade-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/upgrade-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n plan: z.ZodOptional<z.ZodString>;\n product: z.ZodOptional<z.ZodString>;\n amount: z.ZodOptional<z.ZodNumber>;\n currency: z.ZodOptional<z.ZodString>;\n email: z.ZodOptional<z.ZodString>;\n metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;\n referenceId: z.ZodOptional<z.ZodString>;\n callbackURL: z.ZodOptional<z.ZodString>;\n quantity: z.ZodOptional<z.ZodNumber>;\n scheduleAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n cancelAtPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n prorateAndCharge: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n> => initializeTransaction(options, path);\n\nexport const cancelSubscription = <P extends string = \"/cancel-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/cancel-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => disablePaystackSubscription(options, path);\n\nexport const restoreSubscription = <P extends string = \"/restore-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/restore-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => enablePaystackSubscription(options, path);\n\nexport const verifyTransaction = <P extends string = \"/verify-transaction\">(\n options: AnyPaystackOptions,\n path: P = \"/verify-transaction\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n reference: z.ZodString;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n reference: string;\n data: {\n id: number;\n domain: string;\n status: string;\n reference: string;\n receipt_number: string | null;\n amount: number;\n message: string | null;\n gateway_response: string;\n channel: string;\n currency: string;\n ip_address: string | null;\n metadata: (string | Record<string, never> | number) | null;\n log: {\n start_time: number;\n time_spent: number;\n attempts: number;\n errors: number;\n success: boolean;\n mobile: boolean;\n input: unknown[];\n history: {\n type: string;\n message: string;\n time: number;\n }[];\n } | null;\n fees: number | null;\n fees_split: unknown;\n authorization: {\n authorization_code?: string;\n bin?: string | null;\n last4?: string;\n exp_month?: string;\n exp_year?: string;\n channel?: string;\n card_type?: string;\n bank?: string;\n country_code?: string;\n brand?: string;\n reusable?: boolean;\n signature?: string;\n account_name?: string | null;\n receiver_bank_account_number?: string | null;\n receiver_bank?: string | null;\n };\n customer: {\n id: number;\n first_name: string | null;\n last_name: string | null;\n email: string;\n customer_code: string;\n phone: string | null;\n metadata: Record<string, never> | null;\n risk_action: string;\n international_format_phone?: string | null;\n };\n plan: (string | Record<string, never>) | null;\n split: Record<string, never> | null;\n order_id: unknown;\n paidAt: string | null;\n createdAt: string;\n requested_amount: number;\n pos_transaction_data: unknown;\n source: unknown;\n fees_breakdown: unknown;\n connect: unknown;\n transaction_date: string;\n plan_object: {\n id?: number;\n name?: string;\n plan_code?: string;\n description?: unknown;\n amount?: number;\n interval?: string;\n send_invoices?: boolean;\n send_sms?: boolean;\n currency?: string;\n };\n subaccount: Record<string, never> | null;\n };\n }\n> => {\n const verifyBodySchema = z.object({\n reference: z.string(),\n });\n\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"verify-transaction\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n body: verifyBodySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n let data: PaystackTransactionResponse | undefined;\n\n try {\n const verifyRaw = await paystack?.transaction?.verify(ctx.body.reference);\n // unwrapSdkResult might return the data field or the whole body depending on its impl.\n // But with PaystackResponse and ours, it should give us the 'data' part for success.\n data = unwrapSdkResult<PaystackTransactionResponse>(verifyRaw);\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to verify Paystack transaction\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_VERIFY_TRANSACTION\",\n message: errorMessage,\n });\n }\n\n if (data === undefined || data === null) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Failed to fetch transaction data from Paystack.\",\n });\n }\n\n const status = data.status ?? \"failed\";\n const reference = data.reference ?? ctx.body.reference;\n const paystackIdRaw = data.id;\n const paystackId =\n paystackIdRaw !== undefined && paystackIdRaw !== null ? String(paystackIdRaw) : undefined;\n const authorizationCode = (data.authorization as { authorization_code?: string | null })\n ?.authorization_code;\n const allowedSubscriptionChannels = getAllowedSubscriptionChannels(options);\n\n if (status === \"success\") {\n const session = await getSessionFromCtx(ctx);\n\n // Get the local transaction record to know the intended referenceId (Org or User)\n const txRecord = await ctx.context.adapter.findOne<\n PaystackTransaction & { referenceId?: string }\n >({\n model: \"paystackTransaction\",\n where: [{ field: \"reference\", value: reference }],\n });\n\n // Trust the referenceId from the record, fallback to session user if missing\n const referenceId =\n txRecord !== undefined &&\n txRecord !== null &&\n txRecord.referenceId !== undefined &&\n txRecord.referenceId !== null &&\n txRecord.referenceId !== \"\"\n ? txRecord.referenceId\n : session !== undefined && session !== null\n ? session.user.id\n : undefined;\n\n const isSubscriptionFlow =\n (txRecord?.plan !== undefined && txRecord.plan !== null && txRecord.plan !== \"\") ||\n Boolean((data as { plan?: unknown }).plan);\n\n if (\n isSubscriptionFlow &&\n isAllowedSubscriptionChannel(data.channel ?? undefined, allowedSubscriptionChannels) ===\n false\n ) {\n await ctx.context.adapter.update({\n model: \"paystackTransaction\",\n update: {\n status: \"failed\",\n paystackId,\n amount: data.amount,\n currency: data.currency,\n updatedAt: new Date(),\n },\n where: [{ field: \"reference\", value: reference }],\n });\n\n throw new APIError(\"BAD_REQUEST\", {\n code: \"SUBSCRIPTION_PAYMENT_CHANNEL_NOT_ALLOWED\",\n message: `This subscription requires one of: ${allowedSubscriptionChannels?.join(\", \") ?? \"allowed channels\"}.`,\n });\n }\n\n // Authorization check: ensure the current user has access to this referenceId\n if (\n session !== undefined &&\n session !== null &&\n referenceId !== undefined &&\n referenceId !== null &&\n referenceId !== \"\" &&\n referenceId !== session.user.id\n ) {\n const authRef = subscriptionOptions?.authorizeReference;\n let authorized = false;\n if (authRef !== undefined && authRef !== null) {\n authorized = await authRef(\n {\n user: session.user,\n session: session.session,\n referenceId,\n action: \"verify-transaction\",\n },\n ctx as GenericEndpointContext,\n );\n }\n if (authorized === false && options.organization?.enabled === true) {\n const member = await ctx.context.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"userId\", value: session.user.id },\n { field: \"organizationId\", value: referenceId },\n ],\n });\n if (member !== undefined && member !== null) authorized = true;\n }\n\n if (authorized === false) {\n throw new APIError(\"UNAUTHORIZED\");\n }\n }\n\n try {\n await ctx.context.adapter.update({\n model: \"paystackTransaction\",\n update: {\n status: \"success\",\n paystackId,\n amount: data.amount,\n currency: data.currency,\n updatedAt: new Date(),\n },\n where: [{ field: \"reference\", value: reference }],\n });\n\n const paystackCustomerCodeFromPaystack = data.customer?.customer_code;\n if (\n paystackCustomerCodeFromPaystack !== undefined &&\n paystackCustomerCodeFromPaystack !== null &&\n paystackCustomerCodeFromPaystack !== \"\" &&\n referenceId !== undefined &&\n referenceId !== null &&\n referenceId !== \"\"\n ) {\n let isOrg =\n options.organization?.enabled === true &&\n typeof referenceId === \"string\" &&\n referenceId.startsWith(\"org_\");\n if (isOrg === false && options.organization?.enabled === true) {\n const org = await ctx.context.adapter.findOne({\n model: \"organization\",\n where: [{ field: \"id\", value: referenceId }],\n });\n isOrg = org !== undefined && org !== null;\n }\n\n if (isOrg) {\n await ctx.context.adapter.update({\n model: \"organization\",\n update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },\n where: [{ field: \"id\", value: referenceId }],\n });\n } else {\n await ctx.context.adapter.update({\n model: \"user\",\n update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },\n where: [{ field: \"id\", value: referenceId }],\n });\n }\n }\n\n // Decrement product quantity if applicable\n const transaction = await ctx.context.adapter.findOne<PaystackTransaction>({\n model: \"paystackTransaction\",\n where: [{ field: \"reference\", value: reference }],\n });\n if (\n transaction !== undefined &&\n transaction !== null &&\n transaction.product !== undefined &&\n transaction.product !== null &&\n transaction.product !== \"\" &&\n options.paystackClient !== undefined &&\n options.paystackClient !== null\n ) {\n await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);\n }\n\n // Check for trial activation\n let isTrial = false;\n let trialEnd: string | undefined;\n let targetPlan: string | undefined;\n let metadataObj: Record<string, unknown> = {};\n\n if (data.metadata !== undefined && data.metadata !== null && data.metadata !== \"\") {\n metadataObj = (\n typeof data.metadata === \"string\" ? JSON.parse(data.metadata) : data.metadata\n ) as Record<string, unknown>;\n isTrial = metadataObj.isTrial === true || metadataObj.isTrial === \"true\";\n trialEnd = metadataObj.trialEnd as string | undefined;\n targetPlan = metadataObj.plan as string | undefined;\n }\n\n if (metadataObj.type === \"proration\") {\n const subscriptionId = metadataObj.subscriptionId as string | undefined;\n const newPlan = metadataObj.newPlan as string | undefined;\n const newSeatCount = metadataObj.newSeatCount as number | undefined;\n\n if (\n subscriptionId !== undefined &&\n subscriptionId !== \"\" &&\n newPlan !== undefined &&\n newPlan !== \"\"\n ) {\n await ctx.context.adapter.update<Subscription>({\n model: \"subscription\",\n update: {\n plan: newPlan,\n ...(typeof newSeatCount === \"number\" ? { seats: newSeatCount } : {}),\n paystackTransactionReference: reference,\n ...(authorizationCode !== undefined && authorizationCode !== null\n ? { paystackAuthorizationCode: authorizationCode }\n : {}),\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: subscriptionId }],\n });\n }\n\n return ctx.json({ status, reference, data });\n }\n\n let paystackSubscriptionCode: string | undefined;\n\n if (isTrial && targetPlan !== undefined && trialEnd !== undefined) {\n // Trial Flow: Create subscription with future start date using auth code\n const email = data.customer?.email;\n\n const plans = await getPlans(subscriptionOptions);\n const planConfig = plans.find(\n (p) => p.name.toLowerCase() === targetPlan?.toLowerCase(),\n );\n\n // For local plans (no planCode), generate a local subscription code\n if (\n planConfig !== undefined &&\n planConfig !== null &&\n (planConfig.planCode === undefined ||\n planConfig.planCode === null ||\n planConfig.planCode === \"\")\n ) {\n paystackSubscriptionCode = `LOC_${reference}`;\n }\n\n if (\n authorizationCode !== undefined &&\n authorizationCode !== null &&\n email !== undefined &&\n email !== null &&\n email !== \"\" &&\n planConfig?.planCode !== undefined &&\n planConfig.planCode !== null &&\n planConfig.planCode !== \"\"\n ) {\n const subResRaw = await paystack?.subscription?.create({\n body: {\n customer: email,\n plan: planConfig.planCode,\n authorization: authorizationCode,\n start_date: trialEnd,\n },\n });\n const subRes =\n unwrapSdkResult<components[\"schemas\"][\"SubscriptionListResponseArray\"]>(subResRaw);\n paystackSubscriptionCode = subRes?.subscription_code;\n }\n } else if (isTrial === false) {\n const planCodeFromPaystack = (data as { plan?: { plan_code?: string | null } }).plan\n ?.plan_code;\n if (\n planCodeFromPaystack === undefined ||\n planCodeFromPaystack === null ||\n planCodeFromPaystack === \"\"\n ) {\n // Local Plan\n paystackSubscriptionCode = `LOC_${reference}`;\n } else {\n // Native Paystack subscription (if created during charge)\n paystackSubscriptionCode =\n (data as { subscription?: { subscription_code?: string | null } }).subscription\n ?.subscription_code ?? undefined;\n }\n }\n\n const existingSubs = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackTransactionReference\", value: reference }],\n });\n\n const targetSub = existingSubs?.find(\n (s) =>\n referenceId === undefined ||\n referenceId === null ||\n referenceId === \"\" ||\n s.referenceId === referenceId,\n );\n\n let updatedSubscription: Subscription | null = null;\n if (targetSub !== undefined && targetSub !== null) {\n updatedSubscription = await ctx.context.adapter.update<Subscription>({\n model: \"subscription\",\n update: {\n status: isTrial ? \"trialing\" : \"active\",\n periodStart: new Date(),\n updatedAt: new Date(),\n ...(isTrial && trialEnd !== undefined\n ? {\n trialStart: new Date(),\n trialEnd: new Date(trialEnd),\n periodEnd: new Date(trialEnd),\n }\n : {}),\n ...(paystackSubscriptionCode !== undefined ? { paystackSubscriptionCode } : {}),\n ...(authorizationCode !== undefined && authorizationCode !== null\n ? { paystackAuthorizationCode: authorizationCode }\n : {}),\n },\n where: [{ field: \"id\", value: targetSub.id }],\n });\n }\n\n if (\n updatedSubscription !== undefined &&\n updatedSubscription !== null &&\n subscriptionOptions?.onSubscriptionComplete !== undefined\n ) {\n const plans = await getPlans(subscriptionOptions);\n const plan = plans.find(\n (p) => p.name.toLowerCase() === updatedSubscription.plan.toLowerCase(),\n );\n if (plan !== undefined) {\n await subscriptionOptions.onSubscriptionComplete(\n {\n event: data as unknown as PaystackWebhookPayload,\n subscription: updatedSubscription,\n plan,\n },\n ctx as GenericEndpointContext,\n );\n }\n }\n } catch (e: unknown) {\n ctx.context.logger.error(\n \"Failed to update transaction/subscription after verification\",\n e,\n );\n }\n }\n\n return ctx.json({ status, reference, data });\n },\n );\n};\n\nexport const listSubscriptions = <P extends string = \"/list-subscriptions\">(\n options: AnyPaystackOptions,\n path: P = \"/list-subscriptions\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n query: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n subscriptions: Subscription[];\n }\n> => {\n const listQuerySchema = z.object({\n referenceId: z.string().optional(),\n });\n\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"list-subscriptions\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n query: listQuerySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n if (subscriptionOptions?.enabled !== true) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Subscriptions are not enabled in the Paystack options.\",\n });\n }\n const session = await getSessionFromCtx(ctx);\n if (session === undefined || session === null) throw new APIError(\"UNAUTHORIZED\");\n const referenceIdPart = (ctx.context as Record<string, unknown>).referenceId as\n | string\n | undefined;\n const queryRefId = ctx.query?.referenceId;\n const referenceId = referenceIdPart ?? queryRefId ?? (session.user as { id: string }).id;\n const res = await ctx.context.adapter.findMany<Subscription>({\n model: \"subscription\",\n where: [{ field: \"referenceId\", value: referenceId }],\n });\n return ctx.json({ subscriptions: res });\n },\n );\n};\n\nexport const listTransactions = <P extends string = \"/list-transactions\">(\n options: AnyPaystackOptions,\n path: P = \"/list-transactions\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n query: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n transactions: PaystackTransaction[];\n }\n> => {\n const listQuerySchema = z.object({\n referenceId: z.string().optional(),\n });\n\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"list-transactions\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n query: listQuerySchema,\n use: useMiddlewares,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n if (session === undefined || session === null) throw new APIError(\"UNAUTHORIZED\");\n const referenceIdPart = (ctx.context as Record<string, unknown>).referenceId as\n | string\n | undefined;\n const queryRefId = ctx.query?.referenceId;\n const referenceId = referenceIdPart ?? queryRefId ?? (session.user as { id: string }).id;\n const res = await ctx.context.adapter.findMany<PaystackTransaction>({\n model: \"paystackTransaction\",\n where: [{ field: \"referenceId\", value: referenceId }],\n });\n // Sort by createdAt desc locally.\n const sorted = res.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n return ctx.json({ transactions: sorted });\n },\n );\n};\n\nconst enableDisableBodySchema = z.object({\n referenceId: z.string().optional(),\n subscriptionCode: z.string(),\n emailToken: z.string().optional(),\n atPeriodEnd: z.boolean().optional(),\n});\n\nfunction decodeBase64UrlToString(value: string): string {\n const normalized = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = normalized + \"===\".slice((normalized.length + 3) % 4);\n const binaryString = atob(padded);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\nfunction tryGetEmailTokenFromSubscriptionManageLink(link: string): string | undefined {\n try {\n const url = new URL(link);\n const subscriptionToken = url.searchParams.get(\"subscription_token\");\n if (subscriptionToken === undefined || subscriptionToken === null || subscriptionToken === \"\")\n return undefined;\n const parts = subscriptionToken.split(\".\");\n if (parts.length < 2) return undefined;\n const payloadJson = decodeBase64UrlToString(parts[1]);\n const payload = JSON.parse(payloadJson) as Record<string, unknown>;\n return typeof payload.email_token === \"string\" ? payload.email_token : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport const disablePaystackSubscription = <P extends string = \"/disable-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/disable-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => {\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"disable-subscription\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n { method: \"POST\", body: enableDisableBodySchema, use: useMiddlewares },\n async (ctx) => {\n const { subscriptionCode, atPeriodEnd } = ctx.body;\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const subCode = subscriptionCode;\n if (isLocalSubscriptionCode(subCode)) {\n const sub = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (sub !== null && sub !== undefined) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: atPeriodEnd === false ? \"canceled\" : \"active\",\n cancelAtPeriodEnd: atPeriodEnd !== false,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: sub.id }],\n });\n return ctx.json({ status: \"success\" });\n }\n throw new APIError(\"BAD_REQUEST\", { message: \"Subscription not found\" });\n }\n\n let emailToken = ctx.body.emailToken;\n let nextPaymentDate: string | undefined;\n\n try {\n const raw = await paystack?.subscription?.fetch(subscriptionCode);\n const fetchRes =\n unwrapSdkResult<components[\"schemas\"][\"SubscriptionListResponseArray\"]>(raw);\n\n if (fetchRes !== undefined && fetchRes !== null) {\n emailToken ??= fetchRes.email_token ?? undefined;\n nextPaymentDate = fetchRes.next_payment_date ?? undefined;\n }\n } catch {\n // ignore fetch failure\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n try {\n const raw = await paystack?.subscription?.manageLink(subscriptionCode);\n const linkRes = unwrapSdkResult<{ link: string }>(raw);\n const link = linkRes?.link;\n if (link !== undefined && link !== null && link !== \"\") {\n emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);\n }\n } catch {\n // ignore\n }\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n throw new Error(\"Could not retrieve email_token for subscription disable.\");\n }\n\n await paystack?.subscription?.disable({\n body: { code: subscriptionCode, token: emailToken },\n });\n\n const periodEnd =\n nextPaymentDate !== undefined && nextPaymentDate !== null && nextPaymentDate !== \"\"\n ? new Date(nextPaymentDate)\n : undefined;\n\n const sub = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n if (sub !== undefined && sub !== null) {\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: atPeriodEnd === false ? \"canceled\" : \"active\",\n cancelAtPeriodEnd: atPeriodEnd !== false,\n periodEnd,\n updatedAt: new Date(),\n },\n where: [{ field: \"id\", value: sub.id }],\n });\n } else {\n ctx.context.logger.warn(\n `Could not find subscription with code ${subscriptionCode} to disable`,\n );\n }\n\n return ctx.json({ status: \"success\" });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to disable subscription\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_DISABLE_SUBSCRIPTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_DISABLE_SUBSCRIPTION\",\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const enablePaystackSubscription = <P extends string = \"/enable-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/enable-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n referenceId: z.ZodOptional<z.ZodString>;\n subscriptionCode: z.ZodString;\n emailToken: z.ZodOptional<z.ZodString>;\n atPeriodEnd: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n> => {\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [sessionMiddleware, originCheck, referenceMiddleware(options, \"enable-subscription\")]\n : [sessionMiddleware, originCheck];\n\n return createAuthEndpoint(\n path,\n { method: \"POST\", body: enableDisableBodySchema, use: useMiddlewares },\n async (ctx) => {\n const { subscriptionCode } = ctx.body;\n const paystack = getPaystackOps(options.paystackClient);\n try {\n let emailToken = ctx.body.emailToken;\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n try {\n const raw = await paystack?.subscription?.fetch(subscriptionCode);\n const fetchRes =\n unwrapSdkResult<components[\"schemas\"][\"SubscriptionListResponseArray\"]>(raw);\n if (fetchRes !== undefined && fetchRes !== null) {\n emailToken = fetchRes.email_token ?? undefined;\n }\n } catch {\n // ignore\n }\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n try {\n const raw = await paystack?.subscription?.manageLink(subscriptionCode);\n const linkRes = unwrapSdkResult<{ link: string }>(raw);\n const link = linkRes?.link;\n if (link !== undefined && link !== null && link !== \"\") {\n emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);\n }\n } catch {\n // ignore\n }\n }\n\n if (emailToken === undefined || emailToken === null || emailToken === \"\") {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Could not retrieve email_token for subscription enable.\",\n });\n }\n\n await paystack?.subscription?.enable({\n body: { code: subscriptionCode, token: emailToken },\n });\n\n // Update local status immediately\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n status: \"active\",\n updatedAt: new Date(),\n },\n where: [{ field: \"paystackSubscriptionCode\", value: subscriptionCode }],\n });\n\n return ctx.json({ status: \"success\" });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to enable subscription\", error);\n const errorMessage =\n error instanceof Error\n ? error.message\n : PAYSTACK_ERROR_CODES.FAILED_TO_ENABLE_SUBSCRIPTION.message;\n throw new APIError(\"BAD_REQUEST\", {\n code: \"FAILED_TO_ENABLE_SUBSCRIPTION\",\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const getSubscriptionManageLink = <P extends string = \"/subscription-manage-link\">(\n options: AnyPaystackOptions,\n path: P = \"/subscription-manage-link\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n query: z.ZodObject<\n {\n subscriptionCode: z.ZodString;\n },\n z.core.$strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n link: string | null;\n }\n> => {\n const manageLinkQuerySchema = z.object({\n subscriptionCode: z.string(),\n });\n const subscriptionOptions = options.subscription;\n const useMiddlewares =\n subscriptionOptions?.enabled === true\n ? [\n sessionMiddleware,\n originCheck,\n referenceMiddleware(options, \"get-subscription-manage-link\"),\n ]\n : [sessionMiddleware, originCheck];\n\n const handler = async (ctx: GenericEndpointContext) => {\n const { subscriptionCode } = ctx.query;\n\n if (isLocalSubscriptionCode(subscriptionCode as string)) {\n return ctx.json({ link: null, message: \"Local subscriptions cannot be managed on Paystack\" });\n }\n\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.subscription?.manageLink(subscriptionCode as string);\n const res = unwrapSdkResult<{ link: string }>(raw);\n return ctx.json({ link: res?.link || null });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to get subscription manage link\", error);\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to get subscription manage link\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n };\n\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n query: manageLinkQuerySchema,\n use: useMiddlewares,\n },\n handler,\n );\n};\n\nexport const syncProducts = <P extends string = \"/sync-products\">(\n options: AnyPaystackOptions,\n path: P = \"/sync-products\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n metadata: {\n scope: \"server\";\n };\n disableBody: true;\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n | {\n products: never[];\n }\n | {\n status: string;\n count: number;\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n metadata: { ...HIDE_METADATA },\n disableBody: true,\n use: [sessionMiddleware],\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.product?.list({});\n const productsData =\n unwrapSdkResult<components[\"schemas\"][\"ProductListsResponseArray\"][]>(raw);\n\n if (!Array.isArray(productsData)) {\n return ctx.json({ products: [] });\n }\n\n for (const product of productsData) {\n const paystackId = String(product.id);\n const existing = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const productFields = {\n name: product.name ?? \"\",\n description: product.description ?? \"\",\n price: product.price ?? 0,\n currency: product.currency ?? \"\",\n quantity: product.quantity ?? 0,\n unlimited:\n product.unlimited !== undefined &&\n product.unlimited !== null &&\n product.unlimited !== false,\n paystackId,\n slug:\n (product as unknown as { slug?: string }).slug ??\n product.name?.toLowerCase().replace(/\\s+/g, \"-\") ??\n \"\",\n metadata:\n (product as unknown as { metadata?: unknown }).metadata !== undefined &&\n (product as unknown as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((product as unknown as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: productFields,\n where: [{ field: \"id\", value: String(existing.id) }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackProduct\",\n data: { ...productFields, createdAt: new Date() },\n });\n }\n }\n\n return ctx.json({ status: \"success\", count: productsData.length });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync products\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync products\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const listProducts = <P extends string = \"/list-products\">(\n _options: AnyPaystackOptions,\n path: P = \"/list-products\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n products: PaystackProduct[];\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n metadata: {\n openapi: {\n operationId: \"listPaystackProducts\",\n },\n },\n },\n async (ctx) => {\n const res = await ctx.context.adapter.findMany<PaystackProduct>({\n model: \"paystackProduct\",\n });\n const sorted = res.sort((a, b) => a.name.localeCompare(b.name));\n return ctx.json({ products: sorted });\n },\n );\n};\n\nexport const syncPlans = <P extends string = \"/sync-plans\">(\n options: AnyPaystackOptions,\n path: P = \"/sync-plans\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n metadata: {\n scope: \"server\";\n };\n disableBody: true;\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n {\n status: string;\n count: number;\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n metadata: { ...HIDE_METADATA },\n disableBody: true,\n use: [sessionMiddleware],\n },\n async (ctx) => {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.plan?.list();\n const plansData = unwrapSdkResult<components[\"schemas\"][\"PlanListResponseArray\"][]>(raw);\n\n if (!Array.isArray(plansData)) {\n return ctx.json({ status: \"success\", count: 0 });\n }\n\n for (const plan of plansData) {\n const paystackId = String(plan.id);\n const existing = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const planData = {\n name: plan.name ?? \"\",\n description: plan.description ?? \"\",\n amount: plan.amount ?? 0,\n currency: plan.currency ?? \"\",\n interval: plan.interval ?? \"\",\n planCode: plan.plan_code ?? \"\",\n paystackId,\n metadata:\n (plan as unknown as { metadata?: unknown }).metadata !== undefined &&\n (plan as unknown as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((plan as unknown as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackPlan\",\n update: planData,\n where: [{ field: \"id\", value: existing.id! }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackPlan\",\n data: { ...planData, createdAt: new Date() },\n });\n }\n }\n\n return ctx.json({ status: \"success\", count: plansData.length });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync plans\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync plans\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const listPlans = <P extends string = \"/list-plans\">(\n _options: AnyPaystackOptions,\n path: P = \"/list-plans\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n metadata: {\n scope: \"server\";\n };\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n {\n plans: PaystackPlan[];\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n metadata: { ...HIDE_METADATA },\n use: [sessionMiddleware],\n },\n async (ctx) => {\n try {\n const plans = await ctx.context.adapter.findMany<PaystackPlan>({\n model: \"paystackPlan\",\n });\n return ctx.json({ plans });\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to list plans\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to list plans\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n },\n );\n};\n\nexport const getConfig = <P extends string = \"/get-config\">(\n options: AnyPaystackOptions,\n path: P = \"/get-config\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n plans: PaystackPlan[];\n products: PaystackProduct[];\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"GET\",\n metadata: {\n openapi: {\n operationId: \"getPaystackConfig\",\n },\n },\n },\n async (ctx: GenericEndpointContext) => {\n const plans =\n options.subscription?.enabled === true ? await getPlans(options.subscription) : [];\n const products = await getProducts(options.products);\n return ctx.json({ plans, products });\n },\n );\n};\n\nexport { PAYSTACK_ERROR_CODES };\n\nexport const chargeRecurringSubscription = <P extends string = \"/charge-recurring-subscription\">(\n options: AnyPaystackOptions,\n path: P = \"/charge-recurring-subscription\" as P,\n): StrictEndpoint<\n P,\n {\n method: \"POST\";\n body: z.ZodObject<\n {\n subscriptionId: z.ZodString;\n amount: z.ZodOptional<z.ZodNumber>;\n },\n z.core.$strip\n >;\n },\n {\n status: string;\n data: {\n id: number;\n domain: string;\n status: string;\n reference: string;\n receipt_number: string | null;\n amount: number;\n message: string | null;\n gateway_response: string;\n channel: string;\n currency: string;\n ip_address: string | null;\n metadata: (string | Record<string, never> | number) | null;\n log: {\n start_time: number;\n time_spent: number;\n attempts: number;\n errors: number;\n success: boolean;\n mobile: boolean;\n input: unknown[];\n history: {\n type: string;\n message: string;\n time: number;\n }[];\n } | null;\n fees: number | null;\n fees_split: unknown;\n authorization: {\n authorization_code?: string;\n bin?: string | null;\n last4?: string;\n exp_month?: string;\n exp_year?: string;\n channel?: string;\n card_type?: string;\n bank?: string;\n country_code?: string;\n brand?: string;\n reusable?: boolean;\n signature?: string;\n account_name?: string | null;\n receiver_bank_account_number?: string | null;\n receiver_bank?: string | null;\n };\n customer: {\n id: number;\n first_name: string | null;\n last_name: string | null;\n email: string;\n customer_code: string;\n phone: string | null;\n metadata: Record<string, never> | null;\n risk_action: string;\n international_format_phone?: string | null;\n };\n plan: (string | Record<string, never>) | null;\n split: Record<string, never> | null;\n order_id: unknown;\n paidAt: string | null;\n createdAt: string;\n requested_amount: number;\n pos_transaction_data: unknown;\n source: unknown;\n fees_breakdown: unknown;\n connect: unknown;\n transaction_date: string;\n plan_object: {\n id?: number;\n name?: string;\n plan_code?: string;\n description?: unknown;\n amount?: number;\n interval?: string;\n send_invoices?: boolean;\n send_sms?: boolean;\n currency?: string;\n };\n subaccount: Record<string, never> | null;\n };\n }\n> => {\n return createAuthEndpoint(\n path,\n {\n method: \"POST\",\n body: z.object({\n subscriptionId: z.string(),\n amount: z.number().optional(),\n }),\n },\n async (ctx) => {\n const { subscriptionId, amount: bodyAmount } = ctx.body;\n const subscription = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"id\", value: subscriptionId }],\n });\n\n if (subscription === undefined || subscription === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Subscription not found\" });\n }\n\n if (\n subscription.paystackAuthorizationCode === undefined ||\n subscription.paystackAuthorizationCode === null ||\n subscription.paystackAuthorizationCode === \"\"\n ) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"No authorization code found for this subscription\",\n });\n }\n\n const plans = await getPlans(options.subscription);\n const plan = plans.find((p) => p.name.toLowerCase() === subscription.plan.toLowerCase());\n\n if (plan === undefined || plan === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Plan not found\" });\n }\n\n const amount = bodyAmount ?? plan.amount;\n if (amount === undefined || amount === null) {\n throw new APIError(\"BAD_REQUEST\", { message: \"Plan amount is not defined\" });\n }\n\n let email: string | undefined;\n const referenceId = subscription.referenceId;\n if (referenceId !== undefined && referenceId !== null && referenceId !== \"\") {\n const user = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (user !== undefined && user !== null) {\n email = user.email;\n } else if (options.organization?.enabled === true) {\n const ownerMember = await ctx.context.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: referenceId },\n { field: \"role\", value: \"owner\" },\n ],\n });\n if (ownerMember !== undefined && ownerMember !== null) {\n const ownerUser = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: ownerMember.userId }],\n });\n email = ownerUser?.email;\n }\n }\n }\n\n if (email === undefined || email === null || email === \"\") {\n throw new APIError(\"NOT_FOUND\", { message: \"User email not found\" });\n }\n\n const finalCurrency = plan.currency ?? \"NGN\";\n if (!validateMinAmount(amount, finalCurrency)) {\n throw new APIError(\"BAD_REQUEST\", {\n message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,\n status: 400,\n });\n }\n\n const paystack = getPaystackOps(options.paystackClient);\n const chargeResRaw = await paystack?.transaction?.chargeAuthorization({\n body: {\n email,\n amount,\n authorization_code: subscription.paystackAuthorizationCode,\n reference: `rec_${subscription.id}_${Date.now()}`,\n metadata: JSON.stringify({\n subscriptionId,\n referenceId,\n }),\n },\n });\n\n const chargeData = unwrapSdkResult<PaystackTransactionResponse>(chargeResRaw);\n\n if (chargeData?.status === \"success\" && chargeData.reference !== undefined) {\n const now = new Date();\n const nextPeriodEnd = getNextPeriodEnd(now, plan.interval ?? \"monthly\");\n\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n periodStart: now,\n periodEnd: nextPeriodEnd,\n updatedAt: now,\n paystackTransactionReference: chargeData.reference,\n },\n where: [{ field: \"id\", value: subscription.id }],\n });\n\n return ctx.json({ status: \"success\", data: chargeData });\n }\n\n return ctx.json({ status: \"failed\", data: chargeData }, { status: 400 });\n },\n );\n};\n","import type { BetterAuthPluginDBSchema } from \"@better-auth/core/db\";\nimport { mergeSchema } from \"better-auth/db\";\n\nimport type { PaystackOptions } from \"./types\";\n\nexport const transactions: BetterAuthPluginDBSchema = {\n paystackTransaction: {\n fields: {\n reference: {\n type: \"string\",\n required: true,\n unique: true,\n },\n paystackId: {\n type: \"string\",\n required: false,\n },\n referenceId: {\n type: \"string\",\n required: true,\n index: true,\n },\n userId: {\n type: \"string\",\n required: true,\n index: true,\n },\n amount: {\n type: \"number\",\n required: true,\n },\n currency: {\n type: \"string\",\n required: true,\n },\n status: {\n type: \"string\",\n required: true,\n },\n plan: {\n type: \"string\",\n required: false,\n },\n product: {\n type: \"string\",\n required: false,\n },\n metadata: {\n type: \"string\",\n required: false,\n },\n createdAt: {\n type: \"date\",\n required: true,\n },\n updatedAt: {\n type: \"date\",\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const subscriptions: BetterAuthPluginDBSchema = {\n subscription: {\n fields: {\n plan: {\n type: \"string\",\n required: true,\n index: true,\n },\n referenceId: {\n type: \"string\",\n required: true,\n index: true,\n },\n paystackCustomerCode: {\n type: \"string\",\n required: false,\n index: true,\n },\n paystackSubscriptionCode: {\n type: \"string\",\n required: false,\n unique: true,\n },\n paystackTransactionReference: {\n type: \"string\",\n required: false,\n index: true,\n },\n paystackAuthorizationCode: {\n type: \"string\",\n required: false,\n },\n paystackEmailToken: {\n type: \"string\",\n required: false,\n },\n status: {\n type: \"string\",\n defaultValue: \"incomplete\",\n },\n periodStart: {\n type: \"date\",\n required: false,\n },\n periodEnd: {\n type: \"date\",\n required: false,\n },\n trialStart: {\n type: \"date\",\n required: false,\n },\n trialEnd: {\n type: \"date\",\n required: false,\n },\n cancelAtPeriodEnd: {\n type: \"boolean\",\n required: false,\n defaultValue: false,\n },\n groupId: {\n type: \"string\",\n required: false,\n },\n seats: {\n type: \"number\",\n required: false,\n },\n pendingPlan: {\n type: \"string\",\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const user: BetterAuthPluginDBSchema = {\n user: {\n fields: {\n paystackCustomerCode: {\n type: \"string\",\n required: false,\n index: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const organization: BetterAuthPluginDBSchema = {\n organization: {\n fields: {\n paystackCustomerCode: {\n type: \"string\",\n required: false,\n index: true,\n },\n email: {\n type: \"string\",\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const products: BetterAuthPluginDBSchema = {\n paystackProduct: {\n fields: {\n name: {\n type: \"string\",\n required: true,\n },\n description: {\n type: \"string\",\n required: false,\n },\n price: {\n type: \"number\",\n required: true,\n },\n currency: {\n type: \"string\",\n required: true,\n },\n quantity: {\n type: \"number\",\n required: false,\n defaultValue: 0,\n },\n unlimited: {\n type: \"boolean\",\n required: false,\n defaultValue: true,\n },\n paystackId: {\n type: \"string\",\n required: false,\n unique: true,\n },\n slug: {\n type: \"string\",\n required: true,\n unique: true,\n },\n metadata: {\n type: \"string\",\n required: false,\n },\n createdAt: {\n type: \"date\",\n required: true,\n },\n updatedAt: {\n type: \"date\",\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const plans: BetterAuthPluginDBSchema = {\n paystackPlan: {\n fields: {\n name: {\n type: \"string\",\n required: true,\n },\n description: {\n type: \"string\",\n required: false,\n },\n amount: {\n type: \"number\",\n required: true,\n },\n currency: {\n type: \"string\",\n required: true,\n },\n interval: {\n type: \"string\",\n required: true,\n },\n planCode: {\n type: \"string\",\n required: true,\n unique: true,\n },\n paystackId: {\n type: \"string\",\n required: true,\n unique: true,\n },\n metadata: {\n type: \"string\",\n required: false,\n },\n createdAt: {\n type: \"date\",\n required: true,\n },\n updatedAt: {\n type: \"date\",\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = (options: PaystackOptions): BetterAuthPluginDBSchema => {\n let baseSchema: BetterAuthPluginDBSchema;\n\n if (options.subscription?.enabled === true) {\n baseSchema = {\n ...subscriptions,\n ...transactions,\n ...user,\n ...products,\n ...plans,\n };\n } else {\n baseSchema = {\n ...user,\n ...transactions,\n ...products,\n ...plans,\n };\n }\n\n // Add organization schema if organization support is enabled\n if (options.organization?.enabled === true) {\n baseSchema = {\n ...baseSchema,\n ...organization,\n };\n }\n\n if (\n options.schema !== undefined &&\n options.subscription?.enabled !== true &&\n \"subscription\" in options.schema\n ) {\n const { subscription: _subscription, ...restSchema } = options.schema;\n return mergeSchema(baseSchema, restSchema);\n }\n\n return mergeSchema(baseSchema, options.schema);\n};\n","import { APIError } from \"better-auth/api\";\nimport type { GenericEndpointContext } from \"better-auth\";\nimport type { components } from \"@alexasomba/paystack-node\";\n\nimport { getPaystackOps, unwrapSdkResult } from \"./paystack-sdk\";\nimport { getNextPeriodEnd, getPlans, validateMinAmount } from \"./utils\";\nimport type {\n AnyPaystackOptions,\n ChargeRecurringSubscriptionInput,\n ChargeRecurringSubscriptionResult,\n Member,\n PaystackPlan,\n PaystackProduct,\n PaystackSyncResult,\n PaystackTransactionResponse,\n Subscription,\n User,\n} from \"./types\";\n\nexport async function syncPaystackProducts(\n ctx: GenericEndpointContext,\n options: AnyPaystackOptions,\n): Promise<PaystackSyncResult> {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.product?.list({});\n const productsData = unwrapSdkResult<components[\"schemas\"][\"ProductListsResponseArray\"][]>(raw);\n\n if (!Array.isArray(productsData)) {\n return { status: \"success\", count: 0 };\n }\n\n for (const product of productsData) {\n const paystackId = String(product.id);\n const existing = await ctx.context.adapter.findOne<PaystackProduct>({\n model: \"paystackProduct\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const productFields = {\n name: product.name ?? \"\",\n description: product.description ?? \"\",\n price: product.price ?? 0,\n currency: product.currency ?? \"\",\n quantity: product.quantity ?? 0,\n unlimited:\n product.unlimited !== undefined &&\n product.unlimited !== null &&\n product.unlimited !== false,\n paystackId,\n slug:\n (product as { slug?: string }).slug ??\n product.name?.toLowerCase().replace(/\\s+/g, \"-\") ??\n \"\",\n metadata:\n (product as { metadata?: unknown }).metadata !== undefined &&\n (product as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((product as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackProduct\",\n update: productFields,\n where: [{ field: \"id\", value: String(existing.id) }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackProduct\",\n data: { ...productFields, createdAt: new Date() },\n });\n }\n }\n\n return { status: \"success\", count: productsData.length };\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync products\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync products\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n}\n\nexport async function syncPaystackPlans(\n ctx: GenericEndpointContext,\n options: AnyPaystackOptions,\n): Promise<PaystackSyncResult> {\n const paystack = getPaystackOps(options.paystackClient);\n try {\n const raw = await paystack?.plan?.list();\n const plansData = unwrapSdkResult<components[\"schemas\"][\"PlanListResponseArray\"][]>(raw);\n\n if (!Array.isArray(plansData)) {\n return { status: \"success\", count: 0 };\n }\n\n for (const plan of plansData) {\n const paystackId = String(plan.id);\n const existing = await ctx.context.adapter.findOne<PaystackPlan>({\n model: \"paystackPlan\",\n where: [{ field: \"paystackId\", value: paystackId }],\n });\n\n const planData = {\n name: plan.name ?? \"\",\n description: plan.description ?? \"\",\n amount: plan.amount ?? 0,\n currency: plan.currency ?? \"\",\n interval: plan.interval ?? \"\",\n planCode: plan.plan_code ?? \"\",\n paystackId,\n metadata:\n (plan as { metadata?: unknown }).metadata !== undefined &&\n (plan as { metadata?: unknown }).metadata !== null\n ? JSON.stringify((plan as { metadata?: unknown }).metadata)\n : undefined,\n updatedAt: new Date(),\n };\n\n if (existing !== undefined && existing !== null) {\n await ctx.context.adapter.update({\n model: \"paystackPlan\",\n update: planData,\n where: [{ field: \"id\", value: existing.id! }],\n });\n } else {\n await ctx.context.adapter.create({\n model: \"paystackPlan\",\n data: { ...planData, createdAt: new Date() },\n });\n }\n }\n\n return { status: \"success\", count: plansData.length };\n } catch (error: unknown) {\n ctx.context.logger.error(\"Failed to sync plans\", error);\n const errorMessage = error instanceof Error ? error.message : \"Failed to sync plans\";\n throw new APIError(\"BAD_REQUEST\", {\n message: errorMessage,\n });\n }\n}\n\nexport async function chargeSubscriptionRenewal(\n ctx: GenericEndpointContext,\n options: AnyPaystackOptions,\n input: ChargeRecurringSubscriptionInput,\n): Promise<ChargeRecurringSubscriptionResult> {\n const { subscriptionId, amount: bodyAmount } = input;\n const subscription = await ctx.context.adapter.findOne<Subscription>({\n model: \"subscription\",\n where: [{ field: \"id\", value: subscriptionId }],\n });\n\n if (subscription === undefined || subscription === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Subscription not found\" });\n }\n\n if (\n subscription.paystackAuthorizationCode === undefined ||\n subscription.paystackAuthorizationCode === null ||\n subscription.paystackAuthorizationCode === \"\"\n ) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"No authorization code found for this subscription\",\n });\n }\n\n const plans = await getPlans(options.subscription);\n const plan = plans.find(\n (candidate) => candidate.name.toLowerCase() === subscription.plan.toLowerCase(),\n );\n\n if (plan === undefined || plan === null) {\n throw new APIError(\"NOT_FOUND\", { message: \"Plan not found\" });\n }\n\n const amount = bodyAmount ?? plan.amount;\n if (amount === undefined || amount === null) {\n throw new APIError(\"BAD_REQUEST\", { message: \"Plan amount is not defined\" });\n }\n\n let email: string | undefined;\n let billingUserId = subscription.userId;\n const referenceId = subscription.referenceId;\n if (referenceId !== undefined && referenceId !== null && referenceId !== \"\") {\n const user = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: referenceId }],\n });\n if (user !== undefined && user !== null) {\n email = user.email;\n billingUserId = user.id;\n } else if (options.organization?.enabled === true) {\n const ownerMember = await ctx.context.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: referenceId },\n { field: \"role\", value: \"owner\" },\n ],\n });\n if (ownerMember !== undefined && ownerMember !== null) {\n const ownerUser = await ctx.context.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: ownerMember.userId }],\n });\n email = ownerUser?.email;\n billingUserId = ownerUser?.id ?? ownerMember.userId;\n }\n }\n }\n\n if (email === undefined || email === null || email === \"\") {\n throw new APIError(\"NOT_FOUND\", { message: \"User email not found\" });\n }\n\n const finalCurrency = plan.currency ?? \"NGN\";\n if (!validateMinAmount(amount, finalCurrency)) {\n throw new APIError(\"BAD_REQUEST\", {\n message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,\n status: 400,\n });\n }\n\n const paystack = getPaystackOps(options.paystackClient);\n const chargeResRaw = await paystack?.transaction?.chargeAuthorization({\n body: {\n email,\n amount,\n authorization_code: subscription.paystackAuthorizationCode,\n reference: `rec_${subscription.id}_${Date.now()}`,\n metadata: JSON.stringify({\n subscriptionId,\n referenceId,\n }),\n },\n });\n\n const chargeData = unwrapSdkResult<PaystackTransactionResponse>(chargeResRaw);\n if (chargeData?.status === \"success\" && chargeData.reference !== undefined) {\n const now = new Date();\n const nextPeriodEnd = getNextPeriodEnd(now, plan.interval ?? \"monthly\");\n\n await ctx.context.adapter.create({\n model: \"paystackTransaction\",\n data: {\n reference: chargeData.reference,\n paystackId:\n chargeData.id !== undefined && chargeData.id !== null ? String(chargeData.id) : undefined,\n referenceId,\n userId: billingUserId,\n amount: chargeData.amount,\n currency: chargeData.currency,\n status: \"success\",\n plan: plan.name.toLowerCase(),\n metadata: JSON.stringify({\n type: \"renewal\",\n subscriptionId,\n referenceId,\n }),\n createdAt: now,\n updatedAt: now,\n },\n });\n\n await ctx.context.adapter.update({\n model: \"subscription\",\n update: {\n periodStart: now,\n periodEnd: nextPeriodEnd,\n updatedAt: now,\n paystackTransactionReference: chargeData.reference,\n },\n where: [{ field: \"id\", value: subscription.id }],\n });\n\n return { status: \"success\", data: chargeData };\n }\n\n return { status: \"failed\", data: chargeData };\n}\n","import { defineErrorCodes } from \"@better-auth/core/utils/error-codes\";\nimport type { CustomerCreatePayload } from \"@alexasomba/paystack-node\";\nimport type {\n AuthContext,\n BetterAuthPlugin,\n BetterAuthPluginDBSchema,\n GenericEndpointContext,\n MiddlewareInputContext,\n MiddlewareOptions,\n RawError,\n StrictEndpoint,\n ZodBoolean,\n ZodNumber,\n ZodObject,\n ZodOptional,\n ZodRecord,\n ZodString,\n ZodUnknown,\n} from \"better-auth\";\nimport { defu } from \"defu\";\n\nimport {\n disablePaystackSubscription,\n enablePaystackSubscription,\n initializeTransaction,\n listSubscriptions,\n listTransactions,\n paystackWebhook,\n verifyTransaction,\n getConfig,\n getSubscriptionManageLink,\n PAYSTACK_ERROR_CODES,\n createSubscription,\n upgradeSubscription,\n cancelSubscription,\n restoreSubscription,\n listProducts,\n listPlans,\n} from \"./routes\";\nimport { getSchema } from \"./schema\";\nimport { checkSeatLimit, checkTeamLimit, getOrganizationSubscription } from \"./limits\";\nimport { getPlanByName, syncSubscriptionSeats } from \"./utils\";\nimport type {\n PaystackClientLike,\n PaystackOptions,\n PaystackPlan,\n Subscription,\n SubscriptionOptions,\n PaystackProduct,\n PaystackCustomerResponse,\n Member,\n AnyPaystackOptions,\n User,\n PaystackTransaction,\n} from \"./types\";\nimport { getPaystackOps, unwrapSdkResult } from \"./paystack-sdk\";\nimport type { $strip } from \"zod/v4/core\";\n\ndeclare module \"@better-auth/core\" {\n interface BetterAuthPluginRegistry<AuthOptions, Options> {\n paystack: {\n creator: typeof paystack;\n };\n }\n}\n\nconst INTERNAL_ERROR_CODES = defineErrorCodes(\n Object.fromEntries(\n Object.entries(PAYSTACK_ERROR_CODES).map(([key, value]) => [\n key,\n typeof value === \"string\" ? value : (value as { message: string }).message,\n ]),\n ),\n);\n\nexport const paystack = <\n TPaystackClient extends PaystackClientLike = PaystackClientLike,\n O extends PaystackOptions<TPaystackClient> = PaystackOptions<TPaystackClient>,\n>(\n options: O,\n): {\n id: \"paystack\";\n endpoints: {\n initializeTransaction: StrictEndpoint<\n \"/paystack/initialize-transaction\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n plan: ZodOptional<ZodString>;\n product: ZodOptional<ZodString>;\n amount: ZodOptional<ZodNumber>;\n currency: ZodOptional<ZodString>;\n email: ZodOptional<ZodString>;\n metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;\n referenceId: ZodOptional<ZodString>;\n callbackURL: ZodOptional<ZodString>;\n quantity: ZodOptional<ZodNumber>;\n scheduleAtPeriodEnd: ZodOptional<ZodBoolean>;\n cancelAtPeriodEnd: ZodOptional<ZodBoolean>;\n prorateAndCharge: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n >;\n verifyTransaction: StrictEndpoint<\n \"/paystack/verify-transaction\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n reference: ZodString;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n reference: string;\n data: {\n id: number;\n domain: string;\n status: string;\n reference: string;\n receipt_number: string | null;\n amount: number;\n message: string | null;\n gateway_response: string;\n channel: string;\n currency: string;\n ip_address: string | null;\n metadata: (string | Record<string, never> | number) | null;\n log: {\n start_time: number;\n time_spent: number;\n attempts: number;\n errors: number;\n success: boolean;\n mobile: boolean;\n input: unknown[];\n history: {\n type: string;\n message: string;\n time: number;\n }[];\n } | null;\n fees: number | null;\n fees_split: unknown;\n authorization: {\n authorization_code?: string;\n bin?: string | null;\n last4?: string;\n exp_month?: string;\n exp_year?: string;\n channel?: string;\n card_type?: string;\n bank?: string;\n country_code?: string;\n brand?: string;\n reusable?: boolean;\n signature?: string;\n account_name?: string | null;\n receiver_bank_account_number?: string | null;\n receiver_bank?: string | null;\n };\n customer: {\n id: number;\n first_name: string | null;\n last_name: string | null;\n email: string;\n customer_code: string;\n phone: string | null;\n metadata: Record<string, never> | null;\n risk_action: string;\n international_format_phone?: string | null;\n };\n plan: (string | Record<string, never>) | null;\n split: Record<string, never> | null;\n order_id: unknown;\n paidAt: string | null;\n createdAt: string;\n requested_amount: number;\n pos_transaction_data: unknown;\n source: unknown;\n fees_breakdown: unknown;\n connect: unknown;\n transaction_date: string;\n plan_object: {\n id?: number;\n name?: string;\n plan_code?: string;\n description?: unknown;\n amount?: number;\n interval?: string;\n send_invoices?: boolean;\n send_sms?: boolean;\n currency?: string;\n };\n subaccount: Record<string, never> | null;\n };\n }\n >;\n listSubscriptions: StrictEndpoint<\n \"/paystack/list-subscriptions\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n subscriptions: Subscription[];\n }\n >;\n paystackWebhook: StrictEndpoint<\n \"/paystack/webhook\",\n {\n method: \"POST\";\n metadata: {\n openapi: {\n operationId: string;\n };\n scope: \"server\";\n };\n cloneRequest: true;\n disableBody: true;\n },\n {\n received: boolean;\n }\n >;\n listTransactions: StrictEndpoint<\n \"/paystack/list-transactions\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n transactions: PaystackTransaction[];\n }\n >;\n getConfig: StrictEndpoint<\n \"/paystack/config\",\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n plans: PaystackPlan[];\n products: PaystackProduct[];\n }\n >;\n disableSubscription: StrictEndpoint<\n \"/paystack/disable-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n enableSubscription: StrictEndpoint<\n \"/paystack/enable-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n getSubscriptionManageLink: StrictEndpoint<\n \"/paystack/subscription-manage-link\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n subscriptionCode: ZodString;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n link: string | null;\n }\n >;\n subscriptionManageLink: StrictEndpoint<\n \"/paystack/subscription/manage-link\",\n {\n method: \"GET\";\n query: ZodObject<\n {\n subscriptionCode: ZodString;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n link: string | null;\n }\n >;\n createSubscription: StrictEndpoint<\n \"/paystack/create-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n plan: ZodOptional<ZodString>;\n product: ZodOptional<ZodString>;\n amount: ZodOptional<ZodNumber>;\n currency: ZodOptional<ZodString>;\n email: ZodOptional<ZodString>;\n metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;\n referenceId: ZodOptional<ZodString>;\n callbackURL: ZodOptional<ZodString>;\n quantity: ZodOptional<ZodNumber>;\n scheduleAtPeriodEnd: ZodOptional<ZodBoolean>;\n cancelAtPeriodEnd: ZodOptional<ZodBoolean>;\n prorateAndCharge: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n >;\n upgradeSubscription: StrictEndpoint<\n \"/paystack/upgrade-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n plan: ZodOptional<ZodString>;\n product: ZodOptional<ZodString>;\n amount: ZodOptional<ZodNumber>;\n currency: ZodOptional<ZodString>;\n email: ZodOptional<ZodString>;\n metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;\n referenceId: ZodOptional<ZodString>;\n callbackURL: ZodOptional<ZodString>;\n quantity: ZodOptional<ZodNumber>;\n scheduleAtPeriodEnd: ZodOptional<ZodBoolean>;\n cancelAtPeriodEnd: ZodOptional<ZodBoolean>;\n prorateAndCharge: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n | {\n status: string;\n message: string;\n scheduled: boolean;\n }\n | {\n status: string;\n message: string;\n prorated: boolean;\n }\n | {\n url: string;\n reference: string;\n accessCode: string;\n redirect: boolean;\n }\n | undefined\n >;\n cancelSubscription: StrictEndpoint<\n \"/paystack/cancel-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n restoreSubscription: StrictEndpoint<\n \"/paystack/restore-subscription\",\n {\n method: \"POST\";\n body: ZodObject<\n {\n referenceId: ZodOptional<ZodString>;\n subscriptionCode: ZodString;\n emailToken: ZodOptional<ZodString>;\n atPeriodEnd: ZodOptional<ZodBoolean>;\n },\n $strip\n >;\n use: (\n | ((\n getValue: (ctx: GenericEndpointContext) => string | string[],\n ) => (inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<void>)\n | ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<unknown>)\n )[];\n },\n {\n status: string;\n }\n >;\n listProducts: StrictEndpoint<\n \"/paystack/list-products\",\n {\n method: \"GET\";\n metadata: {\n openapi: {\n operationId: string;\n };\n };\n },\n {\n products: PaystackProduct[];\n }\n >;\n listPlans: StrictEndpoint<\n \"/paystack/list-plans\",\n {\n method: \"GET\";\n metadata: {\n scope: \"server\";\n };\n use: ((inputContext: MiddlewareInputContext<MiddlewareOptions>) => Promise<{\n session: {\n session: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n userId: string;\n expiresAt: Date;\n token: string;\n ipAddress?: string | null | undefined;\n userAgent?: string | null | undefined;\n };\n user: Record<string, unknown> & {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n email: string;\n emailVerified: boolean;\n name: string;\n image?: string | null | undefined;\n };\n };\n }>)[];\n },\n {\n plans: PaystackPlan[];\n }\n >;\n };\n schema: BetterAuthPluginDBSchema;\n init: (ctx: AuthContext) => {\n options: {\n databaseHooks: {\n user: {\n create: {\n after(\n user: { id: string; email?: string | null; name?: string | null },\n hookCtx?: GenericEndpointContext | null,\n ): Promise<void>;\n };\n };\n organization:\n | {\n create: {\n after(\n org: { id: string; name: string; email?: string | null },\n hookCtx: GenericEndpointContext | null,\n ): Promise<void>;\n };\n }\n | undefined;\n };\n member: {\n create: {\n before: (\n member: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n after: (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n delete: {\n after: (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n };\n invitation: {\n create: {\n before: (\n invitation: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n after: (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n delete: {\n after: (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n };\n team: {\n create: {\n before: (\n team: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => Promise<void>;\n };\n };\n };\n };\n $ERROR_CODES: Record<string, RawError<string>>;\n options: NoInfer<O>;\n} => {\n const routeOptions = {\n ...(options as unknown as AnyPaystackOptions),\n webhook: {\n ...options.webhook,\n secret: options.webhook?.secret ?? options.paystackWebhookSecret,\n },\n } satisfies AnyPaystackOptions;\n return {\n id: \"paystack\",\n endpoints: {\n initializeTransaction: initializeTransaction(\n routeOptions,\n \"/paystack/initialize-transaction\",\n ),\n verifyTransaction: verifyTransaction(routeOptions, \"/paystack/verify-transaction\"),\n listSubscriptions: listSubscriptions(routeOptions, \"/paystack/list-subscriptions\"),\n paystackWebhook: paystackWebhook(routeOptions, \"/paystack/webhook\"),\n listTransactions: listTransactions(routeOptions, \"/paystack/list-transactions\"),\n getConfig: getConfig(routeOptions, \"/paystack/config\"),\n disableSubscription: disablePaystackSubscription(\n routeOptions,\n \"/paystack/disable-subscription\",\n ),\n enableSubscription: enablePaystackSubscription(routeOptions, \"/paystack/enable-subscription\"),\n getSubscriptionManageLink: getSubscriptionManageLink(\n routeOptions,\n \"/paystack/subscription-manage-link\",\n ),\n subscriptionManageLink: getSubscriptionManageLink(\n routeOptions,\n \"/paystack/subscription/manage-link\",\n ),\n createSubscription: createSubscription(routeOptions, \"/paystack/create-subscription\"),\n upgradeSubscription: upgradeSubscription(routeOptions, \"/paystack/upgrade-subscription\"),\n cancelSubscription: cancelSubscription(routeOptions, \"/paystack/cancel-subscription\"),\n restoreSubscription: restoreSubscription(routeOptions, \"/paystack/restore-subscription\"),\n listProducts: listProducts(routeOptions, \"/paystack/list-products\"),\n listPlans: listPlans(routeOptions, \"/paystack/list-plans\"),\n },\n schema: getSchema(options),\n init: (ctx: AuthContext) => {\n return {\n options: {\n databaseHooks: {\n user: {\n create: {\n async after(\n user: { id: string; email?: string | null; name?: string | null },\n hookCtx?: GenericEndpointContext | null,\n ) {\n if (\n !hookCtx ||\n options.createCustomerOnSignUp !== true ||\n user.email === null ||\n user.email === undefined ||\n user.email === \"\"\n )\n return;\n\n try {\n const paystackOps = getPaystackOps(\n options.paystackClient as PaystackClientLike,\n );\n if (!paystackOps) return;\n const raw =\n (await paystackOps.customer?.create({\n body: {\n email: user.email,\n first_name: user.name ?? undefined,\n metadata: JSON.stringify({\n userId: user.id,\n }),\n },\n })) ??\n (await Promise.reject(new Error(\"Paystack client missing customer ops\")));\n const sdkRes = unwrapSdkResult<PaystackCustomerResponse>(raw);\n const customerCode = sdkRes?.customer_code;\n\n if (\n customerCode !== undefined &&\n customerCode !== null &&\n customerCode !== \"\"\n ) {\n await ctx.adapter.update({\n model: \"user\",\n where: [{ field: \"id\", value: user.id }],\n update: {\n paystackCustomerCode: customerCode,\n },\n });\n\n if (typeof options.onCustomerCreate === \"function\") {\n await options.onCustomerCreate(\n {\n paystackCustomer: sdkRes,\n user: {\n ...(user as User),\n paystackCustomerCode: customerCode,\n },\n },\n hookCtx,\n );\n }\n }\n } catch (error: unknown) {\n ctx.logger.error(\"Failed to create Paystack customer for user\", error);\n }\n },\n },\n },\n organization:\n options.organization?.enabled === true\n ? {\n create: {\n async after(\n org: { id: string; name: string; email?: string | null },\n hookCtx: GenericEndpointContext | null,\n ) {\n try {\n const extraCreateParams =\n typeof options.organization?.getCustomerCreateParams === \"function\"\n ? await (\n options.organization.getCustomerCreateParams as (\n org: Record<string, unknown>,\n hookCtx: GenericEndpointContext,\n ) => Promise<Record<string, unknown>>\n )(org as Record<string, unknown>, hookCtx!)\n : {};\n\n let targetEmail = org.email;\n if (targetEmail === undefined || targetEmail === null) {\n const ownerMember = await ctx.adapter.findOne<Member>({\n model: \"member\",\n where: [\n { field: \"organizationId\", value: org.id },\n { field: \"role\", value: \"owner\" },\n ],\n });\n if (ownerMember !== null && ownerMember !== undefined) {\n const ownerUser = await ctx.adapter.findOne<User>({\n model: \"user\",\n where: [{ field: \"id\", value: ownerMember.userId }],\n });\n targetEmail = ownerUser?.email;\n }\n }\n\n if (targetEmail === undefined || targetEmail === null) return;\n\n const params = defu(\n {\n email: targetEmail,\n first_name: org.name,\n metadata: JSON.stringify({ organizationId: org.id }),\n },\n extraCreateParams,\n );\n const paystackOps = getPaystackOps(\n options.paystackClient as PaystackClientLike,\n );\n if (!paystackOps) return;\n const raw =\n (await paystackOps.customer?.create({\n body: params as CustomerCreatePayload,\n })) ??\n (await Promise.reject(\n new Error(\"Paystack client missing customer ops\"),\n ));\n const sdkRes = unwrapSdkResult<PaystackCustomerResponse>(raw);\n const customerCode = sdkRes?.customer_code as string | undefined;\n\n if (\n customerCode !== undefined &&\n customerCode !== null &&\n customerCode !== \"\" &&\n sdkRes !== undefined &&\n sdkRes !== null\n ) {\n await ctx.adapter.update({\n model: \"organization\",\n where: [{ field: \"id\", value: org.id }],\n update: {\n paystackCustomerCode: customerCode,\n },\n });\n\n if (typeof options.organization?.onCustomerCreate === \"function\") {\n await options.organization.onCustomerCreate(\n {\n paystackCustomer: sdkRes,\n organization: {\n ...org,\n paystackCustomerCode: customerCode,\n },\n },\n hookCtx!,\n );\n }\n }\n } catch (error: unknown) {\n ctx.logger.error(\n \"Failed to create Paystack customer for organization\",\n error,\n );\n }\n },\n },\n }\n : undefined,\n },\n member: {\n create: {\n before: async (\n member: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n member.organizationId &&\n ctx !== null &&\n ctx !== undefined\n ) {\n await checkSeatLimit(ctx, member.organizationId);\n }\n },\n after: async (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof member?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, member.organizationId, routeOptions);\n }\n },\n },\n delete: {\n after: async (\n member: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof member?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, member.organizationId, routeOptions);\n }\n },\n },\n },\n invitation: {\n create: {\n before: async (\n invitation: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n invitation.organizationId &&\n ctx !== null &&\n ctx !== undefined\n ) {\n await checkSeatLimit(ctx, invitation.organizationId);\n }\n },\n after: async (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof invitation?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, invitation.organizationId, routeOptions);\n }\n },\n },\n delete: {\n after: async (\n invitation: { organizationId: string | undefined },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (\n options.subscription?.enabled === true &&\n typeof invitation?.organizationId === \"string\" &&\n ctx\n ) {\n await syncSubscriptionSeats(ctx, invitation.organizationId, routeOptions);\n }\n },\n },\n },\n team: {\n create: {\n before: async (\n team: { organizationId: string },\n ctx: GenericEndpointContext | null | undefined,\n ) => {\n if (options.subscription?.enabled === true && team.organizationId && ctx) {\n const subscription = await getOrganizationSubscription(ctx, team.organizationId);\n if (subscription !== null && subscription !== undefined) {\n const plan = await getPlanByName(routeOptions, subscription.plan);\n const limits = plan?.limits;\n const maxTeams = limits?.teams as number | undefined;\n\n if (typeof maxTeams === \"number\") {\n await checkTeamLimit(ctx, team.organizationId, maxTeams);\n }\n }\n }\n },\n },\n },\n },\n };\n },\n $ERROR_CODES: INTERNAL_ERROR_CODES,\n options: options as NoInfer<O>,\n } satisfies BetterAuthPlugin;\n};\n\nexport type PaystackPlugin<\n TPaystackClient extends PaystackClientLike = PaystackClientLike,\n O extends PaystackOptions<TPaystackClient> = PaystackOptions<TPaystackClient>,\n> = ReturnType<typeof paystack<TPaystackClient, O>>;\n\nexport { chargeSubscriptionRenewal, syncPaystackPlans, syncPaystackProducts } from \"./operations\";\nexport type {\n Subscription,\n SubscriptionOptions,\n PaystackPlan,\n PaystackOptions,\n PaystackProduct,\n PaystackClientLike,\n ChargeRecurringSubscriptionResult,\n PaystackSyncResult,\n} from \"./types\";\n"],"mappings":"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U;EACV,QAAQ;EACT;CACD,8BAA8B;EAC5B,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,2BAA2B;EACzB,MAAM;EACN,UAAU;EACX;CACD,oBAAoB;EAClB,MAAM;EACN,UAAU;EACX;CACD,QAAQ;EACN,MAAM;EACN,cAAc;EACf;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACD,YAAY;EACV,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,mBAAmB;EACjB,MAAM;EACN,UAAU;EACV,cAAc;EACf;CACD,SAAS;EACP,MAAM;EACN,UAAU;EACX;CACD,OAAO;EACL,MAAM;EACN,UAAU;EACX;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACX;CACF,EACF,EACF;AAED,MAAa,OAAiC,EAC5C,MAAM,EACJ,QAAQ,EACN,sBAAsB;CACpB,MAAM;CACN,UAAU;CACV,OAAO;CACR,EACF,EACF,EACF;AAED,MAAa,eAAyC,EACpD,cAAc,EACZ,QAAQ;CACN,sBAAsB;EACpB,MAAM;EACN,UAAU;EACV,OAAO;EACR;CACD,OAAO;EACL,MAAM;EACN,UAAU;EACX;CACF,EACF,EACF;AAED,MAAa,WAAqC,EAChD,iBAAiB,EACf,QAAQ;CACN,MAAM;EACJ,MAAM;EACN,UAAU;EACX;CACD,aAAa;EACX,MAAM;EACN,UAAU;EACX;CACD,OAAO;EACL,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACV,cAAc;EACf;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACV,cAAc;EACf;CACD,YAAY;EACV,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,MAAM;EACJ,MAAM;EACN,UAAU;EACV,QAAQ;EACT;CACD,UAAU;EACR,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACD,WAAW;EACT,MAAM;EACN,UAAU;EACX;CACF,EACF,EACF;AA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"}