@peable.to/sdk 0.2.0 → 0.2.2
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 +14 -0
- package/dist/cjs/core/client.js +3 -0
- package/dist/cjs/core/config.js +11 -0
- package/dist/cjs/core/serviceToken.js +10 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/resources/paymentIntents.js +2 -2
- package/dist/cjs/resources/refunds.js +2 -2
- package/dist/cjs/resources/transfers.js +2 -2
- package/dist/cjs/resources/webhooks.js +16 -8
- package/dist/esm/core/client.js +3 -0
- package/dist/esm/core/config.js +10 -0
- package/dist/esm/core/serviceToken.js +10 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/resources/paymentIntents.js +2 -2
- package/dist/esm/resources/refunds.js +2 -2
- package/dist/esm/resources/transfers.js +2 -2
- package/dist/esm/resources/webhooks.js +16 -8
- package/dist/types/core/client.d.ts +1 -0
- package/dist/types/core/config.d.ts +4 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/resources/paymentIntents.d.ts +3 -1
- package/dist/types/resources/refunds.d.ts +3 -1
- package/dist/types/resources/transfers.d.ts +3 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
### HTTP deadlines and existing operation keys
|
|
3
|
+
|
|
4
|
+
SDK0.2.1 accepts `requestTimeoutMs` on `new Peable(...)`. It bounds each token
|
|
5
|
+
mint and gateway HTTP attempt, including reading its body. Omitting it preserves
|
|
6
|
+
the previous timeout behavior. A consumer replacing a20s HTTP client should
|
|
7
|
+
pass `requestTimeoutMs: 20_000`. A401 may still trigger the existing single token
|
|
8
|
+
refresh/retry; timeouts and interrupted responses never retry a payment POST.
|
|
9
|
+
The remote outcome may be unknown: recover explicitly with the same durable key.
|
|
10
|
+
|
|
11
|
+
`paymentIntents.reject`, `refunds.create`, and `transfers.create` now accept an
|
|
12
|
+
optional second `{ idempotencyKey }` argument, forwarding the existing header.
|
|
13
|
+
Refund/transfer `externalRef` remains their durable business identity; the option
|
|
14
|
+
neither replaces it nor changes gateway financial semantics.
|
package/dist/cjs/core/client.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// maps every non-2xx response via `errors.ts`.
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.createRestClient = createRestClient;
|
|
8
|
+
const config_1 = require("./config");
|
|
8
9
|
const errors_1 = require("./errors");
|
|
9
10
|
const HTTP_UNAUTHORIZED = 401;
|
|
10
11
|
function buildUrl(baseURL, path, query) {
|
|
@@ -32,6 +33,7 @@ async function readJsonBody(response) {
|
|
|
32
33
|
}
|
|
33
34
|
function createRestClient(config, tokenProvider, deps = {}) {
|
|
34
35
|
const fetchImpl = deps.fetch ?? fetch;
|
|
36
|
+
const timeout = (0, config_1.resolveRequestTimeout)(config.requestTimeoutMs);
|
|
35
37
|
async function performRequest(method, path, opts, allowRetryOn401) {
|
|
36
38
|
const token = await tokenProvider.getToken();
|
|
37
39
|
const headers = { Authorization: `Bearer ${token}` };
|
|
@@ -46,6 +48,7 @@ function createRestClient(config, tokenProvider, deps = {}) {
|
|
|
46
48
|
response = await fetchImpl(buildUrl(config.baseURL, path, opts.query), {
|
|
47
49
|
method,
|
|
48
50
|
headers,
|
|
51
|
+
...(timeout === undefined ? {} : { signal: AbortSignal.timeout(timeout) }),
|
|
49
52
|
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
50
53
|
});
|
|
51
54
|
}
|
package/dist/cjs/core/config.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// "Architecture / auth" section.
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.DEFAULT_OXY_API_URL = exports.DEFAULT_GATEWAY_BASE_URL = void 0;
|
|
9
|
+
exports.resolveRequestTimeout = resolveRequestTimeout;
|
|
9
10
|
exports.resolveConfig = resolveConfig;
|
|
10
11
|
/** Default Gateway host (`api.peable.to`) — the SDK never sends a `livemode`
|
|
11
12
|
* flag; the credential's `environment` rides in the minted service JWT and
|
|
@@ -13,6 +14,15 @@ exports.resolveConfig = resolveConfig;
|
|
|
13
14
|
exports.DEFAULT_GATEWAY_BASE_URL = 'https://api.peable.to';
|
|
14
15
|
/** Default oxy-api host used to mint the short-lived service token. */
|
|
15
16
|
exports.DEFAULT_OXY_API_URL = 'https://api.oxy.so';
|
|
17
|
+
function resolveRequestTimeout(value) {
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
const timeout = value;
|
|
21
|
+
if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 2147483647) {
|
|
22
|
+
throw new Error('Peable: requestTimeoutMs must be a positive32-bit integer');
|
|
23
|
+
}
|
|
24
|
+
return timeout;
|
|
25
|
+
}
|
|
16
26
|
function stripTrailingSlash(url) {
|
|
17
27
|
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
18
28
|
}
|
|
@@ -30,6 +40,7 @@ function resolveConfig(config) {
|
|
|
30
40
|
throw new Error('Peable: `secret` is required');
|
|
31
41
|
}
|
|
32
42
|
return {
|
|
43
|
+
requestTimeoutMs: resolveRequestTimeout(config.requestTimeoutMs),
|
|
33
44
|
publicKey: config.publicKey,
|
|
34
45
|
secret: config.secret,
|
|
35
46
|
baseURL: stripTrailingSlash(config.baseURL ?? exports.DEFAULT_GATEWAY_BASE_URL),
|
|
@@ -43,6 +43,7 @@ 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
47
|
headers: { 'Content-Type': 'application/json' },
|
|
47
48
|
body: JSON.stringify({ apiKey: resolved.publicKey, apiSecret: resolved.secret }),
|
|
48
49
|
});
|
|
@@ -50,7 +51,15 @@ function createServiceTokenProvider(cfg, deps = {}) {
|
|
|
50
51
|
catch (cause) {
|
|
51
52
|
throw new errors_1.PeableApiError(`Failed to reach ${resolved.oxyApiUrl} to mint an Peable service token: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
+
let body;
|
|
55
|
+
try {
|
|
56
|
+
body = await readJsonBody(response);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new errors_1.PeableApiError('The Oxy service-token response body could not be read', {
|
|
60
|
+
statusCode: response.status, code: 'invalid_response',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
54
63
|
if (!response.ok) {
|
|
55
64
|
throw (0, errors_1.errorFromResponse)(response.status, body);
|
|
56
65
|
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
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;
|
|
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
6
|
// confidential `ApplicationCredential` (`{publicKey, secret}`); mints and
|
|
@@ -40,7 +40,7 @@ class Peable {
|
|
|
40
40
|
constructor(config) {
|
|
41
41
|
const resolved = (0, config_1.resolveConfig)(config);
|
|
42
42
|
const tokenProvider = (0, serviceToken_1.createServiceTokenProvider)(config);
|
|
43
|
-
const client = (0, client_1.createRestClient)({ baseURL: resolved.baseURL }, tokenProvider);
|
|
43
|
+
const client = (0, client_1.createRestClient)({ baseURL: resolved.baseURL, requestTimeoutMs: resolved.requestTimeoutMs }, tokenProvider);
|
|
44
44
|
this.billing = new billing_1.BillingResource(client);
|
|
45
45
|
this.paymentIntents = new paymentIntents_1.PaymentIntentsResource(client);
|
|
46
46
|
this.paymentLinks = new paymentLinks_1.PaymentLinksResource(client);
|
|
@@ -66,3 +66,4 @@ Object.defineProperty(exports, "PeableApiError", { enumerable: true, get: functi
|
|
|
66
66
|
Object.defineProperty(exports, "PeableSignatureVerificationError", { enumerable: true, get: function () { return errors_1.PeableSignatureVerificationError; } });
|
|
67
67
|
var webhooks_2 = require("./resources/webhooks");
|
|
68
68
|
Object.defineProperty(exports, "WEBHOOK_SIGNATURE_HEADER", { enumerable: true, get: function () { return webhooks_2.WEBHOOK_SIGNATURE_HEADER; } });
|
|
69
|
+
Object.defineProperty(exports, "WebhooksResource", { enumerable: true, get: function () { return webhooks_2.WebhooksResource; } });
|
|
@@ -32,8 +32,8 @@ class PaymentIntentsResource {
|
|
|
32
32
|
* and nothing was decided. Neither is a failure of the request — both are
|
|
33
33
|
* facts about a payment that is still live.
|
|
34
34
|
*/
|
|
35
|
-
reject(id) {
|
|
36
|
-
return this.client.request('POST', `/v1/payment_intents/${encodeURIComponent(id)}/reject
|
|
35
|
+
reject(id, options = {}) {
|
|
36
|
+
return this.client.request('POST', `/v1/payment_intents/${encodeURIComponent(id)}/reject`, { idempotencyKey: options.idempotencyKey });
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* What the PAYER's client has to do next — the resume operation.
|
|
@@ -18,8 +18,8 @@ class RefundsResource {
|
|
|
18
18
|
* caller does not need a second read whose answer can move on before it
|
|
19
19
|
* arrives.
|
|
20
20
|
*/
|
|
21
|
-
create(params) {
|
|
22
|
-
return this.client.request('POST', '/v1/refunds', { body: params });
|
|
21
|
+
create(params, options = {}) {
|
|
22
|
+
return this.client.request('POST', '/v1/refunds', { body: params, idempotencyKey: options.idempotencyKey });
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* What this payment actually came to — gross, fee, net.
|
|
@@ -17,8 +17,8 @@ class TransfersResource {
|
|
|
17
17
|
* would come from the platform's general balance, which is other merchants'
|
|
18
18
|
* money in flight.
|
|
19
19
|
*/
|
|
20
|
-
create(params) {
|
|
21
|
-
return this.client.request('POST', '/v1/transfers', { body: params });
|
|
20
|
+
create(params, options = {}) {
|
|
21
|
+
return this.client.request('POST', '/v1/transfers', { body: params, idempotencyKey: options.idempotencyKey });
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Take some or all of a settlement back.
|
|
@@ -7,13 +7,21 @@ const errors_1 = require("../core/errors");
|
|
|
7
7
|
exports.WEBHOOK_SIGNATURE_HEADER = 'Peable-Signature';
|
|
8
8
|
/** Default replay-tolerance window, matching the backend's own tests/usage. */
|
|
9
9
|
const DEFAULT_TOLERANCE_SEC = 300;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
'payment_intent.
|
|
15
|
-
'payment_intent.
|
|
16
|
-
|
|
10
|
+
// Exhaustive against the published union: adding a backend event makes this
|
|
11
|
+
// fail compilation until the SDK accepts it. Own-key lookup excludes prototype
|
|
12
|
+
// names supplied by an untrusted, even correctly signed, payload.
|
|
13
|
+
const WEBHOOK_EVENT_TYPES = {
|
|
14
|
+
'payment_intent.confirming': true,
|
|
15
|
+
'payment_intent.settled': true,
|
|
16
|
+
'payment_intent.failed': true,
|
|
17
|
+
'payment_intent.rejected': true,
|
|
18
|
+
'payment_intent.expired': true,
|
|
19
|
+
'payment_intent.refunded': true,
|
|
20
|
+
'payment_intent.partially_refunded': true,
|
|
21
|
+
'payment_intent.disputed': true,
|
|
22
|
+
'payment_intent.dispute_closed': true,
|
|
23
|
+
'connected_account.updated': true,
|
|
24
|
+
};
|
|
17
25
|
/**
|
|
18
26
|
* Structural guard for the parsed payload — the signature already proves the
|
|
19
27
|
* bytes were produced by `signWebhook` on the SAME secret, but a caller could
|
|
@@ -27,7 +35,7 @@ function isWebhookEventShape(value) {
|
|
|
27
35
|
return (typeof candidate.id === 'string' &&
|
|
28
36
|
candidate.object === 'event' &&
|
|
29
37
|
typeof candidate.type === 'string' &&
|
|
30
|
-
|
|
38
|
+
Object.prototype.hasOwnProperty.call(WEBHOOK_EVENT_TYPES, candidate.type) &&
|
|
31
39
|
typeof candidate.created === 'string' &&
|
|
32
40
|
typeof candidate.data === 'object' &&
|
|
33
41
|
candidate.data !== null &&
|
package/dist/esm/core/client.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Prefixes `baseURL`, attaches the bearer service token, sets
|
|
3
3
|
// `Idempotency-Key` when given, serializes query params, parses JSON, and
|
|
4
4
|
// maps every non-2xx response via `errors.ts`.
|
|
5
|
+
import { resolveRequestTimeout } from './config.js';
|
|
5
6
|
import { errorFromResponse, PeableApiError } from './errors.js';
|
|
6
7
|
const HTTP_UNAUTHORIZED = 401;
|
|
7
8
|
function buildUrl(baseURL, path, query) {
|
|
@@ -29,6 +30,7 @@ async function readJsonBody(response) {
|
|
|
29
30
|
}
|
|
30
31
|
export function createRestClient(config, tokenProvider, deps = {}) {
|
|
31
32
|
const fetchImpl = deps.fetch ?? fetch;
|
|
33
|
+
const timeout = resolveRequestTimeout(config.requestTimeoutMs);
|
|
32
34
|
async function performRequest(method, path, opts, allowRetryOn401) {
|
|
33
35
|
const token = await tokenProvider.getToken();
|
|
34
36
|
const headers = { Authorization: `Bearer ${token}` };
|
|
@@ -43,6 +45,7 @@ export function createRestClient(config, tokenProvider, deps = {}) {
|
|
|
43
45
|
response = await fetchImpl(buildUrl(config.baseURL, path, opts.query), {
|
|
44
46
|
method,
|
|
45
47
|
headers,
|
|
48
|
+
...(timeout === undefined ? {} : { signal: AbortSignal.timeout(timeout) }),
|
|
46
49
|
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
47
50
|
});
|
|
48
51
|
}
|
package/dist/esm/core/config.js
CHANGED
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
export const DEFAULT_GATEWAY_BASE_URL = 'https://api.peable.to';
|
|
10
10
|
/** Default oxy-api host used to mint the short-lived service token. */
|
|
11
11
|
export const DEFAULT_OXY_API_URL = 'https://api.oxy.so';
|
|
12
|
+
export function resolveRequestTimeout(value) {
|
|
13
|
+
if (value === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
const timeout = value;
|
|
16
|
+
if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 2147483647) {
|
|
17
|
+
throw new Error('Peable: requestTimeoutMs must be a positive32-bit integer');
|
|
18
|
+
}
|
|
19
|
+
return timeout;
|
|
20
|
+
}
|
|
12
21
|
function stripTrailingSlash(url) {
|
|
13
22
|
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
14
23
|
}
|
|
@@ -26,6 +35,7 @@ export function resolveConfig(config) {
|
|
|
26
35
|
throw new Error('Peable: `secret` is required');
|
|
27
36
|
}
|
|
28
37
|
return {
|
|
38
|
+
requestTimeoutMs: resolveRequestTimeout(config.requestTimeoutMs),
|
|
29
39
|
publicKey: config.publicKey,
|
|
30
40
|
secret: config.secret,
|
|
31
41
|
baseURL: stripTrailingSlash(config.baseURL ?? DEFAULT_GATEWAY_BASE_URL),
|
|
@@ -40,6 +40,7 @@ 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
44
|
headers: { 'Content-Type': 'application/json' },
|
|
44
45
|
body: JSON.stringify({ apiKey: resolved.publicKey, apiSecret: resolved.secret }),
|
|
45
46
|
});
|
|
@@ -47,7 +48,15 @@ export function createServiceTokenProvider(cfg, deps = {}) {
|
|
|
47
48
|
catch (cause) {
|
|
48
49
|
throw new PeableApiError(`Failed to reach ${resolved.oxyApiUrl} to mint an Peable service token: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
+
let body;
|
|
52
|
+
try {
|
|
53
|
+
body = await readJsonBody(response);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new PeableApiError('The Oxy service-token response body could not be read', {
|
|
57
|
+
statusCode: response.status, code: 'invalid_response',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
51
60
|
if (!response.ok) {
|
|
52
61
|
throw errorFromResponse(response.status, body);
|
|
53
62
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -37,7 +37,7 @@ export class Peable {
|
|
|
37
37
|
constructor(config) {
|
|
38
38
|
const resolved = resolveConfig(config);
|
|
39
39
|
const tokenProvider = createServiceTokenProvider(config);
|
|
40
|
-
const client = createRestClient({ baseURL: resolved.baseURL }, tokenProvider);
|
|
40
|
+
const client = createRestClient({ baseURL: resolved.baseURL, requestTimeoutMs: resolved.requestTimeoutMs }, tokenProvider);
|
|
41
41
|
this.billing = new BillingResource(client);
|
|
42
42
|
this.paymentIntents = new PaymentIntentsResource(client);
|
|
43
43
|
this.paymentLinks = new PaymentLinksResource(client);
|
|
@@ -52,4 +52,4 @@ export class Peable {
|
|
|
52
52
|
}
|
|
53
53
|
export { DEFAULT_GATEWAY_BASE_URL, DEFAULT_OXY_API_URL } from './core/config.js';
|
|
54
54
|
export { PeableError, PeableAuthenticationError, PeableInvalidRequestError, PeablePermissionError, PeableApiError, PeableSignatureVerificationError, } from './core/errors.js';
|
|
55
|
-
export { WEBHOOK_SIGNATURE_HEADER } from './resources/webhooks.js';
|
|
55
|
+
export { WEBHOOK_SIGNATURE_HEADER, WebhooksResource } from './resources/webhooks.js';
|
|
@@ -29,8 +29,8 @@ export class PaymentIntentsResource {
|
|
|
29
29
|
* and nothing was decided. Neither is a failure of the request — both are
|
|
30
30
|
* facts about a payment that is still live.
|
|
31
31
|
*/
|
|
32
|
-
reject(id) {
|
|
33
|
-
return this.client.request('POST', `/v1/payment_intents/${encodeURIComponent(id)}/reject
|
|
32
|
+
reject(id, options = {}) {
|
|
33
|
+
return this.client.request('POST', `/v1/payment_intents/${encodeURIComponent(id)}/reject`, { idempotencyKey: options.idempotencyKey });
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* What the PAYER's client has to do next — the resume operation.
|
|
@@ -15,8 +15,8 @@ export class RefundsResource {
|
|
|
15
15
|
* caller does not need a second read whose answer can move on before it
|
|
16
16
|
* arrives.
|
|
17
17
|
*/
|
|
18
|
-
create(params) {
|
|
19
|
-
return this.client.request('POST', '/v1/refunds', { body: params });
|
|
18
|
+
create(params, options = {}) {
|
|
19
|
+
return this.client.request('POST', '/v1/refunds', { body: params, idempotencyKey: options.idempotencyKey });
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* What this payment actually came to — gross, fee, net.
|
|
@@ -14,8 +14,8 @@ export class TransfersResource {
|
|
|
14
14
|
* would come from the platform's general balance, which is other merchants'
|
|
15
15
|
* money in flight.
|
|
16
16
|
*/
|
|
17
|
-
create(params) {
|
|
18
|
-
return this.client.request('POST', '/v1/transfers', { body: params });
|
|
17
|
+
create(params, options = {}) {
|
|
18
|
+
return this.client.request('POST', '/v1/transfers', { body: params, idempotencyKey: options.idempotencyKey });
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Take some or all of a settlement back.
|
|
@@ -4,13 +4,21 @@ import { PeableSignatureVerificationError } from '../core/errors.js';
|
|
|
4
4
|
export const WEBHOOK_SIGNATURE_HEADER = 'Peable-Signature';
|
|
5
5
|
/** Default replay-tolerance window, matching the backend's own tests/usage. */
|
|
6
6
|
const DEFAULT_TOLERANCE_SEC = 300;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'payment_intent.
|
|
12
|
-
'payment_intent.
|
|
13
|
-
|
|
7
|
+
// Exhaustive against the published union: adding a backend event makes this
|
|
8
|
+
// fail compilation until the SDK accepts it. Own-key lookup excludes prototype
|
|
9
|
+
// names supplied by an untrusted, even correctly signed, payload.
|
|
10
|
+
const WEBHOOK_EVENT_TYPES = {
|
|
11
|
+
'payment_intent.confirming': true,
|
|
12
|
+
'payment_intent.settled': true,
|
|
13
|
+
'payment_intent.failed': true,
|
|
14
|
+
'payment_intent.rejected': true,
|
|
15
|
+
'payment_intent.expired': true,
|
|
16
|
+
'payment_intent.refunded': true,
|
|
17
|
+
'payment_intent.partially_refunded': true,
|
|
18
|
+
'payment_intent.disputed': true,
|
|
19
|
+
'payment_intent.dispute_closed': true,
|
|
20
|
+
'connected_account.updated': true,
|
|
21
|
+
};
|
|
14
22
|
/**
|
|
15
23
|
* Structural guard for the parsed payload — the signature already proves the
|
|
16
24
|
* bytes were produced by `signWebhook` on the SAME secret, but a caller could
|
|
@@ -24,7 +32,7 @@ function isWebhookEventShape(value) {
|
|
|
24
32
|
return (typeof candidate.id === 'string' &&
|
|
25
33
|
candidate.object === 'event' &&
|
|
26
34
|
typeof candidate.type === 'string' &&
|
|
27
|
-
|
|
35
|
+
Object.prototype.hasOwnProperty.call(WEBHOOK_EVENT_TYPES, candidate.type) &&
|
|
28
36
|
typeof candidate.created === 'string' &&
|
|
29
37
|
typeof candidate.data === 'object' &&
|
|
30
38
|
candidate.data !== null &&
|
|
@@ -11,6 +11,7 @@ export interface RestClient {
|
|
|
11
11
|
export interface RestClientConfig {
|
|
12
12
|
/** Gateway base URL, already stripped of a trailing slash. */
|
|
13
13
|
baseURL: string;
|
|
14
|
+
requestTimeoutMs?: number;
|
|
14
15
|
}
|
|
15
16
|
export declare function createRestClient(config: RestClientConfig, tokenProvider: ServiceTokenProvider, deps?: {
|
|
16
17
|
fetch?: typeof fetch;
|
|
@@ -13,6 +13,8 @@ export interface PeableConfig {
|
|
|
13
13
|
baseURL?: string;
|
|
14
14
|
/** oxy-api host used to mint the service token. Default `https://api.oxy.so`. */
|
|
15
15
|
oxyApiUrl?: string;
|
|
16
|
+
/** Optional deadline per HTTP attempt, including body consumption. */
|
|
17
|
+
requestTimeoutMs?: number;
|
|
16
18
|
}
|
|
17
19
|
/** `PeableConfig` with every optional field defaulted and normalized. */
|
|
18
20
|
export interface ResolvedPeableConfig {
|
|
@@ -20,7 +22,9 @@ export interface ResolvedPeableConfig {
|
|
|
20
22
|
secret: string;
|
|
21
23
|
baseURL: string;
|
|
22
24
|
oxyApiUrl: string;
|
|
25
|
+
requestTimeoutMs?: number;
|
|
23
26
|
}
|
|
27
|
+
export declare function resolveRequestTimeout(value: number | undefined): number | undefined;
|
|
24
28
|
/**
|
|
25
29
|
* Validate and normalize an `PeableConfig`. Throws a plain `Error` for a
|
|
26
30
|
* missing `publicKey`/`secret` — a programmer error caught at construction
|
package/dist/types/index.d.ts
CHANGED
|
@@ -57,5 +57,5 @@ export type { DisputeEvidenceParams, DisputeList, RegisterMerchantParams, Update
|
|
|
57
57
|
export type { CreateRefundParams, RefundList } from './resources/refunds';
|
|
58
58
|
export type { CreateTransferParams, ReverseTransferParams, TransferList, } from './resources/transfers';
|
|
59
59
|
export type { PaymentLinkListParams, PaymentLinkList, UpdatePaymentLinkParams, } from './resources/paymentLinks';
|
|
60
|
-
export { WEBHOOK_SIGNATURE_HEADER } from './resources/webhooks';
|
|
60
|
+
export { WEBHOOK_SIGNATURE_HEADER, WebhooksResource } from './resources/webhooks';
|
|
61
61
|
export type { ConstructEventOptions } from './resources/webhooks';
|
|
@@ -52,7 +52,9 @@ export declare class PaymentIntentsResource {
|
|
|
52
52
|
* and nothing was decided. Neither is a failure of the request — both are
|
|
53
53
|
* facts about a payment that is still live.
|
|
54
54
|
*/
|
|
55
|
-
reject(id: string
|
|
55
|
+
reject(id: string, options?: {
|
|
56
|
+
idempotencyKey?: string;
|
|
57
|
+
}): Promise<PaymentIntent>;
|
|
56
58
|
/**
|
|
57
59
|
* What the PAYER's client has to do next — the resume operation.
|
|
58
60
|
*
|
|
@@ -46,7 +46,9 @@ export declare class RefundsResource {
|
|
|
46
46
|
* caller does not need a second read whose answer can move on before it
|
|
47
47
|
* arrives.
|
|
48
48
|
*/
|
|
49
|
-
create(params: CreateRefundParams
|
|
49
|
+
create(params: CreateRefundParams, options?: {
|
|
50
|
+
idempotencyKey?: string;
|
|
51
|
+
}): Promise<Refund>;
|
|
50
52
|
/**
|
|
51
53
|
* What this payment actually came to — gross, fee, net.
|
|
52
54
|
*
|
|
@@ -57,7 +57,9 @@ export declare class TransfersResource {
|
|
|
57
57
|
* would come from the platform's general balance, which is other merchants'
|
|
58
58
|
* money in flight.
|
|
59
59
|
*/
|
|
60
|
-
create(params: CreateTransferParams
|
|
60
|
+
create(params: CreateTransferParams, options?: {
|
|
61
|
+
idempotencyKey?: string;
|
|
62
|
+
}): Promise<Transfer>;
|
|
61
63
|
/**
|
|
62
64
|
* Take some or all of a settlement back.
|
|
63
65
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peable.to/sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|