@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 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
+ ![npm downloads](https://img.shields.io/npm/dm/@alexasomba/better-auth-paystack.svg)
8
+ [![GitHub stars](https://img.shields.io/github/stars/alexasomba/better-auth-paystack.svg?style=social&label=Star)](https://github.com/alexasomba/better-auth-paystack/stargazers)
9
+ [![GitHub release](https://img.shields.io/github/v/release/alexasomba/better-auth-paystack)](https://github.com/alexasomba/better-auth-paystack/releases)
10
+ [![bundlephobia](https://img.shields.io/bundlephobia/minzip/@alexasomba/better-auth-paystack)](https://bundlephobia.com/result?p=@alexasomba/better-auth-paystack)
11
+ [![Follow on Twitter](https://img.shields.io/twitter/follow/alexasomba?style=social)](https://twitter.com/alexasomba)
12
+ ![GitHub License](https://img.shields.io/github/license/alexasomba/better-auth-paystack)
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 charges for seat and plan upgrades.
16
- - [x] **Popup Modal Flow**: Optional support for Paystack's inline checkout experience via `@alexasomba/paystack-browser`.
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-browser
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
- paystackWebhookSecret: process.env.PAYSTACK_WEBHOOK_SECRET!,
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: [paystackClient({ subscription: true })],
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-browser` for a seamless UI.
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
- If the user has a saved Paystack authorization code, the plugin will execute a prorated charge for the remaining cycle days and immediately sync the new amount/seats.
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, // Will calculate and charge the prorated amount instantly
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, any>;
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 in sync with Paystack:
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
- ```bash
493
- POST /api/auth/paystack/sync-products
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 set up as a pnpm workspace. You can run and build examples via `--filter`.
670
+ This repository is powered by **Vite+**. You use the `vp` CLI to manage the entire workspace.
501
671
 
502
672
  ```bash
503
- # Install everything
504
- pnpm install
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
- pnpm --filter "@alexasomba/better-auth-paystack" build
680
+ vp build
508
681
 
509
- # Run Next.js example (Next.js + Better Auth)
510
- pnpm --filter nextjs-better-auth-paystack dev
682
+ # Run tests
683
+ vp test
511
684
 
512
- # Run TanStack Start example (TanStack Start + Better Auth)
513
- pnpm --filter tanstack-start-better-auth-paystack dev
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] **Charge Authorization Endpoint**: Server-side endpoint (`/charge-recurring`) to charge stored cards for renewals.
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 now automatically captures and stores the `authorization_code`. You can trigger renewals using `authClient.paystack.chargeRecurringSubscription({ subscriptionId })`.
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 Browser SDK: [alexasomba/paystack-browser](https://github.com/alexasomba/paystack-browser)
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/