better-auth-bachs 0.0.0-stage → 0.5.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/LICENSE +21 -0
- package/README.md +590 -2
- package/dist/chunk-JQVRRJWS.js +7 -0
- package/dist/chunk-JQVRRJWS.js.map +1 -0
- package/dist/client.d.ts +287 -0
- package/dist/client.js +243 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +865 -0
- package/dist/index.js +1980 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Greatness Abolade
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,591 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Better Auth Bachs Plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript billing plugin for [Better Auth](https://www.better-auth.com) backed by the official [`@bachs/sdk`](https://docs.bachs.io/sdks/node).
|
|
4
|
+
|
|
5
|
+
[](https://github.com/grtsnx/better-auth-bachs)
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
**Author:** [Greatness Abolade](https://github.com/grtsnx)
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- Official `@bachs/sdk` for every Bachs API call
|
|
13
|
+
- Plan / product name → catalog `productId` mapping (ids stay on the server)
|
|
14
|
+
- User **and** organization billing references
|
|
15
|
+
- Local tables for customers, checkouts, subscriptions, payments, webhook events
|
|
16
|
+
- Verified, deduped webhooks
|
|
17
|
+
- Opt-in **merchant** mode (create products, balance, banks, payouts)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
### Prerequisites
|
|
24
|
+
|
|
25
|
+
- Node.js **22+**
|
|
26
|
+
- Better Auth **1.4+**
|
|
27
|
+
- A [Bachs](https://bachs.io) account (sandbox or live API key + webhook signing secret)
|
|
28
|
+
|
|
29
|
+
### 1. Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install better-auth better-auth-bachs @bachs/sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Environment
|
|
36
|
+
|
|
37
|
+
```env
|
|
38
|
+
BACHS_API_KEY=sk_sandbox_...
|
|
39
|
+
BACHS_WEBHOOK_SECRET=whsec_... # signing secret from Bachs → Webhooks (not the API key)
|
|
40
|
+
BETTER_AUTH_SECRET=...
|
|
41
|
+
BETTER_AUTH_URL=http://localhost:3000
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. Create products in Bachs
|
|
45
|
+
|
|
46
|
+
In the [Bachs dashboard](https://bachs.io) (or via merchant `createProduct` below), create catalog products and copy each `prod_…` id. Map those ids to **app plan/product names** in plugin config — clients never send product ids for normal checkout.
|
|
47
|
+
|
|
48
|
+
### 4. Server plugin
|
|
49
|
+
|
|
50
|
+
Mount Better Auth as usual (e.g. `/api/auth/*`). Add the Bachs plugin:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// auth.ts
|
|
54
|
+
import { Bachs } from "@bachs/sdk";
|
|
55
|
+
import { betterAuth } from "better-auth";
|
|
56
|
+
import { organization } from "better-auth/plugins";
|
|
57
|
+
import { bachs } from "better-auth-bachs";
|
|
58
|
+
|
|
59
|
+
const bachsClient = new Bachs({
|
|
60
|
+
apiKey: process.env.BACHS_API_KEY!,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const auth = betterAuth({
|
|
64
|
+
plugins: [
|
|
65
|
+
organization(), // required only if you enable org billing
|
|
66
|
+
bachs({
|
|
67
|
+
bachsClient,
|
|
68
|
+
webhookSecret: process.env.BACHS_WEBHOOK_SECRET!,
|
|
69
|
+
successUrl: "https://your-app.com/billing/success", // must be a public URL
|
|
70
|
+
cancelUrl: "https://your-app.com/billing",
|
|
71
|
+
organization: {
|
|
72
|
+
enabled: true,
|
|
73
|
+
billingRoles: ["owner", "admin"], // default
|
|
74
|
+
},
|
|
75
|
+
// Merchant routes fail closed unless your server-side admin check allows the user.
|
|
76
|
+
// merchant: true,
|
|
77
|
+
// authorizeMerchant: async (_ctx, userId) => isFinanceAdmin(userId),
|
|
78
|
+
subscription: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
plans: [
|
|
81
|
+
{
|
|
82
|
+
name: "starter",
|
|
83
|
+
productId: "prod_...",
|
|
84
|
+
group: "workspace",
|
|
85
|
+
description: "Starter plan",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "pro",
|
|
89
|
+
productId: "prod_...",
|
|
90
|
+
group: "workspace",
|
|
91
|
+
description: "Pro plan",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
products: {
|
|
96
|
+
products: [{ name: "credits_50", productId: "prod_..." }],
|
|
97
|
+
},
|
|
98
|
+
async onEvent(event) {
|
|
99
|
+
// Fulfil after verified, non-duplicate webhook
|
|
100
|
+
// event.type, event.id, event.data
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`bachsClient` is preferred. You can pass `apiKey` instead and the plugin will construct `new Bachs({ apiKey })`.
|
|
108
|
+
|
|
109
|
+
### 5. Client plugin
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
// lib/auth-client.ts
|
|
113
|
+
import { createAuthClient } from "better-auth/client";
|
|
114
|
+
import { organizationClient } from "better-auth/client/plugins";
|
|
115
|
+
import { bachsClient } from "better-auth-bachs/client";
|
|
116
|
+
|
|
117
|
+
export const authClient = createAuthClient({
|
|
118
|
+
plugins: [
|
|
119
|
+
organizationClient(),
|
|
120
|
+
bachsClient({
|
|
121
|
+
subscription: true,
|
|
122
|
+
// merchant: true, // must match server merchant: true
|
|
123
|
+
}),
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
All billing calls below use `authClient.bachs.*` and require a signed-in session (except the webhook).
|
|
129
|
+
|
|
130
|
+
### 6. Migrate the database
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npx @better-auth/cli generate
|
|
134
|
+
# or
|
|
135
|
+
npx @better-auth/cli migrate
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The customer table now has a unique `(referenceType, referenceId)` index. If upgrading an existing installation, remove any duplicate `bachsCustomer` rows for the same reference before applying the migration.
|
|
139
|
+
|
|
140
|
+
Tables: `bachsCustomer`, `bachsCheckout`, `bachsSubscription`, `bachsPayment`, `bachsWebhookEvent`.
|
|
141
|
+
|
|
142
|
+
### 7. Configure the Bachs webhook
|
|
143
|
+
|
|
144
|
+
In the Bachs dashboard, create an endpoint pointing at:
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
https://your-app.com/api/auth/bachs/webhook
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
(Adjust the `/api/auth` prefix to match your Better Auth base path.)
|
|
151
|
+
|
|
152
|
+
Subscribe at least to:
|
|
153
|
+
|
|
154
|
+
| Event | Why |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `checkout.completed` | Mark checkout paid; often creates customer |
|
|
157
|
+
| `collection.succeeded` | Record successful collection / payment |
|
|
158
|
+
| `customer.subscription.created` | Upsert local subscription |
|
|
159
|
+
| `customer.subscription.updated` | Sync status / period end |
|
|
160
|
+
| `customer.subscription.deleted` | Mark canceled |
|
|
161
|
+
|
|
162
|
+
Paste the endpoint **signing secret** into `BACHS_WEBHOOK_SECRET`.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Concepts
|
|
167
|
+
|
|
168
|
+
| Concept | Meaning |
|
|
169
|
+
| --- | --- |
|
|
170
|
+
| **Plan name** | App string like `"pro"` → server looks up `productId` |
|
|
171
|
+
| **Product name** | One-time catalog alias like `"credits_50"` |
|
|
172
|
+
| **referenceId / referenceType** | Who is billed: signed-in `user` (default) or an `organization` |
|
|
173
|
+
| **Local vs remote** | Local = rows in your DB; remote = live Bachs API |
|
|
174
|
+
| **Merchant mode** | `merchant: true` — create products, payouts, account balance |
|
|
175
|
+
|
|
176
|
+
Organization billing also needs the Better Auth `organization` plugin. Only members with `billingRoles` (default `owner`, `admin`) may manage org billing.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Endpoint reference
|
|
181
|
+
|
|
182
|
+
Base path: `{BETTER_AUTH_URL}/api/auth` (or your auth basePath).
|
|
183
|
+
|
|
184
|
+
### Checkout
|
|
185
|
+
|
|
186
|
+
#### `POST /bachs/create-checkout`
|
|
187
|
+
|
|
188
|
+
Start hosted Bachs checkout for a plan or one-time product.
|
|
189
|
+
Client-provided success and cancel URLs must use the same origin as a server-configured URL.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
// Generate once and reuse this value if you retry the same operation.
|
|
193
|
+
const checkoutKey = crypto.randomUUID();
|
|
194
|
+
const { data, error } = await authClient.bachs.createCheckout({
|
|
195
|
+
plan: "pro", // or product: "credits_50" or productId: "prod_..."
|
|
196
|
+
quantity: 1,
|
|
197
|
+
idempotencyKey: checkoutKey,
|
|
198
|
+
reference: "order_123", // optional merchant reference
|
|
199
|
+
successUrl: "https://your-app.com/billing/success",
|
|
200
|
+
cancelUrl: "https://your-app.com/billing",
|
|
201
|
+
// Organization:
|
|
202
|
+
// referenceId: orgId,
|
|
203
|
+
// referenceType: "organization",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (data?.checkoutUrl) window.location.href = data.checkoutUrl;
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Returns `checkoutId`, `checkoutUrl`, `status`, `plan` / `product`, `referenceId`, `referenceType`.
|
|
210
|
+
|
|
211
|
+
#### `POST /bachs/upgrade-subscription`
|
|
212
|
+
|
|
213
|
+
Checkout for a **new** plan (upgrade path). Optionally pass the subscription being replaced.
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
await authClient.bachs.upgradeSubscription({
|
|
217
|
+
plan: "pro",
|
|
218
|
+
subscriptionId: "sub_...", // optional
|
|
219
|
+
idempotencyKey: crypto.randomUUID(),
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### `GET /bachs/get-checkout`
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
await authClient.bachs.getCheckout({ checkoutId: "chs_..." });
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### `GET /bachs/list-checkouts`
|
|
230
|
+
|
|
231
|
+
Local checkout rows for the billing reference.
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
await authClient.bachs.listCheckouts();
|
|
235
|
+
await authClient.bachs.listCheckouts({
|
|
236
|
+
referenceId: orgId,
|
|
237
|
+
referenceType: "organization",
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### Catalog & config
|
|
244
|
+
|
|
245
|
+
#### `GET /bachs/list-plans`
|
|
246
|
+
|
|
247
|
+
Configured subscription plans (from plugin options).
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
await authClient.bachs.listPlans();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### `GET /bachs/list-products`
|
|
254
|
+
|
|
255
|
+
Configured one-time product aliases.
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
await authClient.bachs.listProducts();
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### `GET /bachs/list-catalog-products`
|
|
262
|
+
|
|
263
|
+
Live products from the Bachs catalog. This endpoint requires merchant authorization.
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
await authClient.bachs.listCatalogProducts({
|
|
267
|
+
limit: 20,
|
|
268
|
+
cursor: undefined,
|
|
269
|
+
includeArchived: false,
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### `GET /bachs/get-catalog-product`
|
|
274
|
+
|
|
275
|
+
Requires merchant authorization and omits product metadata.
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
await authClient.bachs.getCatalogProduct({ productId: "prod_..." });
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### `GET /bachs/config`
|
|
282
|
+
|
|
283
|
+
Safe public config (plan/product **names** only — no product ids).
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
await authClient.bachs.config();
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### Customer & billing status
|
|
292
|
+
|
|
293
|
+
#### `GET /bachs/get-customer`
|
|
294
|
+
|
|
295
|
+
Local `bachsCustomer` row for the reference (linked after checkout/webhook).
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
await authClient.bachs.getCustomer();
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### `GET /bachs/billing-status`
|
|
302
|
+
|
|
303
|
+
Customer id + whether an active/trialing/past_due subscription exists.
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
const { data } = await authClient.bachs.billingStatus();
|
|
307
|
+
// data.hasActiveSubscription, data.subscriptions, data.customerId
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### `POST /bachs/create-portal-session`
|
|
311
|
+
|
|
312
|
+
Open the Bachs customer portal (requires a linked customer).
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
const { data } = await authClient.bachs.createPortalSession({
|
|
316
|
+
idempotencyKey: crypto.randomUUID(),
|
|
317
|
+
});
|
|
318
|
+
if (data?.portalUrl) window.location.href = data.portalUrl;
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
### Subscriptions
|
|
324
|
+
|
|
325
|
+
#### `GET /bachs/list-subscriptions`
|
|
326
|
+
|
|
327
|
+
Local subscription rows.
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
await authClient.bachs.listSubscriptions();
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### `GET /bachs/list-remote-subscriptions`
|
|
334
|
+
|
|
335
|
+
Live Bachs subscriptions for the linked customer.
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
await authClient.bachs.listRemoteSubscriptions({
|
|
339
|
+
status: "active",
|
|
340
|
+
limit: 20,
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### `GET /bachs/get-subscription`
|
|
345
|
+
|
|
346
|
+
Local row + remote snapshot.
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
await authClient.bachs.getSubscription({ subscriptionId: "sub_..." });
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### `POST /bachs/cancel-subscription`
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
await authClient.bachs.cancelSubscription({
|
|
356
|
+
subscriptionId: "sub_...",
|
|
357
|
+
idempotencyKey: crypto.randomUUID(),
|
|
358
|
+
cancelAtPeriodEnd: true, // default true
|
|
359
|
+
reason: "too_expensive",
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
### Payments
|
|
366
|
+
|
|
367
|
+
#### `GET /bachs/list-payments`
|
|
368
|
+
|
|
369
|
+
Local payment rows (filled from webhooks).
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
await authClient.bachs.listPayments();
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### `GET /bachs/get-payment`
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
await authClient.bachs.getPayment({ paymentId: "pay_..." });
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
### Webhook
|
|
384
|
+
|
|
385
|
+
#### `POST /bachs/webhook`
|
|
386
|
+
|
|
387
|
+
Called by Bachs — not by your client. Verifies signature, dedupes by `event.id`, upserts customer / checkout / subscription / payment, then runs `onEvent`.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Merchant endpoints
|
|
392
|
+
|
|
393
|
+
Enable on **both** sides and configure a server-side authorization callback:
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
// server
|
|
397
|
+
bachs({
|
|
398
|
+
merchant: true,
|
|
399
|
+
authorizeMerchant: async (_ctx, userId) => isFinanceAdmin(userId),
|
|
400
|
+
bachsClient,
|
|
401
|
+
...
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
// client
|
|
405
|
+
bachsClient({ merchant: true })
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Merchant routes return `403` unless `authorizeMerchant` is configured and returns `true`. Use your app's trusted staff/admin role source; never authorize based on client-supplied fields. Only enable these routes for apps that operate the Bachs merchant account.
|
|
409
|
+
|
|
410
|
+
### Products
|
|
411
|
+
|
|
412
|
+
#### `POST /bachs/create-product`
|
|
413
|
+
|
|
414
|
+
Create a Bachs catalog product (fixed, free, or custom / pay-what-you-want). Add `billingInterval` for recurring.
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
// Fixed one-time
|
|
418
|
+
await authClient.bachs.createProduct({
|
|
419
|
+
name: "Field Guide",
|
|
420
|
+
currency: "USD",
|
|
421
|
+
amount: "19.00",
|
|
422
|
+
idempotencyKey: crypto.randomUUID(),
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Recurring plan product
|
|
426
|
+
await authClient.bachs.createProduct({
|
|
427
|
+
name: "Pro monthly",
|
|
428
|
+
currency: "USD",
|
|
429
|
+
amount: "29.00",
|
|
430
|
+
billingInterval: "month",
|
|
431
|
+
billingFrequency: 1,
|
|
432
|
+
idempotencyKey: crypto.randomUUID(),
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Pay what you want
|
|
436
|
+
await authClient.bachs.createProduct({
|
|
437
|
+
name: "Tip jar",
|
|
438
|
+
currency: "USD",
|
|
439
|
+
priceType: "custom",
|
|
440
|
+
minimumAmount: "5.00",
|
|
441
|
+
presetAmount: "10.00",
|
|
442
|
+
idempotencyKey: crypto.randomUUID(),
|
|
443
|
+
});
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Copy the returned `id` (`prod_…`) into `subscription.plans` / `products.products` in your auth config (or store it in your own admin DB).
|
|
447
|
+
|
|
448
|
+
> Product **update/archive** are not in `@bachs/sdk` yet — use the Bachs dashboard or REST API.
|
|
449
|
+
|
|
450
|
+
### Account payments (merchant)
|
|
451
|
+
|
|
452
|
+
#### `GET /bachs/list-remote-payments`
|
|
453
|
+
|
|
454
|
+
Account-wide payment list from Bachs (not scoped to one customer).
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
await authClient.bachs.listRemotePayments({
|
|
458
|
+
limit: 50,
|
|
459
|
+
statusFilter: "succeeded",
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Balance & banks
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
await authClient.bachs.getBalance();
|
|
467
|
+
|
|
468
|
+
await authClient.bachs.listBanks({ country: "NG" });
|
|
469
|
+
|
|
470
|
+
await authClient.bachs.resolveBankAccount({
|
|
471
|
+
accountNumber: "0123456789",
|
|
472
|
+
bankCode: "058",
|
|
473
|
+
country: "NG",
|
|
474
|
+
});
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Payouts
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
const { data: dest } = await authClient.bachs.createPayoutDestination({
|
|
481
|
+
currency: "NGN",
|
|
482
|
+
accountNumber: "0123456789",
|
|
483
|
+
bankCode: "058",
|
|
484
|
+
accountName: "Acme Ltd",
|
|
485
|
+
idempotencyKey: crypto.randomUUID(),
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
await authClient.bachs.listPayoutDestinations();
|
|
489
|
+
await authClient.bachs.getPayoutDestination({ destinationId: "..." });
|
|
490
|
+
|
|
491
|
+
const { data: quote } = await authClient.bachs.createPayoutQuote({
|
|
492
|
+
fromCurrency: "USD",
|
|
493
|
+
toCurrency: "NGN",
|
|
494
|
+
amount: "100.00",
|
|
495
|
+
idempotencyKey: crypto.randomUUID(),
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
await authClient.bachs.createPayout({
|
|
499
|
+
destination: dest.id,
|
|
500
|
+
quoteId: quote.quoteId, // or amount: "100.00"
|
|
501
|
+
idempotencyKey: crypto.randomUUID(),
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
await authClient.bachs.listPayouts();
|
|
505
|
+
await authClient.bachs.getPayout({ payoutId: "..." });
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## Full endpoint list
|
|
511
|
+
|
|
512
|
+
### Always available (session required except webhook)
|
|
513
|
+
|
|
514
|
+
| Method | Path | Client |
|
|
515
|
+
| --- | --- | --- |
|
|
516
|
+
| `POST` | `/bachs/create-checkout` | `createCheckout` |
|
|
517
|
+
| `POST` | `/bachs/upgrade-subscription` | `upgradeSubscription` |
|
|
518
|
+
| `GET` | `/bachs/get-checkout` | `getCheckout` |
|
|
519
|
+
| `GET` | `/bachs/list-checkouts` | `listCheckouts` |
|
|
520
|
+
| `POST` | `/bachs/create-portal-session` | `createPortalSession` |
|
|
521
|
+
| `GET` | `/bachs/list-plans` | `listPlans` |
|
|
522
|
+
| `GET` | `/bachs/list-products` | `listProducts` |
|
|
523
|
+
| `GET` | `/bachs/list-catalog-products` | `listCatalogProducts` |
|
|
524
|
+
| `GET` | `/bachs/get-catalog-product` | `getCatalogProduct` |
|
|
525
|
+
| `GET` | `/bachs/get-customer` | `getCustomer` |
|
|
526
|
+
| `GET` | `/bachs/list-subscriptions` | `listSubscriptions` |
|
|
527
|
+
| `GET` | `/bachs/list-remote-subscriptions` | `listRemoteSubscriptions` |
|
|
528
|
+
| `GET` | `/bachs/get-subscription` | `getSubscription` |
|
|
529
|
+
| `POST` | `/bachs/cancel-subscription` | `cancelSubscription` |
|
|
530
|
+
| `GET` | `/bachs/list-payments` | `listPayments` |
|
|
531
|
+
| `GET` | `/bachs/get-payment` | `getPayment` |
|
|
532
|
+
| `GET` | `/bachs/billing-status` | `billingStatus` |
|
|
533
|
+
| `GET` | `/bachs/config` | `config` |
|
|
534
|
+
| `POST` | `/bachs/webhook` | *(Bachs → your server)* |
|
|
535
|
+
|
|
536
|
+
### Merchant (`merchant: true`)
|
|
537
|
+
|
|
538
|
+
| Method | Path | Client |
|
|
539
|
+
| --- | --- | --- |
|
|
540
|
+
| `POST` | `/bachs/create-product` | `createProduct` |
|
|
541
|
+
| `GET` | `/bachs/list-remote-payments` | `listRemotePayments` |
|
|
542
|
+
| `GET` | `/bachs/get-balance` | `getBalance` |
|
|
543
|
+
| `GET` | `/bachs/list-banks` | `listBanks` |
|
|
544
|
+
| `POST` | `/bachs/resolve-bank-account` | `resolveBankAccount` |
|
|
545
|
+
| `GET` | `/bachs/list-payout-destinations` | `listPayoutDestinations` |
|
|
546
|
+
| `GET` | `/bachs/get-payout-destination` | `getPayoutDestination` |
|
|
547
|
+
| `POST` | `/bachs/create-payout-destination` | `createPayoutDestination` |
|
|
548
|
+
| `POST` | `/bachs/create-payout-quote` | `createPayoutQuote` |
|
|
549
|
+
| `POST` | `/bachs/create-payout` | `createPayout` |
|
|
550
|
+
| `GET` | `/bachs/list-payouts` | `listPayouts` |
|
|
551
|
+
| `GET` | `/bachs/get-payout` | `getPayout` |
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## Options
|
|
556
|
+
|
|
557
|
+
| Option | Description |
|
|
558
|
+
| --- | --- |
|
|
559
|
+
| `bachsClient` | Injected `Bachs` from `@bachs/sdk` (**recommended**) |
|
|
560
|
+
| `apiKey` | Used when `bachsClient` is omitted |
|
|
561
|
+
| `clientOptions` | Extra `new Bachs(...)` options when using `apiKey` |
|
|
562
|
+
| `webhookSecret` | Dashboard signing secret for `/bachs/webhook` |
|
|
563
|
+
| `successUrl` / `cancelUrl` | Default checkout return URLs |
|
|
564
|
+
| `subscription.plans` | `{ name, productId, group?, description? }[]` |
|
|
565
|
+
| `subscription.authorizeReference` | Custom auth for billing references |
|
|
566
|
+
| `products.products` | `{ name, productId, description? }[]` |
|
|
567
|
+
| `organization.enabled` | Enable org billing |
|
|
568
|
+
| `organization.billingRoles` | Default `["owner","admin"]` |
|
|
569
|
+
| `merchant` | Enable merchant endpoints (default `false`) |
|
|
570
|
+
| `authorizeMerchant` | Required with `merchant: true`; authorize users through trusted server-side roles |
|
|
571
|
+
| `createCustomerOnCheckout` | Persist `bachsCustomer` from webhooks (default `true`) |
|
|
572
|
+
| `onEvent` | Callback for a verified webhook event; may run again after a failed acknowledgement |
|
|
573
|
+
| `schema` | Schema overrides for Better Auth migrations |
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## Design notes
|
|
578
|
+
|
|
579
|
+
- Clients should send **plan** / **product** names; a `productId` is accepted only when it matches a configured plan or product.
|
|
580
|
+
- Never trust client-supplied money amounts for checkout — Bachs owns catalog prices.
|
|
581
|
+
- Fulfil from **verified webhooks**, not from the success redirect alone.
|
|
582
|
+
- Uncertain SDK writes return a conflict so you can reconcile before retrying with a new idempotency key.
|
|
583
|
+
- Every client initiated resource write requires an idempotency key. Reuse the same key for retries of the same operation; the plugin scopes keys to the signed-in user and operation.
|
|
584
|
+
- Webhook state updates run in a transaction when the Better Auth adapter supports transactions. A short database lease serializes concurrent deliveries; a failed callback may still be retried, so deduplicate fulfillment by `event.id`.
|
|
585
|
+
- SDK coverage today: create/get/list products, checkout, subscriptions cancel/list/get, payments get/list, portal, balance, banks, payouts. Product update/archive are not in the Node SDK yet.
|
|
586
|
+
|
|
587
|
+
Docs: [Bachs Node SDK](https://docs.bachs.io/sdks/node) · [Checkout](https://docs.bachs.io/guides/checkout/checkout-sessions) · [Products](https://docs.bachs.io/guides/products/overview) · [Webhooks](https://docs.bachs.io/guides/webhooks/overview)
|
|
588
|
+
|
|
589
|
+
## License
|
|
590
|
+
|
|
591
|
+
MIT © Greatness Abolade
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = \"0.5.0\";\n"],"mappings":";AAAO,IAAM,kBAAkB;","names":[]}
|