@dotbots-boutique/auth-sdk 1.0.7 → 1.0.9

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
@@ -154,6 +154,42 @@ This is the **only** method that routes via `proxyUrl`. All auth calls and the p
154
154
 
155
155
  ---
156
156
 
157
+ ## Payments
158
+
159
+ Use `auth.charge()` to charge a feature usage to the organisation or app budget. The app never handles token amounts, balances or budgets directly — the platform manages all of that.
160
+
161
+ ```typescript
162
+ const { transactionId } = await auth.charge('ai.generate', 'org');
163
+ ```
164
+
165
+ With a custom quantity:
166
+
167
+ ```typescript
168
+ const { transactionId } = await auth.charge('ai.generate', 'org', 5);
169
+ ```
170
+
171
+ ### Error handling
172
+
173
+ ```typescript
174
+ import { DotBotsAuthError } from '@dotbots-boutique/auth-sdk';
175
+
176
+ try {
177
+ const { transactionId } = await auth.charge('ai.generate', 'org', 1);
178
+ } catch (error) {
179
+ if (error instanceof DotBotsAuthError && error.code === 'PAYMENT_FAILED') {
180
+ if (error.message === 'INSUFFICIENT_BALANCE') {
181
+ // show top-up prompt
182
+ } else if (error.message === 'BUDGET_EXCEEDED') {
183
+ // show budget limit message
184
+ }
185
+ }
186
+ }
187
+ ```
188
+
189
+ Possible `PAYMENT_FAILED` messages: `INSUFFICIENT_BALANCE`, `BUDGET_EXCEEDED`, `FEATURE_NOT_FOUND`, `PAYMENT_REJECTED`.
190
+
191
+ ---
192
+
157
193
  ## Checking permissions
158
194
 
159
195
  ```typescript
@@ -223,6 +259,7 @@ try {
223
259
  | `NETWORK_ERROR` | API unreachable | Yes |
224
260
  | `NOT_INITIALIZED` | `initialize()` has not been called yet | Yes |
225
261
  | `PROXY_UNAVAILABLE` | Proxy config could not be fetched | No — falls back to direct API |
262
+ | `PAYMENT_FAILED` | Payment was rejected (402) — see `message` for reason | No |
226
263
 
227
264
  ---
228
265
 
@@ -301,6 +338,10 @@ Check if the user has a specific role.
301
338
 
302
339
  Authenticated fetch wrapper. Routes through the proxy when available, falls back to `apiUrl` when not. Retries once on 401 after refreshing the token.
303
340
 
341
+ #### `charge(featureCode: string, paidBy: 'org' | 'app', quantity?: number): Promise<{ transactionId: string }>`
342
+
343
+ Charges a feature usage. Calls `POST {proxyUrl}/payments/charge`. Throws `PAYMENT_FAILED` on 402 with a message indicating the reason (`INSUFFICIENT_BALANCE`, `BUDGET_EXCEEDED`, `FEATURE_NOT_FOUND`, `PAYMENT_REJECTED`).
344
+
304
345
  #### `logout(): Promise<void>`
305
346
 
306
347
  Logs out the user. Revokes tokens on `apiUrl`, clears state, and redirects (standalone) or notifies the parent (iframe).
@@ -325,6 +366,7 @@ const {
325
366
  canAny, // (permissions: string[]) => boolean
326
367
  hasRole, // (role: string) => boolean
327
368
  fetch, // auth.fetch proxied
369
+ charge, // auth.charge proxied
328
370
  logout, // auth.logout proxied
329
371
  } = useDotBotsAuth();
330
372
  ```
package/dist/cjs/index.js CHANGED
@@ -303,8 +303,14 @@ class DotBotsAuth {
303
303
  }
304
304
  async fetch(url, options) {
305
305
  this.assertInitialized();
306
- const baseUrl = this.proxyConfigManager.getBaseUrl();
307
- const fullUrl = `${baseUrl}${url.startsWith('/') ? url : `/${url}`}`;
306
+ let fullUrl;
307
+ if (url.startsWith('https://') || url.startsWith('http://')) {
308
+ fullUrl = url;
309
+ }
310
+ else {
311
+ const baseUrl = this.proxyConfigManager.getBaseUrl();
312
+ fullUrl = `${baseUrl}${url.startsWith('/') ? url : `/${url}`}`;
313
+ }
308
314
  let response = await this.buildRequest(fullUrl, options);
309
315
  // On 401, try one refresh then retry
310
316
  if (response.status === 401) {
@@ -319,6 +325,23 @@ class DotBotsAuth {
319
325
  }
320
326
  return response;
321
327
  }
328
+ async charge(featureCode, paidBy, quantity) {
329
+ this.assertInitialized();
330
+ const baseUrl = this.proxyConfigManager.getBaseUrl();
331
+ const response = await this.buildRequest(`${baseUrl}/payments/charge`, {
332
+ method: 'POST',
333
+ headers: { 'Content-Type': 'application/json' },
334
+ body: JSON.stringify({ featureCode, paidBy, quantity: quantity ?? 1 }),
335
+ });
336
+ if (response.status === 402) {
337
+ const body = await response.json();
338
+ throw new DotBotsAuthError('PAYMENT_FAILED', body.error, body.reason);
339
+ }
340
+ if (!response.ok) {
341
+ throw new DotBotsAuthError('NETWORK_ERROR', 'Payment request failed');
342
+ }
343
+ return response.json();
344
+ }
322
345
  async logout() {
323
346
  this.assertInitialized();
324
347
  await this.tokenManager.revoke();
@@ -409,7 +432,7 @@ class DotBotsAuth {
409
432
  }
410
433
  }
411
434
  }
412
- DotBotsAuth.SDK_VERSION = '1.0.7';
435
+ DotBotsAuth.SDK_VERSION = '1.0.9';
413
436
 
414
437
  exports.DotBotsAuth = DotBotsAuth;
415
438
  exports.DotBotsAuthError = DotBotsAuthError;
@@ -49,6 +49,7 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
49
49
  canAny: (permissions) => auth.canAny(permissions),
50
50
  hasRole: (role) => auth.hasRole(role),
51
51
  fetch: (url, options) => auth.fetch(url, options),
52
+ charge: (featureCode, paidBy, quantity) => auth.charge(featureCode, paidBy, quantity),
52
53
  logout: () => auth.logout(),
53
54
  };
54
55
  return (jsxRuntime.jsx(DotBotsAuthContext.Provider, { value: value, children: children }));
package/dist/esm/index.js CHANGED
@@ -301,8 +301,14 @@ class DotBotsAuth {
301
301
  }
302
302
  async fetch(url, options) {
303
303
  this.assertInitialized();
304
- const baseUrl = this.proxyConfigManager.getBaseUrl();
305
- const fullUrl = `${baseUrl}${url.startsWith('/') ? url : `/${url}`}`;
304
+ let fullUrl;
305
+ if (url.startsWith('https://') || url.startsWith('http://')) {
306
+ fullUrl = url;
307
+ }
308
+ else {
309
+ const baseUrl = this.proxyConfigManager.getBaseUrl();
310
+ fullUrl = `${baseUrl}${url.startsWith('/') ? url : `/${url}`}`;
311
+ }
306
312
  let response = await this.buildRequest(fullUrl, options);
307
313
  // On 401, try one refresh then retry
308
314
  if (response.status === 401) {
@@ -317,6 +323,23 @@ class DotBotsAuth {
317
323
  }
318
324
  return response;
319
325
  }
326
+ async charge(featureCode, paidBy, quantity) {
327
+ this.assertInitialized();
328
+ const baseUrl = this.proxyConfigManager.getBaseUrl();
329
+ const response = await this.buildRequest(`${baseUrl}/payments/charge`, {
330
+ method: 'POST',
331
+ headers: { 'Content-Type': 'application/json' },
332
+ body: JSON.stringify({ featureCode, paidBy, quantity: quantity ?? 1 }),
333
+ });
334
+ if (response.status === 402) {
335
+ const body = await response.json();
336
+ throw new DotBotsAuthError('PAYMENT_FAILED', body.error, body.reason);
337
+ }
338
+ if (!response.ok) {
339
+ throw new DotBotsAuthError('NETWORK_ERROR', 'Payment request failed');
340
+ }
341
+ return response.json();
342
+ }
320
343
  async logout() {
321
344
  this.assertInitialized();
322
345
  await this.tokenManager.revoke();
@@ -407,6 +430,6 @@ class DotBotsAuth {
407
430
  }
408
431
  }
409
432
  }
410
- DotBotsAuth.SDK_VERSION = '1.0.7';
433
+ DotBotsAuth.SDK_VERSION = '1.0.9';
411
434
 
412
435
  export { DotBotsAuth, DotBotsAuthError };
@@ -47,6 +47,7 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
47
47
  canAny: (permissions) => auth.canAny(permissions),
48
48
  hasRole: (role) => auth.hasRole(role),
49
49
  fetch: (url, options) => auth.fetch(url, options),
50
+ charge: (featureCode, paidBy, quantity) => auth.charge(featureCode, paidBy, quantity),
50
51
  logout: () => auth.logout(),
51
52
  };
52
53
  return (jsx(DotBotsAuthContext.Provider, { value: value, children: children }));
@@ -8,7 +8,7 @@ export declare class DotBotsAuth {
8
8
  private readonly listeners;
9
9
  private cachedUser;
10
10
  private initialized;
11
- static readonly SDK_VERSION = "1.0.7";
11
+ static readonly SDK_VERSION = "1.0.9";
12
12
  constructor(config: DotBotsConfig);
13
13
  initialize(): Promise<void>;
14
14
  getUser(): Promise<DotBotsUser>;
@@ -17,6 +17,9 @@ export declare class DotBotsAuth {
17
17
  canAny(permissions: string[]): boolean;
18
18
  hasRole(role: string): boolean;
19
19
  fetch(url: string, options?: RequestInit): Promise<Response>;
20
+ charge(featureCode: string, paidBy: 'org' | 'app', quantity?: number): Promise<{
21
+ transactionId: string;
22
+ }>;
20
23
  logout(): Promise<void>;
21
24
  on(event: DotBotsAuthEvent, handler: Function): void;
22
25
  off(event: DotBotsAuthEvent, handler: Function): void;
@@ -301,8 +301,14 @@ class DotBotsAuth {
301
301
  }
302
302
  async fetch(url, options) {
303
303
  this.assertInitialized();
304
- const baseUrl = this.proxyConfigManager.getBaseUrl();
305
- const fullUrl = `${baseUrl}${url.startsWith('/') ? url : `/${url}`}`;
304
+ let fullUrl;
305
+ if (url.startsWith('https://') || url.startsWith('http://')) {
306
+ fullUrl = url;
307
+ }
308
+ else {
309
+ const baseUrl = this.proxyConfigManager.getBaseUrl();
310
+ fullUrl = `${baseUrl}${url.startsWith('/') ? url : `/${url}`}`;
311
+ }
306
312
  let response = await this.buildRequest(fullUrl, options);
307
313
  // On 401, try one refresh then retry
308
314
  if (response.status === 401) {
@@ -317,6 +323,23 @@ class DotBotsAuth {
317
323
  }
318
324
  return response;
319
325
  }
326
+ async charge(featureCode, paidBy, quantity) {
327
+ this.assertInitialized();
328
+ const baseUrl = this.proxyConfigManager.getBaseUrl();
329
+ const response = await this.buildRequest(`${baseUrl}/payments/charge`, {
330
+ method: 'POST',
331
+ headers: { 'Content-Type': 'application/json' },
332
+ body: JSON.stringify({ featureCode, paidBy, quantity: quantity ?? 1 }),
333
+ });
334
+ if (response.status === 402) {
335
+ const body = await response.json();
336
+ throw new DotBotsAuthError('PAYMENT_FAILED', body.error, body.reason);
337
+ }
338
+ if (!response.ok) {
339
+ throw new DotBotsAuthError('NETWORK_ERROR', 'Payment request failed');
340
+ }
341
+ return response.json();
342
+ }
320
343
  async logout() {
321
344
  this.assertInitialized();
322
345
  await this.tokenManager.revoke();
@@ -407,6 +430,6 @@ class DotBotsAuth {
407
430
  }
408
431
  }
409
432
  }
410
- DotBotsAuth.SDK_VERSION = '1.0.7';
433
+ DotBotsAuth.SDK_VERSION = '1.0.9';
411
434
 
412
435
  export { DotBotsAuth, DotBotsAuthError };
@@ -11,6 +11,9 @@ export interface DotBotsAuthContextValue {
11
11
  canAny: (permissions: string[]) => boolean;
12
12
  hasRole: (role: string) => boolean;
13
13
  fetch: (url: string, options?: RequestInit) => Promise<Response>;
14
+ charge: (featureCode: string, paidBy: 'org' | 'app', quantity?: number) => Promise<{
15
+ transactionId: string;
16
+ }>;
14
17
  logout: () => Promise<void>;
15
18
  }
16
19
  export declare const DotBotsAuthContext: import("react").Context<DotBotsAuthContextValue | null>;
@@ -47,6 +47,7 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
47
47
  canAny: (permissions) => auth.canAny(permissions),
48
48
  hasRole: (role) => auth.hasRole(role),
49
49
  fetch: (url, options) => auth.fetch(url, options),
50
+ charge: (featureCode, paidBy, quantity) => auth.charge(featureCode, paidBy, quantity),
50
51
  logout: () => auth.logout(),
51
52
  };
52
53
  return (jsx(DotBotsAuthContext.Provider, { value: value, children: children }));
@@ -41,7 +41,7 @@ export interface DotBotsProxyConfig {
41
41
  }
42
42
  export type ProxyFeature = 'cache' | 'localdb' | 'webhooks' | 'ratelimit';
43
43
  export type DotBotsAuthEvent = 'tokenRefreshed' | 'loggedOut' | 'sessionExpired' | 'userLoaded';
44
- export type DotBotsAuthErrorCode = 'IFRAME_TIMEOUT' | 'CODE_EXPIRED' | 'UNAUTHORIZED' | 'REFRESH_FAILED' | 'NETWORK_ERROR' | 'NOT_INITIALIZED' | 'PROXY_UNAVAILABLE';
44
+ export type DotBotsAuthErrorCode = 'IFRAME_TIMEOUT' | 'CODE_EXPIRED' | 'UNAUTHORIZED' | 'REFRESH_FAILED' | 'NETWORK_ERROR' | 'NOT_INITIALIZED' | 'PROXY_UNAVAILABLE' | 'PAYMENT_FAILED';
45
45
  export interface TokenPair {
46
46
  accessToken: string;
47
47
  refreshToken: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotbots-boutique/auth-sdk",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Authentication SDK for DotBots marketplace apps",
5
5
  "license": "MIT",
6
6
  "type": "module",