@endorr/core-sdk 0.1.0 → 0.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/CHANGELOG.md +27 -0
- package/README.md +76 -0
- package/dist/billing-sync.d.ts +74 -0
- package/dist/billing-sync.d.ts.map +1 -0
- package/dist/billing-sync.js +86 -0
- package/dist/billing-sync.js.map +1 -0
- package/dist/client.d.ts +111 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +61 -2
- package/dist/client.js.map +1 -1
- package/dist/entitlements.d.ts +17 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +44 -0
- package/dist/entitlements.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/nextjs.d.ts +16 -1
- package/dist/nextjs.d.ts.map +1 -1
- package/dist/nextjs.js +37 -0
- package/dist/nextjs.js.map +1 -1
- package/dist/oauth.d.ts +6 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +20 -1
- package/dist/oauth.js.map +1 -1
- package/dist/react.d.ts +34 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +29 -1
- package/dist/react.js.map +1 -1
- package/dist/types.d.ts +302 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -25
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @endorr/core-sdk
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-09-11
|
|
4
|
+
|
|
5
|
+
- Billing: `client.billing.{catalogue, plans, entitlements, overview, checkout, payments, subscription, changePlan, cancel, resume, refresh, claimable, claim}` (needs the new `billing` scope; managing needs organisation owner/admin). `checkout` takes a plan (`studio`, `studio_plus`) or the `printreadysheets` add-on.
|
|
6
|
+
- Tokens: `client.billing.{wallet, spend, refund, buyTokens, refreshPayment}`. Denied spends throw `EndorrEntitlementError` (402 `not_entitled` / `insufficient_tokens`) carrying the access check.
|
|
7
|
+
- Access checks: `client.billing.check` (product, feature or action), `canUse`, `require`; offline `hasProduct`, `hasFeature`, `describeDenial`, `assertAllowed`.
|
|
8
|
+
- `EndorrBillingSync`: a product's server-side client — check, spend and refund for a connected user with client credentials, and report subscriptions the product still bills itself.
|
|
9
|
+
- `EndorrBillingSync.payments({ since, limit })`: paid charges of the add-ons that unlock your product, oldest first (for dealer commission and revenue reports once your billing runs through Endorr).
|
|
10
|
+
- `spend` (both clients) takes `product` when your app runs another product's tools (PrintReadySheets hosting FileFixer), so Core checks that product's entitlement.
|
|
11
|
+
- Next.js: `requireEntitlement`, `withEntitlement`, `entitlementErrorResponse`. React: `useEntitlements`, `useAccess`, `useWallet`, `<Entitled>`.
|
|
12
|
+
- `EndorrApiError.isPaymentRequired`; `StorageUsage` gains `permanent_quota_bytes` and `plan`.
|
|
13
|
+
- Types: `Catalogue`, `Plan`, `Addon`, `TokenPack`, `Entitlements`, `AccessCheck`, `Wallet`, `TokenTransaction`, `TokenSpend`, `Subscription`, `BillingOverview`, `BillingPayment`, `CheckoutSession`, `TokenCheckout`, `PlanChange`, `ExternalSubscriptionInput`, `Money`, `ProductKey`, `FeatureKey`, `TokenAction`.
|
|
14
|
+
|
|
15
|
+
## 0.1.1 — 2026-09-10
|
|
16
|
+
|
|
17
|
+
- Fix: token-endpoint failures that carry Core's generic API error envelope (`{ error: { code, message } }`, e.g. rate limits) now produce a readable `EndorrOAuthError` with `error = code` instead of the message `[object Object]`. RFC 6749 `{ error, error_description }` bodies are unchanged.
|
|
18
|
+
- Fix: non-JSON or empty error bodies map to `server_error` with the HTTP status.
|
|
19
|
+
- Added since 0.1.0: `files.trash`, `files.restore`, paginated `files.recent` and `folders.children` (`{ items | folders/files, next_cursor }`), `organizations.audit`, `organizations.delete`, `organizations.resendInvite`, `teams.rename`, `me.requestEmailChange`, `me.deleteAccount`, `oauth.endSessionUrl`.
|
|
20
|
+
|
|
21
|
+
### Breaking versus 0.1.0
|
|
22
|
+
- `files.recent()` returns `{ items, next_cursor }` instead of an array. Use `(await core.files.recent()).items`.
|
|
23
|
+
- `folders.children()` response gains `next_cursor`; existing fields are unchanged.
|
|
24
|
+
|
|
25
|
+
## 0.1.0 — 2026-09-09
|
|
26
|
+
|
|
27
|
+
Initial release: OAuth/PKCE + token endpoints + ID-token verification, typed `/v1` client, browser uploads, Next.js login/callback handlers, React provider and hooks.
|
package/README.md
CHANGED
|
@@ -77,3 +77,79 @@ await sessions.update({ accessToken: next.access_token, refreshToken: next.refre
|
|
|
77
77
|
|
|
78
78
|
A `401` from the API or `invalid_grant` on refresh means the user disconnected your product in
|
|
79
79
|
Endorr or reuse detection fired: drop your session and send them through login again.
|
|
80
|
+
|
|
81
|
+
## Billing
|
|
82
|
+
|
|
83
|
+
Request the `billing` scope. Any member can read entitlements and check access; owners and admins manage subscriptions.
|
|
84
|
+
Every organisation is on Studio Free unless it pays for Studio or Studio+; PrintReadySheets is an add-on (included in Studio+).
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { EndorrEntitlementError, hasProduct, hasFeature } from '@endorr/core-sdk';
|
|
88
|
+
|
|
89
|
+
const ent = await core.billing.entitlements(); // token's organisation
|
|
90
|
+
ent.plan.key; // 'free' | 'studio' | 'studio_plus'
|
|
91
|
+
hasProduct(ent, 'printreadysheets'); hasFeature(ent, 'batch_workflows'); // offline, for rendering
|
|
92
|
+
|
|
93
|
+
// Live check: a denial is an answer, with what fixes it.
|
|
94
|
+
const access = await core.billing.check({ product: 'printreadysheets' }); // or { feature } or { action, quantity }
|
|
95
|
+
if (!access.allowed) showUpgrade(access.addon ?? access.upgrade); // reason: not_in_plan | addon_required | insufficient_tokens
|
|
96
|
+
await core.billing.canUse('pixlpilot'); // boolean
|
|
97
|
+
await core.billing.require({ feature: 'permissions' }); // throws EndorrEntitlementError (402)
|
|
98
|
+
|
|
99
|
+
const { checkout_url, subscription } = await core.billing.checkout(orgId, { plan: 'studio', redirect_url: 'https://endorr.com/studio/settings/billing' });
|
|
100
|
+
// …redirect to checkout_url; on return:
|
|
101
|
+
await core.billing.refresh(subscription.id);
|
|
102
|
+
await core.billing.checkout(orgId, { plan: 'printreadysheets' }); // the add-on
|
|
103
|
+
await core.billing.buyTokens(orgId, { pack: 'tokens_250' });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Tokens
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// Pay before doing the work (the wallet is locked, so jobs never overdraw it); refund if the work fails.
|
|
110
|
+
const { transaction } = await core.billing.spend({ action: 'upscale', idempotency_key: job.id });
|
|
111
|
+
try { await runUpscale(job); } catch (e) { await core.billing.refund(transaction.id); throw e; }
|
|
112
|
+
|
|
113
|
+
try { await core.billing.spend({ action: 'repair' }); }
|
|
114
|
+
catch (e) { if (e instanceof EndorrEntitlementError) return showTopUp(e.check.top_up, e.message); throw e; }
|
|
115
|
+
|
|
116
|
+
await core.billing.wallet(); // { balance, free_balance, paid_balance, free_resets_at, transactions }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Core owns the prices (`upscale` 2, `repair` 1, `vectorize` 1; see `core.billing.catalogue()`). Only Endorr apps spend,
|
|
120
|
+
and only for organisations that may use them. From a back end without a user token, use the same calls on
|
|
121
|
+
`EndorrBillingSync` with the user's Endorr id: `sync.canUse(userId, 'filefixer')`, `sync.spend(userId, { action })`,
|
|
122
|
+
`sync.refund(userId, transactionId)`.
|
|
123
|
+
|
|
124
|
+
### Subscriptions your product still bills
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { EndorrBillingSync } from '@endorr/core-sdk';
|
|
128
|
+
const sync = new EndorrBillingSync({ baseUrl: 'https://auth.endorr.com', clientId: 'printreadysheets', clientSecret: process.env.ENDORR_CLIENT_SECRET! });
|
|
129
|
+
|
|
130
|
+
await sync.upsert(local.id, {
|
|
131
|
+
user_id: local.endorrUserId ?? null, // Endorr `sub` if the customer connected Endorr…
|
|
132
|
+
customer_email: local.endorrUserId ? null : user.email, // …otherwise their email; they claim it in Endorr
|
|
133
|
+
plan_key: 'prs_shopify', plan_name: 'PrintReadySheets via Shopify', status: 'active',
|
|
134
|
+
provider: 'shopify', amount: { value: '79.00', currency: 'USD' }, current_period_end: local.currentPeriodEnd.toISOString(),
|
|
135
|
+
manage_url: 'https://printreadysheets.com/en/dashboard/billing',
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Send the full state after every change (paid, renewed, failed, cancelled). Core never charges or cancels these.
|
|
140
|
+
|
|
141
|
+
### Next.js and React
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { withEntitlement, requireEntitlement, entitlementErrorResponse } from '@endorr/core-sdk/nextjs';
|
|
145
|
+
export const POST = withEntitlement((req) => coreFor(req), { product: 'printreadysheets' }, async (req, access) => Response.json({ ok: true }));
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { Entitled, useAccess, useEntitlements, useWallet } from '@endorr/core-sdk/react';
|
|
150
|
+
<Entitled product="printreadysheets" fallback={(c) => <UpgradeCard addon={c.addon} plan={c.upgrade} />}>
|
|
151
|
+
<SheetBuilder />
|
|
152
|
+
</Entitled>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`<Entitled>` only hides UI; the server-side check or `spend` is what enforces.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { AccessCheck, Entitlements, ExternalSubscriptionInput, ExternalSubscriptionResult, FeatureKey, ProductKey, ProductPaymentsPage, TokenAction, TokenSpend, Wallet } from './types';
|
|
2
|
+
export interface BillingSyncConfig {
|
|
3
|
+
/** Core origin, e.g. https://auth.endorr.com */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/** Your OAuth client id, e.g. "printreadysheets". */
|
|
6
|
+
clientId: string;
|
|
7
|
+
clientSecret: string;
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
}
|
|
10
|
+
type Check = {
|
|
11
|
+
product?: ProductKey;
|
|
12
|
+
feature?: FeatureKey;
|
|
13
|
+
action?: TokenAction;
|
|
14
|
+
quantity?: number;
|
|
15
|
+
organizationId?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* An app's server-side billing client. Authenticates as your OAuth client (never as a user), so keep
|
|
19
|
+
* it on the server. Every call about a person takes their Endorr user id (`sub`) and is accepted only
|
|
20
|
+
* for people who connected your app.
|
|
21
|
+
*
|
|
22
|
+
* 1. Check access and pay for work in tokens from a back-end job with no user token at hand:
|
|
23
|
+
*
|
|
24
|
+
* if (!(await sync.canUse(userId, 'printreadysheets'))) …
|
|
25
|
+
* const { transaction } = await sync.spend(userId, { action: 'upscale', idempotency_key: job.id });
|
|
26
|
+
*
|
|
27
|
+
* 2. Report subscriptions you still bill yourself (Shopify merchants, for example), so they appear in
|
|
28
|
+
* the customer's Endorr account. Needs a client enabled for billing sync. Send the complete current
|
|
29
|
+
* state whenever it changes; omitted fields are cleared, except the organisation link, which sticks.
|
|
30
|
+
*/
|
|
31
|
+
export declare class EndorrBillingSync {
|
|
32
|
+
private readonly base;
|
|
33
|
+
private readonly f;
|
|
34
|
+
private readonly authorization;
|
|
35
|
+
constructor(cfg: BillingSyncConfig);
|
|
36
|
+
entitlements(userId: string, opts?: {
|
|
37
|
+
organizationId?: string;
|
|
38
|
+
}): Promise<Entitlements>;
|
|
39
|
+
check(userId: string, q: Check): Promise<AccessCheck>;
|
|
40
|
+
canUse(userId: string, product: ProductKey, q?: Omit<Check, 'product'>): Promise<boolean>;
|
|
41
|
+
/** Throws EndorrEntitlementError (402) when denied. */
|
|
42
|
+
require(userId: string, q: Check): Promise<AccessCheck>;
|
|
43
|
+
wallet(userId: string, opts?: {
|
|
44
|
+
organizationId?: string;
|
|
45
|
+
}): Promise<Wallet>;
|
|
46
|
+
/**
|
|
47
|
+
* Pay for work in tokens; throws EndorrEntitlementError (402 not_entitled / insufficient_tokens) when
|
|
48
|
+
* it may not. `product` names the product the work belongs to when your app runs another product's
|
|
49
|
+
* tools (PrintReadySheets hosts FileFixer): Core then checks that product instead of your own.
|
|
50
|
+
*/
|
|
51
|
+
spend(userId: string, input: {
|
|
52
|
+
action: TokenAction;
|
|
53
|
+
quantity?: number;
|
|
54
|
+
idempotency_key?: string;
|
|
55
|
+
organization_id?: string;
|
|
56
|
+
product?: ProductKey;
|
|
57
|
+
}): Promise<TokenSpend>;
|
|
58
|
+
refund(userId: string, transactionId: string): Promise<TokenSpend>;
|
|
59
|
+
/**
|
|
60
|
+
* Paid charges of the add-ons that unlock your product, oldest first — for your own books once your
|
|
61
|
+
* billing runs through Endorr (dealer commission, revenue reports). Pass `next_since` back as
|
|
62
|
+
* `since` to page; replays are safe to skip by `id`.
|
|
63
|
+
*/
|
|
64
|
+
payments(opts?: {
|
|
65
|
+
since?: string | Date;
|
|
66
|
+
limit?: number;
|
|
67
|
+
}): Promise<ProductPaymentsPage>;
|
|
68
|
+
/** Create or replace the subscription Core holds under your id. */
|
|
69
|
+
upsert(externalId: string, input: ExternalSubscriptionInput): Promise<ExternalSubscriptionResult>;
|
|
70
|
+
get(externalId: string): Promise<ExternalSubscriptionResult>;
|
|
71
|
+
private request;
|
|
72
|
+
}
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=billing-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-sync.d.ts","sourceRoot":"","sources":["../src/billing-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAAE,YAAY,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,UAAU,EAAE,UAAU,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAC/J,MAAM,SAAS,CAAC;AAIjB,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAGD,KAAK,KAAK,GAAG;IAAE,OAAO,CAAC,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9H;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,GAAG,EAAE,iBAAiB;IAMlC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,YAAY,CAAC;IAK3F,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC;IAM/C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,GAAE,IAAI,CAAC,KAAK,EAAE,SAAS,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAInG,uDAAuD;IACjD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC;IAI7D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAI/E;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,UAAU,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAIvK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAIlE;;;;OAIG;IACH,QAAQ,CAAC,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAK5F,mEAAmE;IACnE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAIjG,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC;YAI9C,OAAO;CAStB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { throwForResponse } from './errors';
|
|
2
|
+
import { assertAllowed } from './entitlements';
|
|
3
|
+
/**
|
|
4
|
+
* An app's server-side billing client. Authenticates as your OAuth client (never as a user), so keep
|
|
5
|
+
* it on the server. Every call about a person takes their Endorr user id (`sub`) and is accepted only
|
|
6
|
+
* for people who connected your app.
|
|
7
|
+
*
|
|
8
|
+
* 1. Check access and pay for work in tokens from a back-end job with no user token at hand:
|
|
9
|
+
*
|
|
10
|
+
* if (!(await sync.canUse(userId, 'printreadysheets'))) …
|
|
11
|
+
* const { transaction } = await sync.spend(userId, { action: 'upscale', idempotency_key: job.id });
|
|
12
|
+
*
|
|
13
|
+
* 2. Report subscriptions you still bill yourself (Shopify merchants, for example), so they appear in
|
|
14
|
+
* the customer's Endorr account. Needs a client enabled for billing sync. Send the complete current
|
|
15
|
+
* state whenever it changes; omitted fields are cleared, except the organisation link, which sticks.
|
|
16
|
+
*/
|
|
17
|
+
export class EndorrBillingSync {
|
|
18
|
+
base;
|
|
19
|
+
f;
|
|
20
|
+
authorization;
|
|
21
|
+
constructor(cfg) {
|
|
22
|
+
this.base = cfg.baseUrl.replace(/\/$/, '');
|
|
23
|
+
this.f = cfg.fetch ?? ((input, init) => fetch(input, init));
|
|
24
|
+
this.authorization = 'Basic ' + btoa(`${encodeURIComponent(cfg.clientId)}:${encodeURIComponent(cfg.clientSecret)}`);
|
|
25
|
+
}
|
|
26
|
+
entitlements(userId, opts = {}) {
|
|
27
|
+
return this.request('GET', '/v1/billing/entitlements', { user_id: userId, organization_id: opts.organizationId })
|
|
28
|
+
.then((r) => r.entitlements);
|
|
29
|
+
}
|
|
30
|
+
check(userId, q) {
|
|
31
|
+
return this.request('GET', '/v1/billing/entitlements/check', {
|
|
32
|
+
user_id: userId, product: q.product, feature: q.feature, action: q.action, quantity: q.quantity, organization_id: q.organizationId,
|
|
33
|
+
}).then((r) => r.access);
|
|
34
|
+
}
|
|
35
|
+
async canUse(userId, product, q = {}) {
|
|
36
|
+
return (await this.check(userId, { ...q, product })).allowed;
|
|
37
|
+
}
|
|
38
|
+
/** Throws EndorrEntitlementError (402) when denied. */
|
|
39
|
+
async require(userId, q) {
|
|
40
|
+
return assertAllowed(await this.check(userId, q));
|
|
41
|
+
}
|
|
42
|
+
wallet(userId, opts = {}) {
|
|
43
|
+
return this.request('GET', '/v1/billing/tokens', { user_id: userId, organization_id: opts.organizationId }).then((r) => r.wallet);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Pay for work in tokens; throws EndorrEntitlementError (402 not_entitled / insufficient_tokens) when
|
|
47
|
+
* it may not. `product` names the product the work belongs to when your app runs another product's
|
|
48
|
+
* tools (PrintReadySheets hosts FileFixer): Core then checks that product instead of your own.
|
|
49
|
+
*/
|
|
50
|
+
spend(userId, input) {
|
|
51
|
+
return this.request('POST', '/v1/billing/tokens/spend', {}, { ...input, user_id: userId });
|
|
52
|
+
}
|
|
53
|
+
refund(userId, transactionId) {
|
|
54
|
+
return this.request('DELETE', `/v1/billing/tokens/spend/${encodeURIComponent(transactionId)}`, { user_id: userId });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Paid charges of the add-ons that unlock your product, oldest first — for your own books once your
|
|
58
|
+
* billing runs through Endorr (dealer commission, revenue reports). Pass `next_since` back as
|
|
59
|
+
* `since` to page; replays are safe to skip by `id`.
|
|
60
|
+
*/
|
|
61
|
+
payments(opts = {}) {
|
|
62
|
+
const since = opts.since instanceof Date ? opts.since.toISOString() : opts.since;
|
|
63
|
+
return this.request('GET', '/v1/billing/product-payments', { since, limit: opts.limit });
|
|
64
|
+
}
|
|
65
|
+
/** Create or replace the subscription Core holds under your id. */
|
|
66
|
+
upsert(externalId, input) {
|
|
67
|
+
return this.request('PUT', `/v1/billing/external-subscriptions/${encodeURIComponent(externalId)}`, {}, input);
|
|
68
|
+
}
|
|
69
|
+
get(externalId) {
|
|
70
|
+
return this.request('GET', `/v1/billing/external-subscriptions/${encodeURIComponent(externalId)}`);
|
|
71
|
+
}
|
|
72
|
+
async request(method, path, query = {}, body) {
|
|
73
|
+
const url = new URL(this.base + path);
|
|
74
|
+
for (const [k, v] of Object.entries(query))
|
|
75
|
+
if (v !== undefined && v !== null)
|
|
76
|
+
url.searchParams.set(k, String(v));
|
|
77
|
+
const headers = { accept: 'application/json', authorization: this.authorization };
|
|
78
|
+
if (body !== undefined)
|
|
79
|
+
headers['content-type'] = 'application/json';
|
|
80
|
+
const res = await this.f(url.toString(), { method, headers, body: body !== undefined ? JSON.stringify(body) : undefined });
|
|
81
|
+
if (!res.ok)
|
|
82
|
+
await throwForResponse(res);
|
|
83
|
+
return (await res.json());
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=billing-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-sync.js","sourceRoot":"","sources":["../src/billing-sync.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import type { Me, EndorrUser, OrganizationSummary, SessionInfo, ConnectedApp, Member, Invite, Team, EndorrFile, EndorrFolder, UploadTicket, DownloadTicket, Grant, ShareLink, StorageUsage, OrgRole, ResourceRole, PrincipalType, StorageClass, ResourceType } from './types';
|
|
1
|
+
import type { Me, EndorrUser, OrganizationSummary, SessionInfo, ConnectedApp, Member, Invite, Team, EndorrFile, EndorrFolder, UploadTicket, DownloadTicket, Grant, ShareLink, StorageUsage, OrgRole, ResourceRole, PrincipalType, StorageClass, ResourceType, AuditEvent, Page, Plan, PlanKey, Entitlements, BillingOverview, BillingPayment, CheckoutSession, PlanChange, Subscription, AccessCheck, AddonKey, Catalogue, FeatureKey, ProductKey, TokenAction, TokenCheckout, TokenSpend, Wallet } from './types';
|
|
2
|
+
/** What to check: a product, a plan feature, or whether the wallet affords `quantity` × `action`. */
|
|
3
|
+
export interface AccessQuery {
|
|
4
|
+
product?: ProductKey;
|
|
5
|
+
feature?: FeatureKey;
|
|
6
|
+
action?: TokenAction;
|
|
7
|
+
/** Units of `action`; default 1. */
|
|
8
|
+
quantity?: number;
|
|
9
|
+
/** Defaults to the token's organisation. */
|
|
10
|
+
organizationId?: string;
|
|
11
|
+
}
|
|
2
12
|
export interface ClientConfig {
|
|
3
13
|
/** Core origin, e.g. https://auth.endorr.com */
|
|
4
14
|
baseUrl: string;
|
|
@@ -42,6 +52,20 @@ export declare class EndorrClient {
|
|
|
42
52
|
resendVerification: () => Promise<{
|
|
43
53
|
ok: true;
|
|
44
54
|
}>;
|
|
55
|
+
/** Starts an email change; the new address must open the verification link. */
|
|
56
|
+
requestEmailChange: (input: {
|
|
57
|
+
email: string;
|
|
58
|
+
current_password: string;
|
|
59
|
+
}) => Promise<{
|
|
60
|
+
ok: true;
|
|
61
|
+
pending_email: string;
|
|
62
|
+
}>;
|
|
63
|
+
/** Deletes the account (password confirmation). Refused while sole owner of a shared organisation. */
|
|
64
|
+
deleteAccount: (input: {
|
|
65
|
+
current_password: string;
|
|
66
|
+
}) => Promise<{
|
|
67
|
+
ok: true;
|
|
68
|
+
}>;
|
|
45
69
|
organizations: () => Promise<OrganizationSummary[]>;
|
|
46
70
|
createOrganization: (input: {
|
|
47
71
|
name: string;
|
|
@@ -71,6 +95,16 @@ export declare class EndorrClient {
|
|
|
71
95
|
update: (id: string, input: {
|
|
72
96
|
name?: string;
|
|
73
97
|
}) => Promise<OrganizationSummary>;
|
|
98
|
+
/** Owner only. Soft-deletes the organisation and its files. */
|
|
99
|
+
delete: (id: string) => Promise<{
|
|
100
|
+
ok: true;
|
|
101
|
+
}>;
|
|
102
|
+
/** Admin: privileged-activity log, newest first. */
|
|
103
|
+
audit: (id: string, opts?: {
|
|
104
|
+
limit?: number;
|
|
105
|
+
cursor?: string | null;
|
|
106
|
+
types?: string[];
|
|
107
|
+
}) => Promise<Page<AuditEvent>>;
|
|
74
108
|
members: (id: string) => Promise<Member[]>;
|
|
75
109
|
setMemberRole: (id: string, userId: string, role: OrgRole) => Promise<{
|
|
76
110
|
ok: true;
|
|
@@ -86,12 +120,19 @@ export declare class EndorrClient {
|
|
|
86
120
|
revokeInvite: (id: string, inviteId: string) => Promise<{
|
|
87
121
|
ok: true;
|
|
88
122
|
}>;
|
|
123
|
+
resendInvite: (id: string, inviteId: string) => Promise<{
|
|
124
|
+
ok: true;
|
|
125
|
+
}>;
|
|
89
126
|
teams: (id: string) => Promise<Team[]>;
|
|
90
127
|
createTeam: (id: string, input: {
|
|
91
128
|
name: string;
|
|
92
129
|
}) => Promise<Team>;
|
|
93
130
|
};
|
|
94
131
|
teams: {
|
|
132
|
+
rename: (teamId: string, name: string) => Promise<{
|
|
133
|
+
id: string;
|
|
134
|
+
name: string;
|
|
135
|
+
}>;
|
|
95
136
|
delete: (teamId: string) => Promise<{
|
|
96
137
|
ok: true;
|
|
97
138
|
}>;
|
|
@@ -126,7 +167,16 @@ export declare class EndorrClient {
|
|
|
126
167
|
delete: (fileId: string) => Promise<{
|
|
127
168
|
ok: true;
|
|
128
169
|
}>;
|
|
129
|
-
recent: (
|
|
170
|
+
recent: (opts?: {
|
|
171
|
+
limit?: number;
|
|
172
|
+
cursor?: string | null;
|
|
173
|
+
}) => Promise<Page<EndorrFile>>;
|
|
174
|
+
/** Soft-deleted files still recoverable. */
|
|
175
|
+
trash: (opts?: {
|
|
176
|
+
limit?: number;
|
|
177
|
+
cursor?: string | null;
|
|
178
|
+
}) => Promise<Page<EndorrFile>>;
|
|
179
|
+
restore: (fileId: string) => Promise<EndorrFile>;
|
|
130
180
|
addRelationship: (sourceFileId: string, input: {
|
|
131
181
|
derived_file_id: string;
|
|
132
182
|
relationship_type: string;
|
|
@@ -145,10 +195,14 @@ export declare class EndorrClient {
|
|
|
145
195
|
}) => Promise<EndorrFolder>;
|
|
146
196
|
get: (folderId: string) => Promise<EndorrFolder>;
|
|
147
197
|
/** `null` lists the organisation root. */
|
|
148
|
-
children: (folderId: string | null
|
|
198
|
+
children: (folderId: string | null, opts?: {
|
|
199
|
+
limit?: number;
|
|
200
|
+
cursor?: string | null;
|
|
201
|
+
}) => Promise<{
|
|
149
202
|
folder_id: string | null;
|
|
150
203
|
folders: EndorrFolder[];
|
|
151
204
|
files: EndorrFile[];
|
|
205
|
+
next_cursor: string | null;
|
|
152
206
|
}>;
|
|
153
207
|
update: (folderId: string, input: {
|
|
154
208
|
name?: string;
|
|
@@ -197,6 +251,60 @@ export declare class EndorrClient {
|
|
|
197
251
|
storage: {
|
|
198
252
|
usage: () => Promise<StorageUsage>;
|
|
199
253
|
};
|
|
254
|
+
billing: {
|
|
255
|
+
/** Public catalogue: plans, add-ons, token packs and what each action costs. */
|
|
256
|
+
catalogue: () => Promise<Catalogue>;
|
|
257
|
+
plans: () => Promise<Plan[]>;
|
|
258
|
+
/** Plan, add-ons, products, features, storage and monthly tokens. Defaults to the token's organisation. */
|
|
259
|
+
entitlements: (organizationId?: string) => Promise<Entitlements>;
|
|
260
|
+
/** Entitlements, subscriptions, wallet and storage in one call, for an account page. */
|
|
261
|
+
overview: (organizationId: string) => Promise<BillingOverview>;
|
|
262
|
+
/** Subscribe to a plan (studio, studio_plus) or add an add-on (printreadysheets). Send the browser to `checkout_url`. */
|
|
263
|
+
checkout: (organizationId: string, input: {
|
|
264
|
+
plan: Exclude<PlanKey, "free"> | AddonKey;
|
|
265
|
+
redirect_url?: string | null;
|
|
266
|
+
}) => Promise<CheckoutSession>;
|
|
267
|
+
payments: (organizationId: string) => Promise<BillingPayment[]>;
|
|
268
|
+
subscription: (id: string) => Promise<Subscription>;
|
|
269
|
+
/** Studio ↔ Studio+. Upgrades apply once the prorated charge is paid; downgrades at the next renewal. */
|
|
270
|
+
changePlan: (id: string, plan: Exclude<PlanKey, "free">) => Promise<PlanChange>;
|
|
271
|
+
cancel: (id: string) => Promise<Subscription>;
|
|
272
|
+
resume: (id: string) => Promise<Subscription>;
|
|
273
|
+
/** Reconcile with Mollie, e.g. on the checkout return page. */
|
|
274
|
+
refresh: (id: string) => Promise<Subscription>;
|
|
275
|
+
/** Product subscriptions reported for the caller's verified email, waiting to be attached. */
|
|
276
|
+
claimable: () => Promise<Subscription[]>;
|
|
277
|
+
claim: (id: string, organizationId: string) => Promise<Subscription>;
|
|
278
|
+
/** Live access check. A denial is an answer, not an error: read `allowed`, `reason`, and `addon` / `upgrade` / `top_up`. */
|
|
279
|
+
check: (q: AccessQuery) => Promise<AccessCheck>;
|
|
280
|
+
/** `true` when the organisation may use the product (and afford `quantity` × `action`, if given). */
|
|
281
|
+
canUse: (product: ProductKey, q?: Omit<AccessQuery, "product">) => Promise<boolean>;
|
|
282
|
+
/** Like `check`, but throws EndorrEntitlementError (402) when denied. Use it to gate a handler. */
|
|
283
|
+
require: (q: AccessQuery) => Promise<AccessCheck>;
|
|
284
|
+
/** The token wallet: balances and the latest movements. */
|
|
285
|
+
wallet: (organizationId?: string) => Promise<Wallet>;
|
|
286
|
+
/**
|
|
287
|
+
* Pay for metered work in tokens (Endorr apps only). Spend before the work and `refund` if it fails;
|
|
288
|
+
* an idempotency key makes retries count once. Throws EndorrEntitlementError (402 not_entitled /
|
|
289
|
+
* insufficient_tokens) when it may not. `product` names the product the work belongs to when your
|
|
290
|
+
* app runs another product's tools.
|
|
291
|
+
*/
|
|
292
|
+
spend: (input: {
|
|
293
|
+
action: TokenAction;
|
|
294
|
+
quantity?: number;
|
|
295
|
+
idempotency_key?: string;
|
|
296
|
+
organization_id?: string;
|
|
297
|
+
product?: ProductKey;
|
|
298
|
+
}) => Promise<TokenSpend>;
|
|
299
|
+
refund: (transactionId: string) => Promise<TokenSpend>;
|
|
300
|
+
/** Admin: buy a token pack; the tokens land once Mollie reports it paid. */
|
|
301
|
+
buyTokens: (organizationId: string, input: {
|
|
302
|
+
pack: string;
|
|
303
|
+
redirect_url?: string | null;
|
|
304
|
+
}) => Promise<TokenCheckout>;
|
|
305
|
+
/** Reconcile one payment with Mollie (a token pack's return page). */
|
|
306
|
+
refreshPayment: (paymentId: string) => Promise<BillingPayment>;
|
|
307
|
+
};
|
|
200
308
|
}
|
|
201
309
|
export {};
|
|
202
310
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EAAE,UAAU,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAC9G,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { throwForResponse } from './errors';
|
|
2
|
+
import { assertAllowed } from './entitlements';
|
|
2
3
|
/**
|
|
3
4
|
* Typed client for the Endorr Core /v1 API. Every method throws EndorrApiError with the server's
|
|
4
5
|
* stable `code`, so callers can branch on 403 (needs a role) vs 404 (not yours / gone).
|
|
@@ -39,6 +40,10 @@ export class EndorrClient {
|
|
|
39
40
|
updateProfile: (input) => this.request('PATCH', '/v1/me', { body: input }),
|
|
40
41
|
changePassword: (input) => this.request('POST', '/v1/me/password', { body: input }),
|
|
41
42
|
resendVerification: () => this.request('POST', '/v1/me/verification/resend', { body: {} }),
|
|
43
|
+
/** Starts an email change; the new address must open the verification link. */
|
|
44
|
+
requestEmailChange: (input) => this.request('POST', '/v1/me/email', { body: input }),
|
|
45
|
+
/** Deletes the account (password confirmation). Refused while sole owner of a shared organisation. */
|
|
46
|
+
deleteAccount: (input) => this.request('DELETE', '/v1/me', { body: input }),
|
|
42
47
|
organizations: () => this.request('GET', '/v1/me/organizations').then((r) => r.organizations),
|
|
43
48
|
createOrganization: (input) => this.request('POST', '/v1/me/organizations', { body: input }).then((r) => r.organization),
|
|
44
49
|
sessions: {
|
|
@@ -55,16 +60,22 @@ export class EndorrClient {
|
|
|
55
60
|
organizations = {
|
|
56
61
|
get: (id) => this.request('GET', `/v1/organizations/${enc(id)}`).then((r) => r.organization),
|
|
57
62
|
update: (id, input) => this.request('PATCH', `/v1/organizations/${enc(id)}`, { body: input }).then((r) => r.organization),
|
|
63
|
+
/** Owner only. Soft-deletes the organisation and its files. */
|
|
64
|
+
delete: (id) => this.request('DELETE', `/v1/organizations/${enc(id)}`),
|
|
65
|
+
/** Admin: privileged-activity log, newest first. */
|
|
66
|
+
audit: (id, opts = {}) => this.request('GET', `/v1/organizations/${enc(id)}/audit`, { query: { limit: opts.limit, cursor: opts.cursor, types: opts.types?.join(',') } }).then((r) => ({ items: r.events, next_cursor: r.next_cursor })),
|
|
58
67
|
members: (id) => this.request('GET', `/v1/organizations/${enc(id)}/members`).then((r) => r.members),
|
|
59
68
|
setMemberRole: (id, userId, role) => this.request('PATCH', `/v1/organizations/${enc(id)}/members/${enc(userId)}`, { body: { role } }),
|
|
60
69
|
removeMember: (id, userId) => this.request('DELETE', `/v1/organizations/${enc(id)}/members/${enc(userId)}`),
|
|
61
70
|
invites: (id) => this.request('GET', `/v1/organizations/${enc(id)}/invites`).then((r) => r.invites),
|
|
62
71
|
invite: (id, input) => this.request('POST', `/v1/organizations/${enc(id)}/invites`, { body: input }).then((r) => r.invite),
|
|
63
72
|
revokeInvite: (id, inviteId) => this.request('DELETE', `/v1/organizations/${enc(id)}/invites/${enc(inviteId)}`),
|
|
73
|
+
resendInvite: (id, inviteId) => this.request('POST', `/v1/organizations/${enc(id)}/invites/${enc(inviteId)}/resend`, { body: {} }),
|
|
64
74
|
teams: (id) => this.request('GET', `/v1/organizations/${enc(id)}/teams`).then((r) => r.teams),
|
|
65
75
|
createTeam: (id, input) => this.request('POST', `/v1/organizations/${enc(id)}/teams`, { body: input }).then((r) => r.team),
|
|
66
76
|
};
|
|
67
77
|
teams = {
|
|
78
|
+
rename: (teamId, name) => this.request('PATCH', `/v1/teams/${enc(teamId)}`, { body: { name } }).then((r) => r.team),
|
|
68
79
|
delete: (teamId) => this.request('DELETE', `/v1/teams/${enc(teamId)}`),
|
|
69
80
|
addMember: (teamId, userId) => this.request('PUT', `/v1/teams/${enc(teamId)}/members/${enc(userId)}`, { body: {} }),
|
|
70
81
|
removeMember: (teamId, userId) => this.request('DELETE', `/v1/teams/${enc(teamId)}/members/${enc(userId)}`),
|
|
@@ -78,14 +89,17 @@ export class EndorrClient {
|
|
|
78
89
|
update: (fileId, input) => this.request('PATCH', `/v1/files/${enc(fileId)}`, { body: input }).then((r) => r.file),
|
|
79
90
|
keepPermanently: (fileId) => this.files.update(fileId, { storage_class: 'permanent' }),
|
|
80
91
|
delete: (fileId) => this.request('DELETE', `/v1/files/${enc(fileId)}`),
|
|
81
|
-
recent: (
|
|
92
|
+
recent: (opts = {}) => this.request('GET', '/v1/files/recent', { query: { limit: opts.limit, cursor: opts.cursor } }).then((r) => ({ items: r.files, next_cursor: r.next_cursor })),
|
|
93
|
+
/** Soft-deleted files still recoverable. */
|
|
94
|
+
trash: (opts = {}) => this.request('GET', '/v1/files/trash', { query: { limit: opts.limit, cursor: opts.cursor } }).then((r) => ({ items: r.files, next_cursor: r.next_cursor })),
|
|
95
|
+
restore: (fileId) => this.request('POST', `/v1/files/${enc(fileId)}/restore`, { body: {} }).then((r) => r.file),
|
|
82
96
|
addRelationship: (sourceFileId, input) => this.request('POST', `/v1/files/${enc(sourceFileId)}/relationships`, { body: input }).then((r) => r.relationship),
|
|
83
97
|
};
|
|
84
98
|
folders = {
|
|
85
99
|
create: (input) => this.request('POST', '/v1/folders', { body: input }).then((r) => r.folder),
|
|
86
100
|
get: (folderId) => this.request('GET', `/v1/folders/${enc(folderId)}`).then((r) => r.folder),
|
|
87
101
|
/** `null` lists the organisation root. */
|
|
88
|
-
children: (folderId) => this.request('GET', `/v1/folders/${folderId ? enc(folderId) : 'root'}/children
|
|
102
|
+
children: (folderId, opts = {}) => this.request('GET', `/v1/folders/${folderId ? enc(folderId) : 'root'}/children`, { query: { limit: opts.limit, cursor: opts.cursor } }),
|
|
89
103
|
update: (folderId, input) => this.request('PATCH', `/v1/folders/${enc(folderId)}`, { body: input }).then((r) => r.folder),
|
|
90
104
|
delete: (folderId) => this.request('DELETE', `/v1/folders/${enc(folderId)}`),
|
|
91
105
|
};
|
|
@@ -103,6 +117,51 @@ export class EndorrClient {
|
|
|
103
117
|
storage = {
|
|
104
118
|
usage: () => this.request('GET', '/v1/storage/usage'),
|
|
105
119
|
};
|
|
120
|
+
/* ── Billing (billing scope; managing needs organisation owner/admin) ───────────── */
|
|
121
|
+
billing = {
|
|
122
|
+
/** Public catalogue: plans, add-ons, token packs and what each action costs. */
|
|
123
|
+
catalogue: () => this.request('GET', '/v1/billing/plans'),
|
|
124
|
+
plans: () => this.billing.catalogue().then((c) => c.plans),
|
|
125
|
+
/** Plan, add-ons, products, features, storage and monthly tokens. Defaults to the token's organisation. */
|
|
126
|
+
entitlements: (organizationId) => this.request('GET', '/v1/billing/entitlements', { query: { organization_id: organizationId } }).then((r) => r.entitlements),
|
|
127
|
+
/** Entitlements, subscriptions, wallet and storage in one call, for an account page. */
|
|
128
|
+
overview: (organizationId) => this.request('GET', `/v1/organizations/${enc(organizationId)}/billing`),
|
|
129
|
+
/** Subscribe to a plan (studio, studio_plus) or add an add-on (printreadysheets). Send the browser to `checkout_url`. */
|
|
130
|
+
checkout: (organizationId, input) => this.request('POST', `/v1/organizations/${enc(organizationId)}/billing/checkout`, { body: input }),
|
|
131
|
+
payments: (organizationId) => this.request('GET', `/v1/organizations/${enc(organizationId)}/billing/payments`).then((r) => r.payments),
|
|
132
|
+
subscription: (id) => this.request('GET', `/v1/billing/subscriptions/${enc(id)}`).then((r) => r.subscription),
|
|
133
|
+
/** Studio ↔ Studio+. Upgrades apply once the prorated charge is paid; downgrades at the next renewal. */
|
|
134
|
+
changePlan: (id, plan) => this.request('POST', `/v1/billing/subscriptions/${enc(id)}/change-plan`, { body: { plan } }),
|
|
135
|
+
cancel: (id) => this.request('POST', `/v1/billing/subscriptions/${enc(id)}/cancel`, { body: {} }).then((r) => r.subscription),
|
|
136
|
+
resume: (id) => this.request('POST', `/v1/billing/subscriptions/${enc(id)}/resume`, { body: {} }).then((r) => r.subscription),
|
|
137
|
+
/** Reconcile with Mollie, e.g. on the checkout return page. */
|
|
138
|
+
refresh: (id) => this.request('POST', `/v1/billing/subscriptions/${enc(id)}/refresh`, { body: {} }).then((r) => r.subscription),
|
|
139
|
+
/** Product subscriptions reported for the caller's verified email, waiting to be attached. */
|
|
140
|
+
claimable: () => this.request('GET', '/v1/billing/claimable').then((r) => r.subscriptions),
|
|
141
|
+
claim: (id, organizationId) => this.request('POST', `/v1/billing/subscriptions/${enc(id)}/claim`, { body: { organization_id: organizationId } }).then((r) => r.subscription),
|
|
142
|
+
/** Live access check. A denial is an answer, not an error: read `allowed`, `reason`, and `addon` / `upgrade` / `top_up`. */
|
|
143
|
+
check: (q) => this.request('GET', '/v1/billing/entitlements/check', {
|
|
144
|
+
query: { product: q.product, feature: q.feature, action: q.action, quantity: q.quantity, organization_id: q.organizationId },
|
|
145
|
+
}).then((r) => r.access),
|
|
146
|
+
/** `true` when the organisation may use the product (and afford `quantity` × `action`, if given). */
|
|
147
|
+
canUse: async (product, q = {}) => (await this.billing.check({ ...q, product })).allowed,
|
|
148
|
+
/** Like `check`, but throws EndorrEntitlementError (402) when denied. Use it to gate a handler. */
|
|
149
|
+
require: async (q) => assertAllowed(await this.billing.check(q)),
|
|
150
|
+
/** The token wallet: balances and the latest movements. */
|
|
151
|
+
wallet: (organizationId) => this.request('GET', '/v1/billing/tokens', { query: { organization_id: organizationId } }).then((r) => r.wallet),
|
|
152
|
+
/**
|
|
153
|
+
* Pay for metered work in tokens (Endorr apps only). Spend before the work and `refund` if it fails;
|
|
154
|
+
* an idempotency key makes retries count once. Throws EndorrEntitlementError (402 not_entitled /
|
|
155
|
+
* insufficient_tokens) when it may not. `product` names the product the work belongs to when your
|
|
156
|
+
* app runs another product's tools.
|
|
157
|
+
*/
|
|
158
|
+
spend: (input) => this.request('POST', '/v1/billing/tokens/spend', { body: input }),
|
|
159
|
+
refund: (transactionId) => this.request('DELETE', `/v1/billing/tokens/spend/${enc(transactionId)}`),
|
|
160
|
+
/** Admin: buy a token pack; the tokens land once Mollie reports it paid. */
|
|
161
|
+
buyTokens: (organizationId, input) => this.request('POST', `/v1/organizations/${enc(organizationId)}/billing/tokens/checkout`, { body: input }),
|
|
162
|
+
/** Reconcile one payment with Mollie (a token pack's return page). */
|
|
163
|
+
refreshPayment: (paymentId) => this.request('POST', `/v1/billing/payments/${enc(paymentId)}/refresh`, { body: {} }).then((r) => r.payment),
|
|
164
|
+
};
|
|
106
165
|
}
|
|
107
166
|
function enc(s) { return encodeURIComponent(s); }
|
|
108
167
|
//# sourceMappingURL=client.js.map
|