@alexasomba/better-auth-paystack 1.2.0 → 2.1.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.
- package/README.md +156 -47
- package/dist/client.d.mts +74 -222
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +35 -54
- package/dist/client.mjs.map +1 -1
- package/dist/index-Dwbeddkr.d.mts +711 -0
- package/dist/index-Dwbeddkr.d.mts.map +1 -0
- package/dist/index.d.mts +2 -623
- package/dist/index.mjs +638 -704
- package/dist/index.mjs.map +1 -1
- package/package.json +65 -74
- package/dist/index.d.mts.map +0 -1
- package/dist/types-BOpjdQrr.d.mts +0 -395
- package/dist/types-BOpjdQrr.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
A TypeScript-first plugin that integrates Paystack into [Better Auth](https://www.better-auth.com), providing a production-ready billing system with support for subscriptions (native & local), one-time payments, trials, organization billing, and secure webhooks.
|
|
4
4
|
|
|
5
|
+
<div align="center">
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
[](https://github.com/alexasomba/better-auth-paystack/stargazers)
|
|
9
|
+
[](https://github.com/alexasomba/better-auth-paystack/releases)
|
|
10
|
+
[](https://bundlephobia.com/result?p=@alexasomba/better-auth-paystack)
|
|
11
|
+
[](https://twitter.com/alexasomba)
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
5
16
|
[**Live Demo (Tanstack Start)**](https://better-auth-paystack.gittech.workers.dev) | [**Source Code**](https://github.com/alexasomba/better-auth-paystack/tree/main/examples/tanstack)
|
|
6
17
|
|
|
7
18
|
## Features
|
|
@@ -13,14 +24,19 @@ A TypeScript-first plugin that integrates Paystack into [Better Auth](https://ww
|
|
|
13
24
|
- [x] **Enforced Limits & Seats**: Automatic enforcement of member seat upgrades and resource limits (teams).
|
|
14
25
|
- [x] **Scheduled Changes**: Defer subscription updates or cancellations to the end of the billing cycle.
|
|
15
26
|
- [x] **Proration**: Immediate mid-cycle prorated charges for seat and plan upgrades.
|
|
16
|
-
- [x] **Popup Modal Flow**: Optional support for Paystack's inline checkout experience via `@alexasomba/paystack-
|
|
17
|
-
- [x] **Webhook Security**: Pre-configured signature verification (HMAC-SHA512).
|
|
27
|
+
- [x] **Popup Modal Flow**: Optional support for Paystack's inline checkout experience via `@alexasomba/paystack-inline`.
|
|
28
|
+
- [x] **Webhook Security**: Pre-configured signature verification (HMAC-SHA512) and optional IP whitelisting.
|
|
18
29
|
- [x] **Transaction History**: Built-in support for listing and viewing local transaction records.
|
|
19
30
|
|
|
20
31
|
---
|
|
21
32
|
|
|
22
33
|
## Quick Start
|
|
23
34
|
|
|
35
|
+
### Prerequisites
|
|
36
|
+
|
|
37
|
+
- **Node.js**: `v24.0.0` or higher.
|
|
38
|
+
- **Better Auth**: `v1.6.5` or higher.
|
|
39
|
+
|
|
24
40
|
### 1. Install Plugin & SDKs
|
|
25
41
|
|
|
26
42
|
```bash
|
|
@@ -30,7 +46,7 @@ npm install better-auth @alexasomba/better-auth-paystack @alexasomba/paystack-no
|
|
|
30
46
|
#### Optional: Browser SDK (for Popup Modals)
|
|
31
47
|
|
|
32
48
|
```bash
|
|
33
|
-
npm install @alexasomba/paystack-
|
|
49
|
+
npm install @alexasomba/paystack-inline
|
|
34
50
|
```
|
|
35
51
|
|
|
36
52
|
### 2. Configure Environment Variables
|
|
@@ -57,7 +73,7 @@ export const auth = betterAuth({
|
|
|
57
73
|
plugins: [
|
|
58
74
|
paystack({
|
|
59
75
|
paystackClient,
|
|
60
|
-
|
|
76
|
+
webhook: { secret: process.env.PAYSTACK_WEBHOOK_SECRET! },
|
|
61
77
|
createCustomerOnSignUp: true,
|
|
62
78
|
subscription: {
|
|
63
79
|
enabled: true,
|
|
@@ -103,6 +119,60 @@ npx better-auth migrate
|
|
|
103
119
|
|
|
104
120
|
---
|
|
105
121
|
|
|
122
|
+
## Migration Guide
|
|
123
|
+
|
|
124
|
+
Version `2.0.0` contains a security-focused breaking change.
|
|
125
|
+
|
|
126
|
+
- Removed public/client operator actions:
|
|
127
|
+
- `authClient.paystack.syncProducts()`
|
|
128
|
+
- `authClient.paystack.syncPlans()`
|
|
129
|
+
- `authClient.paystack.chargeRecurringSubscription(...)`
|
|
130
|
+
- Removed public Better Auth endpoints for:
|
|
131
|
+
- `/paystack/sync-products`
|
|
132
|
+
- `/paystack/sync-plans`
|
|
133
|
+
- `/paystack/charge-recurring`
|
|
134
|
+
- Added trusted server operations:
|
|
135
|
+
- `chargeSubscriptionRenewal`
|
|
136
|
+
- `syncPaystackProducts`
|
|
137
|
+
- `syncPaystackPlans`
|
|
138
|
+
|
|
139
|
+
### Old
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
await authClient.paystack.syncProducts();
|
|
143
|
+
await authClient.paystack.syncPlans();
|
|
144
|
+
await authClient.paystack.chargeRecurringSubscription({
|
|
145
|
+
subscriptionId: "sub_123",
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### New
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import {
|
|
153
|
+
chargeSubscriptionRenewal,
|
|
154
|
+
syncPaystackPlans,
|
|
155
|
+
syncPaystackProducts,
|
|
156
|
+
} from "@alexasomba/better-auth-paystack";
|
|
157
|
+
|
|
158
|
+
const ctx = { context: await auth.$context } as any;
|
|
159
|
+
const paystackOptions = {
|
|
160
|
+
secretKey: process.env.PAYSTACK_SECRET_KEY!,
|
|
161
|
+
webhook: { secret: process.env.PAYSTACK_WEBHOOK_SECRET! },
|
|
162
|
+
paystackClient,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
await syncPaystackProducts(ctx, paystackOptions);
|
|
166
|
+
await syncPaystackPlans(ctx, paystackOptions);
|
|
167
|
+
await chargeSubscriptionRenewal(ctx, paystackOptions, {
|
|
168
|
+
subscriptionId: "sub_123",
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
These operations are intentionally server-only. Do not expose them through browser-triggered auth client calls.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
106
176
|
## Billing Patterns
|
|
107
177
|
|
|
108
178
|
### 1. Subscriptions
|
|
@@ -195,7 +265,7 @@ Enable `organization.enabled` to bill organizations instead of users.
|
|
|
195
265
|
|
|
196
266
|
### Inline Popup Modal
|
|
197
267
|
|
|
198
|
-
Use `@alexasomba/paystack-
|
|
268
|
+
Use `@alexasomba/paystack-inline` for a seamless UI.
|
|
199
269
|
|
|
200
270
|
```ts
|
|
201
271
|
const { data } = await authClient.subscription.upgrade({ plan: "pro" });
|
|
@@ -203,8 +273,7 @@ if (data?.accessCode) {
|
|
|
203
273
|
const paystack = createPaystack({ publicKey: "pk_test_..." });
|
|
204
274
|
paystack.checkout({
|
|
205
275
|
accessCode: data.accessCode,
|
|
206
|
-
onSuccess: (res) =>
|
|
207
|
-
authClient.paystack.transaction.verify({ reference: res.reference }),
|
|
276
|
+
onSuccess: (res) => authClient.paystack.transaction.verify({ reference: res.reference }),
|
|
208
277
|
});
|
|
209
278
|
}
|
|
210
279
|
```
|
|
@@ -212,6 +281,7 @@ if (data?.accessCode) {
|
|
|
212
281
|
### Scheduled Changes & Cancellation
|
|
213
282
|
|
|
214
283
|
Defer changes to the end of the current billing cycle:
|
|
284
|
+
|
|
215
285
|
- **Upgrades**: Pass `scheduleAtPeriodEnd: true` in `initializeTransaction()`.
|
|
216
286
|
- **Cancellations**: Use `authClient.subscription.cancel({ atPeriodEnd: true })` to keep the subscription active until the period ends.
|
|
217
287
|
|
|
@@ -228,6 +298,20 @@ await authClient.paystack.transaction.initialize({
|
|
|
228
298
|
});
|
|
229
299
|
```
|
|
230
300
|
|
|
301
|
+
### Webhook Security
|
|
302
|
+
|
|
303
|
+
The plugin automatically verifies the `x-paystack-signature` header to ensure events are authentic. For an extra layer of security, you can enable **IP Whitelisting** to restrict processing to Paystack's official servers.
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
paystack({
|
|
307
|
+
webhook: {
|
|
308
|
+
secret: process.env.PAYSTACK_WEBHOOK_SECRET!,
|
|
309
|
+
verifyIP: true, // Enable IP whitelisting (defaults to false for flexible proxy support)
|
|
310
|
+
trustedIPs: ["52.31.139.75", "52.49.173.169", "52.214.14.220"], // Optional: override trusted IPs
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
231
315
|
### Trial Abuse Prevention
|
|
232
316
|
|
|
233
317
|
The plugin checks the `referenceId` history. If a trial was ever used (active, expired, or trialing), it will not be granted again, preventing resubscribe-abuse.
|
|
@@ -240,9 +324,7 @@ React to billing events on the server by providing callbacks in your configurati
|
|
|
240
324
|
|
|
241
325
|
- `onSubscriptionComplete`: Called after successful transaction verification (Native or Local).
|
|
242
326
|
- `onSubscriptionCreated`: Called when a subscription record is first initialized in the DB.
|
|
243
|
-
- `onSubscriptionUpdate`: Called whenever a subscription's status or period is updated.
|
|
244
327
|
- `onSubscriptionCancel`: Called when a user or organization cancels their subscription.
|
|
245
|
-
- `onSubscriptionDelete`: Called when a subscription record is deleted.
|
|
246
328
|
|
|
247
329
|
#### Customer Hooks (`top-level` or `organization.*`)
|
|
248
330
|
|
|
@@ -252,12 +334,33 @@ React to billing events on the server by providing callbacks in your configurati
|
|
|
252
334
|
#### Trial Hooks (`subscription.plans[].freeTrial.*`)
|
|
253
335
|
|
|
254
336
|
- `onTrialStart`: Called when a new trial period begins.
|
|
255
|
-
- `onTrialEnd`: Called when a trial period ends naturally or via manual upgrade.
|
|
256
337
|
|
|
257
338
|
#### Global Hook
|
|
258
339
|
|
|
259
340
|
- `onEvent`: Receives every webhook event payload sent from Paystack for custom processing.
|
|
260
341
|
|
|
342
|
+
### Trusted Server Operations
|
|
343
|
+
|
|
344
|
+
Recurring renewals and Paystack catalog sync are intentionally not exposed through the browser auth client.
|
|
345
|
+
Invoke them from trusted backend code only:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
import {
|
|
349
|
+
chargeSubscriptionRenewal,
|
|
350
|
+
syncPaystackPlans,
|
|
351
|
+
syncPaystackProducts,
|
|
352
|
+
} from "@alexasomba/better-auth-paystack";
|
|
353
|
+
|
|
354
|
+
const ctx = { context: await auth.$context } as any;
|
|
355
|
+
|
|
356
|
+
await chargeSubscriptionRenewal(ctx, paystackOptions, {
|
|
357
|
+
subscriptionId: "sub_123",
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
await syncPaystackProducts(ctx, paystackOptions);
|
|
361
|
+
await syncPaystackPlans(ctx, paystackOptions);
|
|
362
|
+
```
|
|
363
|
+
|
|
261
364
|
### Authorization & Security
|
|
262
365
|
|
|
263
366
|
#### `authorizeReference`
|
|
@@ -428,36 +531,36 @@ The plugin extends your database with the following fields and tables.
|
|
|
428
531
|
|
|
429
532
|
### `paystackTransaction`
|
|
430
533
|
|
|
431
|
-
| Field | Type | Required | Description
|
|
432
|
-
| :------------ | :------- | :------- |
|
|
433
|
-
| `reference` | `string` | Yes | Unique transaction reference.
|
|
434
|
-
| `referenceId` | `string` | Yes | Associated User ID or Organization ID.
|
|
435
|
-
| `userId` | `string` | Yes | The ID of the user who initiated the transaction.
|
|
436
|
-
| `amount` | `number` | Yes | Transaction amount in smallest currency unit.
|
|
437
|
-
| `currency` | `string` | Yes | Currency code (e.g., "NGN").
|
|
438
|
-
| `status` | `string` | Yes | `success`, `pending`, `failed`, `abandoned`.
|
|
439
|
-
| `plan` | `string` | No | Name of the plan associated with the transaction.
|
|
534
|
+
| Field | Type | Required | Description |
|
|
535
|
+
| :------------ | :------- | :------- | :--------------------------------------------------- |
|
|
536
|
+
| `reference` | `string` | Yes | Unique transaction reference. |
|
|
537
|
+
| `referenceId` | `string` | Yes | Associated User ID or Organization ID. |
|
|
538
|
+
| `userId` | `string` | Yes | The ID of the user who initiated the transaction. |
|
|
539
|
+
| `amount` | `number` | Yes | Transaction amount in smallest currency unit. |
|
|
540
|
+
| `currency` | `string` | Yes | Currency code (e.g., "NGN"). |
|
|
541
|
+
| `status` | `string` | Yes | `success`, `pending`, `failed`, `abandoned`. |
|
|
542
|
+
| `plan` | `string` | No | Name of the plan associated with the transaction. |
|
|
440
543
|
| `product` | `string` | No | Name of the product associated with the transaction. |
|
|
441
|
-
| `metadata` | `string` | No | JSON string of extra transaction metadata.
|
|
442
|
-
| `paystackId` | `string` | No | The internal Paystack ID for the transaction.
|
|
443
|
-
| `createdAt` | `Date` | Yes | Transaction creation timestamp.
|
|
444
|
-
| `updatedAt` | `Date` | Yes | Transaction last update timestamp.
|
|
544
|
+
| `metadata` | `string` | No | JSON string of extra transaction metadata. |
|
|
545
|
+
| `paystackId` | `string` | No | The internal Paystack ID for the transaction. |
|
|
546
|
+
| `createdAt` | `Date` | Yes | Transaction creation timestamp. |
|
|
547
|
+
| `updatedAt` | `Date` | Yes | Transaction last update timestamp. |
|
|
445
548
|
|
|
446
549
|
### `paystackProduct`
|
|
447
550
|
|
|
448
|
-
| Field | Type | Required | Description
|
|
449
|
-
| :------------ | :-------- | :------- |
|
|
450
|
-
| `name` | `string` | Yes | Product name.
|
|
451
|
-
| `description` | `string` | No | Product description.
|
|
452
|
-
| `price` | `number` | Yes | Price in smallest currency unit.
|
|
453
|
-
| `currency` | `string` | Yes | Currency code (e.g., "NGN").
|
|
454
|
-
| `quantity` | `number` | No | Available stock quantity.
|
|
455
|
-
| `unlimited` | `boolean` | No | Whether the product has unlimited stock.
|
|
456
|
-
| `paystackId` | `string` | No | The internal Paystack Product ID.
|
|
457
|
-
| `slug` | `string` | Yes | Unique slug for the product.
|
|
458
|
-
| `metadata` | `string` | No | JSON string of extra product metadata.
|
|
459
|
-
| `createdAt` | `Date` | Yes | Product creation timestamp.
|
|
460
|
-
| `updatedAt` | `Date` | Yes | Product last update timestamp.
|
|
551
|
+
| Field | Type | Required | Description |
|
|
552
|
+
| :------------ | :-------- | :------- | :--------------------------------------- |
|
|
553
|
+
| `name` | `string` | Yes | Product name. |
|
|
554
|
+
| `description` | `string` | No | Product description. |
|
|
555
|
+
| `price` | `number` | Yes | Price in smallest currency unit. |
|
|
556
|
+
| `currency` | `string` | Yes | Currency code (e.g., "NGN"). |
|
|
557
|
+
| `quantity` | `number` | No | Available stock quantity. |
|
|
558
|
+
| `unlimited` | `boolean` | No | Whether the product has unlimited stock. |
|
|
559
|
+
| `paystackId` | `string` | No | The internal Paystack Product ID. |
|
|
560
|
+
| `slug` | `string` | Yes | Unique slug for the product. |
|
|
561
|
+
| `metadata` | `string` | No | JSON string of extra product metadata. |
|
|
562
|
+
| `createdAt` | `Date` | Yes | Product creation timestamp. |
|
|
563
|
+
| `updatedAt` | `Date` | Yes | Product last update timestamp. |
|
|
461
564
|
|
|
462
565
|
---
|
|
463
566
|
|
|
@@ -474,6 +577,7 @@ The plugin extends your database with the following fields and tables.
|
|
|
474
577
|
The plugin's schema definition includes recommended indexes and uniqueness constraints for performance. When you run `npx better-auth migrate`, these will be automatically applied to your database.
|
|
475
578
|
|
|
476
579
|
The following fields are indexed:
|
|
580
|
+
|
|
477
581
|
- **`paystackTransaction`**: `reference` (unique), `userId`, `referenceId`.
|
|
478
582
|
- **`subscription`**: `paystackSubscriptionCode` (unique), `referenceId`, `paystackTransactionReference`, `paystackCustomerCode`, `plan`.
|
|
479
583
|
- **`user` & `organization`**: `paystackCustomerCode`.
|
|
@@ -484,9 +588,11 @@ The following fields are indexed:
|
|
|
484
588
|
The plugin provides two ways to keep your product inventory in sync with Paystack:
|
|
485
589
|
|
|
486
590
|
#### 1. Automated Inventory Sync (New)
|
|
591
|
+
|
|
487
592
|
Whenever a successful one-time payment is made (via webhook or manual verification), the plugin automatically calls **`syncProductQuantityFromPaystack`**. This fetches the real-time remaining quantity from the Paystack API and updates your local database record, ensuring your inventory is always accurate.
|
|
488
593
|
|
|
489
594
|
#### 2. Manual Bulk Sync
|
|
595
|
+
|
|
490
596
|
You can synchronize all products with your local database using the `/paystack/sync-products` endpoint.
|
|
491
597
|
|
|
492
598
|
```bash
|
|
@@ -497,20 +603,23 @@ POST /api/auth/paystack/sync-products
|
|
|
497
603
|
|
|
498
604
|
## 🏗️ Development & Contributing
|
|
499
605
|
|
|
500
|
-
This repository is
|
|
606
|
+
This repository is powered by **Vite+**. You use the `vp` CLI to manage the entire workspace.
|
|
501
607
|
|
|
502
608
|
```bash
|
|
503
|
-
# Install
|
|
504
|
-
|
|
609
|
+
# Install dependencies
|
|
610
|
+
vp i
|
|
611
|
+
|
|
612
|
+
# Check project health (format, lint, types)
|
|
613
|
+
vp check --fix
|
|
505
614
|
|
|
506
615
|
# Build the core library
|
|
507
|
-
|
|
616
|
+
vp build
|
|
508
617
|
|
|
509
|
-
# Run
|
|
510
|
-
|
|
618
|
+
# Run tests
|
|
619
|
+
vp test
|
|
511
620
|
|
|
512
|
-
# Run TanStack Start example
|
|
513
|
-
|
|
621
|
+
# Run the TanStack Start example
|
|
622
|
+
vp run examples/tanstack dev
|
|
514
623
|
```
|
|
515
624
|
|
|
516
625
|
Contributions are welcome! Please open an issue or pull request.
|
|
@@ -526,11 +635,11 @@ Future features planned for upcoming versions:
|
|
|
526
635
|
### v1.1.0 - Manual Recurring Subscriptions (Available Now)
|
|
527
636
|
|
|
528
637
|
- [x] **Stored Authorization Codes**: Securely store Paystack authorization codes from verified transactions.
|
|
529
|
-
- [x] **
|
|
638
|
+
- [x] **Trusted Renewal Operation**: Server-side helper to charge stored cards for renewals.
|
|
530
639
|
- [ ] **Card Management UI**: Let users view/delete saved payment methods (masked card data only) - _Upcoming_
|
|
531
640
|
- [ ] **Renewal Scheduler Integration**: Documentation for integrating with Cloudflare Workers Cron, Vercel Cron, etc. - _Upcoming_
|
|
532
641
|
|
|
533
|
-
> **Note**: For local-managed subscriptions (no `planCode`), the plugin
|
|
642
|
+
> **Note**: For local-managed subscriptions (no `planCode`), the plugin automatically captures and stores the `authorization_code`. Trigger renewals from trusted backend code with `chargeSubscriptionRenewal(...)`.
|
|
534
643
|
|
|
535
644
|
### Future Considerations
|
|
536
645
|
|
|
@@ -542,7 +651,7 @@ Future features planned for upcoming versions:
|
|
|
542
651
|
|
|
543
652
|
- GitHub Repository: [alexasomba/better-auth-paystack](https://github.com/alexasomba/better-auth-paystack)
|
|
544
653
|
- Comprehensive and up-to-date Paystack Node SDK: [alexasomba/paystack-node](https://github.com/alexasomba/paystack-node)
|
|
545
|
-
- Comprehensive and up-to-date Paystack
|
|
654
|
+
- Comprehensive and up-to-date Paystack Inline SDK: [alexasomba/paystack-inline](https://github.com/alexasomba/paystack-inline)
|
|
546
655
|
- [TanStack Start Example Implementation](https://github.com/alexasomba/better-auth-paystack/tree/main/examples/tanstack)
|
|
547
656
|
- Paystack Webhooks: https://paystack.com/docs/payments/webhooks/
|
|
548
657
|
- Paystack Transaction API: https://paystack.com/docs/api/transaction/
|