@alexasomba/better-auth-paystack 1.2.1 → 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.
- package/README.md +232 -59
- package/dist/client.d.mts +133 -305
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +90 -94
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +423 -2
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +818 -687
- package/dist/index.mjs.map +1 -1
- package/dist/types-B5ZnlFrq.d.mts +258 -0
- package/dist/types-B5ZnlFrq.d.mts.map +1 -0
- package/package.json +64 -73
- package/dist/index-DoMJ9OLF.d.mts +0 -488
- package/dist/index-DoMJ9OLF.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
|
|
@@ -10,17 +21,23 @@ A TypeScript-first plugin that integrates Paystack into [Better Auth](https://ww
|
|
|
10
21
|
- [x] **Auto Customer Creation**: Optional Paystack customer creation on user sign up or organization creation.
|
|
11
22
|
- [x] **Trial Management**: Configurable trial periods with built-in abuse prevention logic.
|
|
12
23
|
- [x] **Organization Billing**: Associate subscriptions with organizations and authorize access via roles.
|
|
24
|
+
- [x] **Subscription Channel Controls**: Restrict subscription checkout to specific Paystack payment channels such as card-only.
|
|
13
25
|
- [x] **Enforced Limits & Seats**: Automatic enforcement of member seat upgrades and resource limits (teams).
|
|
14
26
|
- [x] **Scheduled Changes**: Defer subscription updates or cancellations to the end of the billing cycle.
|
|
15
|
-
- [x] **Proration**: Immediate mid-cycle prorated
|
|
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] **Proration**: Immediate mid-cycle prorated upgrades for local plans, using saved-card charges when possible and checkout fallback when interactive payment is required.
|
|
28
|
+
- [x] **Popup Modal Flow**: Optional support for Paystack's inline checkout experience via `@alexasomba/paystack-inline`.
|
|
29
|
+
- [x] **Webhook Security**: Pre-configured signature verification (HMAC-SHA512) and optional IP whitelisting.
|
|
18
30
|
- [x] **Transaction History**: Built-in support for listing and viewing local transaction records.
|
|
19
31
|
|
|
20
32
|
---
|
|
21
33
|
|
|
22
34
|
## Quick Start
|
|
23
35
|
|
|
36
|
+
### Prerequisites
|
|
37
|
+
|
|
38
|
+
- **Node.js**: `v24.0.0` or higher.
|
|
39
|
+
- **Better Auth**: `v1.6.5` or higher.
|
|
40
|
+
|
|
24
41
|
### 1. Install Plugin & SDKs
|
|
25
42
|
|
|
26
43
|
```bash
|
|
@@ -30,7 +47,7 @@ npm install better-auth @alexasomba/better-auth-paystack @alexasomba/paystack-no
|
|
|
30
47
|
#### Optional: Browser SDK (for Popup Modals)
|
|
31
48
|
|
|
32
49
|
```bash
|
|
33
|
-
npm install @alexasomba/paystack-
|
|
50
|
+
npm install @alexasomba/paystack-inline
|
|
34
51
|
```
|
|
35
52
|
|
|
36
53
|
### 2. Configure Environment Variables
|
|
@@ -48,6 +65,7 @@ BETTER_AUTH_URL=http://localhost:8787
|
|
|
48
65
|
import { betterAuth } from "better-auth";
|
|
49
66
|
import { paystack } from "@alexasomba/better-auth-paystack";
|
|
50
67
|
import { createPaystack } from "@alexasomba/paystack-node";
|
|
68
|
+
import { admin } from "better-auth/plugins";
|
|
51
69
|
|
|
52
70
|
const paystackClient = createPaystack({
|
|
53
71
|
secretKey: process.env.PAYSTACK_SECRET_KEY!,
|
|
@@ -55,12 +73,14 @@ const paystackClient = createPaystack({
|
|
|
55
73
|
|
|
56
74
|
export const auth = betterAuth({
|
|
57
75
|
plugins: [
|
|
76
|
+
admin(),
|
|
58
77
|
paystack({
|
|
59
78
|
paystackClient,
|
|
60
|
-
|
|
79
|
+
webhook: { secret: process.env.PAYSTACK_WEBHOOK_SECRET! },
|
|
61
80
|
createCustomerOnSignUp: true,
|
|
62
81
|
subscription: {
|
|
63
82
|
enabled: true,
|
|
83
|
+
allowedPaymentChannels: ["card"], // Optional: enforce card-only subscriptions
|
|
64
84
|
plans: [
|
|
65
85
|
{
|
|
66
86
|
name: "pro",
|
|
@@ -84,14 +104,21 @@ export const auth = betterAuth({
|
|
|
84
104
|
});
|
|
85
105
|
```
|
|
86
106
|
|
|
107
|
+
`webhook.secret` is the preferred webhook-signing config.
|
|
108
|
+
If you still have older code using top-level `paystackWebhookSecret`, it is treated as a deprecated alias and falls back to the same signature check.
|
|
109
|
+
|
|
87
110
|
### 4. Configure Client Plugin
|
|
88
111
|
|
|
89
112
|
```ts title="client.ts"
|
|
90
113
|
import { createAuthClient } from "better-auth/client";
|
|
91
114
|
import { paystackClient } from "@alexasomba/better-auth-paystack/client";
|
|
115
|
+
import { adminClient } from "better-auth/client/plugins";
|
|
92
116
|
|
|
93
117
|
export const client = createAuthClient({
|
|
94
|
-
plugins: [
|
|
118
|
+
plugins: [
|
|
119
|
+
adminClient(),
|
|
120
|
+
paystackClient({ subscription: true })
|
|
121
|
+
],
|
|
95
122
|
});
|
|
96
123
|
```
|
|
97
124
|
|
|
@@ -103,6 +130,62 @@ npx better-auth migrate
|
|
|
103
130
|
|
|
104
131
|
---
|
|
105
132
|
|
|
133
|
+
## Migration Guide
|
|
134
|
+
|
|
135
|
+
Version `2.0.0` contains a security-focused breaking change.
|
|
136
|
+
|
|
137
|
+
- Removed public/client operator actions:
|
|
138
|
+
- `authClient.paystack.syncProducts()`
|
|
139
|
+
- `authClient.paystack.syncPlans()`
|
|
140
|
+
- `authClient.paystack.chargeRecurringSubscription(...)`
|
|
141
|
+
- Removed public Better Auth endpoints for:
|
|
142
|
+
- `/paystack/sync-products`
|
|
143
|
+
- `/paystack/sync-plans`
|
|
144
|
+
- `/paystack/charge-recurring`
|
|
145
|
+
- Added trusted server operations:
|
|
146
|
+
- `chargeSubscriptionRenewal`
|
|
147
|
+
- `syncPaystackProducts`
|
|
148
|
+
- `syncPaystackPlans`
|
|
149
|
+
|
|
150
|
+
### Old
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
await authClient.paystack.syncProducts();
|
|
154
|
+
await authClient.paystack.syncPlans();
|
|
155
|
+
await authClient.paystack.chargeRecurringSubscription({
|
|
156
|
+
subscriptionId: "sub_123",
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### New
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import {
|
|
164
|
+
chargeSubscriptionRenewal,
|
|
165
|
+
syncPaystackPlans,
|
|
166
|
+
syncPaystackProducts,
|
|
167
|
+
type ChargeRecurringSubscriptionResult,
|
|
168
|
+
type PaystackSyncResult,
|
|
169
|
+
} from "@alexasomba/better-auth-paystack";
|
|
170
|
+
|
|
171
|
+
const ctx = { context: await auth.$context } as any;
|
|
172
|
+
const paystackOptions = {
|
|
173
|
+
secretKey: process.env.PAYSTACK_SECRET_KEY!,
|
|
174
|
+
webhook: { secret: process.env.PAYSTACK_WEBHOOK_SECRET! },
|
|
175
|
+
paystackClient,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
await syncPaystackProducts(ctx, paystackOptions);
|
|
179
|
+
await syncPaystackPlans(ctx, paystackOptions);
|
|
180
|
+
await chargeSubscriptionRenewal(ctx, paystackOptions, {
|
|
181
|
+
subscriptionId: "sub_123",
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
These operations are intentionally server-only. Do not expose them through browser-triggered auth client calls.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
106
189
|
## Billing Patterns
|
|
107
190
|
|
|
108
191
|
### 1. Subscriptions
|
|
@@ -195,7 +278,7 @@ Enable `organization.enabled` to bill organizations instead of users.
|
|
|
195
278
|
|
|
196
279
|
### Inline Popup Modal
|
|
197
280
|
|
|
198
|
-
Use `@alexasomba/paystack-
|
|
281
|
+
Use `@alexasomba/paystack-inline` for a seamless UI.
|
|
199
282
|
|
|
200
283
|
```ts
|
|
201
284
|
const { data } = await authClient.subscription.upgrade({ plan: "pro" });
|
|
@@ -203,8 +286,7 @@ if (data?.accessCode) {
|
|
|
203
286
|
const paystack = createPaystack({ publicKey: "pk_test_..." });
|
|
204
287
|
paystack.checkout({
|
|
205
288
|
accessCode: data.accessCode,
|
|
206
|
-
onSuccess: (res) =>
|
|
207
|
-
authClient.paystack.transaction.verify({ reference: res.reference }),
|
|
289
|
+
onSuccess: (res) => authClient.paystack.transaction.verify({ reference: res.reference }),
|
|
208
290
|
});
|
|
209
291
|
}
|
|
210
292
|
```
|
|
@@ -212,22 +294,63 @@ if (data?.accessCode) {
|
|
|
212
294
|
### Scheduled Changes & Cancellation
|
|
213
295
|
|
|
214
296
|
Defer changes to the end of the current billing cycle:
|
|
297
|
+
|
|
215
298
|
- **Upgrades**: Pass `scheduleAtPeriodEnd: true` in `initializeTransaction()`.
|
|
216
|
-
- **Cancellations**: Use `authClient.subscription.cancel({ atPeriodEnd: true })` to keep the subscription active until the period ends.
|
|
299
|
+
- **Cancellations**: Use `authClient.subscription.cancel({ subscriptionCode, atPeriodEnd: true })` to keep the subscription active until the period ends.
|
|
217
300
|
|
|
218
301
|
### Mid-Cycle Proration (`prorateAndCharge`)
|
|
219
302
|
|
|
220
303
|
The plugin can dynamically calculate the cost difference for immediate mid-cycle upgrades (like adding more seats).
|
|
221
|
-
|
|
304
|
+
For locally managed plans:
|
|
305
|
+
|
|
306
|
+
- If the subscription already has a reusable Paystack authorization code, the plugin charges the prorated delta off-session, records a local `paystackTransaction`, and immediately updates the subscription.
|
|
307
|
+
- If there is no reusable authorization code available (for example, transfer-based payments), the plugin initializes a new checkout for the prorated delta instead of silently upgrading without payment.
|
|
308
|
+
- If the prorated amount is below Paystack's minimum charge for the currency, the request is rejected so you can schedule the change for period end instead of undercharging.
|
|
222
309
|
|
|
223
310
|
```ts
|
|
224
311
|
await authClient.paystack.transaction.initialize({
|
|
225
312
|
plan: "pro",
|
|
226
313
|
quantity: 5, // Upgrading seats
|
|
227
|
-
prorateAndCharge: true, //
|
|
314
|
+
prorateAndCharge: true, // Charges saved authorization or returns a checkout redirect for the delta
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
When the flow falls back to checkout, verify the returned transaction reference after payment. The plugin uses the stored proration metadata to apply the pending plan/seat change only after successful verification.
|
|
319
|
+
|
|
320
|
+
### Restricting Subscription Payment Channels
|
|
321
|
+
|
|
322
|
+
Use `subscription.allowedPaymentChannels` to constrain which Paystack checkout channels can be used for subscription flows.
|
|
323
|
+
This applies to standard subscription checkout, trial authorization flows, and interactive proration checkout fallbacks.
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
paystack({
|
|
327
|
+
subscription: {
|
|
328
|
+
enabled: true,
|
|
329
|
+
allowedPaymentChannels: ["card"],
|
|
330
|
+
plans: [{ name: "starter", amount: 50000, currency: "NGN", interval: "monthly" }],
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
If a subscription payment is later verified with a disallowed channel, the plugin rejects activation instead of silently creating the subscription.
|
|
336
|
+
|
|
337
|
+
### Webhook Security
|
|
338
|
+
|
|
339
|
+
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.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
paystack({
|
|
343
|
+
webhook: {
|
|
344
|
+
secret: process.env.PAYSTACK_WEBHOOK_SECRET!,
|
|
345
|
+
verifyIP: true, // Enable IP whitelisting (defaults to false for flexible proxy support)
|
|
346
|
+
trustedIPs: ["52.31.139.75", "52.49.173.169", "52.214.14.220"], // Optional: override trusted IPs
|
|
347
|
+
},
|
|
228
348
|
});
|
|
229
349
|
```
|
|
230
350
|
|
|
351
|
+
Resolution order for webhook signature verification is:
|
|
352
|
+
`webhook.secret` -> `paystackWebhookSecret` (deprecated) -> `secretKey`.
|
|
353
|
+
|
|
231
354
|
### Trial Abuse Prevention
|
|
232
355
|
|
|
233
356
|
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 +363,7 @@ React to billing events on the server by providing callbacks in your configurati
|
|
|
240
363
|
|
|
241
364
|
- `onSubscriptionComplete`: Called after successful transaction verification (Native or Local).
|
|
242
365
|
- `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
366
|
- `onSubscriptionCancel`: Called when a user or organization cancels their subscription.
|
|
245
|
-
- `onSubscriptionDelete`: Called when a subscription record is deleted.
|
|
246
367
|
|
|
247
368
|
#### Customer Hooks (`top-level` or `organization.*`)
|
|
248
369
|
|
|
@@ -252,12 +373,33 @@ React to billing events on the server by providing callbacks in your configurati
|
|
|
252
373
|
#### Trial Hooks (`subscription.plans[].freeTrial.*`)
|
|
253
374
|
|
|
254
375
|
- `onTrialStart`: Called when a new trial period begins.
|
|
255
|
-
- `onTrialEnd`: Called when a trial period ends naturally or via manual upgrade.
|
|
256
376
|
|
|
257
377
|
#### Global Hook
|
|
258
378
|
|
|
259
379
|
- `onEvent`: Receives every webhook event payload sent from Paystack for custom processing.
|
|
260
380
|
|
|
381
|
+
### Trusted Server Operations
|
|
382
|
+
|
|
383
|
+
Recurring renewals and Paystack catalog sync are intentionally not exposed through the browser auth client.
|
|
384
|
+
Invoke them from trusted backend code only:
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
import {
|
|
388
|
+
chargeSubscriptionRenewal,
|
|
389
|
+
syncPaystackPlans,
|
|
390
|
+
syncPaystackProducts,
|
|
391
|
+
} from "@alexasomba/better-auth-paystack";
|
|
392
|
+
|
|
393
|
+
const ctx = { context: await auth.$context } as any;
|
|
394
|
+
|
|
395
|
+
await chargeSubscriptionRenewal(ctx, paystackOptions, {
|
|
396
|
+
subscriptionId: "sub_123",
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
await syncPaystackProducts(ctx, paystackOptions);
|
|
400
|
+
await syncPaystackPlans(ctx, paystackOptions);
|
|
401
|
+
```
|
|
402
|
+
|
|
261
403
|
### Authorization & Security
|
|
262
404
|
|
|
263
405
|
#### `authorizeReference`
|
|
@@ -320,7 +462,7 @@ type upgradeSubscription = {
|
|
|
320
462
|
/**
|
|
321
463
|
* Additional metadata to store with the transaction.
|
|
322
464
|
*/
|
|
323
|
-
metadata?: Record<string,
|
|
465
|
+
metadata?: Record<string, unknown>;
|
|
324
466
|
/**
|
|
325
467
|
* Reference ID for the subscription owner (User ID or Org ID).
|
|
326
468
|
* Defaults to the current user's ID.
|
|
@@ -351,6 +493,11 @@ type initializeTransaction = {
|
|
|
351
493
|
* Amount to charge (if sending raw amount).
|
|
352
494
|
*/
|
|
353
495
|
amount?: number;
|
|
496
|
+
/**
|
|
497
|
+
* For existing locally managed subscriptions, calculate a mid-cycle delta and either
|
|
498
|
+
* charge the saved authorization or return a checkout redirect for interactive payment.
|
|
499
|
+
*/
|
|
500
|
+
prorateAndCharge?: boolean;
|
|
354
501
|
// ... same as upgradeSubscription
|
|
355
502
|
};
|
|
356
503
|
```
|
|
@@ -379,6 +526,10 @@ Cancel or restore a subscription.
|
|
|
379
526
|
|
|
380
527
|
```ts
|
|
381
528
|
type cancelSubscription = {
|
|
529
|
+
/**
|
|
530
|
+
* Optional reference owner (user ID or org ID) when managing another billing entity.
|
|
531
|
+
*/
|
|
532
|
+
referenceId?: string;
|
|
382
533
|
/**
|
|
383
534
|
* The Paystack subscription code (e.g. SUB_...)
|
|
384
535
|
*/
|
|
@@ -388,6 +539,10 @@ type cancelSubscription = {
|
|
|
388
539
|
* Optional: The server will try to fetch it if omitted.
|
|
389
540
|
*/
|
|
390
541
|
emailToken?: string;
|
|
542
|
+
/**
|
|
543
|
+
* When true, keep the subscription active until the current period ends.
|
|
544
|
+
*/
|
|
545
|
+
atPeriodEnd?: boolean;
|
|
391
546
|
};
|
|
392
547
|
```
|
|
393
548
|
|
|
@@ -428,36 +583,36 @@ The plugin extends your database with the following fields and tables.
|
|
|
428
583
|
|
|
429
584
|
### `paystackTransaction`
|
|
430
585
|
|
|
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.
|
|
586
|
+
| Field | Type | Required | Description |
|
|
587
|
+
| :------------ | :------- | :------- | :--------------------------------------------------- |
|
|
588
|
+
| `reference` | `string` | Yes | Unique transaction reference. |
|
|
589
|
+
| `referenceId` | `string` | Yes | Associated User ID or Organization ID. |
|
|
590
|
+
| `userId` | `string` | Yes | The ID of the user who initiated the transaction. |
|
|
591
|
+
| `amount` | `number` | Yes | Transaction amount in smallest currency unit. |
|
|
592
|
+
| `currency` | `string` | Yes | Currency code (e.g., "NGN"). |
|
|
593
|
+
| `status` | `string` | Yes | `success`, `pending`, `failed`, `abandoned`. |
|
|
594
|
+
| `plan` | `string` | No | Name of the plan associated with the transaction. |
|
|
440
595
|
| `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.
|
|
596
|
+
| `metadata` | `string` | No | JSON string of extra transaction metadata. |
|
|
597
|
+
| `paystackId` | `string` | No | The internal Paystack ID for the transaction. |
|
|
598
|
+
| `createdAt` | `Date` | Yes | Transaction creation timestamp. |
|
|
599
|
+
| `updatedAt` | `Date` | Yes | Transaction last update timestamp. |
|
|
445
600
|
|
|
446
601
|
### `paystackProduct`
|
|
447
602
|
|
|
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.
|
|
603
|
+
| Field | Type | Required | Description |
|
|
604
|
+
| :------------ | :-------- | :------- | :--------------------------------------- |
|
|
605
|
+
| `name` | `string` | Yes | Product name. |
|
|
606
|
+
| `description` | `string` | No | Product description. |
|
|
607
|
+
| `price` | `number` | Yes | Price in smallest currency unit. |
|
|
608
|
+
| `currency` | `string` | Yes | Currency code (e.g., "NGN"). |
|
|
609
|
+
| `quantity` | `number` | No | Available stock quantity. |
|
|
610
|
+
| `unlimited` | `boolean` | No | Whether the product has unlimited stock. |
|
|
611
|
+
| `paystackId` | `string` | No | The internal Paystack Product ID. |
|
|
612
|
+
| `slug` | `string` | Yes | Unique slug for the product. |
|
|
613
|
+
| `metadata` | `string` | No | JSON string of extra product metadata. |
|
|
614
|
+
| `createdAt` | `Date` | Yes | Product creation timestamp. |
|
|
615
|
+
| `updatedAt` | `Date` | Yes | Product last update timestamp. |
|
|
461
616
|
|
|
462
617
|
---
|
|
463
618
|
|
|
@@ -474,43 +629,61 @@ The plugin extends your database with the following fields and tables.
|
|
|
474
629
|
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
630
|
|
|
476
631
|
The following fields are indexed:
|
|
632
|
+
|
|
477
633
|
- **`paystackTransaction`**: `reference` (unique), `userId`, `referenceId`.
|
|
478
634
|
- **`subscription`**: `paystackSubscriptionCode` (unique), `referenceId`, `paystackTransactionReference`, `paystackCustomerCode`, `plan`.
|
|
479
635
|
- **`user` & `organization`**: `paystackCustomerCode`.
|
|
480
636
|
- **`paystackProduct`**: `slug` (unique), `paystackId` (unique).
|
|
481
637
|
|
|
638
|
+
Proration upgrades and trusted renewal charges also persist `paystackTransaction` rows, so local transaction history stays aligned with successful off-session charges.
|
|
639
|
+
|
|
482
640
|
### Syncing Products
|
|
483
641
|
|
|
484
|
-
The plugin provides two ways to keep your product inventory
|
|
642
|
+
The plugin provides two ways to keep your product inventory aligned with Paystack:
|
|
643
|
+
|
|
644
|
+
#### 1. Automated Inventory Sync
|
|
485
645
|
|
|
486
|
-
#### 1. Automated Inventory Sync (New)
|
|
487
646
|
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
647
|
|
|
489
|
-
#### 2. Manual Bulk Sync
|
|
490
|
-
You can synchronize all products with your local database using the `/paystack/sync-products` endpoint.
|
|
648
|
+
#### 2. Trusted Manual Bulk Sync
|
|
491
649
|
|
|
492
|
-
|
|
493
|
-
|
|
650
|
+
The public `/paystack/sync-products` endpoint was removed in `2.0.0`.
|
|
651
|
+
Run the trusted server operation from backend code instead:
|
|
652
|
+
|
|
653
|
+
```ts
|
|
654
|
+
import { syncPaystackProducts } from "@alexasomba/better-auth-paystack";
|
|
655
|
+
|
|
656
|
+
const ctx = { context: await auth.$context } as any;
|
|
657
|
+
|
|
658
|
+
await syncPaystackProducts(ctx, paystackOptions);
|
|
494
659
|
```
|
|
495
660
|
|
|
661
|
+
### SDK Compatibility Note
|
|
662
|
+
|
|
663
|
+
The plugin now targets the official `@alexasomba/paystack-node` grouped client surface directly.
|
|
664
|
+
If you inject a custom client, it should match the real SDK methods used by the plugin such as `transaction.initialize`, `transaction.verify`, `transaction.chargeAuthorization`, `subscription.create`, `subscription.disable`, and `subscription.enable`.
|
|
665
|
+
|
|
496
666
|
---
|
|
497
667
|
|
|
498
668
|
## 🏗️ Development & Contributing
|
|
499
669
|
|
|
500
|
-
This repository is
|
|
670
|
+
This repository is powered by **Vite+**. You use the `vp` CLI to manage the entire workspace.
|
|
501
671
|
|
|
502
672
|
```bash
|
|
503
|
-
# Install
|
|
504
|
-
|
|
673
|
+
# Install dependencies
|
|
674
|
+
vp i
|
|
675
|
+
|
|
676
|
+
# Check project health (format, lint, types)
|
|
677
|
+
vp check --fix
|
|
505
678
|
|
|
506
679
|
# Build the core library
|
|
507
|
-
|
|
680
|
+
vp build
|
|
508
681
|
|
|
509
|
-
# Run
|
|
510
|
-
|
|
682
|
+
# Run tests
|
|
683
|
+
vp test
|
|
511
684
|
|
|
512
|
-
# Run TanStack Start example
|
|
513
|
-
|
|
685
|
+
# Run the TanStack Start example
|
|
686
|
+
vp run examples/tanstack dev
|
|
514
687
|
```
|
|
515
688
|
|
|
516
689
|
Contributions are welcome! Please open an issue or pull request.
|
|
@@ -526,11 +699,11 @@ Future features planned for upcoming versions:
|
|
|
526
699
|
### v1.1.0 - Manual Recurring Subscriptions (Available Now)
|
|
527
700
|
|
|
528
701
|
- [x] **Stored Authorization Codes**: Securely store Paystack authorization codes from verified transactions.
|
|
529
|
-
- [x] **
|
|
702
|
+
- [x] **Trusted Renewal Operation**: Server-side helper to charge stored cards for renewals.
|
|
530
703
|
- [ ] **Card Management UI**: Let users view/delete saved payment methods (masked card data only) - _Upcoming_
|
|
531
704
|
- [ ] **Renewal Scheduler Integration**: Documentation for integrating with Cloudflare Workers Cron, Vercel Cron, etc. - _Upcoming_
|
|
532
705
|
|
|
533
|
-
> **Note**: For local-managed subscriptions (no `planCode`), the plugin
|
|
706
|
+
> **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
707
|
|
|
535
708
|
### Future Considerations
|
|
536
709
|
|
|
@@ -542,7 +715,7 @@ Future features planned for upcoming versions:
|
|
|
542
715
|
|
|
543
716
|
- GitHub Repository: [alexasomba/better-auth-paystack](https://github.com/alexasomba/better-auth-paystack)
|
|
544
717
|
- Comprehensive and up-to-date Paystack Node SDK: [alexasomba/paystack-node](https://github.com/alexasomba/paystack-node)
|
|
545
|
-
- Comprehensive and up-to-date Paystack
|
|
718
|
+
- Comprehensive and up-to-date Paystack Inline SDK: [alexasomba/paystack-inline](https://github.com/alexasomba/paystack-inline)
|
|
546
719
|
- [TanStack Start Example Implementation](https://github.com/alexasomba/better-auth-paystack/tree/main/examples/tanstack)
|
|
547
720
|
- Paystack Webhooks: https://paystack.com/docs/payments/webhooks/
|
|
548
721
|
- Paystack Transaction API: https://paystack.com/docs/api/transaction/
|