@ophelio/sdk 0.1.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 +158 -0
- package/dist/index.cjs +1116 -0
- package/dist/index.d.cts +4626 -0
- package/dist/index.d.mts +4626 -0
- package/dist/index.mjs +1098 -0
- package/package.json +42 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1098 @@
|
|
|
1
|
+
//#region src/core/errors.ts
|
|
2
|
+
var OphelioError = class extends Error {
|
|
3
|
+
/** HTTP status code, or null for non-HTTP failures (network, timeout). */
|
|
4
|
+
status;
|
|
5
|
+
/** AIP status string (e.g. 'NOT_FOUND'), or null when unavailable. */
|
|
6
|
+
code;
|
|
7
|
+
/** Raw `error.details` array from the response, when present. */
|
|
8
|
+
details;
|
|
9
|
+
/** The raw parsed response body, when present. */
|
|
10
|
+
raw;
|
|
11
|
+
constructor(message, options = {}) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = this.constructor.name;
|
|
14
|
+
this.status = options.status ?? null;
|
|
15
|
+
this.code = options.code ?? null;
|
|
16
|
+
this.details = options.details;
|
|
17
|
+
this.raw = options.raw;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var InvalidArgumentError = class extends OphelioError {};
|
|
21
|
+
var UnauthenticatedError = class extends OphelioError {};
|
|
22
|
+
var PermissionDeniedError = class extends OphelioError {};
|
|
23
|
+
var NotFoundError = class extends OphelioError {};
|
|
24
|
+
var AlreadyExistsError = class extends OphelioError {};
|
|
25
|
+
var ResourceExhaustedError = class extends OphelioError {};
|
|
26
|
+
var InternalError = class extends OphelioError {};
|
|
27
|
+
var UnimplementedError = class extends OphelioError {};
|
|
28
|
+
var UnavailableError = class extends OphelioError {};
|
|
29
|
+
/** Request never reached the API or the response never arrived. */
|
|
30
|
+
var OphelioConnectionError = class extends OphelioError {};
|
|
31
|
+
/** The configured timeoutMs elapsed before the API responded. */
|
|
32
|
+
var OphelioTimeoutError = class extends OphelioError {};
|
|
33
|
+
/** X-Ophelio-Signature did not match the webhook payload. */
|
|
34
|
+
var WebhookSignatureVerificationError = class extends OphelioError {};
|
|
35
|
+
const AIP_CODE_TO_ERROR = {
|
|
36
|
+
INVALID_ARGUMENT: InvalidArgumentError,
|
|
37
|
+
UNAUTHENTICATED: UnauthenticatedError,
|
|
38
|
+
PERMISSION_DENIED: PermissionDeniedError,
|
|
39
|
+
NOT_FOUND: NotFoundError,
|
|
40
|
+
ALREADY_EXISTS: AlreadyExistsError,
|
|
41
|
+
RESOURCE_EXHAUSTED: ResourceExhaustedError,
|
|
42
|
+
INTERNAL: InternalError,
|
|
43
|
+
UNIMPLEMENTED: UnimplementedError,
|
|
44
|
+
UNAVAILABLE: UnavailableError
|
|
45
|
+
};
|
|
46
|
+
const HTTP_STATUS_TO_AIP = {
|
|
47
|
+
400: "INVALID_ARGUMENT",
|
|
48
|
+
401: "UNAUTHENTICATED",
|
|
49
|
+
403: "PERMISSION_DENIED",
|
|
50
|
+
404: "NOT_FOUND",
|
|
51
|
+
409: "ALREADY_EXISTS",
|
|
52
|
+
410: "NOT_FOUND",
|
|
53
|
+
422: "INVALID_ARGUMENT",
|
|
54
|
+
429: "RESOURCE_EXHAUSTED",
|
|
55
|
+
500: "INTERNAL",
|
|
56
|
+
501: "UNIMPLEMENTED",
|
|
57
|
+
503: "UNAVAILABLE"
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Build the most specific OphelioError for a non-2xx response. Prefers the
|
|
61
|
+
* AIP status string from the body; falls back to the HTTP status code when
|
|
62
|
+
* the body isn't a valid envelope (e.g. a proxy error page).
|
|
63
|
+
*/
|
|
64
|
+
function errorFromResponse(httpStatus, bodyText) {
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = JSON.parse(bodyText);
|
|
68
|
+
} catch {
|
|
69
|
+
parsed = void 0;
|
|
70
|
+
}
|
|
71
|
+
const envelope = parsed?.error;
|
|
72
|
+
const aipCode = typeof envelope?.status === "string" ? envelope.status : HTTP_STATUS_TO_AIP[httpStatus];
|
|
73
|
+
const message = typeof envelope?.message === "string" && envelope.message.length > 0 ? envelope.message : `Request failed with status ${httpStatus}.`;
|
|
74
|
+
return new (aipCode && AIP_CODE_TO_ERROR[aipCode] || OphelioError)(message, {
|
|
75
|
+
status: httpStatus,
|
|
76
|
+
code: aipCode ?? null,
|
|
77
|
+
details: Array.isArray(envelope?.details) ? envelope.details : void 0,
|
|
78
|
+
raw: parsed
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/core/retry.ts
|
|
83
|
+
const BASE_DELAY_MS = 500;
|
|
84
|
+
const MAX_BACKOFF_MS = 8e3;
|
|
85
|
+
const MAX_RETRY_AFTER_MS = 3e4;
|
|
86
|
+
const RETRYABLE_STATUSES = new Set([
|
|
87
|
+
429,
|
|
88
|
+
502,
|
|
89
|
+
503,
|
|
90
|
+
504
|
|
91
|
+
]);
|
|
92
|
+
function isRetryableStatus(status) {
|
|
93
|
+
return RETRYABLE_STATUSES.has(status);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Delay before retry attempt `attempt` (0-based). Exponential backoff with
|
|
97
|
+
* full jitter, unless the server sent a usable Retry-After (seconds or
|
|
98
|
+
* HTTP-date), which wins but is capped so a hostile header can't park the
|
|
99
|
+
* caller for minutes.
|
|
100
|
+
*/
|
|
101
|
+
function backoffDelayMs(attempt, retryAfter) {
|
|
102
|
+
if (retryAfter) {
|
|
103
|
+
const seconds = Number(retryAfter);
|
|
104
|
+
if (Number.isFinite(seconds) && seconds >= 0) return Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS);
|
|
105
|
+
const dateMs = Date.parse(retryAfter);
|
|
106
|
+
if (!Number.isNaN(dateMs)) return Math.min(Math.max(dateMs - Date.now(), 0), MAX_RETRY_AFTER_MS);
|
|
107
|
+
}
|
|
108
|
+
const cap = Math.min(MAX_BACKOFF_MS, BASE_DELAY_MS * 2 ** attempt);
|
|
109
|
+
return Math.random() * cap;
|
|
110
|
+
}
|
|
111
|
+
function sleep(ms) {
|
|
112
|
+
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/core/transport.ts
|
|
116
|
+
const DEFAULT_BASE_URL = "https://api.ophel.io";
|
|
117
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
118
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
119
|
+
var Transport = class {
|
|
120
|
+
apiKey;
|
|
121
|
+
baseUrl;
|
|
122
|
+
fetchFn;
|
|
123
|
+
headers;
|
|
124
|
+
timeoutMs;
|
|
125
|
+
maxRetries;
|
|
126
|
+
constructor(options) {
|
|
127
|
+
if (!options.apiKey && !options.headers) throw new OphelioError("Ophelio requires an apiKey (or custom headers carrying your own authentication).");
|
|
128
|
+
this.apiKey = options.apiKey;
|
|
129
|
+
this.baseUrl = (options.baseUrl ?? "https://api.ophel.io").replace(/\/+$/, "");
|
|
130
|
+
this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
131
|
+
this.headers = options.headers ?? {};
|
|
132
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
133
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
134
|
+
}
|
|
135
|
+
async request(params) {
|
|
136
|
+
const url = this.buildUrl(params);
|
|
137
|
+
const headers = this.buildHeaders(params);
|
|
138
|
+
const maxAttempts = params.retryable ? this.maxRetries + 1 : 1;
|
|
139
|
+
const init = {
|
|
140
|
+
method: params.method,
|
|
141
|
+
headers,
|
|
142
|
+
...params.body !== void 0 ? { body: JSON.stringify(params.body) } : {}
|
|
143
|
+
};
|
|
144
|
+
let lastError = new OphelioError("Request failed.");
|
|
145
|
+
let retryAfter = null;
|
|
146
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
147
|
+
if (attempt > 0) await sleep(backoffDelayMs(attempt - 1, retryAfter));
|
|
148
|
+
let response;
|
|
149
|
+
try {
|
|
150
|
+
response = await this.fetchFn(url, {
|
|
151
|
+
...init,
|
|
152
|
+
signal: this.combineSignals(params.options?.signal)
|
|
153
|
+
});
|
|
154
|
+
} catch (error) {
|
|
155
|
+
lastError = this.normaliseFetchError(error, params.options?.signal);
|
|
156
|
+
if (params.options?.signal?.aborted) throw lastError;
|
|
157
|
+
if (attempt < maxAttempts - 1) continue;
|
|
158
|
+
throw lastError;
|
|
159
|
+
}
|
|
160
|
+
if (response.ok) return this.parseBody(response);
|
|
161
|
+
retryAfter = response.headers.get("retry-after");
|
|
162
|
+
const bodyText = await response.text().catch(() => "");
|
|
163
|
+
const error = errorFromResponse(response.status, bodyText);
|
|
164
|
+
if (params.retryable && isRetryableStatus(response.status) && attempt < maxAttempts - 1) {
|
|
165
|
+
lastError = error;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
throw lastError;
|
|
171
|
+
}
|
|
172
|
+
buildUrl(params) {
|
|
173
|
+
const search = new URLSearchParams();
|
|
174
|
+
for (const [key, value] of Object.entries(params.query ?? {})) if (value !== void 0) search.set(key, value);
|
|
175
|
+
const queryString = search.toString();
|
|
176
|
+
return `${this.baseUrl}${params.path}${queryString ? `?${queryString}` : ""}`;
|
|
177
|
+
}
|
|
178
|
+
buildHeaders(params) {
|
|
179
|
+
return {
|
|
180
|
+
Accept: "application/json",
|
|
181
|
+
...params.body !== void 0 ? { "Content-Type": "application/json" } : {},
|
|
182
|
+
...this.apiKey ? { "X-Api-Key": this.apiKey } : {},
|
|
183
|
+
...this.headers,
|
|
184
|
+
...params.idempotencyKey ? { "Idempotency-Key": params.idempotencyKey } : {},
|
|
185
|
+
...params.options?.headers
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
combineSignals(callerSignal) {
|
|
189
|
+
const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
|
|
190
|
+
return callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
|
|
191
|
+
}
|
|
192
|
+
normaliseFetchError(error, callerSignal) {
|
|
193
|
+
if (callerSignal?.aborted) return new OphelioConnectionError("Request aborted by caller.", { raw: error });
|
|
194
|
+
if (error instanceof DOMException && error.name === "TimeoutError") return new OphelioTimeoutError(`Request timed out after ${this.timeoutMs} ms.`, { raw: error });
|
|
195
|
+
return new OphelioConnectionError(`Request failed to send: ${error instanceof Error ? error.message : String(error)}`, { raw: error });
|
|
196
|
+
}
|
|
197
|
+
async parseBody(response) {
|
|
198
|
+
const text = await response.text();
|
|
199
|
+
if (!text) return void 0;
|
|
200
|
+
try {
|
|
201
|
+
return JSON.parse(text);
|
|
202
|
+
} catch {
|
|
203
|
+
throw new OphelioError("API returned a non-JSON response body.", {
|
|
204
|
+
status: response.status,
|
|
205
|
+
raw: text
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/resources/resource.ts
|
|
212
|
+
var APIResource = class {
|
|
213
|
+
transport;
|
|
214
|
+
constructor(transport) {
|
|
215
|
+
this.transport = transport;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/resources/admit.ts
|
|
220
|
+
var AdmitResource = class extends APIResource {
|
|
221
|
+
/** Gate admission check: `GET /admit?card=<cardId>`. */
|
|
222
|
+
check(params, options) {
|
|
223
|
+
return this.transport.request({
|
|
224
|
+
method: "GET",
|
|
225
|
+
path: "/api/v1/admit",
|
|
226
|
+
query: { card: params.card },
|
|
227
|
+
retryable: true,
|
|
228
|
+
options
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region src/resources/billing-option-sales-channels.ts
|
|
234
|
+
var BillingOptionSalesChannelsResource = class extends APIResource {
|
|
235
|
+
/** Remove a sales-channel link. Create via billingOptions.createSalesChannelLink(). */
|
|
236
|
+
async delete(id, options) {
|
|
237
|
+
await this.transport.request({
|
|
238
|
+
method: "DELETE",
|
|
239
|
+
path: `/api/v1/billing-option-sales-channels/${encodeURIComponent(id)}`,
|
|
240
|
+
retryable: false,
|
|
241
|
+
options
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/resources/billing-options.ts
|
|
247
|
+
var BillingOptionsResource = class extends APIResource {
|
|
248
|
+
async list(options) {
|
|
249
|
+
return (await this.transport.request({
|
|
250
|
+
method: "GET",
|
|
251
|
+
path: "/api/v1/billing-options",
|
|
252
|
+
retryable: true,
|
|
253
|
+
options
|
|
254
|
+
})).billing_options;
|
|
255
|
+
}
|
|
256
|
+
get(id, options) {
|
|
257
|
+
return this.transport.request({
|
|
258
|
+
method: "GET",
|
|
259
|
+
path: `/api/v1/billing-options/${encodeURIComponent(id)}`,
|
|
260
|
+
retryable: true,
|
|
261
|
+
options
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
update(id, params, options) {
|
|
265
|
+
return this.transport.request({
|
|
266
|
+
method: "PATCH",
|
|
267
|
+
path: `/api/v1/billing-options/${encodeURIComponent(id)}`,
|
|
268
|
+
body: params,
|
|
269
|
+
retryable: false,
|
|
270
|
+
options
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async delete(id, options) {
|
|
274
|
+
await this.transport.request({
|
|
275
|
+
method: "DELETE",
|
|
276
|
+
path: `/api/v1/billing-options/${encodeURIComponent(id)}`,
|
|
277
|
+
retryable: false,
|
|
278
|
+
options
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
async listSalesChannels(id, options) {
|
|
282
|
+
return (await this.transport.request({
|
|
283
|
+
method: "GET",
|
|
284
|
+
path: `/api/v1/billing-options/${encodeURIComponent(id)}/sales-channels`,
|
|
285
|
+
retryable: true,
|
|
286
|
+
options
|
|
287
|
+
})).sales_channels;
|
|
288
|
+
}
|
|
289
|
+
createSalesChannelLink(id, params, options) {
|
|
290
|
+
return this.transport.request({
|
|
291
|
+
method: "POST",
|
|
292
|
+
path: `/api/v1/billing-options/${encodeURIComponent(id)}/sales-channels`,
|
|
293
|
+
body: params,
|
|
294
|
+
retryable: false,
|
|
295
|
+
options
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/resources/customers.ts
|
|
301
|
+
var CustomersResource = class extends APIResource {
|
|
302
|
+
async list(options) {
|
|
303
|
+
return (await this.transport.request({
|
|
304
|
+
method: "GET",
|
|
305
|
+
path: "/api/v1/customers",
|
|
306
|
+
retryable: true,
|
|
307
|
+
options
|
|
308
|
+
})).customers;
|
|
309
|
+
}
|
|
310
|
+
get(id, options) {
|
|
311
|
+
return this.transport.request({
|
|
312
|
+
method: "GET",
|
|
313
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}`,
|
|
314
|
+
retryable: true,
|
|
315
|
+
options
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
create(params, options) {
|
|
319
|
+
return this.transport.request({
|
|
320
|
+
method: "POST",
|
|
321
|
+
path: "/api/v1/customers",
|
|
322
|
+
body: params,
|
|
323
|
+
retryable: false,
|
|
324
|
+
options
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
update(id, params, options) {
|
|
328
|
+
return this.transport.request({
|
|
329
|
+
method: "PATCH",
|
|
330
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}`,
|
|
331
|
+
body: params,
|
|
332
|
+
retryable: false,
|
|
333
|
+
options
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
async delete(id, options) {
|
|
337
|
+
await this.transport.request({
|
|
338
|
+
method: "DELETE",
|
|
339
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}`,
|
|
340
|
+
retryable: false,
|
|
341
|
+
options
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
async listMemberships(id, options) {
|
|
345
|
+
return (await this.transport.request({
|
|
346
|
+
method: "GET",
|
|
347
|
+
path: `/api/v1/customers/${encodeURIComponent(id)}/memberships`,
|
|
348
|
+
retryable: true,
|
|
349
|
+
options
|
|
350
|
+
})).memberships;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/resources/entitlements.ts
|
|
355
|
+
var EntitlementsResource = class extends APIResource {
|
|
356
|
+
/**
|
|
357
|
+
* Redeem one quota unit for a member. Idempotent: the Idempotency-Key is
|
|
358
|
+
* generated once per call (and reused across the SDK's internal retries),
|
|
359
|
+
* so replays return the original redemption instead of double-spending.
|
|
360
|
+
*/
|
|
361
|
+
redeem(params, options = {}) {
|
|
362
|
+
const { idempotencyKey, ...requestOptions } = options;
|
|
363
|
+
return this.transport.request({
|
|
364
|
+
method: "POST",
|
|
365
|
+
path: "/api/v1/entitlements/redeem",
|
|
366
|
+
body: params,
|
|
367
|
+
idempotencyKey: idempotencyKey ?? crypto.randomUUID(),
|
|
368
|
+
retryable: true,
|
|
369
|
+
options: requestOptions
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Fire-and-forget batch usage reporting (one row per member). Idempotent
|
|
374
|
+
* per (key, member). Returns the recorded usage rows.
|
|
375
|
+
*/
|
|
376
|
+
async recordUsage(params, options = {}) {
|
|
377
|
+
const { idempotencyKey, ...requestOptions } = options;
|
|
378
|
+
return (await this.transport.request({
|
|
379
|
+
method: "POST",
|
|
380
|
+
path: "/api/v1/entitlements/usage",
|
|
381
|
+
body: params,
|
|
382
|
+
idempotencyKey: idempotencyKey ?? crypto.randomUUID(),
|
|
383
|
+
retryable: true,
|
|
384
|
+
options: requestOptions
|
|
385
|
+
})).usage;
|
|
386
|
+
}
|
|
387
|
+
async list(options) {
|
|
388
|
+
return (await this.transport.request({
|
|
389
|
+
method: "GET",
|
|
390
|
+
path: "/api/v1/entitlements",
|
|
391
|
+
retryable: true,
|
|
392
|
+
options
|
|
393
|
+
})).entitlements;
|
|
394
|
+
}
|
|
395
|
+
get(id, options) {
|
|
396
|
+
return this.transport.request({
|
|
397
|
+
method: "GET",
|
|
398
|
+
path: `/api/v1/entitlements/${encodeURIComponent(id)}`,
|
|
399
|
+
retryable: true,
|
|
400
|
+
options
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
create(params, options) {
|
|
404
|
+
return this.transport.request({
|
|
405
|
+
method: "POST",
|
|
406
|
+
path: "/api/v1/entitlements",
|
|
407
|
+
body: params,
|
|
408
|
+
retryable: false,
|
|
409
|
+
options
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
update(id, params, options) {
|
|
413
|
+
return this.transport.request({
|
|
414
|
+
method: "PATCH",
|
|
415
|
+
path: `/api/v1/entitlements/${encodeURIComponent(id)}`,
|
|
416
|
+
body: params,
|
|
417
|
+
retryable: false,
|
|
418
|
+
options
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
async delete(id, options) {
|
|
422
|
+
await this.transport.request({
|
|
423
|
+
method: "DELETE",
|
|
424
|
+
path: `/api/v1/entitlements/${encodeURIComponent(id)}`,
|
|
425
|
+
retryable: false,
|
|
426
|
+
options
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
//#endregion
|
|
431
|
+
//#region src/resources/member-roles.ts
|
|
432
|
+
var MemberRolesResource = class extends APIResource {
|
|
433
|
+
async list(options) {
|
|
434
|
+
return (await this.transport.request({
|
|
435
|
+
method: "GET",
|
|
436
|
+
path: "/api/v1/member-roles",
|
|
437
|
+
retryable: true,
|
|
438
|
+
options
|
|
439
|
+
})).member_roles;
|
|
440
|
+
}
|
|
441
|
+
get(id, options) {
|
|
442
|
+
return this.transport.request({
|
|
443
|
+
method: "GET",
|
|
444
|
+
path: `/api/v1/member-roles/${encodeURIComponent(id)}`,
|
|
445
|
+
retryable: true,
|
|
446
|
+
options
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
create(params, options) {
|
|
450
|
+
return this.transport.request({
|
|
451
|
+
method: "POST",
|
|
452
|
+
path: "/api/v1/member-roles",
|
|
453
|
+
body: params,
|
|
454
|
+
retryable: false,
|
|
455
|
+
options
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
update(id, params, options) {
|
|
459
|
+
return this.transport.request({
|
|
460
|
+
method: "PATCH",
|
|
461
|
+
path: `/api/v1/member-roles/${encodeURIComponent(id)}`,
|
|
462
|
+
body: params,
|
|
463
|
+
retryable: false,
|
|
464
|
+
options
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
async delete(id, options) {
|
|
468
|
+
await this.transport.request({
|
|
469
|
+
method: "DELETE",
|
|
470
|
+
path: `/api/v1/member-roles/${encodeURIComponent(id)}`,
|
|
471
|
+
retryable: false,
|
|
472
|
+
options
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
//#endregion
|
|
477
|
+
//#region src/resources/members.ts
|
|
478
|
+
var MembersResource = class extends APIResource {
|
|
479
|
+
/**
|
|
480
|
+
* Active-member list for offline gate caching. Pass the previous
|
|
481
|
+
* response's `generated_at` as `since` to fetch incrementally.
|
|
482
|
+
*/
|
|
483
|
+
sync(params = {}, options) {
|
|
484
|
+
return this.transport.request({
|
|
485
|
+
method: "GET",
|
|
486
|
+
path: "/api/v1/members/sync",
|
|
487
|
+
query: { since: params.since },
|
|
488
|
+
retryable: true,
|
|
489
|
+
options
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
get(id, options) {
|
|
493
|
+
return this.transport.request({
|
|
494
|
+
method: "GET",
|
|
495
|
+
path: `/api/v1/members/${encodeURIComponent(id)}`,
|
|
496
|
+
retryable: true,
|
|
497
|
+
options
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
async listEntitlements(id, params = {}, options) {
|
|
501
|
+
return (await this.transport.request({
|
|
502
|
+
method: "GET",
|
|
503
|
+
path: `/api/v1/members/${encodeURIComponent(id)}/entitlements`,
|
|
504
|
+
query: { type: params.type },
|
|
505
|
+
retryable: true,
|
|
506
|
+
options
|
|
507
|
+
})).entitlements;
|
|
508
|
+
}
|
|
509
|
+
/** Issue a fresh card id, invalidating the old card at the gate. */
|
|
510
|
+
reissueCard(id, params = {}, options) {
|
|
511
|
+
return this.transport.request({
|
|
512
|
+
method: "POST",
|
|
513
|
+
path: `/api/v1/members/${encodeURIComponent(id)}/reissue-card`,
|
|
514
|
+
body: params,
|
|
515
|
+
retryable: false,
|
|
516
|
+
options
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
//#endregion
|
|
521
|
+
//#region src/resources/memberships.ts
|
|
522
|
+
var MembershipsResource = class extends APIResource {
|
|
523
|
+
async list(options) {
|
|
524
|
+
return (await this.transport.request({
|
|
525
|
+
method: "GET",
|
|
526
|
+
path: "/api/v1/memberships",
|
|
527
|
+
retryable: true,
|
|
528
|
+
options
|
|
529
|
+
})).memberships;
|
|
530
|
+
}
|
|
531
|
+
get(id, options) {
|
|
532
|
+
return this.transport.request({
|
|
533
|
+
method: "GET",
|
|
534
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}`,
|
|
535
|
+
retryable: true,
|
|
536
|
+
options
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
create(params, options) {
|
|
540
|
+
return this.transport.request({
|
|
541
|
+
method: "POST",
|
|
542
|
+
path: "/api/v1/memberships",
|
|
543
|
+
body: params,
|
|
544
|
+
retryable: false,
|
|
545
|
+
options
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
update(id, params, options) {
|
|
549
|
+
return this.transport.request({
|
|
550
|
+
method: "PATCH",
|
|
551
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}`,
|
|
552
|
+
body: params,
|
|
553
|
+
retryable: false,
|
|
554
|
+
options
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
/** Cancellation is a state change, not a delete — see also `uncancel`. */
|
|
558
|
+
cancel(id, params, options) {
|
|
559
|
+
return this.transport.request({
|
|
560
|
+
method: "POST",
|
|
561
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/cancel`,
|
|
562
|
+
body: params,
|
|
563
|
+
retryable: false,
|
|
564
|
+
options
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
uncancel(id, options) {
|
|
568
|
+
return this.transport.request({
|
|
569
|
+
method: "POST",
|
|
570
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/uncancel`,
|
|
571
|
+
retryable: false,
|
|
572
|
+
options
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
async listTransactions(id, options) {
|
|
576
|
+
return (await this.transport.request({
|
|
577
|
+
method: "GET",
|
|
578
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/transactions`,
|
|
579
|
+
retryable: true,
|
|
580
|
+
options
|
|
581
|
+
})).transactions;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Report a payment outcome. Ophel.io never processes payments — your
|
|
585
|
+
* payment provider does; this records the result and drives dunning.
|
|
586
|
+
*/
|
|
587
|
+
createTransaction(id, params, options) {
|
|
588
|
+
return this.transport.request({
|
|
589
|
+
method: "POST",
|
|
590
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/transactions`,
|
|
591
|
+
body: params,
|
|
592
|
+
retryable: false,
|
|
593
|
+
options
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
async listMembers(id, options) {
|
|
597
|
+
return (await this.transport.request({
|
|
598
|
+
method: "GET",
|
|
599
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/members`,
|
|
600
|
+
retryable: true,
|
|
601
|
+
options
|
|
602
|
+
})).members;
|
|
603
|
+
}
|
|
604
|
+
/** Open the next term. Rejected (409) while cancelled or cancellation-pending. */
|
|
605
|
+
renew(id, params, options) {
|
|
606
|
+
return this.transport.request({
|
|
607
|
+
method: "POST",
|
|
608
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/renew`,
|
|
609
|
+
body: params,
|
|
610
|
+
retryable: false,
|
|
611
|
+
options
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Move to a linked higher plan. Applies immediately or schedules at term
|
|
616
|
+
* end — `scheduled_change` is non-null in the deferred case.
|
|
617
|
+
*/
|
|
618
|
+
upgrade(id, params, options) {
|
|
619
|
+
return this.transport.request({
|
|
620
|
+
method: "POST",
|
|
621
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/upgrade`,
|
|
622
|
+
body: params,
|
|
623
|
+
retryable: false,
|
|
624
|
+
options
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
/** Mirror of upgrade towards a linked lower plan. */
|
|
628
|
+
downgrade(id, params, options) {
|
|
629
|
+
return this.transport.request({
|
|
630
|
+
method: "POST",
|
|
631
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/downgrade`,
|
|
632
|
+
body: params,
|
|
633
|
+
retryable: false,
|
|
634
|
+
options
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
async listScheduledChanges(id, params, options) {
|
|
638
|
+
return (await this.transport.request({
|
|
639
|
+
method: "GET",
|
|
640
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/scheduled-changes`,
|
|
641
|
+
query: params?.status ? { status: params.status } : void 0,
|
|
642
|
+
retryable: true,
|
|
643
|
+
options
|
|
644
|
+
})).scheduled_changes;
|
|
645
|
+
}
|
|
646
|
+
async cancelScheduledChange(id, changeId, options) {
|
|
647
|
+
await this.transport.request({
|
|
648
|
+
method: "DELETE",
|
|
649
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/scheduled-changes/${encodeURIComponent(changeId)}`,
|
|
650
|
+
retryable: false,
|
|
651
|
+
options
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
async listTermTransactions(id, termId, options) {
|
|
655
|
+
return (await this.transport.request({
|
|
656
|
+
method: "GET",
|
|
657
|
+
path: `/api/v1/memberships/${encodeURIComponent(id)}/terms/${encodeURIComponent(termId)}/transactions`,
|
|
658
|
+
retryable: true,
|
|
659
|
+
options
|
|
660
|
+
})).transactions;
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region src/resources/plan-entitlements.ts
|
|
665
|
+
var PlanEntitlementsResource = class extends APIResource {
|
|
666
|
+
get(id, options) {
|
|
667
|
+
return this.transport.request({
|
|
668
|
+
method: "GET",
|
|
669
|
+
path: `/api/v1/plan-entitlements/${encodeURIComponent(id)}`,
|
|
670
|
+
retryable: true,
|
|
671
|
+
options
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
update(id, params, options) {
|
|
675
|
+
return this.transport.request({
|
|
676
|
+
method: "PATCH",
|
|
677
|
+
path: `/api/v1/plan-entitlements/${encodeURIComponent(id)}`,
|
|
678
|
+
body: params,
|
|
679
|
+
retryable: false,
|
|
680
|
+
options
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
/** Detach an entitlement from its plan. Attach via plans.createEntitlement(). */
|
|
684
|
+
async delete(id, options) {
|
|
685
|
+
await this.transport.request({
|
|
686
|
+
method: "DELETE",
|
|
687
|
+
path: `/api/v1/plan-entitlements/${encodeURIComponent(id)}`,
|
|
688
|
+
retryable: false,
|
|
689
|
+
options
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
//#endregion
|
|
694
|
+
//#region src/resources/plan-links.ts
|
|
695
|
+
var PlanLinksResource = class extends APIResource {
|
|
696
|
+
/** Remove an upgrade/downgrade link. Create via plans.createPlanLink(). */
|
|
697
|
+
async delete(id, options) {
|
|
698
|
+
await this.transport.request({
|
|
699
|
+
method: "DELETE",
|
|
700
|
+
path: `/api/v1/plan-links/${encodeURIComponent(id)}`,
|
|
701
|
+
retryable: false,
|
|
702
|
+
options
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
//#endregion
|
|
707
|
+
//#region src/resources/plan-pricing-group-mappings.ts
|
|
708
|
+
var PlanPricingGroupMappingsResource = class extends APIResource {
|
|
709
|
+
/** Remove a pricing mapping. Create via plans.createPricingGroupMapping(). */
|
|
710
|
+
async delete(id, options) {
|
|
711
|
+
await this.transport.request({
|
|
712
|
+
method: "DELETE",
|
|
713
|
+
path: `/api/v1/plan-pricing-group-mappings/${encodeURIComponent(id)}`,
|
|
714
|
+
retryable: false,
|
|
715
|
+
options
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
//#endregion
|
|
720
|
+
//#region src/resources/plans.ts
|
|
721
|
+
var PlansResource = class extends APIResource {
|
|
722
|
+
async list(options) {
|
|
723
|
+
return (await this.transport.request({
|
|
724
|
+
method: "GET",
|
|
725
|
+
path: "/api/v1/plans",
|
|
726
|
+
retryable: true,
|
|
727
|
+
options
|
|
728
|
+
})).plans;
|
|
729
|
+
}
|
|
730
|
+
get(id, options) {
|
|
731
|
+
return this.transport.request({
|
|
732
|
+
method: "GET",
|
|
733
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
734
|
+
retryable: true,
|
|
735
|
+
options
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
async listBillingOptions(id, options) {
|
|
739
|
+
return (await this.transport.request({
|
|
740
|
+
method: "GET",
|
|
741
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/billing-options`,
|
|
742
|
+
retryable: true,
|
|
743
|
+
options
|
|
744
|
+
})).billing_options;
|
|
745
|
+
}
|
|
746
|
+
async listEntitlements(id, options) {
|
|
747
|
+
return (await this.transport.request({
|
|
748
|
+
method: "GET",
|
|
749
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/entitlements`,
|
|
750
|
+
retryable: true,
|
|
751
|
+
options
|
|
752
|
+
})).entitlements;
|
|
753
|
+
}
|
|
754
|
+
create(params, options) {
|
|
755
|
+
return this.transport.request({
|
|
756
|
+
method: "POST",
|
|
757
|
+
path: "/api/v1/plans",
|
|
758
|
+
body: params,
|
|
759
|
+
retryable: false,
|
|
760
|
+
options
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
update(id, params, options) {
|
|
764
|
+
return this.transport.request({
|
|
765
|
+
method: "PATCH",
|
|
766
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
767
|
+
body: params,
|
|
768
|
+
retryable: false,
|
|
769
|
+
options
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
async delete(id, options) {
|
|
773
|
+
await this.transport.request({
|
|
774
|
+
method: "DELETE",
|
|
775
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
776
|
+
retryable: false,
|
|
777
|
+
options
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
createBillingOption(id, params, options) {
|
|
781
|
+
return this.transport.request({
|
|
782
|
+
method: "POST",
|
|
783
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/billing-options`,
|
|
784
|
+
body: params,
|
|
785
|
+
retryable: false,
|
|
786
|
+
options
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
async listPlanLinks(id, options) {
|
|
790
|
+
return (await this.transport.request({
|
|
791
|
+
method: "GET",
|
|
792
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/plan-links`,
|
|
793
|
+
retryable: true,
|
|
794
|
+
options
|
|
795
|
+
})).plan_links;
|
|
796
|
+
}
|
|
797
|
+
createPlanLink(id, params, options) {
|
|
798
|
+
return this.transport.request({
|
|
799
|
+
method: "POST",
|
|
800
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/plan-links`,
|
|
801
|
+
body: params,
|
|
802
|
+
retryable: false,
|
|
803
|
+
options
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
createEntitlement(id, params, options) {
|
|
807
|
+
return this.transport.request({
|
|
808
|
+
method: "POST",
|
|
809
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/entitlements`,
|
|
810
|
+
body: params,
|
|
811
|
+
retryable: false,
|
|
812
|
+
options
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
async listPricingGroupMappings(id, options) {
|
|
816
|
+
return (await this.transport.request({
|
|
817
|
+
method: "GET",
|
|
818
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/pricing-group-mappings`,
|
|
819
|
+
retryable: true,
|
|
820
|
+
options
|
|
821
|
+
})).pricing_group_mappings;
|
|
822
|
+
}
|
|
823
|
+
createPricingGroupMapping(id, params, options) {
|
|
824
|
+
return this.transport.request({
|
|
825
|
+
method: "POST",
|
|
826
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/pricing-group-mappings`,
|
|
827
|
+
body: params,
|
|
828
|
+
retryable: false,
|
|
829
|
+
options
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
//#endregion
|
|
834
|
+
//#region src/resources/pricing-groups.ts
|
|
835
|
+
var PricingGroupsResource = class extends APIResource {
|
|
836
|
+
async list(options) {
|
|
837
|
+
return (await this.transport.request({
|
|
838
|
+
method: "GET",
|
|
839
|
+
path: "/api/v1/pricing-groups",
|
|
840
|
+
retryable: true,
|
|
841
|
+
options
|
|
842
|
+
})).pricing_groups;
|
|
843
|
+
}
|
|
844
|
+
get(id, options) {
|
|
845
|
+
return this.transport.request({
|
|
846
|
+
method: "GET",
|
|
847
|
+
path: `/api/v1/pricing-groups/${encodeURIComponent(id)}`,
|
|
848
|
+
retryable: true,
|
|
849
|
+
options
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
create(params, options) {
|
|
853
|
+
return this.transport.request({
|
|
854
|
+
method: "POST",
|
|
855
|
+
path: "/api/v1/pricing-groups",
|
|
856
|
+
body: params,
|
|
857
|
+
retryable: false,
|
|
858
|
+
options
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
update(id, params, options) {
|
|
862
|
+
return this.transport.request({
|
|
863
|
+
method: "PATCH",
|
|
864
|
+
path: `/api/v1/pricing-groups/${encodeURIComponent(id)}`,
|
|
865
|
+
body: params,
|
|
866
|
+
retryable: false,
|
|
867
|
+
options
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
async delete(id, options) {
|
|
871
|
+
await this.transport.request({
|
|
872
|
+
method: "DELETE",
|
|
873
|
+
path: `/api/v1/pricing-groups/${encodeURIComponent(id)}`,
|
|
874
|
+
retryable: false,
|
|
875
|
+
options
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
//#endregion
|
|
880
|
+
//#region src/resources/sales-channels.ts
|
|
881
|
+
var SalesChannelsResource = class extends APIResource {
|
|
882
|
+
async list(options) {
|
|
883
|
+
return (await this.transport.request({
|
|
884
|
+
method: "GET",
|
|
885
|
+
path: "/api/v1/sales-channels",
|
|
886
|
+
retryable: true,
|
|
887
|
+
options
|
|
888
|
+
})).sales_channels;
|
|
889
|
+
}
|
|
890
|
+
get(id, options) {
|
|
891
|
+
return this.transport.request({
|
|
892
|
+
method: "GET",
|
|
893
|
+
path: `/api/v1/sales-channels/${encodeURIComponent(id)}`,
|
|
894
|
+
retryable: true,
|
|
895
|
+
options
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
create(params, options) {
|
|
899
|
+
return this.transport.request({
|
|
900
|
+
method: "POST",
|
|
901
|
+
path: "/api/v1/sales-channels",
|
|
902
|
+
body: params,
|
|
903
|
+
retryable: false,
|
|
904
|
+
options
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
update(id, params, options) {
|
|
908
|
+
return this.transport.request({
|
|
909
|
+
method: "PATCH",
|
|
910
|
+
path: `/api/v1/sales-channels/${encodeURIComponent(id)}`,
|
|
911
|
+
body: params,
|
|
912
|
+
retryable: false,
|
|
913
|
+
options
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
async delete(id, options) {
|
|
917
|
+
await this.transport.request({
|
|
918
|
+
method: "DELETE",
|
|
919
|
+
path: `/api/v1/sales-channels/${encodeURIComponent(id)}`,
|
|
920
|
+
retryable: false,
|
|
921
|
+
options
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
//#endregion
|
|
926
|
+
//#region src/resources/webhook-subscriptions.ts
|
|
927
|
+
var WebhookSubscriptionsResource = class extends APIResource {
|
|
928
|
+
async list(options) {
|
|
929
|
+
return (await this.transport.request({
|
|
930
|
+
method: "GET",
|
|
931
|
+
path: "/api/v1/webhook-subscriptions",
|
|
932
|
+
retryable: true,
|
|
933
|
+
options
|
|
934
|
+
})).webhook_subscriptions;
|
|
935
|
+
}
|
|
936
|
+
get(id, options) {
|
|
937
|
+
return this.transport.request({
|
|
938
|
+
method: "GET",
|
|
939
|
+
path: `/api/v1/webhook-subscriptions/${encodeURIComponent(id)}`,
|
|
940
|
+
retryable: true,
|
|
941
|
+
options
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
async listDeliveries(id, options) {
|
|
945
|
+
return (await this.transport.request({
|
|
946
|
+
method: "GET",
|
|
947
|
+
path: `/api/v1/webhook-subscriptions/${encodeURIComponent(id)}/deliveries`,
|
|
948
|
+
retryable: true,
|
|
949
|
+
options
|
|
950
|
+
})).webhook_deliveries;
|
|
951
|
+
}
|
|
952
|
+
/** The HMAC signing secret is only returned on creation — store it then. */
|
|
953
|
+
create(params, options) {
|
|
954
|
+
return this.transport.request({
|
|
955
|
+
method: "POST",
|
|
956
|
+
path: "/api/v1/webhook-subscriptions",
|
|
957
|
+
body: params,
|
|
958
|
+
retryable: false,
|
|
959
|
+
options
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
update(id, params, options) {
|
|
963
|
+
return this.transport.request({
|
|
964
|
+
method: "PATCH",
|
|
965
|
+
path: `/api/v1/webhook-subscriptions/${encodeURIComponent(id)}`,
|
|
966
|
+
body: params,
|
|
967
|
+
retryable: false,
|
|
968
|
+
options
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
async delete(id, options) {
|
|
972
|
+
await this.transport.request({
|
|
973
|
+
method: "DELETE",
|
|
974
|
+
path: `/api/v1/webhook-subscriptions/${encodeURIComponent(id)}`,
|
|
975
|
+
retryable: false,
|
|
976
|
+
options
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
//#endregion
|
|
981
|
+
//#region src/webhooks/verify.ts
|
|
982
|
+
const encoder = new TextEncoder();
|
|
983
|
+
async function computeSignatureHex(payload, secret) {
|
|
984
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
|
|
985
|
+
name: "HMAC",
|
|
986
|
+
hash: "SHA-256"
|
|
987
|
+
}, false, ["sign"]);
|
|
988
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
|
|
989
|
+
return Array.from(new Uint8Array(signature)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
990
|
+
}
|
|
991
|
+
function timingSafeEqualHex(a, b) {
|
|
992
|
+
if (a.length !== b.length) return false;
|
|
993
|
+
let mismatch = 0;
|
|
994
|
+
for (let i = 0; i < a.length; i++) mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
995
|
+
return mismatch === 0;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Verify the `X-Ophelio-Signature` header against the raw request body.
|
|
999
|
+
* Pass the body exactly as received — re-serialising the parsed JSON can
|
|
1000
|
+
* change key order/whitespace and break verification.
|
|
1001
|
+
*/
|
|
1002
|
+
async function verifyWebhookSignature(payload, signature, secret) {
|
|
1003
|
+
const provided = signature.startsWith("sha256=") ? signature.slice(7) : signature;
|
|
1004
|
+
return timingSafeEqualHex(await computeSignatureHex(payload, secret), provided.toLowerCase());
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Verify and parse a webhook delivery in one step. Throws
|
|
1008
|
+
* WebhookSignatureVerificationError when the signature doesn't match, or
|
|
1009
|
+
* OphelioError when the payload isn't a structurally valid event.
|
|
1010
|
+
*/
|
|
1011
|
+
async function constructEvent(payload, signature, secret) {
|
|
1012
|
+
if (!await verifyWebhookSignature(payload, signature, secret)) throw new WebhookSignatureVerificationError("Webhook signature verification failed.");
|
|
1013
|
+
let parsed;
|
|
1014
|
+
try {
|
|
1015
|
+
parsed = JSON.parse(payload);
|
|
1016
|
+
} catch {
|
|
1017
|
+
throw new OphelioError("Webhook payload is not valid JSON.");
|
|
1018
|
+
}
|
|
1019
|
+
const event = parsed;
|
|
1020
|
+
if (!event || typeof event.id !== "string" || typeof event.type !== "string" || typeof event.create_time !== "string" || typeof event.data !== "object" || event.data === null) throw new OphelioError("Webhook payload is not a valid Ophelio event.");
|
|
1021
|
+
return event;
|
|
1022
|
+
}
|
|
1023
|
+
//#endregion
|
|
1024
|
+
//#region src/client.ts
|
|
1025
|
+
/**
|
|
1026
|
+
* Ophel.io API client.
|
|
1027
|
+
*
|
|
1028
|
+
* ```ts
|
|
1029
|
+
* const ophelio = new Ophelio({ apiKey: process.env.OPHELIO_API_KEY })
|
|
1030
|
+
* const result = await ophelio.admit.check({ card: 'M-123456' })
|
|
1031
|
+
* ```
|
|
1032
|
+
*/
|
|
1033
|
+
var Ophelio = class Ophelio {
|
|
1034
|
+
admit;
|
|
1035
|
+
members;
|
|
1036
|
+
memberships;
|
|
1037
|
+
customers;
|
|
1038
|
+
entitlements;
|
|
1039
|
+
plans;
|
|
1040
|
+
billingOptions;
|
|
1041
|
+
memberRoles;
|
|
1042
|
+
pricingGroups;
|
|
1043
|
+
salesChannels;
|
|
1044
|
+
planEntitlements;
|
|
1045
|
+
planLinks;
|
|
1046
|
+
planPricingGroupMappings;
|
|
1047
|
+
billingOptionSalesChannels;
|
|
1048
|
+
webhookSubscriptions;
|
|
1049
|
+
/** Webhook helpers — usable without a client instance (no API key). */
|
|
1050
|
+
static webhooks = {
|
|
1051
|
+
verifySignature: verifyWebhookSignature,
|
|
1052
|
+
constructEvent
|
|
1053
|
+
};
|
|
1054
|
+
webhooks = Ophelio.webhooks;
|
|
1055
|
+
constructor(options) {
|
|
1056
|
+
const transport = new Transport(options);
|
|
1057
|
+
this.admit = new AdmitResource(transport);
|
|
1058
|
+
this.members = new MembersResource(transport);
|
|
1059
|
+
this.memberships = new MembershipsResource(transport);
|
|
1060
|
+
this.customers = new CustomersResource(transport);
|
|
1061
|
+
this.entitlements = new EntitlementsResource(transport);
|
|
1062
|
+
this.plans = new PlansResource(transport);
|
|
1063
|
+
this.billingOptions = new BillingOptionsResource(transport);
|
|
1064
|
+
this.memberRoles = new MemberRolesResource(transport);
|
|
1065
|
+
this.pricingGroups = new PricingGroupsResource(transport);
|
|
1066
|
+
this.salesChannels = new SalesChannelsResource(transport);
|
|
1067
|
+
this.planEntitlements = new PlanEntitlementsResource(transport);
|
|
1068
|
+
this.planLinks = new PlanLinksResource(transport);
|
|
1069
|
+
this.planPricingGroupMappings = new PlanPricingGroupMappingsResource(transport);
|
|
1070
|
+
this.billingOptionSalesChannels = new BillingOptionSalesChannelsResource(transport);
|
|
1071
|
+
this.webhookSubscriptions = new WebhookSubscriptionsResource(transport);
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
/**
|
|
1075
|
+
* Canonical webhook event codes for the Ophel.io platform.
|
|
1076
|
+
*
|
|
1077
|
+
* This is the protocol-level source of truth shared by the API (event
|
|
1078
|
+
* emission), the web app (subscription UI), and the SDK (typed webhook
|
|
1079
|
+
* events). UI copy (labels, descriptions, categories) lives in ophelio-web.
|
|
1080
|
+
*/
|
|
1081
|
+
const WEBHOOK_EVENT_CODES = [
|
|
1082
|
+
"membership.created",
|
|
1083
|
+
"membership.renewed",
|
|
1084
|
+
"membership.renewal_due",
|
|
1085
|
+
"membership.payment_failed",
|
|
1086
|
+
"membership.expired",
|
|
1087
|
+
"membership.grace_entered",
|
|
1088
|
+
"membership.cancelled",
|
|
1089
|
+
"membership.upgraded",
|
|
1090
|
+
"membership.downgraded",
|
|
1091
|
+
"scheduled_change.failed",
|
|
1092
|
+
"entitlement.used",
|
|
1093
|
+
"member.card_issued",
|
|
1094
|
+
"member.removed",
|
|
1095
|
+
"plan.updated"
|
|
1096
|
+
];
|
|
1097
|
+
//#endregion
|
|
1098
|
+
export { AlreadyExistsError, DEFAULT_BASE_URL, InternalError, InvalidArgumentError, NotFoundError, Ophelio, OphelioConnectionError, OphelioError, OphelioTimeoutError, PermissionDeniedError, ResourceExhaustedError, UnauthenticatedError, UnavailableError, UnimplementedError, WEBHOOK_EVENT_CODES, WebhookSignatureVerificationError, constructEvent, verifyWebhookSignature };
|