@peable.to/sdk 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/browser/mount.js +55 -8
- package/dist/cjs/browser/payerClient.js +58 -8
- package/dist/cjs/checkout.js +9 -1
- package/dist/cjs/core/client.js +22 -1
- package/dist/cjs/core/errors.js +1 -1
- package/dist/cjs/core/serviceToken.js +1 -1
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/resources/billing.js +25 -0
- package/dist/cjs/resources/checkoutSessions.js +2 -1
- package/dist/cjs/resources/connectedAccounts.js +63 -0
- package/dist/cjs/resources/merchants.js +51 -0
- package/dist/cjs/resources/paymentIntents.js +28 -0
- package/dist/cjs/resources/refunds.js +54 -0
- package/dist/cjs/resources/transfers.js +49 -0
- package/dist/esm/browser/mount.js +52 -7
- package/dist/esm/browser/payerClient.js +58 -8
- package/dist/esm/checkout.js +5 -0
- package/dist/esm/core/client.js +22 -1
- package/dist/esm/core/errors.js +1 -1
- package/dist/esm/core/serviceToken.js +1 -1
- package/dist/esm/index.js +27 -0
- package/dist/esm/resources/billing.js +21 -0
- package/dist/esm/resources/checkoutSessions.js +2 -1
- package/dist/esm/resources/connectedAccounts.js +59 -0
- package/dist/esm/resources/merchants.js +46 -0
- package/dist/esm/resources/paymentIntents.js +28 -0
- package/dist/esm/resources/refunds.js +50 -0
- package/dist/esm/resources/transfers.js +45 -0
- package/dist/types/browser/mount.d.ts +23 -1
- package/dist/types/browser/payerClient.d.ts +56 -2
- package/dist/types/checkout.d.ts +2 -1
- package/dist/types/core/errors.d.ts +2 -2
- package/dist/types/index.d.ts +40 -1
- package/dist/types/resources/billing.d.ts +15 -0
- package/dist/types/resources/checkoutSessions.d.ts +15 -1
- package/dist/types/resources/connectedAccounts.d.ts +84 -0
- package/dist/types/resources/merchants.d.ts +113 -0
- package/dist/types/resources/paymentIntents.d.ts +44 -0
- package/dist/types/resources/refunds.d.ts +75 -0
- package/dist/types/resources/transfers.d.ts +73 -0
- package/package.json +2 -2
|
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.PeableCheckout = void 0;
|
|
6
|
+
exports.PeableCheckout = exports.PeableRailUnsupportedError = void 0;
|
|
7
7
|
exports.deriveIntentId = deriveIntentId;
|
|
8
|
+
exports.payDeepLinkFor = payDeepLinkFor;
|
|
8
9
|
exports.buildPayDeepLink = buildPayDeepLink;
|
|
9
10
|
exports.isMobileUserAgent = isMobileUserAgent;
|
|
10
11
|
exports.createEmitter = createEmitter;
|
|
@@ -60,6 +61,47 @@ function deriveIntentId(clientSecret, intentId) {
|
|
|
60
61
|
/** The `peable://pay` deep-link scheme + host — mirrors `PAYMENT_REQUEST_PREFIX`
|
|
61
62
|
* in the wallet's `packages/frontend/src/pay/payment-request.ts`. */
|
|
62
63
|
const PAYMENT_REQUEST_PREFIX = 'peable://pay';
|
|
64
|
+
/**
|
|
65
|
+
* Thrown when a deep link is asked for on an intent that has no chain payment
|
|
66
|
+
* behind it.
|
|
67
|
+
*
|
|
68
|
+
* `address` and `network` became nullable when the card rail landed (gateway
|
|
69
|
+
* ADR 0001 D6), and the tempting fix was a `?? ''` at the call site. That would
|
|
70
|
+
* mint `peable://pay?...&address=&network=` — a link the wallet's parser
|
|
71
|
+
* accepts structurally and which sends a payer to an empty address. Failing
|
|
72
|
+
* here is the only safe direction.
|
|
73
|
+
*/
|
|
74
|
+
class PeableRailUnsupportedError extends errors_1.PeableError {
|
|
75
|
+
constructor(rail) {
|
|
76
|
+
// Extends `PeableError`, not `Error`: the widget reports everything through
|
|
77
|
+
// its `error` event, whose payload type is `PeableError`, and a bare `Error`
|
|
78
|
+
// would arrive there only after being flattened into a `PeableApiError`
|
|
79
|
+
// that a caller cannot tell apart from a network failure.
|
|
80
|
+
// `invalid_request_error`, because that is what it is: the caller asked
|
|
81
|
+
// this widget for a surface the intent does not have. Nothing went wrong
|
|
82
|
+
// upstream and retrying cannot help.
|
|
83
|
+
super('invalid_request_error', `the '${rail}' rail has no wallet deep link; it is not a chain payment`);
|
|
84
|
+
this.name = 'PeableRailUnsupportedError';
|
|
85
|
+
Object.setPrototypeOf(this, PeableRailUnsupportedError.prototype);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.PeableRailUnsupportedError = PeableRailUnsupportedError;
|
|
89
|
+
/**
|
|
90
|
+
* Build the deep link for an intent, refusing any intent that is not a chain
|
|
91
|
+
* payment. The narrowing is what lets `buildPayDeepLink` keep non-null fields.
|
|
92
|
+
*/
|
|
93
|
+
function payDeepLinkFor(intent) {
|
|
94
|
+
if (intent.rail !== 'faircoin' || intent.address === null || intent.network === null) {
|
|
95
|
+
throw new PeableRailUnsupportedError(intent.rail);
|
|
96
|
+
}
|
|
97
|
+
return buildPayDeepLink({
|
|
98
|
+
intentId: intent.id,
|
|
99
|
+
clientSecret: intent.clientSecret,
|
|
100
|
+
address: intent.address,
|
|
101
|
+
amount: intent.amount,
|
|
102
|
+
network: intent.network,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
63
105
|
/**
|
|
64
106
|
* Build an `peable://pay?...` deep link. Param names AND percent-encoding
|
|
65
107
|
* must match the wallet's parser byte-for-byte
|
|
@@ -251,13 +293,18 @@ function mount(target, opts) {
|
|
|
251
293
|
button.addEventListener('click', () => {
|
|
252
294
|
if (!latestIntent)
|
|
253
295
|
return; // initial snapshot hasn't loaded yet
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
296
|
+
// A card intent has no wallet to deep-link into. `error` rather than a
|
|
297
|
+
// throw into an event handler nobody is listening to: this widget's whole
|
|
298
|
+
// contract is that a host page learns what happened through `on(...)`, and
|
|
299
|
+
// an uncaught exception here would leave the button silently dead.
|
|
300
|
+
let deepLink;
|
|
301
|
+
try {
|
|
302
|
+
deepLink = payDeepLinkFor(latestIntent);
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
emitter.emit('error', toPeableError(err));
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
261
308
|
if (isMobileUserAgent(navigator.userAgent)) {
|
|
262
309
|
window.location.href = deepLink;
|
|
263
310
|
}
|
|
@@ -4,7 +4,7 @@ exports.createPeableCheckout = createPeableCheckout;
|
|
|
4
4
|
// The payer-side REST+socket client core — the "./checkout" entry's payer
|
|
5
5
|
// capability (Fase 2 SDK plan, Task 5). Mirrors the wallet's
|
|
6
6
|
// `gateway-client.ts`/`gateway-socket.ts` but standalone: no
|
|
7
|
-
// `@
|
|
7
|
+
// `@oxy.so/services`/`@oxy.so/core` — this is a third-party browser bundle, so
|
|
8
8
|
// it talks to the Gateway over bare `fetch` + `socket.io-client`.
|
|
9
9
|
//
|
|
10
10
|
// Holds NO service token and NO private key — only a public `client_secret`,
|
|
@@ -65,6 +65,25 @@ function createPeableCheckout(opts = {}) {
|
|
|
65
65
|
throw (0, errors_1.errorFromResponse)(response.status, body);
|
|
66
66
|
return body;
|
|
67
67
|
}
|
|
68
|
+
async function getClientAction(id, clientSecret) {
|
|
69
|
+
let response;
|
|
70
|
+
try {
|
|
71
|
+
response = await fetch(`${baseUrl}/v1/payment_intents/${encodeURIComponent(id)}/client_action`, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
// The HEADER, never a query param, for the same reason the snapshot
|
|
74
|
+
// read uses one: a capability token in a URL lands in every access
|
|
75
|
+
// log between here and the gateway.
|
|
76
|
+
headers: { 'X-Peable-Client-Secret': clientSecret },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (cause) {
|
|
80
|
+
throw new errors_1.PeableApiError(`Failed to reach the Peable Gateway at ${baseUrl}: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
81
|
+
}
|
|
82
|
+
const body = await readJsonBody(response);
|
|
83
|
+
if (!response.ok)
|
|
84
|
+
throw (0, errors_1.errorFromResponse)(response.status, body);
|
|
85
|
+
return body;
|
|
86
|
+
}
|
|
68
87
|
/**
|
|
69
88
|
* Opens its OWN socket per call (never a shared/module-level connection —
|
|
70
89
|
* this SDK has no session to key a singleton off) and connects with NO
|
|
@@ -74,7 +93,7 @@ function createPeableCheckout(opts = {}) {
|
|
|
74
93
|
* the real authorization check — it requires this intent's `client_secret`
|
|
75
94
|
* — so nothing here needs an Oxy session.
|
|
76
95
|
*/
|
|
77
|
-
async function subscribe(id, clientSecret, onUpdate) {
|
|
96
|
+
async function subscribe(id, clientSecret, onUpdate, onConnectionChange) {
|
|
78
97
|
const socket = (0, socket_io_client_1.io)(baseUrl, {
|
|
79
98
|
transports: ['websocket'],
|
|
80
99
|
});
|
|
@@ -105,6 +124,22 @@ function createPeableCheckout(opts = {}) {
|
|
|
105
124
|
}
|
|
106
125
|
};
|
|
107
126
|
socket.on('intent.updated', listener);
|
|
127
|
+
// A drop AFTER this point used to be invisible to the caller. `subscribe`
|
|
128
|
+
// had already resolved, so a `.catch` on it could not fire again, and the
|
|
129
|
+
// reconnect below restores DELIVERY but replays nothing — socket.io has no
|
|
130
|
+
// buffer for frames emitted while the socket was gone. A payment that
|
|
131
|
+
// settled during a thirty-second blip therefore never reached the page, and
|
|
132
|
+
// the payer watched a snapshot that would never update again.
|
|
133
|
+
//
|
|
134
|
+
// Reported rather than handled here: the SDK cannot know what a host wants
|
|
135
|
+
// to do about it. The hosted checkout turns REST polling on and re-reads
|
|
136
|
+
// the intent before turning it off again.
|
|
137
|
+
let closedByUs = false;
|
|
138
|
+
const onDisconnect = () => {
|
|
139
|
+
if (!closedByUs)
|
|
140
|
+
onConnectionChange?.('lost');
|
|
141
|
+
};
|
|
142
|
+
socket.on('disconnect', onDisconnect);
|
|
108
143
|
// socket.io-client's default reconnection restores the TRANSPORT, but the
|
|
109
144
|
// server spins up a fresh Socket with no room membership — so every
|
|
110
145
|
// reconnect (mobile network blip, backgrounded tab, LB cycle) must re-run
|
|
@@ -115,21 +150,36 @@ function createPeableCheckout(opts = {}) {
|
|
|
115
150
|
// the user reopening the screen. `reconnect` is a Manager-level event
|
|
116
151
|
// (`socket.io`, not `socket` itself) in socket.io-client.
|
|
117
152
|
const onReconnect = () => {
|
|
118
|
-
void socket
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
//
|
|
153
|
+
void socket
|
|
154
|
+
.emitWithAck('subscribe', { intentId: id, clientSecret })
|
|
155
|
+
.then((ack) => {
|
|
156
|
+
// `'live'` only once the ROOM is rejoined, never on the transport
|
|
157
|
+
// alone: a socket that is connected and in no room delivers nothing,
|
|
158
|
+
// and telling the host it is live there would have them stand their
|
|
159
|
+
// fallback down into permanent silence.
|
|
160
|
+
if (ack.ok)
|
|
161
|
+
onConnectionChange?.('live');
|
|
162
|
+
})
|
|
163
|
+
.catch(() => {
|
|
164
|
+
// The subscription is gone for good (the intent expired between drop
|
|
165
|
+
// and reconnect, say). Staying `lost` is the honest report: socket.io
|
|
166
|
+
// keeps retrying the transport, but nothing will arrive on it.
|
|
123
167
|
});
|
|
124
168
|
};
|
|
125
169
|
socket.io.on('reconnect', onReconnect);
|
|
126
170
|
return {
|
|
127
171
|
unsubscribe() {
|
|
172
|
+
// Set BEFORE `disconnect()`: a deliberate close fires `disconnect` like
|
|
173
|
+
// any other, and reporting `'lost'` there would have a host spin up a
|
|
174
|
+
// fallback for a subscription it just tore down — on an unmounting
|
|
175
|
+
// component, at that.
|
|
176
|
+
closedByUs = true;
|
|
128
177
|
socket.off('intent.updated', listener);
|
|
178
|
+
socket.off('disconnect', onDisconnect);
|
|
129
179
|
socket.io.off('reconnect', onReconnect);
|
|
130
180
|
socket.disconnect();
|
|
131
181
|
},
|
|
132
182
|
};
|
|
133
183
|
}
|
|
134
|
-
return { getPaymentIntent, subscribe, submitTx };
|
|
184
|
+
return { getPaymentIntent, subscribe, submitTx, getClientAction };
|
|
135
185
|
}
|
package/dist/cjs/checkout.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PeableCheckout = exports.createPeableCheckout = void 0;
|
|
3
|
+
exports.PeableRailUnsupportedError = exports.buildPayDeepLink = exports.payDeepLinkFor = exports.PeableCheckout = exports.createPeableCheckout = void 0;
|
|
4
4
|
// Browser entry (`@peable.to/sdk/checkout`) — the anonymous payer-side client
|
|
5
5
|
// core (Task 5) plus the embeddable pay-button widget (Task 6). Deliberately
|
|
6
6
|
// imports nothing from `./core/*` beyond `core/errors.ts` (a pure
|
|
@@ -15,3 +15,11 @@ var payerClient_1 = require("./browser/payerClient");
|
|
|
15
15
|
Object.defineProperty(exports, "createPeableCheckout", { enumerable: true, get: function () { return payerClient_1.createPeableCheckout; } });
|
|
16
16
|
var mount_1 = require("./browser/mount");
|
|
17
17
|
Object.defineProperty(exports, "PeableCheckout", { enumerable: true, get: function () { return mount_1.PeableCheckout; } });
|
|
18
|
+
// The rail-aware deep-link builder and its refusal. Exported because
|
|
19
|
+
// `packages/checkout` renders its own pay button over the payer client core
|
|
20
|
+
// and must make the same decision this widget makes — one definition of "which
|
|
21
|
+
// intents have a wallet deep link", not two.
|
|
22
|
+
var mount_2 = require("./browser/mount");
|
|
23
|
+
Object.defineProperty(exports, "payDeepLinkFor", { enumerable: true, get: function () { return mount_2.payDeepLinkFor; } });
|
|
24
|
+
Object.defineProperty(exports, "buildPayDeepLink", { enumerable: true, get: function () { return mount_2.buildPayDeepLink; } });
|
|
25
|
+
Object.defineProperty(exports, "PeableRailUnsupportedError", { enumerable: true, get: function () { return mount_2.PeableRailUnsupportedError; } });
|
package/dist/cjs/core/client.js
CHANGED
|
@@ -63,10 +63,31 @@ function createRestClient(config, tokenProvider, deps = {}) {
|
|
|
63
63
|
// The Gateway echoes a date-based `Peable-Version` response header
|
|
64
64
|
// (Stripe parity). No SDK behavior depends on it yet, so it is
|
|
65
65
|
// deliberately not read here — nothing to do with it today.
|
|
66
|
-
|
|
66
|
+
let body;
|
|
67
|
+
try {
|
|
68
|
+
body = await readJsonBody(response);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Headers can arrive before the connection fails during body consumption.
|
|
72
|
+
// Preserve the known status, but do not infer whether a mutation committed
|
|
73
|
+
// or retry it automatically. Recovery keeps the caller's original key.
|
|
74
|
+
throw new errors_1.PeableApiError('The Peable Gateway response body could not be read; remote outcome is unknown', {
|
|
75
|
+
statusCode: response.status,
|
|
76
|
+
code: 'invalid_response',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
67
79
|
if (!response.ok) {
|
|
68
80
|
throw (0, errors_1.errorFromResponse)(response.status, body);
|
|
69
81
|
}
|
|
82
|
+
// Only explicit No Content can satisfy a void response. A missing or
|
|
83
|
+
// malformed JSON success is indeterminate, not a typed resource. Do not
|
|
84
|
+
// retry a mutation automatically: callers retain the original intent key.
|
|
85
|
+
if (body === undefined && response.status !== 204) {
|
|
86
|
+
throw new errors_1.PeableApiError('The Peable Gateway returned an invalid JSON response', {
|
|
87
|
+
statusCode: response.status,
|
|
88
|
+
code: 'invalid_response',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
70
91
|
return body;
|
|
71
92
|
}
|
|
72
93
|
return {
|
package/dist/cjs/core/errors.js
CHANGED
|
@@ -78,7 +78,7 @@ function isFlatGatewayErrorBody(body) {
|
|
|
78
78
|
* request was rejected:
|
|
79
79
|
* - The Gateway's own route handlers use the Stripe-style nested envelope
|
|
80
80
|
* `{ error: { type, message } }` (`lib/http.ts`'s `sendError`).
|
|
81
|
-
* - `@
|
|
81
|
+
* - `@oxy.so/core`'s Express auth middleware (`oxy.middleware.service()` /
|
|
82
82
|
* `requireScope()` — rejecting a missing/expired/insufficiently-scoped
|
|
83
83
|
* service token BEFORE a route handler ever runs) and oxy-api's own
|
|
84
84
|
* `ApiError.toJSON()` use a FLAT envelope: `{ error: <CODE_STRING>,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Mints a short-lived Oxy service token from an `ApplicationCredential`
|
|
3
3
|
// (`{publicKey, secret}`) by presenting it to oxy-api's `POST
|
|
4
|
-
// /auth/service-token` — the exact mechanism `
|
|
4
|
+
// /auth/service-token` — the exact mechanism `oxy.middleware.service()`
|
|
5
5
|
// validates at the Gateway. Caches the token until near expiry and re-mints
|
|
6
6
|
// reactively when `RestClient` sees a 401 (see `client.ts`).
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
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;
|
|
4
|
+
const billing_1 = require("./resources/billing");
|
|
4
5
|
// Server entry (`@peable.to/sdk`) — the merchant-authed SDK. Configured with a
|
|
5
6
|
// confidential `ApplicationCredential` (`{publicKey, secret}`); mints and
|
|
6
7
|
// caches an Oxy service token, and exposes Stripe-ergonomics resource
|
|
@@ -15,15 +16,41 @@ const paymentIntents_1 = require("./resources/paymentIntents");
|
|
|
15
16
|
const paymentLinks_1 = require("./resources/paymentLinks");
|
|
16
17
|
const checkoutSessions_1 = require("./resources/checkoutSessions");
|
|
17
18
|
const webhooks_1 = require("./resources/webhooks");
|
|
19
|
+
const connectedAccounts_1 = require("./resources/connectedAccounts");
|
|
20
|
+
const merchants_1 = require("./resources/merchants");
|
|
21
|
+
const refunds_1 = require("./resources/refunds");
|
|
22
|
+
const transfers_1 = require("./resources/transfers");
|
|
23
|
+
/**
|
|
24
|
+
* The merchant-authed client.
|
|
25
|
+
*
|
|
26
|
+
* ## Why the namespaces below are not optional extras
|
|
27
|
+
*
|
|
28
|
+
* This class published four: intents, links, checkout and webhooks. The gateway
|
|
29
|
+
* has always served merchants, connected accounts, refunds, transfers and
|
|
30
|
+
* disputes as well — so every integrator reaching those wrote their own HTTP
|
|
31
|
+
* client and their own partial types for responses this package already knew.
|
|
32
|
+
* Mercaria did exactly that. Two descriptions of one wire format, in two
|
|
33
|
+
* repositories, with nothing comparing them: a field renamed in the gateway is
|
|
34
|
+
* a runtime failure over there, found by a settlement that does not happen.
|
|
35
|
+
*
|
|
36
|
+
* An SDK that covers less than the API it wraps does not reduce surface, it
|
|
37
|
+
* relocates it.
|
|
38
|
+
*/
|
|
18
39
|
class Peable {
|
|
19
40
|
constructor(config) {
|
|
20
41
|
const resolved = (0, config_1.resolveConfig)(config);
|
|
21
42
|
const tokenProvider = (0, serviceToken_1.createServiceTokenProvider)(config);
|
|
22
43
|
const client = (0, client_1.createRestClient)({ baseURL: resolved.baseURL }, tokenProvider);
|
|
44
|
+
this.billing = new billing_1.BillingResource(client);
|
|
23
45
|
this.paymentIntents = new paymentIntents_1.PaymentIntentsResource(client);
|
|
24
46
|
this.paymentLinks = new paymentLinks_1.PaymentLinksResource(client);
|
|
25
47
|
this.checkout = new checkoutSessions_1.CheckoutResource(client);
|
|
26
48
|
this.webhooks = new webhooks_1.WebhooksResource();
|
|
49
|
+
this.merchants = new merchants_1.MerchantsResource(client);
|
|
50
|
+
this.connectedAccounts = new connectedAccounts_1.ConnectedAccountsResource(client);
|
|
51
|
+
this.refunds = new refunds_1.RefundsResource(client);
|
|
52
|
+
this.transfers = new transfers_1.TransfersResource(client);
|
|
53
|
+
this.disputes = new merchants_1.DisputesResource(client);
|
|
27
54
|
}
|
|
28
55
|
}
|
|
29
56
|
exports.Peable = Peable;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BillingResource = void 0;
|
|
4
|
+
/** Recurring billing is separate from one-off checkout and marketplace transfers. */
|
|
5
|
+
class BillingResource {
|
|
6
|
+
constructor(client) {
|
|
7
|
+
this.client = client;
|
|
8
|
+
}
|
|
9
|
+
ensureCustomer(params, options) {
|
|
10
|
+
return this.client.request('POST', '/v1/billing/customers', { body: params, idempotencyKey: options.idempotencyKey });
|
|
11
|
+
}
|
|
12
|
+
createCheckoutSession(params, options) {
|
|
13
|
+
return this.client.request('POST', '/v1/billing/checkout_sessions', { body: params, idempotencyKey: options.idempotencyKey });
|
|
14
|
+
}
|
|
15
|
+
createPortalSession(params, options) {
|
|
16
|
+
return this.client.request('POST', '/v1/billing/portal_sessions', { body: params, idempotencyKey: options.idempotencyKey });
|
|
17
|
+
}
|
|
18
|
+
retrieveSubscription(ref) {
|
|
19
|
+
return this.client.request('GET', `/v1/billing/subscriptions/${encodeURIComponent(ref)}`);
|
|
20
|
+
}
|
|
21
|
+
cancelAtPeriodEnd(ref, options) {
|
|
22
|
+
return this.client.request('POST', `/v1/billing/subscriptions/${encodeURIComponent(ref)}/cancel_at_period_end`, { body: {}, idempotencyKey: options.idempotencyKey });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.BillingResource = BillingResource;
|
|
@@ -5,9 +5,10 @@ class CheckoutSessionsResource {
|
|
|
5
5
|
constructor(client) {
|
|
6
6
|
this.client = client;
|
|
7
7
|
}
|
|
8
|
-
create(params) {
|
|
8
|
+
create(params, options = {}) {
|
|
9
9
|
return this.client.request('POST', '/v1/checkout_sessions', {
|
|
10
10
|
body: params,
|
|
11
|
+
...(options.idempotencyKey === undefined ? {} : { idempotencyKey: options.idempotencyKey }),
|
|
11
12
|
});
|
|
12
13
|
}
|
|
13
14
|
retrieve(id) {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConnectedAccountsResource = void 0;
|
|
4
|
+
class ConnectedAccountsResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Open the account for a seller, or return the one they already have.
|
|
10
|
+
*
|
|
11
|
+
* Converges rather than erroring, because the underlying object CANNOT BE
|
|
12
|
+
* DELETED: opening two for one seller leaves them with two forever, one of
|
|
13
|
+
* which nobody uses and which keeps generating requirement emails.
|
|
14
|
+
*/
|
|
15
|
+
create(params) {
|
|
16
|
+
return this.client.request('POST', '/v1/connected_accounts', {
|
|
17
|
+
body: params,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
list(params = {}) {
|
|
21
|
+
return this.client.request('GET', '/v1/connected_accounts', {
|
|
22
|
+
query: { limit: params.limit, starting_after: params.starting_after },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
retrieve(accountId) {
|
|
26
|
+
return this.client.request('GET', `/v1/connected_accounts/${encodeURIComponent(accountId)}`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* By the merchant's OWN reference.
|
|
30
|
+
*
|
|
31
|
+
* The recovery read: a merchant who lost the `ca_…` — a create whose response
|
|
32
|
+
* never arrived — still has the reference they chose, and this is what makes
|
|
33
|
+
* finding the account possible without a list scan.
|
|
34
|
+
*/
|
|
35
|
+
retrieveByRef(externalRef) {
|
|
36
|
+
return this.client.request('GET', `/v1/connected_accounts/by_ref/${encodeURIComponent(externalRef)}`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Re-read this account from the provider now.
|
|
40
|
+
*
|
|
41
|
+
* Rarely needed: readiness arrives on its own through the gateway's event
|
|
42
|
+
* drain and a backstop sweep. This is for a seller staring at a dashboard who
|
|
43
|
+
* has just finished onboarding.
|
|
44
|
+
*/
|
|
45
|
+
refresh(accountId) {
|
|
46
|
+
return this.client.request('POST', `/v1/connected_accounts/${encodeURIComponent(accountId)}/refresh`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A hosted onboarding link.
|
|
50
|
+
*
|
|
51
|
+
* SINGLE-USE and short-lived at the provider, so it is minted on demand and
|
|
52
|
+
* must never be stored, emailed or put in a chat message — a stored link is
|
|
53
|
+
* one that has already expired by the time anyone follows it, and the failure
|
|
54
|
+
* looks like the seller's fault.
|
|
55
|
+
*
|
|
56
|
+
* A visit to `returnUrl` proves NOTHING about verification. Read the account
|
|
57
|
+
* back, or wait for the readiness event.
|
|
58
|
+
*/
|
|
59
|
+
createAccountLink(accountId, params) {
|
|
60
|
+
return this.client.request('POST', `/v1/connected_accounts/${encodeURIComponent(accountId)}/account_links`, { body: params });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.ConnectedAccountsResource = ConnectedAccountsResource;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DisputesResource = exports.MerchantsResource = void 0;
|
|
4
|
+
class MerchantsResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
register(params = {}) {
|
|
9
|
+
return this.client.request('POST', '/v1/merchants', { body: params });
|
|
10
|
+
}
|
|
11
|
+
retrieve() {
|
|
12
|
+
return this.client.request('GET', '/v1/merchants/me');
|
|
13
|
+
}
|
|
14
|
+
update(params) {
|
|
15
|
+
return this.client.request('PATCH', '/v1/merchants/me', { body: params });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.MerchantsResource = MerchantsResource;
|
|
19
|
+
/**
|
|
20
|
+
* Disputes: read them, and answer one.
|
|
21
|
+
*
|
|
22
|
+
* A dispute is initiated by the card NETWORK, not by you, so there is nothing
|
|
23
|
+
* to create — the only write is a response. This surface was read-only for a
|
|
24
|
+
* while, because the gateway had no way to deliver evidence and a route that
|
|
25
|
+
* accepted it and did nothing would have been worse than none: you would have
|
|
26
|
+
* believed you had responded.
|
|
27
|
+
*/
|
|
28
|
+
class DisputesResource {
|
|
29
|
+
constructor(client) {
|
|
30
|
+
this.client = client;
|
|
31
|
+
}
|
|
32
|
+
listForPaymentIntent(paymentIntentId) {
|
|
33
|
+
return this.client.request('GET', `/v1/payment_intents/${encodeURIComponent(paymentIntentId)}/disputes`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Answer a dispute. **One shot.**
|
|
37
|
+
*
|
|
38
|
+
* Submitting is one-way at the card network, so this cannot be revised and a
|
|
39
|
+
* second call does not send a second response — it answers 200 with the
|
|
40
|
+
* dispute as it stands. Send everything you have the first time.
|
|
41
|
+
*
|
|
42
|
+
* Refused (409) when the dispute is already answered, already decided, or
|
|
43
|
+
* past `evidenceDueAt`. That last one is checked by the gateway rather than
|
|
44
|
+
* by the acquirer, so you learn it was the clock and not your request — a
|
|
45
|
+
* late response is the one failure here that no retry fixes.
|
|
46
|
+
*/
|
|
47
|
+
submitEvidence(disputeId, evidence) {
|
|
48
|
+
return this.client.request('POST', `/v1/disputes/${encodeURIComponent(disputeId)}/evidence`, { body: evidence });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.DisputesResource = DisputesResource;
|
|
@@ -23,8 +23,36 @@ class PaymentIntentsResource {
|
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Reject a payment the payer has not completed.
|
|
28
|
+
*
|
|
29
|
+
* The gateway CANCELS it at the provider before it announces anything, so
|
|
30
|
+
* this can lose a race and say so: a 409 means the payer confirmed first and
|
|
31
|
+
* the payment is settled, and a 502 means the provider could not be reached
|
|
32
|
+
* and nothing was decided. Neither is a failure of the request — both are
|
|
33
|
+
* facts about a payment that is still live.
|
|
34
|
+
*/
|
|
26
35
|
reject(id) {
|
|
27
36
|
return this.client.request('POST', `/v1/payment_intents/${encodeURIComponent(id)}/reject`);
|
|
28
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* What the PAYER's client has to do next — the resume operation.
|
|
40
|
+
*
|
|
41
|
+
* The payment-intent shape carries no confirmation credential, because a
|
|
42
|
+
* credential on that shape would be handed out by every list and every
|
|
43
|
+
* re-read. This is the bounded way to get one: a single payment, named
|
|
44
|
+
* explicitly, for a caller who has proven they may pay it.
|
|
45
|
+
*
|
|
46
|
+
* Call it to resume an unpaid checkout rather than creating a second payment
|
|
47
|
+
* — which is what a buyer returning the next day would otherwise produce, and
|
|
48
|
+
* an idempotency key that has aged out at the provider will not prevent.
|
|
49
|
+
*
|
|
50
|
+
* @throws {PeableInvalidRequestError} (409) when the payment can no longer be
|
|
51
|
+
* paid, so a surface handed this can never render a card form over a
|
|
52
|
+
* settled payment.
|
|
53
|
+
*/
|
|
54
|
+
clientAction(id) {
|
|
55
|
+
return this.client.request('POST', `/v1/payment_intents/${encodeURIComponent(id)}/client_action`);
|
|
56
|
+
}
|
|
29
57
|
}
|
|
30
58
|
exports.PaymentIntentsResource = PaymentIntentsResource;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RefundsResource = void 0;
|
|
4
|
+
class RefundsResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Refund part or all of a payment.
|
|
10
|
+
*
|
|
11
|
+
* **`status` is the refund's own lifecycle and it is not always
|
|
12
|
+
* `succeeded`.** A provider can report a refund `pending` — and a bank can
|
|
13
|
+
* reject one days later, which arrives as an event and moves the row to
|
|
14
|
+
* `failed`. Treating the creation as completion is how a merchant's books
|
|
15
|
+
* come to say money went back that is still with them.
|
|
16
|
+
*
|
|
17
|
+
* `paymentStatus` on the response is where the PAYMENT stands after it, so a
|
|
18
|
+
* caller does not need a second read whose answer can move on before it
|
|
19
|
+
* arrives.
|
|
20
|
+
*/
|
|
21
|
+
create(params) {
|
|
22
|
+
return this.client.request('POST', '/v1/refunds', { body: params });
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* What this payment actually came to — gross, fee, net.
|
|
26
|
+
*
|
|
27
|
+
* On this resource because it is the same question you ask in the same
|
|
28
|
+
* breath: how much of this money is really mine.
|
|
29
|
+
*
|
|
30
|
+
* **A missing figure is `null` with a `status`, never `0`.** `unknown` means
|
|
31
|
+
* there is nothing to read — an uncaptured payment, a FairCoin one, a
|
|
32
|
+
* provider that could not be reached — and treating it as zero is how a
|
|
33
|
+
* reconciliation silently gains money that was never there.
|
|
34
|
+
*
|
|
35
|
+
* It reports what the provider took. It does NOT say which entity bears that
|
|
36
|
+
* cost: a fee paid by the operator of a Peable deployment is not
|
|
37
|
+
* automatically your expense, and that is a commercial question this API does
|
|
38
|
+
* not answer.
|
|
39
|
+
*/
|
|
40
|
+
settlement(paymentIntentId) {
|
|
41
|
+
return this.client.request('GET', `/v1/payment_intents/${encodeURIComponent(paymentIntentId)}/settlement`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Every refund against one payment, including ones made OUTSIDE Peable.
|
|
45
|
+
*
|
|
46
|
+
* A refund issued from the acquirer's dashboard, or created by the network
|
|
47
|
+
* resolving a dispute, is imported with `origin: 'provider'` and no
|
|
48
|
+
* `externalRef` — the merchant did not make it and has no id for it.
|
|
49
|
+
*/
|
|
50
|
+
listForPaymentIntent(paymentIntentId) {
|
|
51
|
+
return this.client.request('GET', `/v1/payment_intents/${encodeURIComponent(paymentIntentId)}/refunds`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.RefundsResource = RefundsResource;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransfersResource = void 0;
|
|
4
|
+
class TransfersResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Settle one seller.
|
|
10
|
+
*
|
|
11
|
+
* Answers 201 for a new settlement and 200 for an order already settled,
|
|
12
|
+
* which is the distinction to act on after a timeout: "did I just pay this
|
|
13
|
+
* seller a second time" is a question with a real answer.
|
|
14
|
+
*
|
|
15
|
+
* Refused when the payment cannot fund it — the total settled out of one
|
|
16
|
+
* payment may not exceed what that payment brought in, because the overflow
|
|
17
|
+
* would come from the platform's general balance, which is other merchants'
|
|
18
|
+
* money in flight.
|
|
19
|
+
*/
|
|
20
|
+
create(params) {
|
|
21
|
+
return this.client.request('POST', '/v1/transfers', { body: params });
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Take some or all of a settlement back.
|
|
25
|
+
*
|
|
26
|
+
* The response carries the transfer AND the reversal leg, so a caller can
|
|
27
|
+
* tell which operation it is reading: `amountReversed` on the transfer is the
|
|
28
|
+
* provider's CUMULATIVE figure, while `reversal` is this one.
|
|
29
|
+
*/
|
|
30
|
+
reverse(transferId, params, options = {}) {
|
|
31
|
+
if (params.externalRef === undefined && options.idempotencyKey === undefined) {
|
|
32
|
+
// Refused HERE rather than at the gateway, because the failure it
|
|
33
|
+
// prevents is silent: without an identity a retry is a second reversal,
|
|
34
|
+
// and a seller loses the amount twice with nothing saying so.
|
|
35
|
+
throw new TypeError('a reversal needs an identity: pass externalRef, or options.idempotencyKey');
|
|
36
|
+
}
|
|
37
|
+
return this.client.request('POST', `/v1/transfers/${encodeURIComponent(transferId)}/reversals`, {
|
|
38
|
+
body: params,
|
|
39
|
+
...(options.idempotencyKey === undefined
|
|
40
|
+
? {}
|
|
41
|
+
: { idempotencyKey: options.idempotencyKey }),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** What one payment settled — the reconciliation read. */
|
|
45
|
+
listForPaymentIntent(paymentIntentId) {
|
|
46
|
+
return this.client.request('GET', `/v1/payment_intents/${encodeURIComponent(paymentIntentId)}/transfers`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.TransfersResource = TransfersResource;
|