g14o
Packages@g14o/paystack-better-auth

Database schema

Tables and fields added by the Paystack Better Auth plugin.

Run Better Auth CLI to generate migrations:

npx auth@latest generate

Schema

The plugin extends the core user table and creates subscription, payment, and webhookEvent tables. Better Auth auto-adds an id field (string, primary key) on new tables. Numeric Paystack ID fields and payment.amount use bigint in PostgreSQL — see Numeric columns.

User

Table Name: user (extends core user table)

FieldTypeDescription
paystackCustomerCode?string
Paystack customer code (e.g. CUS_xxx)
paystackCustomerId?number (bigint)
Paystack customer ID

Subscription

only when subscription.enabled is true

Table Name: subscription

FieldTypeDescription
idstringRequired
Unique identifier (primary key, added automatically by Better Auth)
userIdstringRequired
The ID of the user. References user.id
referenceIdstringRequired
Reference ID for the subscription (typically the user or organization ID)
providerstringRequired
Billing provider identifierDefault: paystack
subscriptionCodestringRequired
Paystack subscription code. Unique
customerIdnumber (bigint)Required
Paystack customer ID
customerCodestringRequired
Paystack customer code
planCodestringRequired
Paystack plan code
planNamestringRequired
Display name of the plan
emailTokenstringRequired
Paystack email token used for cancel and resume actions
statusstringRequired
Normalized subscription status
currentPeriodStartDateRequired
Start of the current billing period
currentPeriodEnd?Date
End of the current billing period
cancelAtPeriodEndbooleanRequired
Whether the subscription cancels at period endDefault: false
metadata?string
JSON metadata stored as a string

Payment

omit when disablePaymentPersistence: true

Table Name: payment

FieldTypeDescription
idstringRequired
Unique identifier (primary key, added automatically by Better Auth)
referencestringRequired
Paystack transaction reference. Unique
transactionIdnumber (bigint)Required
Paystack transaction ID
userId?string
The ID of the user (if linked). References user.id
referenceId?string
Optional reference ID for the payment
providerstringRequired
Billing provider identifierDefault: paystack
customerCodestringRequired
Paystack customer code
customerIdnumber (bigint)Required
Paystack customer ID
amountnumber (bigint)Required
Amount in the smallest currency unit (kobo, pesewas, cents, etc.)
currencystringRequired
ISO currency code (e.g. GHS, NGN)
statusstringRequired
Normalized payment status
channel?string
Payment channel (e.g. card, mobile_money)
paidAtDateRequired
When the payment was completed
metadata?string
JSON metadata stored as a string

Webhook event

omit when disableWebhookPersistence: true

Table Name: webhookEvent

FieldTypeDescription
idstringRequired
Unique identifier (primary key, added automatically by Better Auth)
eventIdstringRequired
Unique Paystack event ID for deduplication. Unique
typestringRequired
Paystack event type (e.g. charge.success)
payloadstringRequired
Raw webhook payload as JSON string
statusstringRequired
Processing status (pending, processed, failed)Default: pending
processedAt?Date
When the event was processed
errorMessage?string
Error message if processing failed

Paystack numeric IDs and payment.amount can exceed PostgreSQL INTEGER (max 2,147,483,647). Use BIGINT for the columns listed below or inserts may fail. New projects using this plugin get bigint: true by default; existing databases that already migrated with INTEGER need a manual migration.

Numeric columns (use BIGINT)

Paystack returns large numeric IDs from its API. For example, customer ID 6293201508 is greater than the PostgreSQL INTEGER maximum (2,147,483,647), so an INTEGER column will reject the value on insert.

TableColumnWhy BIGINT
userpaystackCustomerIdPaystack customer ID
subscriptioncustomerIdPaystack customer ID
paymentcustomerIdPaystack customer ID
paymenttransactionIdPaystack transaction ID
paymentamountAmount in smallest currency unit (can exceed INTEGER at high values)

String fields (paystackCustomerCode, subscriptionCode, reference, eventId, etc.) are unaffected. amount is the payment value in kobo, pesewas, cents, etc. — not a Paystack resource ID.

Fix existing databases

If you already ran npx auth@latest generate before upgrading the plugin, edit your schema or migration to use BIGINT for the columns above.

Edit schema.prisma after generate, then run prisma migrate dev:

// user table
paystackCustomerId BigInt?

// subscription table
customerId BigInt

// payment table
customerId    BigInt
transactionId BigInt
amount        BigInt

Prisma returns BigInt at runtime for these fields. Coerce with Number() when passing values to APIs that expect number if needed.

Change integer(...) to bigint(..., { mode: "number" }) in the generated auth schema:

import { bigint } from "drizzle-orm/pg-core";

paystackCustomerId: bigint("paystack_customer_id", { mode: "number" }),
customerId: bigint("customer_id", { mode: "number" }),
transactionId: bigint("transaction_id", { mode: "number" }),
amount: bigint("amount", { mode: "number" }),

Use mode: "number" so values stay as JS number (safe for Paystack IDs under Number.MAX_SAFE_INTEGER). Run drizzle-kit generate / migrate after editing.

For Kysely adapter output or manual migrations (PostgreSQL):

ALTER TABLE "user" ALTER COLUMN "paystackCustomerId" TYPE BIGINT;
ALTER TABLE "subscription" ALTER COLUMN "customerId" TYPE BIGINT;
ALTER TABLE "payment" ALTER COLUMN "customerId" TYPE BIGINT;
ALTER TABLE "payment" ALTER COLUMN "transactionId" TYPE BIGINT;
ALTER TABLE "payment" ALTER COLUMN "amount" TYPE BIGINT;

Adjust quoted identifiers and table names to match your generated output. For MySQL and SQLite, use BIGINT equivalently.

Persistence options

OptionEffect
disablePaymentPersistence: trueSkip payment table; still use onCheckoutComplete
disableWebhookPersistence: trueSkip webhookEvent table

On this page