@conto_finance/sdk 0.1.0 → 0.4.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 +455 -4
- package/dist/browserbase.d.mts +139 -0
- package/dist/browserbase.d.ts +139 -0
- package/dist/browserbase.js +1980 -0
- package/dist/browserbase.mjs +604 -0
- package/dist/checkout-verifier.d.mts +95 -0
- package/dist/checkout-verifier.d.ts +95 -0
- package/dist/checkout-verifier.js +400 -0
- package/dist/checkout-verifier.mjs +372 -0
- package/dist/chunk-B7F5QLE5.mjs +34 -0
- package/dist/chunk-HM4EG5MH.mjs +1256 -0
- package/dist/chunk-O5RAAB2U.mjs +123 -0
- package/dist/index.d.mts +1775 -143
- package/dist/index.d.ts +1775 -143
- package/dist/index.js +1467 -160
- package/dist/index.mjs +1348 -188
- package/dist/playwright.d.mts +255 -0
- package/dist/playwright.d.ts +255 -0
- package/dist/playwright.js +1394 -0
- package/dist/playwright.mjs +22 -0
- package/package.json +37 -9
package/dist/index.mjs
CHANGED
|
@@ -1,39 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
throw mod = 0, e;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
var __copyProps = (to, from, except, desc) => {
|
|
15
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
-
for (let key of __getOwnPropNames(from))
|
|
17
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
-
}
|
|
20
|
-
return to;
|
|
21
|
-
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
-
mod
|
|
29
|
-
));
|
|
1
|
+
import {
|
|
2
|
+
BrowserCheckoutTargetVerificationError,
|
|
3
|
+
canonicalizeBrowserCheckoutTarget,
|
|
4
|
+
createVerifiedBrowserCheckoutDriver,
|
|
5
|
+
fingerprintBrowserCheckoutTarget
|
|
6
|
+
} from "./chunk-O5RAAB2U.mjs";
|
|
7
|
+
import {
|
|
8
|
+
__commonJS,
|
|
9
|
+
__toESM
|
|
10
|
+
} from "./chunk-B7F5QLE5.mjs";
|
|
30
11
|
|
|
31
12
|
// package.json
|
|
32
13
|
var require_package = __commonJS({
|
|
33
14
|
"package.json"(exports, module) {
|
|
34
15
|
module.exports = {
|
|
35
16
|
name: "@conto_finance/sdk",
|
|
36
|
-
version: "0.
|
|
17
|
+
version: "0.4.0",
|
|
37
18
|
description: "Conto SDK for AI agent payment authorization",
|
|
38
19
|
main: "./dist/index.js",
|
|
39
20
|
module: "./dist/index.mjs",
|
|
@@ -43,22 +24,41 @@ var require_package = __commonJS({
|
|
|
43
24
|
types: "./dist/index.d.ts",
|
|
44
25
|
import: "./dist/index.mjs",
|
|
45
26
|
require: "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./playwright": {
|
|
29
|
+
types: "./dist/playwright.d.ts",
|
|
30
|
+
import: "./dist/playwright.mjs",
|
|
31
|
+
require: "./dist/playwright.js"
|
|
32
|
+
},
|
|
33
|
+
"./browserbase": {
|
|
34
|
+
types: "./dist/browserbase.d.ts",
|
|
35
|
+
import: "./dist/browserbase.mjs",
|
|
36
|
+
require: "./dist/browserbase.js"
|
|
37
|
+
},
|
|
38
|
+
"./checkout-verifier": {
|
|
39
|
+
types: "./dist/checkout-verifier.d.ts",
|
|
40
|
+
import: "./dist/checkout-verifier.mjs",
|
|
41
|
+
require: "./dist/checkout-verifier.js"
|
|
46
42
|
}
|
|
47
43
|
},
|
|
48
44
|
publishConfig: {
|
|
49
45
|
access: "public"
|
|
50
46
|
},
|
|
51
47
|
engines: {
|
|
52
|
-
node: ">=
|
|
48
|
+
node: ">=20.19.0"
|
|
53
49
|
},
|
|
54
50
|
files: [
|
|
55
51
|
"dist"
|
|
56
52
|
],
|
|
57
53
|
scripts: {
|
|
58
|
-
build: "tsup src/index.ts --format cjs,esm --dts",
|
|
59
|
-
dev: "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
54
|
+
build: "tsup src/index.ts src/playwright.ts src/browserbase.ts src/checkout-verifier.ts --format cjs,esm --dts",
|
|
55
|
+
dev: "tsup src/index.ts src/playwright.ts src/browserbase.ts src/checkout-verifier.ts --format cjs,esm --dts --watch",
|
|
60
56
|
lint: "tsc --noEmit",
|
|
61
|
-
|
|
57
|
+
test: "npm run lint && npm run test:contract && npm run test:consumer",
|
|
58
|
+
"test:contract": "npm run build && node --test test/contract.test.mjs test/playwright.test.mjs test/browserbase.test.mjs test/checkout-verifier.test.mjs test/developer-experience.test.mjs",
|
|
59
|
+
"test:consumer": "npm run build && node --test test/consumer.test.mjs",
|
|
60
|
+
"verify:release": "node scripts/verify-release.mjs",
|
|
61
|
+
prepublishOnly: "npm run verify:release && npm run lint && npm run test:contract && npm run test:consumer"
|
|
62
62
|
},
|
|
63
63
|
keywords: [
|
|
64
64
|
"conto",
|
|
@@ -75,18 +75,27 @@ var require_package = __commonJS({
|
|
|
75
75
|
url: "https://github.com/conto-finance/conto.git",
|
|
76
76
|
directory: "packages/sdk"
|
|
77
77
|
},
|
|
78
|
-
homepage: "https://conto.finance/docs/sdk",
|
|
78
|
+
homepage: "https://conto.finance/docs/sdk/installation",
|
|
79
79
|
bugs: {
|
|
80
|
-
|
|
80
|
+
email: "support@conto.finance"
|
|
81
81
|
},
|
|
82
82
|
dependencies: {
|
|
83
|
-
"@sentry/node": "^10.
|
|
83
|
+
"@sentry/node": "^10.63.0"
|
|
84
|
+
},
|
|
85
|
+
peerDependencies: {
|
|
86
|
+
"playwright-core": ">=1.50.0"
|
|
87
|
+
},
|
|
88
|
+
peerDependenciesMeta: {
|
|
89
|
+
"playwright-core": {
|
|
90
|
+
optional: true
|
|
91
|
+
}
|
|
84
92
|
},
|
|
85
93
|
overrides: {
|
|
86
94
|
esbuild: "0.28.1"
|
|
87
95
|
},
|
|
88
96
|
devDependencies: {
|
|
89
|
-
"@types/node": "
|
|
97
|
+
"@types/node": "22.19.18",
|
|
98
|
+
"playwright-core": "1.61.0",
|
|
90
99
|
tsup: "^8.5.1",
|
|
91
100
|
typescript: "^5.9.3"
|
|
92
101
|
}
|
|
@@ -109,10 +118,225 @@ var require_package_version = __commonJS({
|
|
|
109
118
|
// src/index.ts
|
|
110
119
|
var import_package_version2 = __toESM(require_package_version());
|
|
111
120
|
import * as Sentry2 from "@sentry/node";
|
|
121
|
+
import { randomUUID } from "crypto";
|
|
122
|
+
|
|
123
|
+
// src/http.ts
|
|
124
|
+
var PUBLIC_PAYMENT_REASONS = /* @__PURE__ */ new Set([
|
|
125
|
+
"The available balance is insufficient for this payment.",
|
|
126
|
+
"This payment exceeds a configured spending limit.",
|
|
127
|
+
"This recipient is not allowed by the current payment controls.",
|
|
128
|
+
"This payment is outside the permitted schedule.",
|
|
129
|
+
"The available payment budget is insufficient.",
|
|
130
|
+
"This payment does not meet the current payment controls.",
|
|
131
|
+
"This payment requires approval before it can proceed."
|
|
132
|
+
]);
|
|
133
|
+
function customerPaymentReasons(value) {
|
|
134
|
+
return Array.isArray(value) ? [
|
|
135
|
+
...new Set(
|
|
136
|
+
value.filter(
|
|
137
|
+
(reason) => typeof reason === "string" && PUBLIC_PAYMENT_REASONS.has(reason)
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
] : [];
|
|
141
|
+
}
|
|
142
|
+
var VALIDATION_FIELDS = /* @__PURE__ */ new Set([
|
|
143
|
+
"amount",
|
|
144
|
+
"recipientAddress",
|
|
145
|
+
"senderAddress",
|
|
146
|
+
"recipientName",
|
|
147
|
+
"purpose",
|
|
148
|
+
"category",
|
|
149
|
+
"chainId",
|
|
150
|
+
"walletId",
|
|
151
|
+
"idempotencyKey",
|
|
152
|
+
"txHash",
|
|
153
|
+
"approvalToken"
|
|
154
|
+
]);
|
|
155
|
+
function customerFieldErrors(body, status) {
|
|
156
|
+
if (![400, 422].includes(status) || !["VALIDATION_ERROR", "INVALID_REQUEST", "INVALID_INPUT"].includes(String(body.code)))
|
|
157
|
+
return void 0;
|
|
158
|
+
const fields = record(body.details).fields;
|
|
159
|
+
if (!Array.isArray(fields)) return void 0;
|
|
160
|
+
const names = [
|
|
161
|
+
...new Set(
|
|
162
|
+
fields.map((item) => record(item).field).filter(
|
|
163
|
+
(field) => typeof field === "string" && VALIDATION_FIELDS.has(field)
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
];
|
|
167
|
+
return names.length ? names.map((field) => ({
|
|
168
|
+
field,
|
|
169
|
+
message: "Check this field against the documented request schema."
|
|
170
|
+
})) : void 0;
|
|
171
|
+
}
|
|
172
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
173
|
+
function assertSecureBaseUrl(baseUrl) {
|
|
174
|
+
let parsed;
|
|
175
|
+
try {
|
|
176
|
+
parsed = new URL(baseUrl);
|
|
177
|
+
} catch {
|
|
178
|
+
throw new Error(`Invalid base URL: ${baseUrl}`);
|
|
179
|
+
}
|
|
180
|
+
if (parsed.protocol !== "https:" && parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1") {
|
|
181
|
+
throw new Error("baseUrl must use https (except for localhost)");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function abortError() {
|
|
185
|
+
const error = new Error("Request aborted");
|
|
186
|
+
error.name = "AbortError";
|
|
187
|
+
return error;
|
|
188
|
+
}
|
|
189
|
+
function wait(ms, signal) {
|
|
190
|
+
if (signal?.aborted) return Promise.reject(abortError());
|
|
191
|
+
return new Promise((resolve, reject) => {
|
|
192
|
+
const onAbort = () => {
|
|
193
|
+
clearTimeout(timeout);
|
|
194
|
+
reject(abortError());
|
|
195
|
+
};
|
|
196
|
+
const timeout = setTimeout(() => {
|
|
197
|
+
signal?.removeEventListener("abort", onAbort);
|
|
198
|
+
resolve();
|
|
199
|
+
}, ms);
|
|
200
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function parseRetryAfter(value) {
|
|
204
|
+
if (!value) return void 0;
|
|
205
|
+
const seconds = Number(value);
|
|
206
|
+
if (Number.isFinite(seconds) && seconds >= 0) return Math.ceil(seconds);
|
|
207
|
+
const date = Date.parse(value);
|
|
208
|
+
if (!Number.isFinite(date)) return void 0;
|
|
209
|
+
return Math.max(0, Math.ceil((date - Date.now()) / 1e3));
|
|
210
|
+
}
|
|
211
|
+
function retryDelayMs(response, attempt) {
|
|
212
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
213
|
+
if (retryAfter !== void 0) {
|
|
214
|
+
return Math.min(retryAfter * 1e3, MAX_RETRY_DELAY_MS);
|
|
215
|
+
}
|
|
216
|
+
return Math.min(1e3 * 2 ** attempt, 1e4);
|
|
217
|
+
}
|
|
218
|
+
async function fetchWithRetry(url, options, policy) {
|
|
219
|
+
const maxRetries = policy.maxRetries ?? 3;
|
|
220
|
+
let lastError;
|
|
221
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
222
|
+
try {
|
|
223
|
+
const response = await fetch(url, options);
|
|
224
|
+
const transient = response.status === 429 || response.status >= 500;
|
|
225
|
+
if (policy.retryable && transient && attempt < maxRetries) {
|
|
226
|
+
await wait(retryDelayMs(response, attempt), options.signal);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
return response;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
232
|
+
if (normalized.name === "AbortError" || options.signal?.aborted) throw normalized;
|
|
233
|
+
lastError = normalized;
|
|
234
|
+
if (!policy.retryable || attempt >= maxRetries) throw normalized;
|
|
235
|
+
await wait(Math.min(1e3 * 2 ** attempt, 1e4), options.signal);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
throw lastError ?? new Error("Request failed after retries");
|
|
239
|
+
}
|
|
240
|
+
async function readResponseData(response) {
|
|
241
|
+
if (typeof response.text !== "function") return response.json();
|
|
242
|
+
const text = await response.text();
|
|
243
|
+
if (!text) return {};
|
|
244
|
+
try {
|
|
245
|
+
return JSON.parse(text);
|
|
246
|
+
} catch {
|
|
247
|
+
return { error: text };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function record(value) {
|
|
251
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
252
|
+
}
|
|
253
|
+
function fallbackCode(status) {
|
|
254
|
+
if (status === 400 || status === 422) return "INVALID_REQUEST";
|
|
255
|
+
if (status === 401) return "AUTH_FAILED";
|
|
256
|
+
if (status === 403) return "FORBIDDEN";
|
|
257
|
+
if (status === 404) return "NOT_FOUND";
|
|
258
|
+
if (status === 409) return "CONFLICT";
|
|
259
|
+
if (status === 429) return "RATE_LIMITED";
|
|
260
|
+
if (status >= 500) return "SERVER_ERROR";
|
|
261
|
+
return "REQUEST_FAILED";
|
|
262
|
+
}
|
|
263
|
+
function safeErrorCode(value, status) {
|
|
264
|
+
return typeof value === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(value) ? value : fallbackCode(status);
|
|
265
|
+
}
|
|
266
|
+
function safeRequestId(...values) {
|
|
267
|
+
return values.find(
|
|
268
|
+
(value) => typeof value === "string" && value.length > 0 && value.length <= 200 && /^[A-Za-z0-9._:@/-]+$/.test(value)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
function safeActionUrl(value, code) {
|
|
272
|
+
if (code !== "ACTION_REQUIRED" || typeof value !== "string" || value.length > 2048) {
|
|
273
|
+
return void 0;
|
|
274
|
+
}
|
|
275
|
+
if (value.startsWith("/") && !value.startsWith("//")) return value;
|
|
276
|
+
try {
|
|
277
|
+
const url = new URL(value);
|
|
278
|
+
return url.protocol === "https:" ? value : void 0;
|
|
279
|
+
} catch {
|
|
280
|
+
return void 0;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function safeErrorMessage(status) {
|
|
284
|
+
if (status === 400 || status === 422) return "The request was invalid.";
|
|
285
|
+
if (status === 401) return "Authentication failed.";
|
|
286
|
+
if (status === 403) return "The request is not permitted.";
|
|
287
|
+
if (status === 404) return "The requested resource was not found.";
|
|
288
|
+
if (status === 409) return "The request conflicts with the current resource state.";
|
|
289
|
+
if (status === 429) return "Too many requests.";
|
|
290
|
+
if (status >= 500) return "The service is temporarily unavailable.";
|
|
291
|
+
return "The request could not be completed.";
|
|
292
|
+
}
|
|
293
|
+
function fallbackRetryGuidance(status) {
|
|
294
|
+
if (status === 400 || status === 422) return "Review the request before trying again.";
|
|
295
|
+
if (status === 401) return "Verify the API key and that it is intended for this client.";
|
|
296
|
+
if (status === 403) return "Verify credentials and permissions before trying again.";
|
|
297
|
+
if (status === 404) return "Verify the resource identifier before trying again.";
|
|
298
|
+
if (status === 409) return "Check the current resource state before trying again.";
|
|
299
|
+
if (status === 429) return "Wait for retryAfter seconds before retrying.";
|
|
300
|
+
if (status >= 500)
|
|
301
|
+
return "Retry only if this operation is read-only or protected by idempotency.";
|
|
302
|
+
return void 0;
|
|
303
|
+
}
|
|
304
|
+
function responseErrorMessage(_data, status) {
|
|
305
|
+
return safeErrorMessage(status);
|
|
306
|
+
}
|
|
307
|
+
function responseErrorMetadata(response, data) {
|
|
308
|
+
const body = record(data);
|
|
309
|
+
const code = safeErrorCode(body.code, response.status);
|
|
310
|
+
const bodyRetryAfter = typeof body.retryAfter === "number" || typeof body.retryAfter === "string" ? parseRetryAfter(String(body.retryAfter)) : void 0;
|
|
311
|
+
return {
|
|
312
|
+
code,
|
|
313
|
+
status: response.status,
|
|
314
|
+
fieldErrors: customerFieldErrors(body, response.status),
|
|
315
|
+
requestId: safeRequestId(
|
|
316
|
+
response.headers.get("x-request-id"),
|
|
317
|
+
response.headers.get("x-correlation-id"),
|
|
318
|
+
body.requestId
|
|
319
|
+
),
|
|
320
|
+
retryAfter: parseRetryAfter(response.headers.get("retry-after")) ?? bodyRetryAfter,
|
|
321
|
+
retryGuidance: fallbackRetryGuidance(response.status),
|
|
322
|
+
actionUrl: safeActionUrl(body.actionUrl, code)
|
|
323
|
+
};
|
|
324
|
+
}
|
|
112
325
|
|
|
113
326
|
// src/admin.ts
|
|
114
327
|
var import_package_version = __toESM(require_package_version());
|
|
115
328
|
import * as Sentry from "@sentry/node";
|
|
329
|
+
var ContoAdminError = class extends Error {
|
|
330
|
+
constructor(message, metadata) {
|
|
331
|
+
super(message);
|
|
332
|
+
this.name = "ContoAdminError";
|
|
333
|
+
this.code = metadata.code;
|
|
334
|
+
this.status = metadata.status;
|
|
335
|
+
this.requestId = metadata.requestId;
|
|
336
|
+
this.retryAfter = metadata.retryAfter;
|
|
337
|
+
this.retryGuidance = metadata.retryGuidance;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
116
340
|
var ContoAdmin = class {
|
|
117
341
|
constructor(config) {
|
|
118
342
|
if (!config.orgApiKey) {
|
|
@@ -130,77 +354,178 @@ var ContoAdmin = class {
|
|
|
130
354
|
}
|
|
131
355
|
this.orgApiKey = config.orgApiKey;
|
|
132
356
|
this.baseUrl = (config.baseUrl || "https://conto.finance").replace(/\/$/, "");
|
|
357
|
+
assertSecureBaseUrl(this.baseUrl);
|
|
133
358
|
this.timeout = config.timeout || 3e4;
|
|
134
359
|
this.agents = new AgentsClient(this);
|
|
135
360
|
this.wallets = new WalletsClient(this);
|
|
361
|
+
this.cards = new AdminCardsClient(this);
|
|
136
362
|
this.policies = new PoliciesClient(this);
|
|
137
363
|
this.team = new TeamClient(this);
|
|
138
|
-
|
|
139
|
-
async fetchWithRetry(url, options, maxRetries = 3) {
|
|
140
|
-
let lastError = null;
|
|
141
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
142
|
-
try {
|
|
143
|
-
const response = await fetch(url, options);
|
|
144
|
-
if (response.status === 429 || response.status >= 500 && attempt < maxRetries) {
|
|
145
|
-
const retryAfter = response.headers.get("retry-after");
|
|
146
|
-
const delay = retryAfter ? parseInt(retryAfter, 10) * 1e3 : Math.min(1e3 * 2 ** attempt, 1e4);
|
|
147
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
return response;
|
|
151
|
-
} catch (err) {
|
|
152
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
153
|
-
if (attempt < maxRetries) {
|
|
154
|
-
const delay = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
155
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
throw lastError || new Error("Request failed after retries");
|
|
364
|
+
this.paymentRails = new PaymentRailsClient(this);
|
|
161
365
|
}
|
|
162
366
|
/** Make an authenticated request to the Conto API */
|
|
163
367
|
async request(method, path, body) {
|
|
164
368
|
const url = `${this.baseUrl}${path}`;
|
|
165
369
|
const controller = new AbortController();
|
|
166
370
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
371
|
+
const normalizedMethod = method.toUpperCase();
|
|
372
|
+
const retryable = ["GET", "HEAD", "OPTIONS"].includes(normalizedMethod);
|
|
167
373
|
try {
|
|
168
|
-
const response = await
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
374
|
+
const response = await fetchWithRetry(
|
|
375
|
+
url,
|
|
376
|
+
{
|
|
377
|
+
method: normalizedMethod,
|
|
378
|
+
headers: {
|
|
379
|
+
Authorization: `Bearer ${this.orgApiKey}`,
|
|
380
|
+
"Content-Type": "application/json",
|
|
381
|
+
"User-Agent": `@conto_finance/sdk-admin/${import_package_version.SDK_PACKAGE_VERSION}`
|
|
382
|
+
},
|
|
383
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
384
|
+
signal: controller.signal
|
|
174
385
|
},
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
clearTimeout(timeoutId);
|
|
179
|
-
const data = await response.json();
|
|
386
|
+
{ retryable }
|
|
387
|
+
);
|
|
388
|
+
const data = await readResponseData(response);
|
|
180
389
|
if (!response.ok) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
throw error;
|
|
390
|
+
throw new ContoAdminError(
|
|
391
|
+
responseErrorMessage(data, response.status),
|
|
392
|
+
responseErrorMetadata(response, data)
|
|
393
|
+
);
|
|
186
394
|
}
|
|
187
395
|
return data;
|
|
188
396
|
} catch (error) {
|
|
189
|
-
|
|
397
|
+
if (error instanceof ContoAdminError) throw error;
|
|
190
398
|
if (error instanceof Error && error.name === "AbortError") {
|
|
191
|
-
const timeoutError = new
|
|
192
|
-
|
|
193
|
-
|
|
399
|
+
const timeoutError = new ContoAdminError("Request timeout", {
|
|
400
|
+
code: "TIMEOUT",
|
|
401
|
+
status: 0,
|
|
402
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; inspect the resource before trying again."
|
|
403
|
+
});
|
|
194
404
|
Sentry.captureException(timeoutError, { extra: { method, path } });
|
|
195
405
|
throw timeoutError;
|
|
196
406
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
407
|
+
const networkError = new ContoAdminError("Network request failed", {
|
|
408
|
+
code: "NETWORK_ERROR",
|
|
409
|
+
status: 0,
|
|
410
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; inspect the resource before trying again."
|
|
411
|
+
});
|
|
412
|
+
Sentry.captureException(networkError, { extra: { method, path } });
|
|
413
|
+
throw networkError;
|
|
414
|
+
} finally {
|
|
415
|
+
clearTimeout(timeoutId);
|
|
201
416
|
}
|
|
202
417
|
}
|
|
203
418
|
};
|
|
419
|
+
var PaymentRailsClient = class {
|
|
420
|
+
constructor(client) {
|
|
421
|
+
this.client = client;
|
|
422
|
+
}
|
|
423
|
+
/** Read gate and configuration readiness without calling Stripe. */
|
|
424
|
+
async getStripeLinkCheckoutReadiness() {
|
|
425
|
+
return this.client.request("GET", "/api/stripe-link/checkout-readiness");
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Ask Conto to verify that its configured Stripe credential resolves to its
|
|
429
|
+
* configured merchant account. This call never returns either identifier.
|
|
430
|
+
*/
|
|
431
|
+
async verifyStripeLinkCheckoutAccount() {
|
|
432
|
+
return this.client.request("POST", "/api/stripe-link/checkout-readiness");
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
var AdminCardsClient = class {
|
|
436
|
+
constructor(client) {
|
|
437
|
+
this.client = client;
|
|
438
|
+
}
|
|
439
|
+
/** List masked external-card aliases. No card credentials are returned. */
|
|
440
|
+
async list() {
|
|
441
|
+
return this.client.request("GET", "/api/external-cards");
|
|
442
|
+
}
|
|
443
|
+
/** Register a masked alias for a customer card. Never pass PAN or CVC. */
|
|
444
|
+
async register(input) {
|
|
445
|
+
return this.client.request(
|
|
446
|
+
"POST",
|
|
447
|
+
"/api/external-cards",
|
|
448
|
+
input
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
/** Assign a card alias and cooperative policy controls to an agent. */
|
|
452
|
+
async assignToAgent(agentId, input) {
|
|
453
|
+
return this.client.request(
|
|
454
|
+
"POST",
|
|
455
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards`,
|
|
456
|
+
input
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
async listForAgent(agentId) {
|
|
460
|
+
return this.client.request("GET", `/api/agents/${encodeURIComponent(agentId)}/external-cards`);
|
|
461
|
+
}
|
|
462
|
+
/** Update limits, merchant rules, hours, and optional start or end dates. */
|
|
463
|
+
async updateAgentAssignment(agentId, cardId, input) {
|
|
464
|
+
return this.client.request(
|
|
465
|
+
"PATCH",
|
|
466
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`,
|
|
467
|
+
{ action: "UPDATE", ...input }
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
/** Pause an assignment and cancel its unused approvals. */
|
|
471
|
+
async pauseForAgent(agentId, cardId) {
|
|
472
|
+
return this.client.request(
|
|
473
|
+
"PATCH",
|
|
474
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`,
|
|
475
|
+
{ action: "PAUSE" }
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
/** Resume a paused assignment when its alias and date range permit it. */
|
|
479
|
+
async resumeForAgent(agentId, cardId) {
|
|
480
|
+
return this.client.request(
|
|
481
|
+
"PATCH",
|
|
482
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`,
|
|
483
|
+
{ action: "RESUME" }
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Revoke an agent's overlay assignment. Unused grants are cancelled; claimed
|
|
488
|
+
* executions remain visible for reconciliation.
|
|
489
|
+
*/
|
|
490
|
+
async revokeFromAgent(agentId, cardId) {
|
|
491
|
+
return this.client.request(
|
|
492
|
+
"DELETE",
|
|
493
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
/** List overlay requests, including executions that need receipt reconciliation. */
|
|
497
|
+
async listRequests(input = {}) {
|
|
498
|
+
const query = new URLSearchParams();
|
|
499
|
+
if (input.status) query.set("status", input.status);
|
|
500
|
+
if (input.limit !== void 0) query.set("limit", String(input.limit));
|
|
501
|
+
const suffix = query.toString();
|
|
502
|
+
return this.client.request("GET", `/api/external-cards/requests${suffix ? `?${suffix}` : ""}`);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Resolve a claimed execution after checking the customer's processor or
|
|
506
|
+
* receipt. Resolving NOT_EXECUTED releases the reserved policy amount.
|
|
507
|
+
*/
|
|
508
|
+
async resolveRequest(requestId, input) {
|
|
509
|
+
return this.client.request(
|
|
510
|
+
"POST",
|
|
511
|
+
`/api/external-cards/requests/${encodeURIComponent(requestId)}/resolve`,
|
|
512
|
+
input
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
/** Pause the Conto-controlled path and revoke live overlay grants. */
|
|
516
|
+
async pause(cardId) {
|
|
517
|
+
return this.setState(cardId, "PAUSED");
|
|
518
|
+
}
|
|
519
|
+
/** Resume the Conto-controlled path. This does not activate a card at its issuer. */
|
|
520
|
+
async resume(cardId) {
|
|
521
|
+
return this.setState(cardId, "ACTIVE");
|
|
522
|
+
}
|
|
523
|
+
async setState(cardId, state) {
|
|
524
|
+
return this.client.request("PATCH", `/api/external-cards/${encodeURIComponent(cardId)}/state`, {
|
|
525
|
+
state
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
};
|
|
204
529
|
var AgentsClient = class {
|
|
205
530
|
constructor(client) {
|
|
206
531
|
this.client = client;
|
|
@@ -225,48 +550,55 @@ var AgentsClient = class {
|
|
|
225
550
|
}
|
|
226
551
|
/** Get a single agent by ID */
|
|
227
552
|
async get(agentId) {
|
|
228
|
-
return this.client.request("GET", `/api/agents/${agentId}`);
|
|
553
|
+
return this.client.request("GET", `/api/agents/${encodeURIComponent(agentId)}`);
|
|
229
554
|
}
|
|
230
555
|
/** Update an agent */
|
|
231
556
|
async update(agentId, input) {
|
|
232
557
|
return this.client.request(
|
|
233
558
|
"PATCH",
|
|
234
|
-
`/api/agents/${agentId}`,
|
|
559
|
+
`/api/agents/${encodeURIComponent(agentId)}`,
|
|
235
560
|
input
|
|
236
561
|
);
|
|
237
562
|
}
|
|
238
563
|
/** Delete an agent (soft delete) */
|
|
239
564
|
async delete(agentId) {
|
|
240
|
-
return this.client.request(
|
|
565
|
+
return this.client.request(
|
|
566
|
+
"DELETE",
|
|
567
|
+
`/api/agents/${encodeURIComponent(agentId)}`
|
|
568
|
+
);
|
|
241
569
|
}
|
|
242
570
|
/** Freeze an agent, blocking all transactions */
|
|
243
571
|
async freeze(agentId, input) {
|
|
244
572
|
return this.client.request(
|
|
245
573
|
"POST",
|
|
246
|
-
`/api/agents/${agentId}/freeze`,
|
|
247
|
-
|
|
574
|
+
`/api/agents/${encodeURIComponent(agentId)}/freeze`,
|
|
575
|
+
{
|
|
576
|
+
...input.freezeWallets !== void 0 && { freezeWallets: input.freezeWallets }
|
|
577
|
+
}
|
|
248
578
|
);
|
|
249
579
|
}
|
|
250
580
|
/** Unfreeze an agent, restoring transaction ability */
|
|
251
581
|
async unfreeze(agentId, input) {
|
|
252
582
|
return this.client.request(
|
|
253
583
|
"POST",
|
|
254
|
-
`/api/agents/${agentId}/unfreeze`,
|
|
255
|
-
|
|
584
|
+
`/api/agents/${encodeURIComponent(agentId)}/unfreeze`,
|
|
585
|
+
{
|
|
586
|
+
...input.unfreezeWallets !== void 0 && { unfreezeWallets: input.unfreezeWallets }
|
|
587
|
+
}
|
|
256
588
|
);
|
|
257
589
|
}
|
|
258
590
|
/** List wallets linked to an agent */
|
|
259
591
|
async listWallets(agentId) {
|
|
260
592
|
return this.client.request(
|
|
261
593
|
"GET",
|
|
262
|
-
`/api/agents/${agentId}/wallets`
|
|
594
|
+
`/api/agents/${encodeURIComponent(agentId)}/wallets`
|
|
263
595
|
);
|
|
264
596
|
}
|
|
265
597
|
/** Link a wallet to an agent with spending limits */
|
|
266
598
|
async linkWallet(agentId, input) {
|
|
267
599
|
return this.client.request(
|
|
268
600
|
"POST",
|
|
269
|
-
`/api/agents/${agentId}/wallets`,
|
|
601
|
+
`/api/agents/${encodeURIComponent(agentId)}/wallets`,
|
|
270
602
|
input
|
|
271
603
|
);
|
|
272
604
|
}
|
|
@@ -274,39 +606,45 @@ var AgentsClient = class {
|
|
|
274
606
|
async unlinkWallet(agentId, walletId) {
|
|
275
607
|
return this.client.request(
|
|
276
608
|
"DELETE",
|
|
277
|
-
`/api/agents/${agentId}/wallets/${walletId}`
|
|
609
|
+
`/api/agents/${encodeURIComponent(agentId)}/wallets/${encodeURIComponent(walletId)}`
|
|
278
610
|
);
|
|
279
611
|
}
|
|
280
612
|
/** List policies assigned to an agent */
|
|
281
613
|
async listPolicies(agentId) {
|
|
282
614
|
return this.client.request(
|
|
283
615
|
"GET",
|
|
284
|
-
`/api/agents/${agentId}/policies`
|
|
616
|
+
`/api/agents/${encodeURIComponent(agentId)}/policies`
|
|
285
617
|
);
|
|
286
618
|
}
|
|
287
619
|
/** Assign a policy to an agent */
|
|
288
|
-
async assignPolicy(agentId, policyId
|
|
289
|
-
return this.client.request(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
620
|
+
async assignPolicy(agentId, policyId) {
|
|
621
|
+
return this.client.request(
|
|
622
|
+
"POST",
|
|
623
|
+
`/api/agents/${encodeURIComponent(agentId)}/policies`,
|
|
624
|
+
{
|
|
625
|
+
policyId
|
|
626
|
+
}
|
|
627
|
+
);
|
|
293
628
|
}
|
|
294
629
|
/** Unassign a policy from an agent */
|
|
295
630
|
async unassignPolicy(agentId, policyId) {
|
|
296
631
|
return this.client.request(
|
|
297
632
|
"DELETE",
|
|
298
|
-
`/api/agents/${agentId}/policies?policyId=${policyId}`
|
|
633
|
+
`/api/agents/${encodeURIComponent(agentId)}/policies?policyId=${encodeURIComponent(policyId)}`
|
|
299
634
|
);
|
|
300
635
|
}
|
|
301
636
|
/** List SDK keys for an agent */
|
|
302
637
|
async listSdkKeys(agentId) {
|
|
303
|
-
return this.client.request(
|
|
638
|
+
return this.client.request(
|
|
639
|
+
"GET",
|
|
640
|
+
`/api/agents/${encodeURIComponent(agentId)}/sdk-keys`
|
|
641
|
+
);
|
|
304
642
|
}
|
|
305
643
|
/** Create an SDK key for an agent (key value is returned only once) */
|
|
306
644
|
async createSdkKey(agentId, input) {
|
|
307
645
|
return this.client.request(
|
|
308
646
|
"POST",
|
|
309
|
-
`/api/agents/${agentId}/sdk-keys`,
|
|
647
|
+
`/api/agents/${encodeURIComponent(agentId)}/sdk-keys`,
|
|
310
648
|
input
|
|
311
649
|
);
|
|
312
650
|
}
|
|
@@ -314,7 +652,7 @@ var AgentsClient = class {
|
|
|
314
652
|
async revokeSdkKey(agentId, keyId) {
|
|
315
653
|
return this.client.request(
|
|
316
654
|
"DELETE",
|
|
317
|
-
`/api/agents/${agentId}/sdk-keys?keyId=${keyId}`
|
|
655
|
+
`/api/agents/${encodeURIComponent(agentId)}/sdk-keys?keyId=${encodeURIComponent(keyId)}`
|
|
318
656
|
);
|
|
319
657
|
}
|
|
320
658
|
};
|
|
@@ -333,7 +671,7 @@ var WalletsClient = class {
|
|
|
333
671
|
const qs = query.toString() ? `?${query.toString()}` : "";
|
|
334
672
|
return this.client.request("GET", `/api/wallets${qs}`);
|
|
335
673
|
}
|
|
336
|
-
/** Create a
|
|
674
|
+
/** Create a managed wallet or register an external address. */
|
|
337
675
|
async create(input) {
|
|
338
676
|
return this.client.request(
|
|
339
677
|
"POST",
|
|
@@ -343,25 +681,28 @@ var WalletsClient = class {
|
|
|
343
681
|
}
|
|
344
682
|
/** Get a single wallet by ID */
|
|
345
683
|
async get(walletId) {
|
|
346
|
-
return this.client.request("GET", `/api/wallets/${walletId}`);
|
|
684
|
+
return this.client.request("GET", `/api/wallets/${encodeURIComponent(walletId)}`);
|
|
347
685
|
}
|
|
348
686
|
/** Update a wallet */
|
|
349
687
|
async update(walletId, input) {
|
|
350
688
|
return this.client.request(
|
|
351
689
|
"PATCH",
|
|
352
|
-
`/api/wallets/${walletId}`,
|
|
690
|
+
`/api/wallets/${encodeURIComponent(walletId)}`,
|
|
353
691
|
input
|
|
354
692
|
);
|
|
355
693
|
}
|
|
356
694
|
/** Delete a wallet (cannot delete if linked to agents) */
|
|
357
695
|
async delete(walletId) {
|
|
358
|
-
return this.client.request(
|
|
696
|
+
return this.client.request(
|
|
697
|
+
"DELETE",
|
|
698
|
+
`/api/wallets/${encodeURIComponent(walletId)}`
|
|
699
|
+
);
|
|
359
700
|
}
|
|
360
701
|
/** Provision a wallet for onchain transactions */
|
|
361
702
|
async provision(walletId) {
|
|
362
703
|
return this.client.request(
|
|
363
704
|
"POST",
|
|
364
|
-
`/api/wallets/${walletId}/provision`,
|
|
705
|
+
`/api/wallets/${encodeURIComponent(walletId)}/provision`,
|
|
365
706
|
{}
|
|
366
707
|
);
|
|
367
708
|
}
|
|
@@ -369,7 +710,7 @@ var WalletsClient = class {
|
|
|
369
710
|
async refreshBalance(walletId) {
|
|
370
711
|
return this.client.request(
|
|
371
712
|
"POST",
|
|
372
|
-
`/api/wallets/${walletId}/refresh-balance`,
|
|
713
|
+
`/api/wallets/${encodeURIComponent(walletId)}/refresh-balance`,
|
|
373
714
|
{}
|
|
374
715
|
);
|
|
375
716
|
}
|
|
@@ -396,43 +737,53 @@ var PoliciesClient = class {
|
|
|
396
737
|
}
|
|
397
738
|
/** Get a single policy by ID */
|
|
398
739
|
async get(policyId) {
|
|
399
|
-
return this.client.request("GET", `/api/policies/${policyId}`);
|
|
740
|
+
return this.client.request("GET", `/api/policies/${encodeURIComponent(policyId)}`);
|
|
400
741
|
}
|
|
401
742
|
/** Update a policy */
|
|
402
743
|
async update(policyId, input) {
|
|
403
744
|
return this.client.request(
|
|
404
745
|
"PATCH",
|
|
405
|
-
`/api/policies/${policyId}`,
|
|
746
|
+
`/api/policies/${encodeURIComponent(policyId)}`,
|
|
406
747
|
input
|
|
407
748
|
);
|
|
408
749
|
}
|
|
409
750
|
/** Delete a policy */
|
|
410
751
|
async delete(policyId) {
|
|
411
|
-
return this.client.request(
|
|
752
|
+
return this.client.request(
|
|
753
|
+
"DELETE",
|
|
754
|
+
`/api/policies/${encodeURIComponent(policyId)}`
|
|
755
|
+
);
|
|
412
756
|
}
|
|
413
757
|
/** List rules for a policy */
|
|
414
758
|
async listRules(policyId) {
|
|
415
|
-
return this.client.request(
|
|
759
|
+
return this.client.request(
|
|
760
|
+
"GET",
|
|
761
|
+
`/api/policies/${encodeURIComponent(policyId)}/rules`
|
|
762
|
+
);
|
|
416
763
|
}
|
|
417
764
|
/** Add a single rule to a policy */
|
|
418
765
|
async addRule(policyId, rule) {
|
|
419
766
|
return this.client.request(
|
|
420
767
|
"POST",
|
|
421
|
-
`/api/policies/${policyId}/rules`,
|
|
768
|
+
`/api/policies/${encodeURIComponent(policyId)}/rules`,
|
|
422
769
|
rule
|
|
423
770
|
);
|
|
424
771
|
}
|
|
425
772
|
/** Add multiple rules to a policy */
|
|
426
773
|
async addRules(policyId, rules) {
|
|
427
|
-
return this.client.request(
|
|
428
|
-
|
|
429
|
-
|
|
774
|
+
return this.client.request(
|
|
775
|
+
"POST",
|
|
776
|
+
`/api/policies/${encodeURIComponent(policyId)}/rules`,
|
|
777
|
+
{
|
|
778
|
+
rules
|
|
779
|
+
}
|
|
780
|
+
);
|
|
430
781
|
}
|
|
431
782
|
/** Update a single rule */
|
|
432
783
|
async updateRule(policyId, ruleId, input) {
|
|
433
784
|
return this.client.request(
|
|
434
785
|
"PATCH",
|
|
435
|
-
`/api/policies/${policyId}/rules/${ruleId}`,
|
|
786
|
+
`/api/policies/${encodeURIComponent(policyId)}/rules/${encodeURIComponent(ruleId)}`,
|
|
436
787
|
input
|
|
437
788
|
);
|
|
438
789
|
}
|
|
@@ -440,14 +791,51 @@ var PoliciesClient = class {
|
|
|
440
791
|
async deleteRule(policyId, ruleId) {
|
|
441
792
|
return this.client.request(
|
|
442
793
|
"DELETE",
|
|
443
|
-
`/api/policies/${policyId}/rules/${ruleId}`
|
|
794
|
+
`/api/policies/${encodeURIComponent(policyId)}/rules/${encodeURIComponent(ruleId)}`
|
|
444
795
|
);
|
|
445
796
|
}
|
|
446
797
|
/** Delete all rules for a policy */
|
|
447
798
|
async deleteAllRules(policyId) {
|
|
448
799
|
return this.client.request(
|
|
449
800
|
"DELETE",
|
|
450
|
-
`/api/policies/${policyId}/rules`
|
|
801
|
+
`/api/policies/${encodeURIComponent(policyId)}/rules`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
/** List the version history for a policy (newest first). */
|
|
805
|
+
async listVersions(policyId, params) {
|
|
806
|
+
const query = new URLSearchParams();
|
|
807
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
808
|
+
if (params?.offset) query.set("offset", String(params.offset));
|
|
809
|
+
const qs = query.toString() ? `?${query.toString()}` : "";
|
|
810
|
+
return this.client.request(
|
|
811
|
+
"GET",
|
|
812
|
+
`/api/policies/${encodeURIComponent(policyId)}/versions${qs}`
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
/** Get a single version's snapshot. */
|
|
816
|
+
async getVersion(policyId, version) {
|
|
817
|
+
return this.client.request(
|
|
818
|
+
"GET",
|
|
819
|
+
`/api/policies/${encodeURIComponent(policyId)}/versions/${encodeURIComponent(version)}`
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
/** Diff two versions of a policy. */
|
|
823
|
+
async compareVersions(policyId, from, to) {
|
|
824
|
+
return this.client.request(
|
|
825
|
+
"GET",
|
|
826
|
+
`/api/policies/${encodeURIComponent(policyId)}/versions/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Restore an earlier version. Appends a new ROLLED_BACK version. If the
|
|
831
|
+
* organization requires governance approval for policy changes, the API
|
|
832
|
+
* returns a pending-approval response instead of applying the rollback.
|
|
833
|
+
*/
|
|
834
|
+
async rollbackVersion(policyId, version, options) {
|
|
835
|
+
return this.client.request(
|
|
836
|
+
"POST",
|
|
837
|
+
`/api/policies/${encodeURIComponent(policyId)}/versions/${encodeURIComponent(version)}/rollback`,
|
|
838
|
+
options ?? {}
|
|
451
839
|
);
|
|
452
840
|
}
|
|
453
841
|
};
|
|
@@ -473,6 +861,107 @@ if (process.env.SENTRY_DSN) {
|
|
|
473
861
|
enableLogs: true
|
|
474
862
|
});
|
|
475
863
|
}
|
|
864
|
+
function isValidBrowserActionObservation(value) {
|
|
865
|
+
if (!value || typeof value !== "object") return false;
|
|
866
|
+
const observation = value;
|
|
867
|
+
if (typeof observation.reference !== "string" || !observation.reference.trim() || observation.reference.length > 255 || typeof observation.observedAt !== "string" || Number.isNaN(Date.parse(observation.observedAt)) || typeof observation.pageUrl !== "string" || observation.pageUrl.length > 2048) {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
const pageUrl = new URL(observation.pageUrl);
|
|
872
|
+
return pageUrl.protocol === "https:" && !pageUrl.username && !pageUrl.password;
|
|
873
|
+
} catch {
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
var ContoError = class extends Error {
|
|
878
|
+
constructor(message, metadata) {
|
|
879
|
+
super(message);
|
|
880
|
+
this.name = "ContoError";
|
|
881
|
+
this.code = metadata.code;
|
|
882
|
+
this.status = metadata.status;
|
|
883
|
+
this.requestId = metadata.requestId;
|
|
884
|
+
this.retryAfter = metadata.retryAfter;
|
|
885
|
+
this.retryGuidance = metadata.retryGuidance;
|
|
886
|
+
this.actionUrl = metadata.actionUrl;
|
|
887
|
+
this.fieldErrors = metadata.fieldErrors;
|
|
888
|
+
this.reasons = metadata.reasons;
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
var ExternalCardConfirmationPendingError = class extends ContoError {
|
|
892
|
+
constructor(requestId, confirmationInput, receipt) {
|
|
893
|
+
super("External card execution completed but confirmation is pending", {
|
|
894
|
+
code: "CARD_CONFIRMATION_PENDING",
|
|
895
|
+
status: 0,
|
|
896
|
+
requestId,
|
|
897
|
+
retryGuidance: "Persist the receipt and confirmation input, then retry cards.confirm(). Do not execute the card payment again."
|
|
898
|
+
});
|
|
899
|
+
this.name = "ExternalCardConfirmationPendingError";
|
|
900
|
+
this.confirmationInput = confirmationInput;
|
|
901
|
+
this.receipt = receipt;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
var BrowserActionFinalizationPendingError = class extends ContoError {
|
|
905
|
+
constructor(input) {
|
|
906
|
+
super("Browser action completed but finalization is pending", {
|
|
907
|
+
code: "BROWSER_ACTION_FINALIZATION_PENDING",
|
|
908
|
+
status: 0,
|
|
909
|
+
retryGuidance: "Persist the result and observation, then retry finalizeBrowserAction with the same dispatch attempt. Do not execute the browser action again."
|
|
910
|
+
});
|
|
911
|
+
this.name = "BrowserActionFinalizationPendingError";
|
|
912
|
+
this.authorizationId = input.authorizationId;
|
|
913
|
+
this.dispatchAttemptId = input.dispatchAttemptId;
|
|
914
|
+
this.result = input.result;
|
|
915
|
+
this.observation = input.observation;
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
function generatedIdempotencyKey(prefix) {
|
|
919
|
+
return `${prefix}_${randomUUID()}`;
|
|
920
|
+
}
|
|
921
|
+
function requireFinancialIdentifier(value, field) {
|
|
922
|
+
if (!value || value.trim().length === 0) {
|
|
923
|
+
throw new ContoError(`${field} is required for this financial write`, {
|
|
924
|
+
code: "IDEMPOTENCY_KEY_REQUIRED",
|
|
925
|
+
status: 0,
|
|
926
|
+
retryGuidance: `Generate a stable unique ${field} and reuse it when reconciling or retrying this operation.`
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
if (value.length > 255) {
|
|
930
|
+
throw new ContoError(`${field} must be 255 characters or fewer`, {
|
|
931
|
+
code: "INVALID_IDEMPOTENCY_KEY",
|
|
932
|
+
status: 0
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
return value;
|
|
936
|
+
}
|
|
937
|
+
function withQuery(path, query) {
|
|
938
|
+
if (!query) return path;
|
|
939
|
+
const searchParams = new URLSearchParams();
|
|
940
|
+
for (const [key, value] of Object.entries(query)) {
|
|
941
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
942
|
+
searchParams.set(key, String(value));
|
|
943
|
+
}
|
|
944
|
+
const queryString = searchParams.toString();
|
|
945
|
+
return queryString ? `${path}?${queryString}` : path;
|
|
946
|
+
}
|
|
947
|
+
function rethrowAmbiguousCommerceMutation(error, executionId, action) {
|
|
948
|
+
if (error instanceof ContoError && (error.status === 0 || error.status >= 500)) {
|
|
949
|
+
throw new ContoError(error.message, {
|
|
950
|
+
code: error.code,
|
|
951
|
+
status: error.status,
|
|
952
|
+
requestId: error.requestId,
|
|
953
|
+
retryAfter: error.retryAfter,
|
|
954
|
+
retryGuidance: action === "authorize" ? `Call getCommerceExecution(${JSON.stringify(
|
|
955
|
+
executionId
|
|
956
|
+
)}). If it is PROCESSING, treat the authorization as consumed and do not authorize or click again.` : `Call getCommerceExecution(${JSON.stringify(
|
|
957
|
+
executionId
|
|
958
|
+
)}), then refreshCommerceExecution(${JSON.stringify(
|
|
959
|
+
executionId
|
|
960
|
+
)}). Do not ${action} again until the existing execution is reconciled.`
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
throw error;
|
|
964
|
+
}
|
|
476
965
|
var Conto = class {
|
|
477
966
|
constructor(config) {
|
|
478
967
|
if (!config.apiKey) {
|
|
@@ -483,78 +972,600 @@ var Conto = class {
|
|
|
483
972
|
}
|
|
484
973
|
this.apiKey = config.apiKey;
|
|
485
974
|
this.baseUrl = (config.baseUrl || "https://conto.finance").replace(/\/$/, "");
|
|
975
|
+
assertSecureBaseUrl(this.baseUrl);
|
|
486
976
|
this.timeout = config.timeout || 3e4;
|
|
487
977
|
this.payments = new PaymentsClient(this);
|
|
978
|
+
this.cards = new CardsClient(this);
|
|
979
|
+
this.connectedCards = new ConnectedCardsClient(this);
|
|
980
|
+
this.x402 = new X402Client(this);
|
|
981
|
+
this.mpp = new MppClient(this);
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* List masked Link payment methods for a session-created connection that is
|
|
985
|
+
* explicitly bound to this agent. No Link token or payment artifact is
|
|
986
|
+
* returned through the agent SDK.
|
|
987
|
+
*/
|
|
988
|
+
async listStripeLinkPaymentMethods(connectionId) {
|
|
989
|
+
return this.request(
|
|
990
|
+
"GET",
|
|
991
|
+
withQuery("/api/sdk/stripe-link/payment-methods", { connectionId })
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Normalize and durably revise a partial agent purchase proposal without
|
|
996
|
+
* creating a payment side effect. Missing facts return as NEEDS_CONTEXT
|
|
997
|
+
* requirements; changed writes require the exact prior revision.
|
|
998
|
+
*/
|
|
999
|
+
async normalizeCommerceProposal(input) {
|
|
1000
|
+
const proposalKey = input.proposalKey?.trim();
|
|
1001
|
+
if (!proposalKey || proposalKey.length < 8 || proposalKey.length > 255) {
|
|
1002
|
+
throw new ContoError("proposalKey must contain 8 to 255 characters", {
|
|
1003
|
+
code: "INVALID_PROPOSAL_KEY",
|
|
1004
|
+
status: 0
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
return this.request(
|
|
1008
|
+
"POST",
|
|
1009
|
+
"/api/sdk/commerce-proposals/normalize",
|
|
1010
|
+
{ ...input, proposalKey },
|
|
1011
|
+
{ idempotent: true }
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
/** Reload one durable proposal head in this agent and tenant scope. */
|
|
1015
|
+
async getCommerceProposal(proposalId) {
|
|
1016
|
+
return this.request(
|
|
1017
|
+
"GET",
|
|
1018
|
+
`/api/sdk/commerce-proposals/${encodeURIComponent(proposalId)}`
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Evaluate policy and begin one exact Stripe Link commerce intent.
|
|
1023
|
+
*
|
|
1024
|
+
* The stable idempotency key makes transport retries safe. Reusing it with a
|
|
1025
|
+
* different intent is rejected by the API.
|
|
1026
|
+
*/
|
|
1027
|
+
async beginCommerceExecution(input) {
|
|
1028
|
+
const idempotencyKey = input.idempotencyKey?.trim();
|
|
1029
|
+
if (!idempotencyKey || idempotencyKey.length < 8 || idempotencyKey.length > 255) {
|
|
1030
|
+
throw new ContoError("idempotencyKey must contain 8 to 255 characters", {
|
|
1031
|
+
code: "INVALID_IDEMPOTENCY_KEY",
|
|
1032
|
+
status: 0,
|
|
1033
|
+
retryGuidance: "Generate one stable identifier per commerce intent and reuse it for retries."
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
return this.request(
|
|
1037
|
+
"POST",
|
|
1038
|
+
input.executionMethod === "AGENT_CHECKOUT" || input.executionMethod === "BROWSER_CHECKOUT" ? "/api/sdk/checkout-guard/executions" : "/api/sdk/commerce-executions",
|
|
1039
|
+
{
|
|
1040
|
+
..."connectionId" in input ? {
|
|
1041
|
+
connectionId: input.connectionId,
|
|
1042
|
+
paymentMethodId: input.paymentMethodId
|
|
1043
|
+
} : {},
|
|
1044
|
+
..."browserSessionId" in input ? {
|
|
1045
|
+
browserSessionId: input.browserSessionId,
|
|
1046
|
+
action: input.action
|
|
1047
|
+
} : {},
|
|
1048
|
+
executionMethod: input.executionMethod ?? "MPP",
|
|
1049
|
+
idempotencyKey,
|
|
1050
|
+
intentId: input.intentId,
|
|
1051
|
+
amountMinor: input.amountMinor,
|
|
1052
|
+
currency: input.currency,
|
|
1053
|
+
purpose: input.purpose,
|
|
1054
|
+
category: input.category,
|
|
1055
|
+
counterparty: input.counterparty,
|
|
1056
|
+
cart: input.cart,
|
|
1057
|
+
providerPresentation: input.providerPresentation
|
|
1058
|
+
},
|
|
1059
|
+
{ idempotent: true }
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
/** Atomically consume the Conto authorization for one exact browser action. */
|
|
1063
|
+
async authorizeCheckoutAction(executionId, browserSessionId, action) {
|
|
1064
|
+
try {
|
|
1065
|
+
return await this.request(
|
|
1066
|
+
"POST",
|
|
1067
|
+
`/api/sdk/commerce-executions/${encodeURIComponent(executionId)}/authorize-action`,
|
|
1068
|
+
{ browserSessionId, action }
|
|
1069
|
+
);
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
rethrowAmbiguousCommerceMutation(error, executionId, "authorize");
|
|
1072
|
+
}
|
|
488
1073
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
1074
|
+
/** Claim a durable browser action for one stable privileged-runtime attempt. */
|
|
1075
|
+
async claimBrowserAction(authorizationId, dispatchAttemptId, browserSessionId, action) {
|
|
1076
|
+
return this.request(
|
|
1077
|
+
"POST",
|
|
1078
|
+
`/api/sdk/browser-actions/${encodeURIComponent(authorizationId)}/claim`,
|
|
1079
|
+
{ dispatchAttemptId, browserSessionId, action },
|
|
1080
|
+
{ idempotent: true }
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
/** Finalize the exact dispatch attempt; this never performs the browser side effect. */
|
|
1084
|
+
async finalizeBrowserAction(authorizationId, input) {
|
|
1085
|
+
return this.request(
|
|
1086
|
+
"POST",
|
|
1087
|
+
`/api/sdk/browser-actions/${encodeURIComponent(authorizationId)}/finalize`,
|
|
1088
|
+
input,
|
|
1089
|
+
{ idempotent: true }
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Own the complete privileged-browser enforcement sequence. The stable
|
|
1094
|
+
* dispatchAttemptId must be persisted by the runtime before this call.
|
|
1095
|
+
*/
|
|
1096
|
+
async runGuardedCheckout(input, runtime) {
|
|
1097
|
+
const begun = await this.beginCommerceExecution(input);
|
|
1098
|
+
if (!["READY", "PROCESSING"].includes(begun.execution.status)) {
|
|
1099
|
+
throw new ContoError("Checkout action is not ready", {
|
|
1100
|
+
code: "COMMERCE_EXECUTION_NOT_READY",
|
|
1101
|
+
status: 409
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
const authorized = await this.authorizeCheckoutAction(
|
|
1105
|
+
begun.execution.id,
|
|
1106
|
+
input.browserSessionId,
|
|
1107
|
+
input.action
|
|
1108
|
+
);
|
|
1109
|
+
await this.claimBrowserAction(
|
|
1110
|
+
authorized.authorization.authorizationId,
|
|
1111
|
+
runtime.dispatchAttemptId,
|
|
1112
|
+
input.browserSessionId,
|
|
1113
|
+
input.action
|
|
1114
|
+
);
|
|
1115
|
+
let dispatched;
|
|
1116
|
+
try {
|
|
1117
|
+
dispatched = await runtime.execute(authorized.authorization);
|
|
1118
|
+
} catch (error) {
|
|
492
1119
|
try {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
1120
|
+
await this.finalizeBrowserAction(authorized.authorization.authorizationId, {
|
|
1121
|
+
dispatchAttemptId: runtime.dispatchAttemptId,
|
|
1122
|
+
outcome: "INDETERMINATE"
|
|
1123
|
+
});
|
|
1124
|
+
} catch {
|
|
1125
|
+
throw new ContoError("Browser action failed and its final state is indeterminate", {
|
|
1126
|
+
code: "BROWSER_ACTION_DISPATCH_INDETERMINATE",
|
|
1127
|
+
status: 0,
|
|
1128
|
+
retryGuidance: "Do not execute the browser action again. Reconcile the merchant page or payment provider before taking another action."
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
throw error;
|
|
1132
|
+
}
|
|
1133
|
+
if (!dispatched || !isValidBrowserActionObservation(dispatched.observation)) {
|
|
1134
|
+
try {
|
|
1135
|
+
await this.finalizeBrowserAction(authorized.authorization.authorizationId, {
|
|
1136
|
+
dispatchAttemptId: runtime.dispatchAttemptId,
|
|
1137
|
+
outcome: "INDETERMINATE"
|
|
1138
|
+
});
|
|
1139
|
+
} catch {
|
|
508
1140
|
}
|
|
1141
|
+
throw new ContoError("Privileged browser executor returned an invalid observation", {
|
|
1142
|
+
code: "BROWSER_ACTION_OBSERVATION_INVALID",
|
|
1143
|
+
status: 0,
|
|
1144
|
+
retryGuidance: "Do not execute the browser action again. Reconcile the merchant page, then record a valid observation or resolve the indeterminate attempt."
|
|
1145
|
+
});
|
|
509
1146
|
}
|
|
510
|
-
|
|
1147
|
+
try {
|
|
1148
|
+
await this.finalizeBrowserAction(authorized.authorization.authorizationId, {
|
|
1149
|
+
dispatchAttemptId: runtime.dispatchAttemptId,
|
|
1150
|
+
outcome: "CONSUMED",
|
|
1151
|
+
observation: dispatched.observation
|
|
1152
|
+
});
|
|
1153
|
+
} catch {
|
|
1154
|
+
throw new BrowserActionFinalizationPendingError({
|
|
1155
|
+
authorizationId: authorized.authorization.authorizationId,
|
|
1156
|
+
dispatchAttemptId: runtime.dispatchAttemptId,
|
|
1157
|
+
result: dispatched.result,
|
|
1158
|
+
observation: dispatched.observation
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
...authorized,
|
|
1163
|
+
dispatchAttemptId: runtime.dispatchAttemptId,
|
|
1164
|
+
result: dispatched.result,
|
|
1165
|
+
observation: dispatched.observation
|
|
1166
|
+
};
|
|
511
1167
|
}
|
|
512
|
-
/**
|
|
513
|
-
|
|
1168
|
+
/**
|
|
1169
|
+
* Reference privileged-driver integration. Only this wrapper should be
|
|
1170
|
+
* exposed as the agent's payment-sensitive browser tool.
|
|
1171
|
+
*/
|
|
1172
|
+
async runGuardedCheckoutWithDriver(input, runtime) {
|
|
1173
|
+
return this.runGuardedCheckout(input, {
|
|
1174
|
+
dispatchAttemptId: runtime.dispatchAttemptId,
|
|
1175
|
+
execute: (authorization) => runtime.driver.dispatch({
|
|
1176
|
+
authorization,
|
|
1177
|
+
browserSessionId: input.browserSessionId,
|
|
1178
|
+
action: input.action
|
|
1179
|
+
})
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Policy-check and authorize an exact action before invoking a legacy callback.
|
|
1184
|
+
* @deprecated Use runGuardedCheckout so the durable claim and final
|
|
1185
|
+
* observation cannot be omitted.
|
|
1186
|
+
*/
|
|
1187
|
+
async guardCheckout(input, executor) {
|
|
1188
|
+
const begun = await this.beginCommerceExecution(input);
|
|
1189
|
+
if (begun.execution.status !== "READY") {
|
|
1190
|
+
throw new ContoError("Checkout action is not ready", {
|
|
1191
|
+
code: "COMMERCE_EXECUTION_NOT_READY",
|
|
1192
|
+
status: 409
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
const authorized = await this.authorizeCheckoutAction(
|
|
1196
|
+
begun.execution.id,
|
|
1197
|
+
input.browserSessionId,
|
|
1198
|
+
input.action
|
|
1199
|
+
);
|
|
1200
|
+
const result = await executor(authorized.authorization);
|
|
1201
|
+
return { ...authorized, result };
|
|
1202
|
+
}
|
|
1203
|
+
/** Read one agent-scoped commerce execution and its normalized evidence. */
|
|
1204
|
+
async getCommerceExecution(executionId) {
|
|
1205
|
+
return this.request(
|
|
1206
|
+
"GET",
|
|
1207
|
+
`/api/sdk/commerce-executions/${encodeURIComponent(executionId)}`
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Observe Link state and reconcile terminal accounting. This operation is
|
|
1212
|
+
* safe to retry because it does not request another checkout execution.
|
|
1213
|
+
*/
|
|
1214
|
+
async refreshCommerceExecution(executionId) {
|
|
1215
|
+
return this.request(
|
|
1216
|
+
"POST",
|
|
1217
|
+
`/api/sdk/commerce-executions/${encodeURIComponent(executionId)}/refresh`,
|
|
1218
|
+
{},
|
|
1219
|
+
{ idempotent: true }
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Execute the approved provider-direct or server-only checkout handoff. Transport retries are
|
|
1224
|
+
* deliberately disabled. Read or refresh the execution after an ambiguous
|
|
1225
|
+
* failure before taking another execution action.
|
|
1226
|
+
*/
|
|
1227
|
+
async executeCommerceExecution(executionId) {
|
|
1228
|
+
try {
|
|
1229
|
+
return await this.request(
|
|
1230
|
+
"POST",
|
|
1231
|
+
`/api/sdk/commerce-executions/${encodeURIComponent(executionId)}/execute`,
|
|
1232
|
+
{}
|
|
1233
|
+
);
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
rethrowAmbiguousCommerceMutation(error, executionId, "execute");
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Request cancellation and release reserved spend after Link confirms it.
|
|
1240
|
+
* Transport retries are disabled because a lost provider response requires
|
|
1241
|
+
* reconciliation.
|
|
1242
|
+
*/
|
|
1243
|
+
async cancelCommerceExecution(executionId) {
|
|
1244
|
+
try {
|
|
1245
|
+
return await this.request(
|
|
1246
|
+
"POST",
|
|
1247
|
+
`/api/sdk/commerce-executions/${encodeURIComponent(executionId)}/cancel`,
|
|
1248
|
+
{}
|
|
1249
|
+
);
|
|
1250
|
+
} catch (error) {
|
|
1251
|
+
rethrowAmbiguousCommerceMutation(error, executionId, "cancel");
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
/** Read credentials, linked wallets, effective policies, and scopes without creating a payment. */
|
|
1255
|
+
async setup() {
|
|
1256
|
+
return this.request("GET", "/api/sdk/setup");
|
|
1257
|
+
}
|
|
1258
|
+
/** Make an authenticated request to the Conto API */
|
|
1259
|
+
async request(method, path, body, options = {}) {
|
|
514
1260
|
const url = `${this.baseUrl}${path}`;
|
|
515
1261
|
const controller = new AbortController();
|
|
516
1262
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1263
|
+
const normalizedMethod = method.toUpperCase();
|
|
1264
|
+
const retryable = options.idempotent === true || ["GET", "HEAD", "OPTIONS"].includes(normalizedMethod);
|
|
517
1265
|
try {
|
|
518
|
-
const response = await
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
1266
|
+
const response = await fetchWithRetry(
|
|
1267
|
+
url,
|
|
1268
|
+
{
|
|
1269
|
+
method: normalizedMethod,
|
|
1270
|
+
headers: {
|
|
1271
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
1272
|
+
"Content-Type": "application/json",
|
|
1273
|
+
"User-Agent": `@conto_finance/sdk/${import_package_version2.SDK_PACKAGE_VERSION}`
|
|
1274
|
+
},
|
|
1275
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
1276
|
+
signal: controller.signal
|
|
524
1277
|
},
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
clearTimeout(timeoutId);
|
|
529
|
-
const data = await response.json();
|
|
1278
|
+
{ retryable, maxRetries: options.maxRetries }
|
|
1279
|
+
);
|
|
1280
|
+
const data = await readResponseData(response);
|
|
530
1281
|
if (!response.ok) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
throw error;
|
|
1282
|
+
throw new ContoError(
|
|
1283
|
+
responseErrorMessage(data, response.status),
|
|
1284
|
+
responseErrorMetadata(response, data)
|
|
1285
|
+
);
|
|
536
1286
|
}
|
|
537
1287
|
return data;
|
|
538
1288
|
} catch (error) {
|
|
539
|
-
|
|
1289
|
+
if (error instanceof ContoError) throw error;
|
|
540
1290
|
if (error instanceof Error && error.name === "AbortError") {
|
|
541
|
-
const timeoutError = new
|
|
542
|
-
|
|
543
|
-
|
|
1291
|
+
const timeoutError = new ContoError("Request timeout", {
|
|
1292
|
+
code: "TIMEOUT",
|
|
1293
|
+
status: 0,
|
|
1294
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; check operation status before trying again."
|
|
1295
|
+
});
|
|
544
1296
|
Sentry2.captureException(timeoutError, { extra: { method, path } });
|
|
545
1297
|
throw timeoutError;
|
|
546
1298
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
1299
|
+
const networkError = new ContoError("Network request failed", {
|
|
1300
|
+
code: "NETWORK_ERROR",
|
|
1301
|
+
status: 0,
|
|
1302
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; check operation status before trying again."
|
|
1303
|
+
});
|
|
1304
|
+
Sentry2.captureException(networkError, { extra: { method, path } });
|
|
1305
|
+
throw networkError;
|
|
1306
|
+
} finally {
|
|
1307
|
+
clearTimeout(timeoutId);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
var CardsClient = class {
|
|
1312
|
+
constructor(client) {
|
|
1313
|
+
this.client = client;
|
|
1314
|
+
}
|
|
1315
|
+
async approve(input) {
|
|
1316
|
+
const idempotencyKey = input.idempotencyKey?.trim();
|
|
1317
|
+
if (!idempotencyKey || idempotencyKey.length < 8 || idempotencyKey.length > 128) {
|
|
1318
|
+
throw new ContoError("idempotencyKey must contain 8 to 128 characters", {
|
|
1319
|
+
code: "INVALID_IDEMPOTENCY_KEY",
|
|
1320
|
+
status: 0,
|
|
1321
|
+
retryGuidance: "Generate one stable identifier per purchase intent and reuse it for retries."
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
return this.client.request(
|
|
1325
|
+
"POST",
|
|
1326
|
+
"/api/sdk/external-cards/approve",
|
|
1327
|
+
{
|
|
1328
|
+
...input,
|
|
1329
|
+
idempotencyKey,
|
|
1330
|
+
currency: input.currency || "USD"
|
|
1331
|
+
},
|
|
1332
|
+
{ idempotent: true }
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
async confirm(requestId, input) {
|
|
1336
|
+
return this.client.request(
|
|
1337
|
+
"POST",
|
|
1338
|
+
`/api/sdk/external-cards/${encodeURIComponent(requestId)}/confirm`,
|
|
1339
|
+
input,
|
|
1340
|
+
{ idempotent: true }
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Atomically claim the approved intent before customer code accesses or uses
|
|
1345
|
+
* the card credential. A request can be claimed only once.
|
|
1346
|
+
*/
|
|
1347
|
+
async begin(requestId, input) {
|
|
1348
|
+
return this.client.request(
|
|
1349
|
+
"POST",
|
|
1350
|
+
`/api/sdk/external-cards/${encodeURIComponent(requestId)}/begin`,
|
|
1351
|
+
input,
|
|
1352
|
+
{ idempotent: true }
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
/** Read execution and reconciliation state without exposing card credentials. */
|
|
1356
|
+
async status(requestId) {
|
|
1357
|
+
return this.client.request(
|
|
1358
|
+
"GET",
|
|
1359
|
+
`/api/sdk/external-cards/${encodeURIComponent(requestId)}`
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Create a reference payment tool whose executor callback is unreachable
|
|
1364
|
+
* until Conto has returned a live approval grant.
|
|
1365
|
+
*
|
|
1366
|
+
* @deprecated Use the separate `@conto_finance/card-access` product package for
|
|
1367
|
+
* customer-hosted card enforcement. This helper remains for compatibility.
|
|
1368
|
+
*/
|
|
1369
|
+
controlled(executor) {
|
|
1370
|
+
return new ControlledExternalCardPaymentTool(this, executor);
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
var ControlledExternalCardPaymentTool = class {
|
|
1374
|
+
constructor(cardsClient, executor) {
|
|
1375
|
+
this.cardsClient = cardsClient;
|
|
1376
|
+
this.executor = executor;
|
|
1377
|
+
}
|
|
1378
|
+
async pay(input) {
|
|
1379
|
+
const idempotencyKey = input.idempotencyKey.trim();
|
|
1380
|
+
const approval = await this.cardsClient.approve({ ...input, idempotencyKey });
|
|
1381
|
+
if (!approval.approved || !approval.approvalToken) {
|
|
1382
|
+
const executionAlreadyClaimed = approval.decision === "EXECUTING" || approval.decision === "CONFIRMING";
|
|
1383
|
+
throw new ContoError("External card payment was not approved", {
|
|
1384
|
+
code: executionAlreadyClaimed ? "CARD_EXECUTION_RECONCILIATION_REQUIRED" : approval.decision === "COMPLETED" ? "CARD_PAYMENT_ALREADY_COMPLETED" : approval.decision === "REQUIRES_APPROVAL" ? "REQUIRES_APPROVAL" : "CARD_PAYMENT_DENIED",
|
|
1385
|
+
status: executionAlreadyClaimed || approval.decision === "COMPLETED" ? 409 : approval.decision === "REQUIRES_APPROVAL" ? 202 : 403,
|
|
1386
|
+
requestId: approval.requestId,
|
|
1387
|
+
retryGuidance: executionAlreadyClaimed ? "Do not execute the payment again. Check cards.status() and confirm or reconcile the existing request." : approval.decision === "COMPLETED" ? "The purchase intent is already complete. Use cards.status() to read its transaction." : approval.decision === "REQUIRES_APPROVAL" ? "Wait for review; do not access or use the card credential." : "Review the purchase intent; do not access or use the card credential."
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
if (!approval.expiresAt || Date.parse(approval.expiresAt) <= Date.now()) {
|
|
1391
|
+
throw new ContoError("External card approval has expired", {
|
|
1392
|
+
code: "CARD_APPROVAL_EXPIRED",
|
|
1393
|
+
status: 400,
|
|
1394
|
+
requestId: approval.requestId,
|
|
1395
|
+
retryGuidance: "Request a new approval before accessing or using the card credential."
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
const claim = await this.cardsClient.begin(approval.requestId, {
|
|
1399
|
+
approvalToken: approval.approvalToken
|
|
1400
|
+
});
|
|
1401
|
+
if (!claim.claimed) {
|
|
1402
|
+
throw new ContoError("External card execution was already claimed", {
|
|
1403
|
+
code: "CARD_EXECUTION_RECONCILIATION_REQUIRED",
|
|
1404
|
+
status: 409,
|
|
1405
|
+
requestId: approval.requestId,
|
|
1406
|
+
retryGuidance: "Do not execute the payment again. Check cards.status() and confirm or reconcile the existing request."
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
let execution;
|
|
1410
|
+
try {
|
|
1411
|
+
execution = await this.executor({
|
|
1412
|
+
requestId: approval.requestId,
|
|
1413
|
+
idempotencyKey,
|
|
1414
|
+
cardId: input.cardId,
|
|
1415
|
+
amount: input.amount,
|
|
1416
|
+
currency: input.currency || "USD",
|
|
1417
|
+
merchantName: input.merchantName,
|
|
1418
|
+
merchantId: input.merchantId,
|
|
1419
|
+
merchantCategory: input.merchantCategory,
|
|
1420
|
+
merchantAddress: input.merchantAddress,
|
|
1421
|
+
merchantCountry: input.merchantCountry,
|
|
1422
|
+
purpose: input.purpose,
|
|
1423
|
+
context: input.context
|
|
1424
|
+
});
|
|
1425
|
+
} catch (error) {
|
|
1426
|
+
const outcomeError = new ContoError(
|
|
1427
|
+
"External card executor failed after execution was claimed",
|
|
1428
|
+
{
|
|
1429
|
+
code: "CARD_EXECUTION_OUTCOME_UNKNOWN",
|
|
1430
|
+
status: 0,
|
|
1431
|
+
requestId: approval.requestId,
|
|
1432
|
+
retryGuidance: "Do not execute the payment again. Verify the customer card outcome, then reconcile this request."
|
|
1433
|
+
}
|
|
1434
|
+
);
|
|
1435
|
+
Sentry2.captureException(outcomeError, {
|
|
1436
|
+
extra: {
|
|
1437
|
+
requestId: approval.requestId,
|
|
1438
|
+
executorErrorName: error instanceof Error ? error.name : typeof error
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
throw outcomeError;
|
|
1442
|
+
}
|
|
1443
|
+
const confirmationInput = {
|
|
1444
|
+
approvalToken: approval.approvalToken,
|
|
1445
|
+
externalTxId: execution.externalTxId,
|
|
1446
|
+
authorizationCode: execution.authorizationCode,
|
|
1447
|
+
actualAmount: execution.actualAmount,
|
|
1448
|
+
merchantDetails: execution.merchantDetails
|
|
1449
|
+
};
|
|
1450
|
+
let confirmation;
|
|
1451
|
+
try {
|
|
1452
|
+
confirmation = await this.cardsClient.confirm(approval.requestId, confirmationInput);
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
const pending = new ExternalCardConfirmationPendingError(
|
|
1455
|
+
approval.requestId,
|
|
1456
|
+
confirmationInput,
|
|
1457
|
+
execution.receipt
|
|
1458
|
+
);
|
|
1459
|
+
Sentry2.captureException(pending, {
|
|
1460
|
+
extra: {
|
|
1461
|
+
requestId: approval.requestId,
|
|
1462
|
+
confirmationErrorName: error instanceof Error ? error.name : typeof error
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1465
|
+
throw pending;
|
|
1466
|
+
}
|
|
1467
|
+
return {
|
|
1468
|
+
approval,
|
|
1469
|
+
confirmation,
|
|
1470
|
+
receipt: execution.receipt
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
var ConnectedCardsClient = class {
|
|
1475
|
+
constructor(client) {
|
|
1476
|
+
this.client = client;
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Draw one single-use card credential against a verified mandate.
|
|
1480
|
+
*
|
|
1481
|
+
* On an APPROVED, minted draw the result carries live card material in
|
|
1482
|
+
* `credential`, returned exactly ONCE.
|
|
1483
|
+
*
|
|
1484
|
+
* CREDENTIAL SAFETY (load-bearing): the credential is the real, usable card.
|
|
1485
|
+
* It is revealed a single time and cannot be re-fetched. Use it in-process at
|
|
1486
|
+
* the intended merchant checkout only. Never log, print, serialize, persist,
|
|
1487
|
+
* cache, or forward it, and never place it in an error, a monitoring event,
|
|
1488
|
+
* or any other sink. See {@link ConnectedCardCredential}.
|
|
1489
|
+
*
|
|
1490
|
+
* Transport-level retry is deliberately OFF for this call. The server is
|
|
1491
|
+
* idempotent on `idempotencyKey`, so an automatic retry after a successful
|
|
1492
|
+
* mint would replay the draw and return the decision WITHOUT the credential,
|
|
1493
|
+
* silently losing the one-shot card material. If a `draw` fails ambiguously,
|
|
1494
|
+
* do not blindly retry: check the outcome, and reuse the same
|
|
1495
|
+
* `idempotencyKey` only to reconcile the same draw.
|
|
1496
|
+
*
|
|
1497
|
+
* @example
|
|
1498
|
+
* ```typescript
|
|
1499
|
+
* const draw = await conto.connectedCards.draw({
|
|
1500
|
+
* cardId: 'cmm59z8fj000l49h7bjqza11p',
|
|
1501
|
+
* mandateId: 'cmm5a1b2c000m49h7defgh22q',
|
|
1502
|
+
* idempotencyKey: 'checkout_01J8...',
|
|
1503
|
+
* amountMinor: 4999,
|
|
1504
|
+
* merchant: { merchantName: 'Example Store', merchantCategory: '5311' },
|
|
1505
|
+
* });
|
|
1506
|
+
* if (draw.decision === 'APPROVED' && draw.credential) {
|
|
1507
|
+
* // Keep the credential in memory, acknowledge receipt, then use it at checkout.
|
|
1508
|
+
* await conto.connectedCards.acknowledgeDelivery(draw.requestId);
|
|
1509
|
+
* // Do not log or store draw.credential.
|
|
1510
|
+
* }
|
|
1511
|
+
* ```
|
|
1512
|
+
*/
|
|
1513
|
+
async draw(input) {
|
|
1514
|
+
const idempotencyKey = input.idempotencyKey?.trim();
|
|
1515
|
+
if (!idempotencyKey || idempotencyKey.length < 8 || idempotencyKey.length > 128) {
|
|
1516
|
+
throw new ContoError("idempotencyKey must contain 8 to 128 characters", {
|
|
1517
|
+
code: "INVALID_IDEMPOTENCY_KEY",
|
|
1518
|
+
status: 0,
|
|
1519
|
+
retryGuidance: "Generate one stable identifier per draw and reuse it only to reconcile the same draw."
|
|
1520
|
+
});
|
|
551
1521
|
}
|
|
1522
|
+
return this.client.request("POST", "/api/sdk/connected-cards/draws", {
|
|
1523
|
+
...input,
|
|
1524
|
+
idempotencyKey,
|
|
1525
|
+
currency: input.currency ?? "USD"
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* List the connected cards and active mandates this agent may draw against.
|
|
1530
|
+
*
|
|
1531
|
+
* The response is masked: no PAN and no provider tokens. This is a read-only
|
|
1532
|
+
* GET, so transport retry on transient failures is safe.
|
|
1533
|
+
*/
|
|
1534
|
+
/** Record receipt after a fresh draw. Does not confirm checkout or payment settlement. */
|
|
1535
|
+
async acknowledgeDelivery(requestId) {
|
|
1536
|
+
return this.client.request(
|
|
1537
|
+
"POST",
|
|
1538
|
+
`/api/sdk/connected-cards/draws/${encodeURIComponent(requestId)}/acknowledge`,
|
|
1539
|
+
{},
|
|
1540
|
+
{ idempotent: true }
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
async list() {
|
|
1544
|
+
return this.client.request("GET", "/api/sdk/connected-cards");
|
|
552
1545
|
}
|
|
553
1546
|
};
|
|
554
1547
|
var PaymentsClient = class {
|
|
555
1548
|
constructor(client) {
|
|
556
1549
|
this.client = client;
|
|
557
1550
|
}
|
|
1551
|
+
/** Authorize an externally signed transfer. This write is not automatically retried. */
|
|
1552
|
+
async approve(input) {
|
|
1553
|
+
return this.client.request(
|
|
1554
|
+
"POST",
|
|
1555
|
+
"/api/sdk/payments/approve",
|
|
1556
|
+
{ ...input },
|
|
1557
|
+
{ maxRetries: 0 }
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
/** Record an existing transfer. Never sends funds; reconcile conflicts using status(). */
|
|
1561
|
+
async confirm(requestId, input) {
|
|
1562
|
+
return this.client.request(
|
|
1563
|
+
"POST",
|
|
1564
|
+
`/api/sdk/payments/${encodeURIComponent(requestId)}/confirm`,
|
|
1565
|
+
{ ...input },
|
|
1566
|
+
{ maxRetries: 0 }
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
558
1569
|
/**
|
|
559
1570
|
* Request authorization for a payment.
|
|
560
1571
|
*
|
|
@@ -584,10 +1595,22 @@ var PaymentsClient = class {
|
|
|
584
1595
|
* ```
|
|
585
1596
|
*/
|
|
586
1597
|
async request(input) {
|
|
1598
|
+
if (typeof input.amount !== "number" || !Number.isFinite(input.amount) || input.amount <= 0) {
|
|
1599
|
+
throw new ContoError("amount must be a positive finite number", {
|
|
1600
|
+
code: "INVALID_AMOUNT",
|
|
1601
|
+
status: 0,
|
|
1602
|
+
retryGuidance: "Provide a positive amount before requesting a payment."
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
const requestInput = {
|
|
1606
|
+
...input,
|
|
1607
|
+
idempotencyKey: input.idempotencyKey ?? generatedIdempotencyKey("conto_payment")
|
|
1608
|
+
};
|
|
587
1609
|
return this.client.request(
|
|
588
1610
|
"POST",
|
|
589
1611
|
"/api/sdk/payments/request",
|
|
590
|
-
|
|
1612
|
+
requestInput,
|
|
1613
|
+
{ idempotent: true }
|
|
591
1614
|
);
|
|
592
1615
|
}
|
|
593
1616
|
/**
|
|
@@ -601,16 +1624,34 @@ var PaymentsClient = class {
|
|
|
601
1624
|
* @example
|
|
602
1625
|
* ```typescript
|
|
603
1626
|
* const result = await conto.payments.execute(request.requestId);
|
|
604
|
-
* console.log(`
|
|
605
|
-
* console.log(`
|
|
1627
|
+
* console.log(`Status: ${result.status}`);
|
|
1628
|
+
* console.log(`Conto transaction: ${result.receipt.transactionId}`);
|
|
1629
|
+
* console.log(`Settlement mode: ${result.receipt.settlementMode}`);
|
|
1630
|
+
* console.log(`Transaction: ${result.receipt.txHash}`);
|
|
1631
|
+
* if (result.receipt.explorerUrl) console.log(`Explorer: ${result.receipt.explorerUrl}`);
|
|
606
1632
|
* ```
|
|
607
1633
|
*/
|
|
608
1634
|
async execute(requestId) {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1635
|
+
try {
|
|
1636
|
+
return await this.client.request(
|
|
1637
|
+
"POST",
|
|
1638
|
+
`/api/sdk/payments/${encodeURIComponent(requestId)}/execute`,
|
|
1639
|
+
{}
|
|
1640
|
+
);
|
|
1641
|
+
} catch (error) {
|
|
1642
|
+
if (error instanceof ContoError && error.status === 0) {
|
|
1643
|
+
throw new ContoError(error.message, {
|
|
1644
|
+
code: error.code,
|
|
1645
|
+
status: error.status,
|
|
1646
|
+
requestId: error.requestId,
|
|
1647
|
+
retryAfter: error.retryAfter,
|
|
1648
|
+
retryGuidance: `Check payments.status(${JSON.stringify(
|
|
1649
|
+
requestId
|
|
1650
|
+
)}) before attempting execution again.`
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
throw error;
|
|
1654
|
+
}
|
|
614
1655
|
}
|
|
615
1656
|
/**
|
|
616
1657
|
* Get the status of a payment request.
|
|
@@ -620,15 +1661,15 @@ var PaymentsClient = class {
|
|
|
620
1661
|
* @example
|
|
621
1662
|
* ```typescript
|
|
622
1663
|
* const status = await conto.payments.status(requestId);
|
|
623
|
-
* if (status.transaction?.status === '
|
|
624
|
-
* console.log('Payment confirmed
|
|
1664
|
+
* if (status.transaction?.status === 'completed') {
|
|
1665
|
+
* console.log('Payment confirmed', status.transaction.txHash);
|
|
625
1666
|
* }
|
|
626
1667
|
* ```
|
|
627
1668
|
*/
|
|
628
1669
|
async status(requestId) {
|
|
629
1670
|
return this.client.request(
|
|
630
1671
|
"GET",
|
|
631
|
-
`/api/sdk/payments/${requestId}`,
|
|
1672
|
+
`/api/sdk/payments/${encodeURIComponent(requestId)}`,
|
|
632
1673
|
void 0
|
|
633
1674
|
);
|
|
634
1675
|
}
|
|
@@ -646,7 +1687,9 @@ var PaymentsClient = class {
|
|
|
646
1687
|
* recipientAddress: '0x...',
|
|
647
1688
|
* purpose: 'Service fee'
|
|
648
1689
|
* });
|
|
649
|
-
* console.log(`
|
|
1690
|
+
* console.log(`Payment status: ${result.status}`);
|
|
1691
|
+
* console.log(`Conto transaction: ${result.receipt.transactionId}`);
|
|
1692
|
+
* console.log(`Transaction: ${result.receipt.txHash}`);
|
|
650
1693
|
* } catch (error) {
|
|
651
1694
|
* if (error.code === 'REQUIRES_APPROVAL') {
|
|
652
1695
|
* console.log('Payment needs human approval');
|
|
@@ -660,24 +1703,141 @@ var PaymentsClient = class {
|
|
|
660
1703
|
const { autoExecute: _autoExecute, ...requestInput } = input;
|
|
661
1704
|
const request = await this.request(requestInput);
|
|
662
1705
|
if (request.status === "DENIED") {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1706
|
+
throw new ContoError("Payment was denied.", {
|
|
1707
|
+
code: "PAYMENT_DENIED",
|
|
1708
|
+
status: 403,
|
|
1709
|
+
reasons: customerPaymentReasons(request.reasons),
|
|
1710
|
+
requestId: request.requestId,
|
|
1711
|
+
retryGuidance: "Review the payment request before trying again."
|
|
1712
|
+
});
|
|
667
1713
|
}
|
|
668
1714
|
if (request.status === "REQUIRES_APPROVAL") {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
1715
|
+
throw new ContoError("Payment requires manual approval", {
|
|
1716
|
+
code: "REQUIRES_APPROVAL",
|
|
1717
|
+
status: 202,
|
|
1718
|
+
requestId: request.requestId,
|
|
1719
|
+
retryGuidance: "Wait for approval, then execute the same request ID."
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
if (request.status === "ACTION_REQUIRED") {
|
|
1723
|
+
throw new ContoError("Payment requires an additional action", {
|
|
1724
|
+
code: "ACTION_REQUIRED",
|
|
1725
|
+
status: 402,
|
|
1726
|
+
requestId: request.requestId,
|
|
1727
|
+
actionUrl: request.actionUrl,
|
|
1728
|
+
retryGuidance: request.actionUrl ? `Complete the required action at ${request.actionUrl}, then check the payment status.` : "Complete the required action, then check the payment status."
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
if (request.status === "PROCESSING") {
|
|
1732
|
+
throw new ContoError("Payment is processing", {
|
|
1733
|
+
code: "PAYMENT_PROCESSING",
|
|
1734
|
+
status: 202,
|
|
1735
|
+
requestId: request.requestId,
|
|
1736
|
+
retryGuidance: "Check the payment status before taking another payment action."
|
|
1737
|
+
});
|
|
673
1738
|
}
|
|
674
1739
|
return this.execute(request.requestId);
|
|
675
1740
|
}
|
|
676
1741
|
};
|
|
1742
|
+
var X402Client = class {
|
|
1743
|
+
constructor(client) {
|
|
1744
|
+
this.client = client;
|
|
1745
|
+
}
|
|
1746
|
+
async preAuthorize(input) {
|
|
1747
|
+
return this.client.request(
|
|
1748
|
+
"POST",
|
|
1749
|
+
"/api/sdk/x402/pre-authorize",
|
|
1750
|
+
input,
|
|
1751
|
+
{ idempotent: true }
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
async record(input) {
|
|
1755
|
+
const paymentId = requireFinancialIdentifier(input.paymentId, "paymentId");
|
|
1756
|
+
const requestInput = {
|
|
1757
|
+
...input,
|
|
1758
|
+
paymentId
|
|
1759
|
+
};
|
|
1760
|
+
return this.client.request(
|
|
1761
|
+
"POST",
|
|
1762
|
+
"/api/sdk/x402/record",
|
|
1763
|
+
requestInput
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
async budget(options) {
|
|
1767
|
+
return this.client.request(
|
|
1768
|
+
"GET",
|
|
1769
|
+
withQuery("/api/sdk/x402/budget", { sessionId: options?.sessionId }),
|
|
1770
|
+
void 0
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
async services(options) {
|
|
1774
|
+
return this.client.request(
|
|
1775
|
+
"GET",
|
|
1776
|
+
withQuery("/api/sdk/x402/services", {
|
|
1777
|
+
period: options?.period,
|
|
1778
|
+
limit: options?.limit
|
|
1779
|
+
}),
|
|
1780
|
+
void 0
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
};
|
|
1784
|
+
var MppClient = class {
|
|
1785
|
+
constructor(client) {
|
|
1786
|
+
this.client = client;
|
|
1787
|
+
}
|
|
1788
|
+
async preAuthorize(input) {
|
|
1789
|
+
return this.client.request(
|
|
1790
|
+
"POST",
|
|
1791
|
+
"/api/sdk/mpp/pre-authorize",
|
|
1792
|
+
input,
|
|
1793
|
+
{ idempotent: true }
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
async record(input) {
|
|
1797
|
+
const credentialId = requireFinancialIdentifier(input.credentialId, "credentialId");
|
|
1798
|
+
const requestInput = {
|
|
1799
|
+
...input,
|
|
1800
|
+
credentialId
|
|
1801
|
+
};
|
|
1802
|
+
return this.client.request(
|
|
1803
|
+
"POST",
|
|
1804
|
+
"/api/sdk/mpp/record",
|
|
1805
|
+
requestInput
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
async budget(options) {
|
|
1809
|
+
return this.client.request(
|
|
1810
|
+
"GET",
|
|
1811
|
+
withQuery("/api/sdk/mpp/budget", { sessionId: options?.sessionId }),
|
|
1812
|
+
void 0
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
async services(options) {
|
|
1816
|
+
return this.client.request(
|
|
1817
|
+
"GET",
|
|
1818
|
+
withQuery("/api/sdk/mpp/services", {
|
|
1819
|
+
period: options?.period,
|
|
1820
|
+
limit: options?.limit
|
|
1821
|
+
}),
|
|
1822
|
+
void 0
|
|
1823
|
+
);
|
|
1824
|
+
}
|
|
1825
|
+
};
|
|
677
1826
|
var index_default = Conto;
|
|
678
1827
|
export {
|
|
1828
|
+
BrowserActionFinalizationPendingError,
|
|
1829
|
+
BrowserCheckoutTargetVerificationError,
|
|
1830
|
+
CardsClient,
|
|
1831
|
+
ConnectedCardsClient,
|
|
679
1832
|
Conto,
|
|
680
1833
|
ContoAdmin,
|
|
1834
|
+
ContoAdminError,
|
|
1835
|
+
ContoError,
|
|
1836
|
+
ControlledExternalCardPaymentTool,
|
|
1837
|
+
ExternalCardConfirmationPendingError,
|
|
681
1838
|
Conto as Giro,
|
|
682
|
-
|
|
1839
|
+
canonicalizeBrowserCheckoutTarget,
|
|
1840
|
+
createVerifiedBrowserCheckoutDriver,
|
|
1841
|
+
index_default as default,
|
|
1842
|
+
fingerprintBrowserCheckoutTarget
|
|
683
1843
|
};
|