npm.io
0.3.0 • Published 5d ago

@g14o/paystack-better-auth

Licence
MIT
Version
0.3.0
Deps
0
Size
117 kB
Vulns
0
Weekly
239

@g14o/paystack-better-auth

Paystack billing plugin for Better Auth. Supports customers, hosted checkout, subscriptions, authorization charges, and webhook synchronization.

Requires @g14o/paystack for the Paystack API client.

Installation

pnpm add @g14o/paystack @g14o/paystack-better-auth better-auth zod

Server setup

Create a Paystack client with your API keys, then pass it to the plugin as paystackClient.

// lib/auth.ts
import { betterAuth } from "better-auth";
import { Paystack } from "@g14o/paystack";
import { paystack } from "@g14o/paystack-better-auth";

const paystackClient = new Paystack({
  secretKey: process.env.PAYSTACK_SECRET_KEY!,
});

export const auth = betterAuth({
  // ...your auth config
  plugins: [
    paystack({
      paystackClient,
      createCustomerOnSignUp: true,
      subscription: {
        enabled: true,
        plans: [
          {
            name: "basic",
            interval: "monthly",
            amount: "500",
            currency: "GHS",
          },
          {
            name: "pro",
            planCode: "PLN_pro_monthly",
            annualDiscountedPlanCode: "PLN_pro_annual",
          },
        ],
        onSubscriptionComplete: async ({ subscription, plan }) => {
          console.log(`Welcome ${subscription.referenceId} to ${plan?.name}`);
        },
      },
      onEvent: async (event) => {
        console.log("Paystack event", event.event);
      },
    }),
  ],
});

Client setup

// lib/auth-client.ts
import { createAuthClient } from "better-auth/client";
import { paystackClient } from "@g14o/paystack-better-auth/client";

export const authClient = createAuthClient({
  plugins: [
    paystackClient({
      subscription: true // enable subscription
    })
  ],
});

Checkout (one-time payments)

const { data, error } = await authClient.paystack.createCheckoutSession({
  amount: 1500,
  currency: "GHS",
  callbackUrl: "https://app.example.com/payment/callback",
  metadata: { orderId: "order_123" },
});

Fulfill one-time orders on the server via checkout.onCheckoutComplete when Paystack sends charge.success (no transactions.verify call needed):

paystack({
  paystackClient,
  checkout: {
    onCheckoutComplete: async ({ reference, amount, metadata, payment }) => {
      // mark order paid, grant access, send receipt, etc.
    },
  },
});

Set disablePaymentPersistence: true to skip the payment table while still using onCheckoutComplete.

Subscriptions

When a plan entry includes planCode, the plugin fetches billing details from Paystack instead of creating a plan. Pass annual: true on upgrade to use annualDiscountedPlanCode.

const { data, error } = await authClient.paystack.subscription.upgrade({
  plan: "pro",
  annual: true,
  subscriptionCode: "SUB_existing",
  callbackUrl: "https://app.example.com/payment/callback",
  cancelActionUrl: "https://app.example.com/pricing",
});

await authClient.paystack.subscription.cancel();
await authClient.paystack.subscription.resume();
await authClient.paystack.subscription.get();
await authClient.paystack.subscription.list();
Server-side subscription API
Server Client equivalent
auth.api.upgradeSubscription authClient.paystack.subscription.upgrade
auth.api.cancelSubscription authClient.paystack.subscription.cancel
auth.api.resumeSubscription authClient.paystack.subscription.resume
auth.api.getSubscription authClient.paystack.subscription.get
auth.api.listSubscriptions authClient.paystack.subscription.list

Webhooks

Configure your Paystack dashboard webhook URL to:

https://your-domain.com/api/auth/paystack/webhook

The plugin verifies x-paystack-signature via paystackClient.webhook.verifyPaystackWebhookSignature. Events are deduplicated and persisted in paystackWebhookEvent.

Database schema

Run Better Auth CLI to generate migrations:

npx auth@latest generate

Tables added:

  • user field: paystackCustomerCode and paystackCustomerId
  • subscription (includes emailToken for cancel/resume; when subscription.enabled)
  • payment (one-time checkout records; omit when disablePaymentPersistence: true)
  • webhookEvent (included by default; omit when disableWebhookPersistence: true)

License

MIT

Keywords