@peable.to/sdk 0.2.3-oxy-one.0 → 0.2.5-oxy-one.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### Authenticating without a key pair
2
+
3
+ SDK 0.2.5-oxy-one.0 accepts `tokenProvider: { getToken(), invalidate() }` on
4
+ `new Peable(...)` in place of `publicKey` + `secret`, for a workload that already
5
+ holds an Oxy identity (workload identity, for instance) and so has no
6
+ ApplicationCredential to hand over. The SDK then mints nothing: every Gateway
7
+ request carries `getToken()`'s result as the bearer, and a 401 calls
8
+ `invalidate()` before the one existing retry. Passing both, or neither, throws at
9
+ construction. The credential path is unchanged.
10
+
1
11
 
2
12
  ### HTTP deadlines and existing operation keys
3
13
 
@@ -1,9 +1,15 @@
1
1
  "use strict";
2
- // Configuration for the server-side `Peable` client. The SDK is configured
3
- // with a confidential `ApplicationCredential` (`{publicKey, secret}`, the
4
- // SAME credential Oxy Console already issues for a `type:'service'`
5
- // credential) — never a bespoke "Peable API key". See the SDK plan's
6
- // "Architecture / auth" section.
2
+ // Configuration for the server-side `Peable` client. The SDK authenticates with
3
+ // an Oxy service token, obtained one of two ways:
4
+ //
5
+ // - a confidential `ApplicationCredential` (`{publicKey, secret}`, the SAME
6
+ // credential Oxy Console already issues for a `type:'service'` credential) —
7
+ // never a bespoke "Peable API key" — from which the SDK mints the token
8
+ // itself. See the SDK plan's "Architecture / auth" section; or
9
+ // - a caller-supplied `tokenProvider`, for a workload that already holds an Oxy
10
+ // identity (e.g. an ECS task role attested through workload identity) and
11
+ // has no key pair to hand over. The Gateway resolves the merchant from the
12
+ // token's application id and environment, not from how it was minted.
7
13
  Object.defineProperty(exports, "__esModule", { value: true });
8
14
  exports.DEFAULT_OXY_API_URL = exports.DEFAULT_GATEWAY_BASE_URL = void 0;
9
15
  exports.resolveRequestTimeout = resolveRequestTimeout;
@@ -26,24 +32,47 @@ function resolveRequestTimeout(value) {
26
32
  function stripTrailingSlash(url) {
27
33
  return url.endsWith('/') ? url.slice(0, -1) : url;
28
34
  }
29
- /**
30
- * Validate and normalize an `PeableConfig`. Throws a plain `Error` for a
31
- * missing `publicKey`/`secret` — a programmer error caught at construction
32
- * time, not an API-response failure, so it deliberately does not go through
33
- * the `PeableError` hierarchy (which models mapped HTTP responses).
34
- */
35
+ function isServiceTokenProvider(value) {
36
+ if (typeof value !== 'object' || value === null)
37
+ return false;
38
+ const candidate = value;
39
+ return typeof candidate.getToken === 'function' && typeof candidate.invalidate === 'function';
40
+ }
35
41
  function resolveConfig(config) {
36
- if (!config.publicKey) {
37
- throw new Error('Peable: `publicKey` is required');
42
+ // Read loosely: a JavaScript caller is not held to the union, and "both" is
43
+ // exactly the shape the types forbid.
44
+ const { tokenProvider, publicKey, secret } = config;
45
+ if (tokenProvider !== undefined) {
46
+ if (publicKey !== undefined || secret !== undefined) {
47
+ throw new Error('Peable: pass either `tokenProvider` or `publicKey` + `secret`, not both');
48
+ }
49
+ if (!isServiceTokenProvider(tokenProvider)) {
50
+ throw new Error('Peable: `tokenProvider` must have `getToken()` and `invalidate()`');
51
+ }
38
52
  }
39
- if (!config.secret) {
40
- throw new Error('Peable: `secret` is required');
53
+ else {
54
+ if (publicKey === undefined && secret === undefined) {
55
+ throw new Error('Peable: either `publicKey` + `secret` or a `tokenProvider` is required');
56
+ }
57
+ if (!config.publicKey) {
58
+ throw new Error('Peable: `publicKey` is required');
59
+ }
60
+ if (!config.secret) {
61
+ throw new Error('Peable: `secret` is required');
62
+ }
41
63
  }
42
- return {
64
+ const options = {
43
65
  requestTimeoutMs: resolveRequestTimeout(config.requestTimeoutMs),
44
- publicKey: config.publicKey,
45
- secret: config.secret,
66
+ taxQuoteAuthorityKeys: config.taxQuoteAuthorityKeys
67
+ ? Object.freeze({ ...config.taxQuoteAuthorityKeys })
68
+ : undefined,
69
+ invoiceAuthorityKeys: config.invoiceAuthorityKeys
70
+ ? Object.freeze({ ...config.invoiceAuthorityKeys })
71
+ : undefined,
46
72
  baseURL: stripTrailingSlash(config.baseURL ?? exports.DEFAULT_GATEWAY_BASE_URL),
47
73
  oxyApiUrl: stripTrailingSlash(config.oxyApiUrl ?? exports.DEFAULT_OXY_API_URL),
48
74
  };
75
+ return config.tokenProvider !== undefined
76
+ ? { ...options, tokenProvider: config.tokenProvider }
77
+ : { ...options, publicKey: config.publicKey, secret: config.secret };
49
78
  }
@@ -43,7 +43,9 @@ function createServiceTokenProvider(cfg, deps = {}) {
43
43
  try {
44
44
  response = await fetchImpl(`${resolved.oxyApiUrl}${SERVICE_TOKEN_PATH}`, {
45
45
  method: 'POST',
46
- ...(resolved.requestTimeoutMs === undefined ? {} : { signal: AbortSignal.timeout(resolved.requestTimeoutMs) }),
46
+ ...(resolved.requestTimeoutMs === undefined
47
+ ? {}
48
+ : { signal: AbortSignal.timeout(resolved.requestTimeoutMs) }),
47
49
  headers: { 'Content-Type': 'application/json' },
48
50
  body: JSON.stringify({ apiKey: resolved.publicKey, apiSecret: resolved.secret }),
49
51
  });
@@ -57,7 +59,8 @@ function createServiceTokenProvider(cfg, deps = {}) {
57
59
  }
58
60
  catch {
59
61
  throw new errors_1.PeableApiError('The Oxy service-token response body could not be read', {
60
- statusCode: response.status, code: 'invalid_response',
62
+ statusCode: response.status,
63
+ code: 'invalid_response',
61
64
  });
62
65
  }
63
66
  if (!response.ok) {
package/dist/cjs/index.js CHANGED
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WebhooksResource = exports.WEBHOOK_SIGNATURE_HEADER = exports.PeableSignatureVerificationError = exports.PeableApiError = exports.PeablePermissionError = exports.PeableInvalidRequestError = exports.PeableAuthenticationError = exports.PeableError = exports.DEFAULT_OXY_API_URL = exports.DEFAULT_GATEWAY_BASE_URL = exports.Peable = void 0;
4
4
  const billing_1 = require("./resources/billing");
5
5
  // Server entry (`@peable.to/sdk`) — the merchant-authed SDK. Configured with a
6
- // confidential `ApplicationCredential` (`{publicKey, secret}`); mints and
7
- // caches an Oxy service token, and exposes Stripe-ergonomics resource
6
+ // confidential `ApplicationCredential` (`{publicKey, secret}`), from which it
7
+ // mints and caches an Oxy service token, or with a caller-supplied
8
+ // `tokenProvider` that already holds one; exposes Stripe-ergonomics resource
8
9
  // namespaces over the Gateway REST contract.
9
10
  //
10
11
  // Deliberately never imports `socket.io-client` or `./browser/*` — the
@@ -39,9 +40,11 @@ const transfers_1 = require("./resources/transfers");
39
40
  class Peable {
40
41
  constructor(config) {
41
42
  const resolved = (0, config_1.resolveConfig)(config);
42
- const tokenProvider = (0, serviceToken_1.createServiceTokenProvider)(config);
43
+ const tokenProvider = config.tokenProvider !== undefined
44
+ ? config.tokenProvider
45
+ : (0, serviceToken_1.createServiceTokenProvider)(config);
43
46
  const client = (0, client_1.createRestClient)({ baseURL: resolved.baseURL, requestTimeoutMs: resolved.requestTimeoutMs }, tokenProvider);
44
- this.billing = new billing_1.BillingResource(client);
47
+ this.billing = new billing_1.BillingResource(client, resolved.invoiceAuthorityKeys, resolved.taxQuoteAuthorityKeys);
45
48
  this.paymentIntents = new paymentIntents_1.PaymentIntentsResource(client);
46
49
  this.paymentLinks = new paymentLinks_1.PaymentLinksResource(client);
47
50
  this.checkout = new checkoutSessions_1.CheckoutResource(client);
@@ -1,23 +1,112 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BillingResource = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const shared_types_1 = require("@peable.to/shared-types");
4
6
  /** Recurring billing is separate from one-off checkout and marketplace transfers. */
5
7
  class BillingResource {
6
- constructor(client) {
8
+ constructor(client, invoiceAuthorityKeys, taxQuoteAuthorityKeys) {
7
9
  this.client = client;
10
+ this.invoiceAuthorityKeys = invoiceAuthorityKeys;
11
+ this.taxQuoteAuthorityKeys = taxQuoteAuthorityKeys;
8
12
  }
9
13
  ensureCustomer(params, options) {
10
- return this.client.request('POST', '/v1/billing/customers', { body: params, idempotencyKey: options.idempotencyKey });
14
+ return this.client.request('POST', '/v1/billing/customers', {
15
+ body: params,
16
+ idempotencyKey: options.idempotencyKey,
17
+ });
11
18
  }
12
19
  createCheckoutSession(params, options) {
13
- return this.client.request('POST', '/v1/billing/checkout_sessions', { body: params, idempotencyKey: options.idempotencyKey });
20
+ return this.client.request('POST', '/v1/billing/checkout_sessions', {
21
+ body: params,
22
+ idempotencyKey: options.idempotencyKey,
23
+ });
14
24
  }
15
25
  createPortalSession(params, options) {
16
- return this.client.request('POST', '/v1/billing/portal_sessions', { body: params, idempotencyKey: options.idempotencyKey });
26
+ return this.client.request('POST', '/v1/billing/portal_sessions', {
27
+ body: params,
28
+ idempotencyKey: options.idempotencyKey,
29
+ });
30
+ }
31
+ retrieveCheckout(ref) {
32
+ return this.client.request('GET', `/v1/billing/checkout_sessions/${encodeURIComponent(ref)}`);
33
+ }
34
+ async createTaxQuote(params) {
35
+ if (!this.taxQuoteAuthorityKeys || Object.keys(this.taxQuoteAuthorityKeys).length === 0)
36
+ throw new Error('Peable: tax quote authority verification is unconfigured');
37
+ const result = await this.client.request('POST', '/v1/billing/tax_quotes', {
38
+ body: params,
39
+ });
40
+ try {
41
+ const pinned = this.taxQuoteAuthorityKeys[result.signature.keyId];
42
+ if (!pinned ||
43
+ result.schemaVersion !== 1 ||
44
+ result.signature.algorithm !== 'Ed25519' ||
45
+ !/^[A-Za-z0-9_-]{86}$/.test(result.signature.value) ||
46
+ result.source.storeId !== params.storeId ||
47
+ result.source.planId !== params.planId ||
48
+ result.quote.customerLocationEvidenceId !== params.customerLocationEvidenceId ||
49
+ !Number.isFinite(Date.parse(result.quote.quotedAt)) ||
50
+ Date.parse(result.quote.quotedAt) > Date.now() ||
51
+ !Number.isFinite(Date.parse(result.quote.expiresAt)) ||
52
+ Date.parse(result.quote.expiresAt) <= Date.now())
53
+ throw new Error('Invalid quote');
54
+ const key = (0, node_crypto_1.createPublicKey)(pinned);
55
+ if (key.asymmetricKeyType !== 'ed25519' ||
56
+ !(0, node_crypto_1.verify)(null, Buffer.from((0, shared_types_1.canonicalBillingTaxQuote)(result)), key, Buffer.from(result.signature.value, 'base64url')))
57
+ throw new Error('Invalid signature');
58
+ }
59
+ catch {
60
+ throw new Error('Peable: tax quote authority verification failed');
61
+ }
62
+ return result;
63
+ }
64
+ async retrieveFinalInvoiceAuthority(subscriptionRef, invoiceRef) {
65
+ if (!this.invoiceAuthorityKeys || Object.keys(this.invoiceAuthorityKeys).length === 0)
66
+ throw new Error('Peable: invoice authority verification is unconfigured');
67
+ const authority = await this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_authorities/${encodeURIComponent(invoiceRef)}`);
68
+ try {
69
+ const pinnedKey = this.invoiceAuthorityKeys[authority.signature.keyId];
70
+ if (authority.schemaVersion !== 1 ||
71
+ !pinnedKey ||
72
+ authority.signature.algorithm !== 'Ed25519' ||
73
+ !/^[A-Za-z0-9_-]{86}$/.test(authority.signature.value) ||
74
+ authority.source.invoiceId !== invoiceRef ||
75
+ authority.source.subscriptionId !== subscriptionRef ||
76
+ authority.invoice.context.providerSubscriptionId !== subscriptionRef ||
77
+ !['card', 'faircoin'].includes(authority.method) ||
78
+ !Number.isFinite(Date.parse(authority.invoice.issuedAt)) ||
79
+ Date.parse(authority.invoice.issuedAt) > Date.now() ||
80
+ !Number.isFinite(Date.parse(authority.invoice.expiresAt)) ||
81
+ Date.parse(authority.invoice.expiresAt) <= Date.now())
82
+ throw new Error('Invalid authority');
83
+ if (authority.method === 'faircoin') {
84
+ const quote = authority.invoice.faircoinQuote;
85
+ if (!quote ||
86
+ !/^[1-9][0-9]*$/.test(quote.amountBaseUnits) ||
87
+ !Number.isFinite(Date.parse(quote.quotedAt)) ||
88
+ Date.parse(quote.quotedAt) > Date.now() ||
89
+ !Number.isFinite(Date.parse(quote.expiresAt)) ||
90
+ Date.parse(quote.expiresAt) <= Date.now() ||
91
+ Date.parse(quote.expiresAt) > Date.parse(authority.invoice.expiresAt))
92
+ throw new Error('Invalid quote');
93
+ }
94
+ const key = (0, node_crypto_1.createPublicKey)(pinnedKey);
95
+ if (key.asymmetricKeyType !== 'ed25519' ||
96
+ !(0, node_crypto_1.verify)(null, Buffer.from((0, shared_types_1.canonicalBillingAuthority)(authority), 'utf8'), key, Buffer.from(authority.signature.value, 'base64url')))
97
+ throw new Error('Invalid signature');
98
+ }
99
+ catch {
100
+ throw new Error('Peable: invoice authority verification failed');
101
+ }
102
+ return authority;
103
+ }
104
+ retrievePaidInvoice(subscriptionRef, invoiceRef) {
105
+ return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/paid_invoices/${encodeURIComponent(invoiceRef)}`);
106
+ }
107
+ retrieveInvoiceState(subscriptionRef, invoiceRef) {
108
+ return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_states/${encodeURIComponent(invoiceRef)}`);
17
109
  }
18
- retrieveCheckout(ref) { return this.client.request('GET', `/v1/billing/checkout_sessions/${encodeURIComponent(ref)}`); }
19
- retrievePaidInvoice(subscriptionRef, invoiceRef) { return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/paid_invoices/${encodeURIComponent(invoiceRef)}`); }
20
- retrieveInvoiceState(subscriptionRef, invoiceRef) { return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_states/${encodeURIComponent(invoiceRef)}`); }
21
110
  retrieveSubscription(ref) {
22
111
  return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(ref)}`);
23
112
  }
@@ -19,7 +19,10 @@ class RefundsResource {
19
19
  * arrives.
20
20
  */
21
21
  create(params, options = {}) {
22
- return this.client.request('POST', '/v1/refunds', { body: params, idempotencyKey: options.idempotencyKey });
22
+ return this.client.request('POST', '/v1/refunds', {
23
+ body: params,
24
+ idempotencyKey: options.idempotencyKey,
25
+ });
23
26
  }
24
27
  /**
25
28
  * What this payment actually came to — gross, fee, net.
@@ -18,7 +18,10 @@ class TransfersResource {
18
18
  * money in flight.
19
19
  */
20
20
  create(params, options = {}) {
21
- return this.client.request('POST', '/v1/transfers', { body: params, idempotencyKey: options.idempotencyKey });
21
+ return this.client.request('POST', '/v1/transfers', {
22
+ body: params,
23
+ idempotencyKey: options.idempotencyKey,
24
+ });
22
25
  }
23
26
  /**
24
27
  * Take some or all of a settlement back.
@@ -36,9 +39,7 @@ class TransfersResource {
36
39
  }
37
40
  return this.client.request('POST', `/v1/transfers/${encodeURIComponent(transferId)}/reversals`, {
38
41
  body: params,
39
- ...(options.idempotencyKey === undefined
40
- ? {}
41
- : { idempotencyKey: options.idempotencyKey }),
42
+ ...(options.idempotencyKey === undefined ? {} : { idempotencyKey: options.idempotencyKey }),
42
43
  });
43
44
  }
44
45
  /** What one payment settled — the reconciliation read. */
@@ -33,14 +33,14 @@ function isWebhookEventShape(value) {
33
33
  if (typeof value !== 'object' || value === null)
34
34
  return false;
35
35
  const candidate = value;
36
- const valid = (typeof candidate.id === 'string' &&
36
+ const valid = typeof candidate.id === 'string' &&
37
37
  candidate.object === 'event' &&
38
38
  typeof candidate.type === 'string' &&
39
39
  Object.prototype.hasOwnProperty.call(WEBHOOK_EVENT_TYPES, candidate.type) &&
40
40
  typeof candidate.created === 'string' &&
41
41
  typeof candidate.data === 'object' &&
42
42
  candidate.data !== null &&
43
- 'object' in candidate.data);
43
+ 'object' in candidate.data;
44
44
  if (!valid)
45
45
  return false;
46
46
  if (candidate.type === 'billing.observation.updated') {
@@ -48,7 +48,16 @@ function isWebhookEventShape(value) {
48
48
  if (!data || typeof data !== 'object')
49
49
  return false;
50
50
  const v = data;
51
- return v.object === 'billing_observation' && (v.resourceKind === 'subscription' || v.resourceKind === 'invoice') && typeof v.resourceId === 'string' && v.resourceId.length > 0 && typeof v.revision === 'number' && Number.isSafeInteger(v.revision) && v.revision > 0 && typeof v.observedAt === 'string' && Number.isFinite(Date.parse(v.observedAt)) && Object.keys(v).every(k => ['object', 'resourceKind', 'resourceId', 'revision', 'observedAt'].includes(k));
51
+ return (v.object === 'billing_observation' &&
52
+ (v.resourceKind === 'subscription' || v.resourceKind === 'invoice') &&
53
+ typeof v.resourceId === 'string' &&
54
+ v.resourceId.length > 0 &&
55
+ typeof v.revision === 'number' &&
56
+ Number.isSafeInteger(v.revision) &&
57
+ v.revision > 0 &&
58
+ typeof v.observedAt === 'string' &&
59
+ Number.isFinite(Date.parse(v.observedAt)) &&
60
+ Object.keys(v).every((k) => ['object', 'resourceKind', 'resourceId', 'revision', 'observedAt'].includes(k)));
52
61
  }
53
62
  return true;
54
63
  }
@@ -1,8 +1,14 @@
1
- // Configuration for the server-side `Peable` client. The SDK is configured
2
- // with a confidential `ApplicationCredential` (`{publicKey, secret}`, the
3
- // SAME credential Oxy Console already issues for a `type:'service'`
4
- // credential) — never a bespoke "Peable API key". See the SDK plan's
5
- // "Architecture / auth" section.
1
+ // Configuration for the server-side `Peable` client. The SDK authenticates with
2
+ // an Oxy service token, obtained one of two ways:
3
+ //
4
+ // - a confidential `ApplicationCredential` (`{publicKey, secret}`, the SAME
5
+ // credential Oxy Console already issues for a `type:'service'` credential) —
6
+ // never a bespoke "Peable API key" — from which the SDK mints the token
7
+ // itself. See the SDK plan's "Architecture / auth" section; or
8
+ // - a caller-supplied `tokenProvider`, for a workload that already holds an Oxy
9
+ // identity (e.g. an ECS task role attested through workload identity) and
10
+ // has no key pair to hand over. The Gateway resolves the merchant from the
11
+ // token's application id and environment, not from how it was minted.
6
12
  /** Default Gateway host (`api.peable.to`) — the SDK never sends a `livemode`
7
13
  * flag; the credential's `environment` rides in the minted service JWT and
8
14
  * the Gateway resolves test/live from it. */
@@ -21,24 +27,47 @@ export function resolveRequestTimeout(value) {
21
27
  function stripTrailingSlash(url) {
22
28
  return url.endsWith('/') ? url.slice(0, -1) : url;
23
29
  }
24
- /**
25
- * Validate and normalize an `PeableConfig`. Throws a plain `Error` for a
26
- * missing `publicKey`/`secret` — a programmer error caught at construction
27
- * time, not an API-response failure, so it deliberately does not go through
28
- * the `PeableError` hierarchy (which models mapped HTTP responses).
29
- */
30
+ function isServiceTokenProvider(value) {
31
+ if (typeof value !== 'object' || value === null)
32
+ return false;
33
+ const candidate = value;
34
+ return typeof candidate.getToken === 'function' && typeof candidate.invalidate === 'function';
35
+ }
30
36
  export function resolveConfig(config) {
31
- if (!config.publicKey) {
32
- throw new Error('Peable: `publicKey` is required');
37
+ // Read loosely: a JavaScript caller is not held to the union, and "both" is
38
+ // exactly the shape the types forbid.
39
+ const { tokenProvider, publicKey, secret } = config;
40
+ if (tokenProvider !== undefined) {
41
+ if (publicKey !== undefined || secret !== undefined) {
42
+ throw new Error('Peable: pass either `tokenProvider` or `publicKey` + `secret`, not both');
43
+ }
44
+ if (!isServiceTokenProvider(tokenProvider)) {
45
+ throw new Error('Peable: `tokenProvider` must have `getToken()` and `invalidate()`');
46
+ }
33
47
  }
34
- if (!config.secret) {
35
- throw new Error('Peable: `secret` is required');
48
+ else {
49
+ if (publicKey === undefined && secret === undefined) {
50
+ throw new Error('Peable: either `publicKey` + `secret` or a `tokenProvider` is required');
51
+ }
52
+ if (!config.publicKey) {
53
+ throw new Error('Peable: `publicKey` is required');
54
+ }
55
+ if (!config.secret) {
56
+ throw new Error('Peable: `secret` is required');
57
+ }
36
58
  }
37
- return {
59
+ const options = {
38
60
  requestTimeoutMs: resolveRequestTimeout(config.requestTimeoutMs),
39
- publicKey: config.publicKey,
40
- secret: config.secret,
61
+ taxQuoteAuthorityKeys: config.taxQuoteAuthorityKeys
62
+ ? Object.freeze({ ...config.taxQuoteAuthorityKeys })
63
+ : undefined,
64
+ invoiceAuthorityKeys: config.invoiceAuthorityKeys
65
+ ? Object.freeze({ ...config.invoiceAuthorityKeys })
66
+ : undefined,
41
67
  baseURL: stripTrailingSlash(config.baseURL ?? DEFAULT_GATEWAY_BASE_URL),
42
68
  oxyApiUrl: stripTrailingSlash(config.oxyApiUrl ?? DEFAULT_OXY_API_URL),
43
69
  };
70
+ return config.tokenProvider !== undefined
71
+ ? { ...options, tokenProvider: config.tokenProvider }
72
+ : { ...options, publicKey: config.publicKey, secret: config.secret };
44
73
  }
@@ -40,7 +40,9 @@ export function createServiceTokenProvider(cfg, deps = {}) {
40
40
  try {
41
41
  response = await fetchImpl(`${resolved.oxyApiUrl}${SERVICE_TOKEN_PATH}`, {
42
42
  method: 'POST',
43
- ...(resolved.requestTimeoutMs === undefined ? {} : { signal: AbortSignal.timeout(resolved.requestTimeoutMs) }),
43
+ ...(resolved.requestTimeoutMs === undefined
44
+ ? {}
45
+ : { signal: AbortSignal.timeout(resolved.requestTimeoutMs) }),
44
46
  headers: { 'Content-Type': 'application/json' },
45
47
  body: JSON.stringify({ apiKey: resolved.publicKey, apiSecret: resolved.secret }),
46
48
  });
@@ -54,7 +56,8 @@ export function createServiceTokenProvider(cfg, deps = {}) {
54
56
  }
55
57
  catch {
56
58
  throw new PeableApiError('The Oxy service-token response body could not be read', {
57
- statusCode: response.status, code: 'invalid_response',
59
+ statusCode: response.status,
60
+ code: 'invalid_response',
58
61
  });
59
62
  }
60
63
  if (!response.ok) {
package/dist/esm/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { BillingResource } from './resources/billing.js';
2
2
  // Server entry (`@peable.to/sdk`) — the merchant-authed SDK. Configured with a
3
- // confidential `ApplicationCredential` (`{publicKey, secret}`); mints and
4
- // caches an Oxy service token, and exposes Stripe-ergonomics resource
3
+ // confidential `ApplicationCredential` (`{publicKey, secret}`), from which it
4
+ // mints and caches an Oxy service token, or with a caller-supplied
5
+ // `tokenProvider` that already holds one; exposes Stripe-ergonomics resource
5
6
  // namespaces over the Gateway REST contract.
6
7
  //
7
8
  // Deliberately never imports `socket.io-client` or `./browser/*` — the
@@ -36,9 +37,11 @@ import { TransfersResource } from './resources/transfers.js';
36
37
  export class Peable {
37
38
  constructor(config) {
38
39
  const resolved = resolveConfig(config);
39
- const tokenProvider = createServiceTokenProvider(config);
40
+ const tokenProvider = config.tokenProvider !== undefined
41
+ ? config.tokenProvider
42
+ : createServiceTokenProvider(config);
40
43
  const client = createRestClient({ baseURL: resolved.baseURL, requestTimeoutMs: resolved.requestTimeoutMs }, tokenProvider);
41
- this.billing = new BillingResource(client);
44
+ this.billing = new BillingResource(client, resolved.invoiceAuthorityKeys, resolved.taxQuoteAuthorityKeys);
42
45
  this.paymentIntents = new PaymentIntentsResource(client);
43
46
  this.paymentLinks = new PaymentLinksResource(client);
44
47
  this.checkout = new CheckoutResource(client);
@@ -1,20 +1,109 @@
1
+ import { createPublicKey, verify } from 'node:crypto';
2
+ import { canonicalBillingAuthority, canonicalBillingTaxQuote } from '@peable.to/shared-types';
1
3
  /** Recurring billing is separate from one-off checkout and marketplace transfers. */
2
4
  export class BillingResource {
3
- constructor(client) {
5
+ constructor(client, invoiceAuthorityKeys, taxQuoteAuthorityKeys) {
4
6
  this.client = client;
7
+ this.invoiceAuthorityKeys = invoiceAuthorityKeys;
8
+ this.taxQuoteAuthorityKeys = taxQuoteAuthorityKeys;
5
9
  }
6
10
  ensureCustomer(params, options) {
7
- return this.client.request('POST', '/v1/billing/customers', { body: params, idempotencyKey: options.idempotencyKey });
11
+ return this.client.request('POST', '/v1/billing/customers', {
12
+ body: params,
13
+ idempotencyKey: options.idempotencyKey,
14
+ });
8
15
  }
9
16
  createCheckoutSession(params, options) {
10
- return this.client.request('POST', '/v1/billing/checkout_sessions', { body: params, idempotencyKey: options.idempotencyKey });
17
+ return this.client.request('POST', '/v1/billing/checkout_sessions', {
18
+ body: params,
19
+ idempotencyKey: options.idempotencyKey,
20
+ });
11
21
  }
12
22
  createPortalSession(params, options) {
13
- return this.client.request('POST', '/v1/billing/portal_sessions', { body: params, idempotencyKey: options.idempotencyKey });
23
+ return this.client.request('POST', '/v1/billing/portal_sessions', {
24
+ body: params,
25
+ idempotencyKey: options.idempotencyKey,
26
+ });
27
+ }
28
+ retrieveCheckout(ref) {
29
+ return this.client.request('GET', `/v1/billing/checkout_sessions/${encodeURIComponent(ref)}`);
30
+ }
31
+ async createTaxQuote(params) {
32
+ if (!this.taxQuoteAuthorityKeys || Object.keys(this.taxQuoteAuthorityKeys).length === 0)
33
+ throw new Error('Peable: tax quote authority verification is unconfigured');
34
+ const result = await this.client.request('POST', '/v1/billing/tax_quotes', {
35
+ body: params,
36
+ });
37
+ try {
38
+ const pinned = this.taxQuoteAuthorityKeys[result.signature.keyId];
39
+ if (!pinned ||
40
+ result.schemaVersion !== 1 ||
41
+ result.signature.algorithm !== 'Ed25519' ||
42
+ !/^[A-Za-z0-9_-]{86}$/.test(result.signature.value) ||
43
+ result.source.storeId !== params.storeId ||
44
+ result.source.planId !== params.planId ||
45
+ result.quote.customerLocationEvidenceId !== params.customerLocationEvidenceId ||
46
+ !Number.isFinite(Date.parse(result.quote.quotedAt)) ||
47
+ Date.parse(result.quote.quotedAt) > Date.now() ||
48
+ !Number.isFinite(Date.parse(result.quote.expiresAt)) ||
49
+ Date.parse(result.quote.expiresAt) <= Date.now())
50
+ throw new Error('Invalid quote');
51
+ const key = createPublicKey(pinned);
52
+ if (key.asymmetricKeyType !== 'ed25519' ||
53
+ !verify(null, Buffer.from(canonicalBillingTaxQuote(result)), key, Buffer.from(result.signature.value, 'base64url')))
54
+ throw new Error('Invalid signature');
55
+ }
56
+ catch {
57
+ throw new Error('Peable: tax quote authority verification failed');
58
+ }
59
+ return result;
60
+ }
61
+ async retrieveFinalInvoiceAuthority(subscriptionRef, invoiceRef) {
62
+ if (!this.invoiceAuthorityKeys || Object.keys(this.invoiceAuthorityKeys).length === 0)
63
+ throw new Error('Peable: invoice authority verification is unconfigured');
64
+ const authority = await this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_authorities/${encodeURIComponent(invoiceRef)}`);
65
+ try {
66
+ const pinnedKey = this.invoiceAuthorityKeys[authority.signature.keyId];
67
+ if (authority.schemaVersion !== 1 ||
68
+ !pinnedKey ||
69
+ authority.signature.algorithm !== 'Ed25519' ||
70
+ !/^[A-Za-z0-9_-]{86}$/.test(authority.signature.value) ||
71
+ authority.source.invoiceId !== invoiceRef ||
72
+ authority.source.subscriptionId !== subscriptionRef ||
73
+ authority.invoice.context.providerSubscriptionId !== subscriptionRef ||
74
+ !['card', 'faircoin'].includes(authority.method) ||
75
+ !Number.isFinite(Date.parse(authority.invoice.issuedAt)) ||
76
+ Date.parse(authority.invoice.issuedAt) > Date.now() ||
77
+ !Number.isFinite(Date.parse(authority.invoice.expiresAt)) ||
78
+ Date.parse(authority.invoice.expiresAt) <= Date.now())
79
+ throw new Error('Invalid authority');
80
+ if (authority.method === 'faircoin') {
81
+ const quote = authority.invoice.faircoinQuote;
82
+ if (!quote ||
83
+ !/^[1-9][0-9]*$/.test(quote.amountBaseUnits) ||
84
+ !Number.isFinite(Date.parse(quote.quotedAt)) ||
85
+ Date.parse(quote.quotedAt) > Date.now() ||
86
+ !Number.isFinite(Date.parse(quote.expiresAt)) ||
87
+ Date.parse(quote.expiresAt) <= Date.now() ||
88
+ Date.parse(quote.expiresAt) > Date.parse(authority.invoice.expiresAt))
89
+ throw new Error('Invalid quote');
90
+ }
91
+ const key = createPublicKey(pinnedKey);
92
+ if (key.asymmetricKeyType !== 'ed25519' ||
93
+ !verify(null, Buffer.from(canonicalBillingAuthority(authority), 'utf8'), key, Buffer.from(authority.signature.value, 'base64url')))
94
+ throw new Error('Invalid signature');
95
+ }
96
+ catch {
97
+ throw new Error('Peable: invoice authority verification failed');
98
+ }
99
+ return authority;
100
+ }
101
+ retrievePaidInvoice(subscriptionRef, invoiceRef) {
102
+ return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/paid_invoices/${encodeURIComponent(invoiceRef)}`);
103
+ }
104
+ retrieveInvoiceState(subscriptionRef, invoiceRef) {
105
+ return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_states/${encodeURIComponent(invoiceRef)}`);
14
106
  }
15
- retrieveCheckout(ref) { return this.client.request('GET', `/v1/billing/checkout_sessions/${encodeURIComponent(ref)}`); }
16
- retrievePaidInvoice(subscriptionRef, invoiceRef) { return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/paid_invoices/${encodeURIComponent(invoiceRef)}`); }
17
- retrieveInvoiceState(subscriptionRef, invoiceRef) { return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_states/${encodeURIComponent(invoiceRef)}`); }
18
107
  retrieveSubscription(ref) {
19
108
  return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(ref)}`);
20
109
  }
@@ -16,7 +16,10 @@ export class RefundsResource {
16
16
  * arrives.
17
17
  */
18
18
  create(params, options = {}) {
19
- return this.client.request('POST', '/v1/refunds', { body: params, idempotencyKey: options.idempotencyKey });
19
+ return this.client.request('POST', '/v1/refunds', {
20
+ body: params,
21
+ idempotencyKey: options.idempotencyKey,
22
+ });
20
23
  }
21
24
  /**
22
25
  * What this payment actually came to — gross, fee, net.
@@ -15,7 +15,10 @@ export class TransfersResource {
15
15
  * money in flight.
16
16
  */
17
17
  create(params, options = {}) {
18
- return this.client.request('POST', '/v1/transfers', { body: params, idempotencyKey: options.idempotencyKey });
18
+ return this.client.request('POST', '/v1/transfers', {
19
+ body: params,
20
+ idempotencyKey: options.idempotencyKey,
21
+ });
19
22
  }
20
23
  /**
21
24
  * Take some or all of a settlement back.
@@ -33,9 +36,7 @@ export class TransfersResource {
33
36
  }
34
37
  return this.client.request('POST', `/v1/transfers/${encodeURIComponent(transferId)}/reversals`, {
35
38
  body: params,
36
- ...(options.idempotencyKey === undefined
37
- ? {}
38
- : { idempotencyKey: options.idempotencyKey }),
39
+ ...(options.idempotencyKey === undefined ? {} : { idempotencyKey: options.idempotencyKey }),
39
40
  });
40
41
  }
41
42
  /** What one payment settled — the reconciliation read. */
@@ -30,14 +30,14 @@ function isWebhookEventShape(value) {
30
30
  if (typeof value !== 'object' || value === null)
31
31
  return false;
32
32
  const candidate = value;
33
- const valid = (typeof candidate.id === 'string' &&
33
+ const valid = typeof candidate.id === 'string' &&
34
34
  candidate.object === 'event' &&
35
35
  typeof candidate.type === 'string' &&
36
36
  Object.prototype.hasOwnProperty.call(WEBHOOK_EVENT_TYPES, candidate.type) &&
37
37
  typeof candidate.created === 'string' &&
38
38
  typeof candidate.data === 'object' &&
39
39
  candidate.data !== null &&
40
- 'object' in candidate.data);
40
+ 'object' in candidate.data;
41
41
  if (!valid)
42
42
  return false;
43
43
  if (candidate.type === 'billing.observation.updated') {
@@ -45,7 +45,16 @@ function isWebhookEventShape(value) {
45
45
  if (!data || typeof data !== 'object')
46
46
  return false;
47
47
  const v = data;
48
- return v.object === 'billing_observation' && (v.resourceKind === 'subscription' || v.resourceKind === 'invoice') && typeof v.resourceId === 'string' && v.resourceId.length > 0 && typeof v.revision === 'number' && Number.isSafeInteger(v.revision) && v.revision > 0 && typeof v.observedAt === 'string' && Number.isFinite(Date.parse(v.observedAt)) && Object.keys(v).every(k => ['object', 'resourceKind', 'resourceId', 'revision', 'observedAt'].includes(k));
48
+ return (v.object === 'billing_observation' &&
49
+ (v.resourceKind === 'subscription' || v.resourceKind === 'invoice') &&
50
+ typeof v.resourceId === 'string' &&
51
+ v.resourceId.length > 0 &&
52
+ typeof v.revision === 'number' &&
53
+ Number.isSafeInteger(v.revision) &&
54
+ v.revision > 0 &&
55
+ typeof v.observedAt === 'string' &&
56
+ Number.isFinite(Date.parse(v.observedAt)) &&
57
+ Object.keys(v).every((k) => ['object', 'resourceKind', 'resourceId', 'revision', 'observedAt'].includes(k)));
49
58
  }
50
59
  return true;
51
60
  }
@@ -1,34 +1,71 @@
1
+ import type { ServiceTokenProvider } from './serviceToken';
1
2
  /** Default Gateway host (`api.peable.to`) — the SDK never sends a `livemode`
2
3
  * flag; the credential's `environment` rides in the minted service JWT and
3
4
  * the Gateway resolves test/live from it. */
4
5
  export declare const DEFAULT_GATEWAY_BASE_URL = "https://api.peable.to";
5
6
  /** Default oxy-api host used to mint the short-lived service token. */
6
7
  export declare const DEFAULT_OXY_API_URL = "https://api.oxy.so";
7
- export interface PeableConfig {
8
- /** ApplicationCredential publicKey (e.g. `oxy_dk_...`). */
9
- publicKey: string;
10
- /** ApplicationCredential plaintext secret. */
11
- secret: string;
8
+ /** Options shared by both ways of authenticating. */
9
+ interface PeableConfigOptions {
12
10
  /** Gateway base URL. Default `https://api.peable.to`. */
13
11
  baseURL?: string;
14
- /** oxy-api host used to mint the service token. Default `https://api.oxy.so`. */
12
+ /** oxy-api host used to mint the service token. Default `https://api.oxy.so`.
13
+ * Unused with `tokenProvider`, which mints its own. */
15
14
  oxyApiUrl?: string;
16
15
  /** Optional deadline per HTTP attempt, including body consumption. */
17
16
  requestTimeoutMs?: number;
17
+ /** Pinned Ed25519 authority keys, obtained from trusted deployment configuration. */
18
+ invoiceAuthorityKeys?: Readonly<Record<string, string>>;
19
+ taxQuoteAuthorityKeys?: Readonly<Record<string, string>>;
18
20
  }
19
- /** `PeableConfig` with every optional field defaulted and normalized. */
20
- export interface ResolvedPeableConfig {
21
+ /** Authenticate with an `ApplicationCredential`; the SDK mints the token. */
22
+ export interface PeableCredentialConfig extends PeableConfigOptions {
23
+ /** ApplicationCredential publicKey (e.g. `oxy_dk_...`). */
21
24
  publicKey: string;
25
+ /** ApplicationCredential plaintext secret. */
22
26
  secret: string;
27
+ tokenProvider?: undefined;
28
+ }
29
+ /** Authenticate with Oxy service tokens the caller already knows how to obtain. */
30
+ export interface PeableTokenProviderConfig extends PeableConfigOptions {
31
+ /** Supplies the bearer for every Gateway request. `invalidate()` is called
32
+ * when the Gateway answers 401, before the single retry; the next
33
+ * `getToken()` must then return a freshly minted token. */
34
+ tokenProvider: ServiceTokenProvider;
35
+ publicKey?: undefined;
36
+ secret?: undefined;
37
+ }
38
+ export type PeableConfig = PeableCredentialConfig | PeableTokenProviderConfig;
39
+ interface ResolvedPeableConfigOptions {
23
40
  baseURL: string;
24
41
  oxyApiUrl: string;
25
42
  requestTimeoutMs?: number;
43
+ invoiceAuthorityKeys?: Readonly<Record<string, string>>;
44
+ taxQuoteAuthorityKeys?: Readonly<Record<string, string>>;
26
45
  }
46
+ /** `PeableCredentialConfig` with every optional field defaulted and normalized. */
47
+ export interface ResolvedPeableCredentialConfig extends ResolvedPeableConfigOptions {
48
+ publicKey: string;
49
+ secret: string;
50
+ tokenProvider?: undefined;
51
+ }
52
+ /** `PeableTokenProviderConfig` with every optional field defaulted and normalized. */
53
+ export interface ResolvedPeableTokenProviderConfig extends ResolvedPeableConfigOptions {
54
+ tokenProvider: ServiceTokenProvider;
55
+ publicKey?: undefined;
56
+ secret?: undefined;
57
+ }
58
+ /** `PeableConfig` with every optional field defaulted and normalized. */
59
+ export type ResolvedPeableConfig = ResolvedPeableCredentialConfig | ResolvedPeableTokenProviderConfig;
27
60
  export declare function resolveRequestTimeout(value: number | undefined): number | undefined;
28
61
  /**
29
62
  * Validate and normalize an `PeableConfig`. Throws a plain `Error` for a
30
- * missing `publicKey`/`secret` — a programmer error caught at construction
31
- * time, not an API-response failure, so it deliberately does not go through
32
- * the `PeableError` hierarchy (which models mapped HTTP responses).
63
+ * missing `publicKey`/`secret`, for a `tokenProvider` passed alongside either
64
+ * of them or for neither being passed — a programmer error caught at
65
+ * construction time, not an API-response failure, so it deliberately does not
66
+ * go through the `PeableError` hierarchy (which models mapped HTTP responses).
33
67
  */
68
+ export declare function resolveConfig(config: PeableCredentialConfig): ResolvedPeableCredentialConfig;
69
+ export declare function resolveConfig(config: PeableTokenProviderConfig): ResolvedPeableTokenProviderConfig;
34
70
  export declare function resolveConfig(config: PeableConfig): ResolvedPeableConfig;
71
+ export {};
@@ -1,4 +1,4 @@
1
- import { type PeableConfig } from './config';
1
+ import { type PeableCredentialConfig } from './config';
2
2
  export interface ServiceTokenProvider {
3
3
  /** Mints once, caches until near expiry, re-mints on expiry/401. */
4
4
  getToken(): Promise<string>;
@@ -6,6 +6,6 @@ export interface ServiceTokenProvider {
6
6
  * `RestClient` after a Gateway request comes back 401. */
7
7
  invalidate(): void;
8
8
  }
9
- export declare function createServiceTokenProvider(cfg: PeableConfig, deps?: {
9
+ export declare function createServiceTokenProvider(cfg: PeableCredentialConfig, deps?: {
10
10
  fetch?: typeof fetch;
11
11
  }): ServiceTokenProvider;
@@ -1,6 +1,6 @@
1
1
  import { BillingResource } from './resources/billing';
2
2
  export type { BillingRequestOptions } from './resources/billing';
3
- export type { BillingObservation, BillingCustomer, BillingHostedSession, BillingCheckoutSession, BillingCheckoutObservation, BillingPaidInvoice, BillingInvoiceState, BillingSubscription, CreateBillingCheckoutParams, CreateBillingPortalParams, EnsureBillingCustomerParams } from '@peable.to/shared-types';
3
+ export type { CreateBillingTaxQuoteParams, BillingTaxQuote, BillingInvoiceAuthoritySource, BillingFinalInvoice, BillingFinalInvoiceAuthority, BillingFaircoinRenewalChoice, BillingFaircoinRenewalConsent, BillingFaircoinRenewalRevocation, BillingObservation, BillingCustomer, BillingHostedSession, BillingCheckoutSession, BillingCheckoutObservation, BillingPaidInvoice, BillingInvoiceState, BillingSubscription, CreateBillingCheckoutParams, CreateBillingPortalParams, EnsureBillingCustomerParams, } from '@peable.to/shared-types';
4
4
  import { type PeableConfig } from './core/config';
5
5
  import { PaymentIntentsResource } from './resources/paymentIntents';
6
6
  import { PaymentLinksResource } from './resources/paymentLinks';
@@ -44,7 +44,7 @@ export declare class Peable {
44
44
  readonly disputes: DisputesResource;
45
45
  constructor(config: PeableConfig);
46
46
  }
47
- export type { PeableConfig, ResolvedPeableConfig } from './core/config';
47
+ export type { PeableConfig, PeableCredentialConfig, PeableTokenProviderConfig, ResolvedPeableConfig, } from './core/config';
48
48
  export { DEFAULT_GATEWAY_BASE_URL, DEFAULT_OXY_API_URL } from './core/config';
49
49
  export { PeableError, PeableAuthenticationError, PeableInvalidRequestError, PeablePermissionError, PeableApiError, PeableSignatureVerificationError, } from './core/errors';
50
50
  export type { PeableErrorType, PeableErrorDetails } from './core/errors';
@@ -1,4 +1,4 @@
1
- import type { BillingCustomer, BillingHostedSession, BillingCheckoutSession, BillingCheckoutObservation, BillingPaidInvoice, BillingInvoiceState, BillingSubscription, CreateBillingCheckoutParams, CreateBillingPortalParams, EnsureBillingCustomerParams } from '@peable.to/shared-types';
1
+ import type { BillingTaxQuote, CreateBillingTaxQuoteParams, BillingFinalInvoiceAuthority, BillingCustomer, BillingHostedSession, BillingCheckoutSession, BillingCheckoutObservation, BillingPaidInvoice, BillingInvoiceState, BillingSubscription, CreateBillingCheckoutParams, CreateBillingPortalParams, EnsureBillingCustomerParams } from '@peable.to/shared-types';
2
2
  import type { RestClient } from '../core/client';
3
3
  export interface BillingRequestOptions {
4
4
  idempotencyKey: string;
@@ -6,11 +6,15 @@ export interface BillingRequestOptions {
6
6
  /** Recurring billing is separate from one-off checkout and marketplace transfers. */
7
7
  export declare class BillingResource {
8
8
  private readonly client;
9
- constructor(client: RestClient);
9
+ private readonly invoiceAuthorityKeys?;
10
+ private readonly taxQuoteAuthorityKeys?;
11
+ constructor(client: RestClient, invoiceAuthorityKeys?: Readonly<Record<string, string>> | undefined, taxQuoteAuthorityKeys?: Readonly<Record<string, string>> | undefined);
10
12
  ensureCustomer(params: EnsureBillingCustomerParams, options: BillingRequestOptions): Promise<BillingCustomer>;
11
13
  createCheckoutSession(params: CreateBillingCheckoutParams, options: BillingRequestOptions): Promise<BillingCheckoutSession>;
12
14
  createPortalSession(params: CreateBillingPortalParams, options: BillingRequestOptions): Promise<BillingHostedSession>;
13
15
  retrieveCheckout(ref: string): Promise<BillingCheckoutObservation>;
16
+ createTaxQuote(params: CreateBillingTaxQuoteParams): Promise<BillingTaxQuote>;
17
+ retrieveFinalInvoiceAuthority(subscriptionRef: string, invoiceRef: string): Promise<BillingFinalInvoiceAuthority>;
14
18
  retrievePaidInvoice(subscriptionRef: string, invoiceRef: string): Promise<BillingPaidInvoice>;
15
19
  retrieveInvoiceState(subscriptionRef: string, invoiceRef: string): Promise<BillingInvoiceState>;
16
20
  retrieveSubscription(ref: string): Promise<BillingSubscription>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peable.to/sdk",
3
- "version": "0.2.3-oxy-one.0",
3
+ "version": "0.2.5-oxy-one.0",
4
4
  "description": "Peable SDK — a Stripe-ergonomics TypeScript client over the Peable Gateway REST + realtime contract. Server entry (`@peable.to/sdk`) mints Oxy service tokens from an ApplicationCredential and exposes paymentIntents/paymentLinks/checkout.sessions/webhooks; browser entry (`@peable.to/sdk/checkout`) is the anonymous payer-side client core.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -67,13 +67,12 @@
67
67
  "build:types": "tsc -p tsconfig.types.json",
68
68
  "clean": "rm -rf dist",
69
69
  "dev": "tsc --watch",
70
- "lint": "tsc --noEmit",
71
70
  "typecheck": "tsc --noEmit",
72
71
  "test": "bun test",
73
72
  "prepublishOnly": "bun run clean && bun run build"
74
73
  },
75
74
  "dependencies": {
76
- "@peable.to/shared-types": "0.3.1-oxy-one.0",
75
+ "@peable.to/shared-types": "0.3.2-oxy-one.0",
77
76
  "qrcode-generator": "^2.0.4",
78
77
  "socket.io-client": "^4.8.3"
79
78
  },