@peable.to/sdk 0.2.4-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 +10 -0
- package/dist/cjs/core/config.js +47 -20
- package/dist/cjs/core/serviceToken.js +5 -2
- package/dist/cjs/index.js +6 -3
- package/dist/cjs/resources/billing.js +58 -22
- package/dist/cjs/resources/refunds.js +4 -1
- package/dist/cjs/resources/transfers.js +5 -4
- package/dist/cjs/resources/webhooks.js +12 -3
- package/dist/esm/core/config.js +47 -20
- package/dist/esm/core/serviceToken.js +5 -2
- package/dist/esm/index.js +6 -3
- package/dist/esm/resources/billing.js +58 -22
- package/dist/esm/resources/refunds.js +4 -1
- package/dist/esm/resources/transfers.js +5 -4
- package/dist/esm/resources/webhooks.js +12 -3
- package/dist/types/core/config.d.ts +43 -11
- package/dist/types/core/serviceToken.d.ts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/package.json +1 -2
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
|
|
package/dist/cjs/core/config.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Configuration for the server-side `Peable` client. The SDK
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
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,26 +32,47 @@ function resolveRequestTimeout(value) {
|
|
|
26
32
|
function stripTrailingSlash(url) {
|
|
27
33
|
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
28
34
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
64
|
+
const options = {
|
|
43
65
|
requestTimeoutMs: resolveRequestTimeout(config.requestTimeoutMs),
|
|
44
|
-
taxQuoteAuthorityKeys: config.taxQuoteAuthorityKeys
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
taxQuoteAuthorityKeys: config.taxQuoteAuthorityKeys
|
|
67
|
+
? Object.freeze({ ...config.taxQuoteAuthorityKeys })
|
|
68
|
+
: undefined,
|
|
69
|
+
invoiceAuthorityKeys: config.invoiceAuthorityKeys
|
|
70
|
+
? Object.freeze({ ...config.invoiceAuthorityKeys })
|
|
71
|
+
: undefined,
|
|
48
72
|
baseURL: stripTrailingSlash(config.baseURL ?? exports.DEFAULT_GATEWAY_BASE_URL),
|
|
49
73
|
oxyApiUrl: stripTrailingSlash(config.oxyApiUrl ?? exports.DEFAULT_OXY_API_URL),
|
|
50
74
|
};
|
|
75
|
+
return config.tokenProvider !== undefined
|
|
76
|
+
? { ...options, tokenProvider: config.tokenProvider }
|
|
77
|
+
: { ...options, publicKey: config.publicKey, secret: config.secret };
|
|
51
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
|
|
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,
|
|
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}`)
|
|
7
|
-
// caches an Oxy service token,
|
|
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,7 +40,9 @@ 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 =
|
|
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
47
|
this.billing = new billing_1.BillingResource(client, resolved.invoiceAuthorityKeys, resolved.taxQuoteAuthorityKeys);
|
|
45
48
|
this.paymentIntents = new paymentIntents_1.PaymentIntentsResource(client);
|
|
@@ -11,28 +11,49 @@ class BillingResource {
|
|
|
11
11
|
this.taxQuoteAuthorityKeys = taxQuoteAuthorityKeys;
|
|
12
12
|
}
|
|
13
13
|
ensureCustomer(params, options) {
|
|
14
|
-
return this.client.request('POST', '/v1/billing/customers', {
|
|
14
|
+
return this.client.request('POST', '/v1/billing/customers', {
|
|
15
|
+
body: params,
|
|
16
|
+
idempotencyKey: options.idempotencyKey,
|
|
17
|
+
});
|
|
15
18
|
}
|
|
16
19
|
createCheckoutSession(params, options) {
|
|
17
|
-
return this.client.request('POST', '/v1/billing/checkout_sessions', {
|
|
20
|
+
return this.client.request('POST', '/v1/billing/checkout_sessions', {
|
|
21
|
+
body: params,
|
|
22
|
+
idempotencyKey: options.idempotencyKey,
|
|
23
|
+
});
|
|
18
24
|
}
|
|
19
25
|
createPortalSession(params, options) {
|
|
20
|
-
return this.client.request('POST', '/v1/billing/portal_sessions', {
|
|
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)}`);
|
|
21
33
|
}
|
|
22
|
-
retrieveCheckout(ref) { return this.client.request('GET', `/v1/billing/checkout_sessions/${encodeURIComponent(ref)}`); }
|
|
23
34
|
async createTaxQuote(params) {
|
|
24
35
|
if (!this.taxQuoteAuthorityKeys || Object.keys(this.taxQuoteAuthorityKeys).length === 0)
|
|
25
36
|
throw new Error('Peable: tax quote authority verification is unconfigured');
|
|
26
|
-
const result = await this.client.request('POST', '/v1/billing/tax_quotes', {
|
|
37
|
+
const result = await this.client.request('POST', '/v1/billing/tax_quotes', {
|
|
38
|
+
body: params,
|
|
39
|
+
});
|
|
27
40
|
try {
|
|
28
41
|
const pinned = this.taxQuoteAuthorityKeys[result.signature.keyId];
|
|
29
|
-
if (!pinned ||
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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())
|
|
33
53
|
throw new Error('Invalid quote');
|
|
34
54
|
const key = (0, node_crypto_1.createPublicKey)(pinned);
|
|
35
|
-
if (key.asymmetricKeyType !== 'ed25519' ||
|
|
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')))
|
|
36
57
|
throw new Error('Invalid signature');
|
|
37
58
|
}
|
|
38
59
|
catch {
|
|
@@ -46,22 +67,33 @@ class BillingResource {
|
|
|
46
67
|
const authority = await this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_authorities/${encodeURIComponent(invoiceRef)}`);
|
|
47
68
|
try {
|
|
48
69
|
const pinnedKey = this.invoiceAuthorityKeys[authority.signature.keyId];
|
|
49
|
-
if (authority.schemaVersion !== 1 ||
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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())
|
|
55
82
|
throw new Error('Invalid authority');
|
|
56
83
|
if (authority.method === 'faircoin') {
|
|
57
84
|
const quote = authority.invoice.faircoinQuote;
|
|
58
|
-
if (!quote ||
|
|
59
|
-
|
|
60
|
-
|
|
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))
|
|
61
92
|
throw new Error('Invalid quote');
|
|
62
93
|
}
|
|
63
94
|
const key = (0, node_crypto_1.createPublicKey)(pinnedKey);
|
|
64
|
-
if (key.asymmetricKeyType !== 'ed25519' ||
|
|
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')))
|
|
65
97
|
throw new Error('Invalid signature');
|
|
66
98
|
}
|
|
67
99
|
catch {
|
|
@@ -69,8 +101,12 @@ class BillingResource {
|
|
|
69
101
|
}
|
|
70
102
|
return authority;
|
|
71
103
|
}
|
|
72
|
-
retrievePaidInvoice(subscriptionRef, invoiceRef) {
|
|
73
|
-
|
|
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)}`);
|
|
109
|
+
}
|
|
74
110
|
retrieveSubscription(ref) {
|
|
75
111
|
return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(ref)}`);
|
|
76
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', {
|
|
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', {
|
|
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 =
|
|
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' &&
|
|
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
|
}
|
package/dist/esm/core/config.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
// Configuration for the server-side `Peable` client. The SDK
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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,26 +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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
59
|
+
const options = {
|
|
38
60
|
requestTimeoutMs: resolveRequestTimeout(config.requestTimeoutMs),
|
|
39
|
-
taxQuoteAuthorityKeys: config.taxQuoteAuthorityKeys
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
61
|
+
taxQuoteAuthorityKeys: config.taxQuoteAuthorityKeys
|
|
62
|
+
? Object.freeze({ ...config.taxQuoteAuthorityKeys })
|
|
63
|
+
: undefined,
|
|
64
|
+
invoiceAuthorityKeys: config.invoiceAuthorityKeys
|
|
65
|
+
? Object.freeze({ ...config.invoiceAuthorityKeys })
|
|
66
|
+
: undefined,
|
|
43
67
|
baseURL: stripTrailingSlash(config.baseURL ?? DEFAULT_GATEWAY_BASE_URL),
|
|
44
68
|
oxyApiUrl: stripTrailingSlash(config.oxyApiUrl ?? DEFAULT_OXY_API_URL),
|
|
45
69
|
};
|
|
70
|
+
return config.tokenProvider !== undefined
|
|
71
|
+
? { ...options, tokenProvider: config.tokenProvider }
|
|
72
|
+
: { ...options, publicKey: config.publicKey, secret: config.secret };
|
|
46
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
|
|
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,
|
|
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}`)
|
|
4
|
-
// caches an Oxy service token,
|
|
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,7 +37,9 @@ import { TransfersResource } from './resources/transfers.js';
|
|
|
36
37
|
export class Peable {
|
|
37
38
|
constructor(config) {
|
|
38
39
|
const resolved = resolveConfig(config);
|
|
39
|
-
const tokenProvider =
|
|
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
44
|
this.billing = new BillingResource(client, resolved.invoiceAuthorityKeys, resolved.taxQuoteAuthorityKeys);
|
|
42
45
|
this.paymentIntents = new PaymentIntentsResource(client);
|
|
@@ -8,28 +8,49 @@ export class BillingResource {
|
|
|
8
8
|
this.taxQuoteAuthorityKeys = taxQuoteAuthorityKeys;
|
|
9
9
|
}
|
|
10
10
|
ensureCustomer(params, options) {
|
|
11
|
-
return this.client.request('POST', '/v1/billing/customers', {
|
|
11
|
+
return this.client.request('POST', '/v1/billing/customers', {
|
|
12
|
+
body: params,
|
|
13
|
+
idempotencyKey: options.idempotencyKey,
|
|
14
|
+
});
|
|
12
15
|
}
|
|
13
16
|
createCheckoutSession(params, options) {
|
|
14
|
-
return this.client.request('POST', '/v1/billing/checkout_sessions', {
|
|
17
|
+
return this.client.request('POST', '/v1/billing/checkout_sessions', {
|
|
18
|
+
body: params,
|
|
19
|
+
idempotencyKey: options.idempotencyKey,
|
|
20
|
+
});
|
|
15
21
|
}
|
|
16
22
|
createPortalSession(params, options) {
|
|
17
|
-
return this.client.request('POST', '/v1/billing/portal_sessions', {
|
|
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)}`);
|
|
18
30
|
}
|
|
19
|
-
retrieveCheckout(ref) { return this.client.request('GET', `/v1/billing/checkout_sessions/${encodeURIComponent(ref)}`); }
|
|
20
31
|
async createTaxQuote(params) {
|
|
21
32
|
if (!this.taxQuoteAuthorityKeys || Object.keys(this.taxQuoteAuthorityKeys).length === 0)
|
|
22
33
|
throw new Error('Peable: tax quote authority verification is unconfigured');
|
|
23
|
-
const result = await this.client.request('POST', '/v1/billing/tax_quotes', {
|
|
34
|
+
const result = await this.client.request('POST', '/v1/billing/tax_quotes', {
|
|
35
|
+
body: params,
|
|
36
|
+
});
|
|
24
37
|
try {
|
|
25
38
|
const pinned = this.taxQuoteAuthorityKeys[result.signature.keyId];
|
|
26
|
-
if (!pinned ||
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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())
|
|
30
50
|
throw new Error('Invalid quote');
|
|
31
51
|
const key = createPublicKey(pinned);
|
|
32
|
-
if (key.asymmetricKeyType !== 'ed25519' ||
|
|
52
|
+
if (key.asymmetricKeyType !== 'ed25519' ||
|
|
53
|
+
!verify(null, Buffer.from(canonicalBillingTaxQuote(result)), key, Buffer.from(result.signature.value, 'base64url')))
|
|
33
54
|
throw new Error('Invalid signature');
|
|
34
55
|
}
|
|
35
56
|
catch {
|
|
@@ -43,22 +64,33 @@ export class BillingResource {
|
|
|
43
64
|
const authority = await this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(subscriptionRef)}/invoice_authorities/${encodeURIComponent(invoiceRef)}`);
|
|
44
65
|
try {
|
|
45
66
|
const pinnedKey = this.invoiceAuthorityKeys[authority.signature.keyId];
|
|
46
|
-
if (authority.schemaVersion !== 1 ||
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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())
|
|
52
79
|
throw new Error('Invalid authority');
|
|
53
80
|
if (authority.method === 'faircoin') {
|
|
54
81
|
const quote = authority.invoice.faircoinQuote;
|
|
55
|
-
if (!quote ||
|
|
56
|
-
|
|
57
|
-
|
|
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))
|
|
58
89
|
throw new Error('Invalid quote');
|
|
59
90
|
}
|
|
60
91
|
const key = createPublicKey(pinnedKey);
|
|
61
|
-
if (key.asymmetricKeyType !== 'ed25519' ||
|
|
92
|
+
if (key.asymmetricKeyType !== 'ed25519' ||
|
|
93
|
+
!verify(null, Buffer.from(canonicalBillingAuthority(authority), 'utf8'), key, Buffer.from(authority.signature.value, 'base64url')))
|
|
62
94
|
throw new Error('Invalid signature');
|
|
63
95
|
}
|
|
64
96
|
catch {
|
|
@@ -66,8 +98,12 @@ export class BillingResource {
|
|
|
66
98
|
}
|
|
67
99
|
return authority;
|
|
68
100
|
}
|
|
69
|
-
retrievePaidInvoice(subscriptionRef, invoiceRef) {
|
|
70
|
-
|
|
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)}`);
|
|
106
|
+
}
|
|
71
107
|
retrieveSubscription(ref) {
|
|
72
108
|
return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(ref)}`);
|
|
73
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', {
|
|
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', {
|
|
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 =
|
|
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' &&
|
|
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,17 +1,16 @@
|
|
|
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
|
-
|
|
8
|
-
|
|
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;
|
|
@@ -19,21 +18,54 @@ export interface PeableConfig {
|
|
|
19
18
|
invoiceAuthorityKeys?: Readonly<Record<string, string>>;
|
|
20
19
|
taxQuoteAuthorityKeys?: Readonly<Record<string, string>>;
|
|
21
20
|
}
|
|
22
|
-
/**
|
|
23
|
-
export interface
|
|
21
|
+
/** Authenticate with an `ApplicationCredential`; the SDK mints the token. */
|
|
22
|
+
export interface PeableCredentialConfig extends PeableConfigOptions {
|
|
23
|
+
/** ApplicationCredential publicKey (e.g. `oxy_dk_...`). */
|
|
24
24
|
publicKey: string;
|
|
25
|
+
/** ApplicationCredential plaintext secret. */
|
|
25
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 {
|
|
26
40
|
baseURL: string;
|
|
27
41
|
oxyApiUrl: string;
|
|
28
42
|
requestTimeoutMs?: number;
|
|
29
43
|
invoiceAuthorityKeys?: Readonly<Record<string, string>>;
|
|
30
44
|
taxQuoteAuthorityKeys?: Readonly<Record<string, string>>;
|
|
31
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;
|
|
32
60
|
export declare function resolveRequestTimeout(value: number | undefined): number | undefined;
|
|
33
61
|
/**
|
|
34
62
|
* Validate and normalize an `PeableConfig`. Throws a plain `Error` for a
|
|
35
|
-
* missing `publicKey`/`secret
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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).
|
|
38
67
|
*/
|
|
68
|
+
export declare function resolveConfig(config: PeableCredentialConfig): ResolvedPeableCredentialConfig;
|
|
69
|
+
export declare function resolveConfig(config: PeableTokenProviderConfig): ResolvedPeableTokenProviderConfig;
|
|
39
70
|
export declare function resolveConfig(config: PeableConfig): ResolvedPeableConfig;
|
|
71
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
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:
|
|
9
|
+
export declare function createServiceTokenProvider(cfg: PeableCredentialConfig, deps?: {
|
|
10
10
|
fetch?: typeof fetch;
|
|
11
11
|
}): ServiceTokenProvider;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BillingResource } from './resources/billing';
|
|
2
2
|
export type { BillingRequestOptions } from './resources/billing';
|
|
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';
|
|
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';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peable.to/sdk",
|
|
3
|
-
"version": "0.2.
|
|
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,7 +67,6 @@
|
|
|
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"
|