@conto_finance/sdk 0.1.0 → 0.3.1
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 +147 -2
- package/dist/index.d.mts +802 -141
- package/dist/index.d.ts +802 -141
- package/dist/index.js +714 -133
- package/dist/index.mjs +709 -133
- package/package.json +8 -5
package/dist/index.mjs
CHANGED
|
@@ -33,7 +33,7 @@ var require_package = __commonJS({
|
|
|
33
33
|
"package.json"(exports, module) {
|
|
34
34
|
module.exports = {
|
|
35
35
|
name: "@conto_finance/sdk",
|
|
36
|
-
version: "0.1
|
|
36
|
+
version: "0.3.1",
|
|
37
37
|
description: "Conto SDK for AI agent payment authorization",
|
|
38
38
|
main: "./dist/index.js",
|
|
39
39
|
module: "./dist/index.mjs",
|
|
@@ -49,7 +49,7 @@ var require_package = __commonJS({
|
|
|
49
49
|
access: "public"
|
|
50
50
|
},
|
|
51
51
|
engines: {
|
|
52
|
-
node: ">=
|
|
52
|
+
node: ">=20.19.0"
|
|
53
53
|
},
|
|
54
54
|
files: [
|
|
55
55
|
"dist"
|
|
@@ -58,7 +58,10 @@ var require_package = __commonJS({
|
|
|
58
58
|
build: "tsup src/index.ts --format cjs,esm --dts",
|
|
59
59
|
dev: "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
60
60
|
lint: "tsc --noEmit",
|
|
61
|
-
|
|
61
|
+
"test:contract": "npm run build && node --test test/contract.test.mjs",
|
|
62
|
+
"test:consumer": "npm run build && node --test test/consumer.test.mjs",
|
|
63
|
+
"verify:release": "node scripts/verify-release.mjs",
|
|
64
|
+
prepublishOnly: "npm run verify:release && npm run lint && npm run test:contract && npm run test:consumer"
|
|
62
65
|
},
|
|
63
66
|
keywords: [
|
|
64
67
|
"conto",
|
|
@@ -80,13 +83,13 @@ var require_package = __commonJS({
|
|
|
80
83
|
url: "https://github.com/conto-finance/conto/issues"
|
|
81
84
|
},
|
|
82
85
|
dependencies: {
|
|
83
|
-
"@sentry/node": "^10.
|
|
86
|
+
"@sentry/node": "^10.63.0"
|
|
84
87
|
},
|
|
85
88
|
overrides: {
|
|
86
89
|
esbuild: "0.28.1"
|
|
87
90
|
},
|
|
88
91
|
devDependencies: {
|
|
89
|
-
"@types/node": "
|
|
92
|
+
"@types/node": "22.19.18",
|
|
90
93
|
tsup: "^8.5.1",
|
|
91
94
|
typescript: "^5.9.3"
|
|
92
95
|
}
|
|
@@ -109,10 +112,165 @@ var require_package_version = __commonJS({
|
|
|
109
112
|
// src/index.ts
|
|
110
113
|
var import_package_version2 = __toESM(require_package_version());
|
|
111
114
|
import * as Sentry2 from "@sentry/node";
|
|
115
|
+
import { randomUUID } from "crypto";
|
|
116
|
+
|
|
117
|
+
// src/http.ts
|
|
118
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
119
|
+
function abortError() {
|
|
120
|
+
const error = new Error("Request aborted");
|
|
121
|
+
error.name = "AbortError";
|
|
122
|
+
return error;
|
|
123
|
+
}
|
|
124
|
+
function wait(ms, signal) {
|
|
125
|
+
if (signal?.aborted) return Promise.reject(abortError());
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const onAbort = () => {
|
|
128
|
+
clearTimeout(timeout);
|
|
129
|
+
reject(abortError());
|
|
130
|
+
};
|
|
131
|
+
const timeout = setTimeout(() => {
|
|
132
|
+
signal?.removeEventListener("abort", onAbort);
|
|
133
|
+
resolve();
|
|
134
|
+
}, ms);
|
|
135
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function parseRetryAfter(value) {
|
|
139
|
+
if (!value) return void 0;
|
|
140
|
+
const seconds = Number(value);
|
|
141
|
+
if (Number.isFinite(seconds) && seconds >= 0) return Math.ceil(seconds);
|
|
142
|
+
const date = Date.parse(value);
|
|
143
|
+
if (!Number.isFinite(date)) return void 0;
|
|
144
|
+
return Math.max(0, Math.ceil((date - Date.now()) / 1e3));
|
|
145
|
+
}
|
|
146
|
+
function retryDelayMs(response, attempt) {
|
|
147
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
148
|
+
if (retryAfter !== void 0) {
|
|
149
|
+
return Math.min(retryAfter * 1e3, MAX_RETRY_DELAY_MS);
|
|
150
|
+
}
|
|
151
|
+
return Math.min(1e3 * 2 ** attempt, 1e4);
|
|
152
|
+
}
|
|
153
|
+
async function fetchWithRetry(url, options, policy) {
|
|
154
|
+
const maxRetries = policy.maxRetries ?? 3;
|
|
155
|
+
let lastError;
|
|
156
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
157
|
+
try {
|
|
158
|
+
const response = await fetch(url, options);
|
|
159
|
+
const transient = response.status === 429 || response.status >= 500;
|
|
160
|
+
if (policy.retryable && transient && attempt < maxRetries) {
|
|
161
|
+
await wait(retryDelayMs(response, attempt), options.signal);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
return response;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
167
|
+
if (normalized.name === "AbortError" || options.signal?.aborted) throw normalized;
|
|
168
|
+
lastError = normalized;
|
|
169
|
+
if (!policy.retryable || attempt >= maxRetries) throw normalized;
|
|
170
|
+
await wait(Math.min(1e3 * 2 ** attempt, 1e4), options.signal);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
throw lastError ?? new Error("Request failed after retries");
|
|
174
|
+
}
|
|
175
|
+
async function readResponseData(response) {
|
|
176
|
+
if (typeof response.text !== "function") return response.json();
|
|
177
|
+
const text = await response.text();
|
|
178
|
+
if (!text) return {};
|
|
179
|
+
try {
|
|
180
|
+
return JSON.parse(text);
|
|
181
|
+
} catch {
|
|
182
|
+
return { error: text };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function record(value) {
|
|
186
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
187
|
+
}
|
|
188
|
+
function fallbackCode(status) {
|
|
189
|
+
if (status === 400 || status === 422) return "INVALID_REQUEST";
|
|
190
|
+
if (status === 401) return "AUTH_FAILED";
|
|
191
|
+
if (status === 403) return "FORBIDDEN";
|
|
192
|
+
if (status === 404) return "NOT_FOUND";
|
|
193
|
+
if (status === 409) return "CONFLICT";
|
|
194
|
+
if (status === 429) return "RATE_LIMITED";
|
|
195
|
+
if (status >= 500) return "SERVER_ERROR";
|
|
196
|
+
return "REQUEST_FAILED";
|
|
197
|
+
}
|
|
198
|
+
function safeErrorCode(value, status) {
|
|
199
|
+
return typeof value === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(value) ? value : fallbackCode(status);
|
|
200
|
+
}
|
|
201
|
+
function safeRequestId(...values) {
|
|
202
|
+
return values.find(
|
|
203
|
+
(value) => typeof value === "string" && value.length > 0 && value.length <= 200 && /^[A-Za-z0-9._:@/-]+$/.test(value)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
function safeActionUrl(value, code) {
|
|
207
|
+
if (code !== "ACTION_REQUIRED" || typeof value !== "string" || value.length > 2048) {
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
if (value.startsWith("/") && !value.startsWith("//")) return value;
|
|
211
|
+
try {
|
|
212
|
+
const url = new URL(value);
|
|
213
|
+
return url.protocol === "https:" ? value : void 0;
|
|
214
|
+
} catch {
|
|
215
|
+
return void 0;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function safeErrorMessage(status) {
|
|
219
|
+
if (status === 400 || status === 422) return "The request was invalid.";
|
|
220
|
+
if (status === 401) return "Authentication failed.";
|
|
221
|
+
if (status === 403) return "The request is not permitted.";
|
|
222
|
+
if (status === 404) return "The requested resource was not found.";
|
|
223
|
+
if (status === 409) return "The request conflicts with the current resource state.";
|
|
224
|
+
if (status === 429) return "Too many requests.";
|
|
225
|
+
if (status >= 500) return "The service is temporarily unavailable.";
|
|
226
|
+
return "The request could not be completed.";
|
|
227
|
+
}
|
|
228
|
+
function fallbackRetryGuidance(status) {
|
|
229
|
+
if (status === 400 || status === 422) return "Review the request before trying again.";
|
|
230
|
+
if (status === 401) return "Verify the API key and that it is intended for this client.";
|
|
231
|
+
if (status === 403) return "Verify credentials and permissions before trying again.";
|
|
232
|
+
if (status === 404) return "Verify the resource identifier before trying again.";
|
|
233
|
+
if (status === 409) return "Check the current resource state before trying again.";
|
|
234
|
+
if (status === 429) return "Wait for retryAfter seconds before retrying.";
|
|
235
|
+
if (status >= 500)
|
|
236
|
+
return "Retry only if this operation is read-only or protected by idempotency.";
|
|
237
|
+
return void 0;
|
|
238
|
+
}
|
|
239
|
+
function responseErrorMessage(_data, status) {
|
|
240
|
+
return safeErrorMessage(status);
|
|
241
|
+
}
|
|
242
|
+
function responseErrorMetadata(response, data) {
|
|
243
|
+
const body = record(data);
|
|
244
|
+
const code = safeErrorCode(body.code, response.status);
|
|
245
|
+
const bodyRetryAfter = typeof body.retryAfter === "number" || typeof body.retryAfter === "string" ? parseRetryAfter(String(body.retryAfter)) : void 0;
|
|
246
|
+
return {
|
|
247
|
+
code,
|
|
248
|
+
status: response.status,
|
|
249
|
+
requestId: safeRequestId(
|
|
250
|
+
response.headers.get("x-request-id"),
|
|
251
|
+
response.headers.get("x-correlation-id"),
|
|
252
|
+
body.requestId
|
|
253
|
+
),
|
|
254
|
+
retryAfter: parseRetryAfter(response.headers.get("retry-after")) ?? bodyRetryAfter,
|
|
255
|
+
retryGuidance: fallbackRetryGuidance(response.status),
|
|
256
|
+
actionUrl: safeActionUrl(body.actionUrl, code)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
112
259
|
|
|
113
260
|
// src/admin.ts
|
|
114
261
|
var import_package_version = __toESM(require_package_version());
|
|
115
262
|
import * as Sentry from "@sentry/node";
|
|
263
|
+
var ContoAdminError = class extends Error {
|
|
264
|
+
constructor(message, metadata) {
|
|
265
|
+
super(message);
|
|
266
|
+
this.name = "ContoAdminError";
|
|
267
|
+
this.code = metadata.code;
|
|
268
|
+
this.status = metadata.status;
|
|
269
|
+
this.requestId = metadata.requestId;
|
|
270
|
+
this.retryAfter = metadata.retryAfter;
|
|
271
|
+
this.retryGuidance = metadata.retryGuidance;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
116
274
|
var ContoAdmin = class {
|
|
117
275
|
constructor(config) {
|
|
118
276
|
if (!config.orgApiKey) {
|
|
@@ -133,74 +291,157 @@ var ContoAdmin = class {
|
|
|
133
291
|
this.timeout = config.timeout || 3e4;
|
|
134
292
|
this.agents = new AgentsClient(this);
|
|
135
293
|
this.wallets = new WalletsClient(this);
|
|
294
|
+
this.cards = new AdminCardsClient(this);
|
|
136
295
|
this.policies = new PoliciesClient(this);
|
|
137
296
|
this.team = new TeamClient(this);
|
|
138
297
|
}
|
|
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");
|
|
161
|
-
}
|
|
162
298
|
/** Make an authenticated request to the Conto API */
|
|
163
299
|
async request(method, path, body) {
|
|
164
300
|
const url = `${this.baseUrl}${path}`;
|
|
165
301
|
const controller = new AbortController();
|
|
166
302
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
303
|
+
const normalizedMethod = method.toUpperCase();
|
|
304
|
+
const retryable = ["GET", "HEAD", "OPTIONS"].includes(normalizedMethod);
|
|
167
305
|
try {
|
|
168
|
-
const response = await
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
306
|
+
const response = await fetchWithRetry(
|
|
307
|
+
url,
|
|
308
|
+
{
|
|
309
|
+
method: normalizedMethod,
|
|
310
|
+
headers: {
|
|
311
|
+
Authorization: `Bearer ${this.orgApiKey}`,
|
|
312
|
+
"Content-Type": "application/json",
|
|
313
|
+
"User-Agent": `@conto_finance/sdk-admin/${import_package_version.SDK_PACKAGE_VERSION}`
|
|
314
|
+
},
|
|
315
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
316
|
+
signal: controller.signal
|
|
174
317
|
},
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
clearTimeout(timeoutId);
|
|
179
|
-
const data = await response.json();
|
|
318
|
+
{ retryable }
|
|
319
|
+
);
|
|
320
|
+
const data = await readResponseData(response);
|
|
180
321
|
if (!response.ok) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
throw error;
|
|
322
|
+
throw new ContoAdminError(
|
|
323
|
+
responseErrorMessage(data, response.status),
|
|
324
|
+
responseErrorMetadata(response, data)
|
|
325
|
+
);
|
|
186
326
|
}
|
|
187
327
|
return data;
|
|
188
328
|
} catch (error) {
|
|
189
|
-
|
|
329
|
+
if (error instanceof ContoAdminError) throw error;
|
|
190
330
|
if (error instanceof Error && error.name === "AbortError") {
|
|
191
|
-
const timeoutError = new
|
|
192
|
-
|
|
193
|
-
|
|
331
|
+
const timeoutError = new ContoAdminError("Request timeout", {
|
|
332
|
+
code: "TIMEOUT",
|
|
333
|
+
status: 0,
|
|
334
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; inspect the resource before trying again."
|
|
335
|
+
});
|
|
194
336
|
Sentry.captureException(timeoutError, { extra: { method, path } });
|
|
195
337
|
throw timeoutError;
|
|
196
338
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
339
|
+
const networkError = new ContoAdminError("Network request failed", {
|
|
340
|
+
code: "NETWORK_ERROR",
|
|
341
|
+
status: 0,
|
|
342
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; inspect the resource before trying again."
|
|
343
|
+
});
|
|
344
|
+
Sentry.captureException(networkError, { extra: { method, path } });
|
|
345
|
+
throw networkError;
|
|
346
|
+
} finally {
|
|
347
|
+
clearTimeout(timeoutId);
|
|
201
348
|
}
|
|
202
349
|
}
|
|
203
350
|
};
|
|
351
|
+
var AdminCardsClient = class {
|
|
352
|
+
constructor(client) {
|
|
353
|
+
this.client = client;
|
|
354
|
+
}
|
|
355
|
+
/** List masked external-card aliases. No card credentials are returned. */
|
|
356
|
+
async list() {
|
|
357
|
+
return this.client.request("GET", "/api/external-cards");
|
|
358
|
+
}
|
|
359
|
+
/** Register a masked alias for a customer card. Never pass PAN or CVC. */
|
|
360
|
+
async register(input) {
|
|
361
|
+
return this.client.request(
|
|
362
|
+
"POST",
|
|
363
|
+
"/api/external-cards",
|
|
364
|
+
input
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
/** Assign a card alias and cooperative policy controls to an agent. */
|
|
368
|
+
async assignToAgent(agentId, input) {
|
|
369
|
+
return this.client.request(
|
|
370
|
+
"POST",
|
|
371
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards`,
|
|
372
|
+
input
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
async listForAgent(agentId) {
|
|
376
|
+
return this.client.request("GET", `/api/agents/${encodeURIComponent(agentId)}/external-cards`);
|
|
377
|
+
}
|
|
378
|
+
/** Update limits, merchant rules, hours, and optional start or end dates. */
|
|
379
|
+
async updateAgentAssignment(agentId, cardId, input) {
|
|
380
|
+
return this.client.request(
|
|
381
|
+
"PATCH",
|
|
382
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`,
|
|
383
|
+
{ action: "UPDATE", ...input }
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
/** Pause an assignment and cancel its unused approvals. */
|
|
387
|
+
async pauseForAgent(agentId, cardId) {
|
|
388
|
+
return this.client.request(
|
|
389
|
+
"PATCH",
|
|
390
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`,
|
|
391
|
+
{ action: "PAUSE" }
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
/** Resume a paused assignment when its alias and date range permit it. */
|
|
395
|
+
async resumeForAgent(agentId, cardId) {
|
|
396
|
+
return this.client.request(
|
|
397
|
+
"PATCH",
|
|
398
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`,
|
|
399
|
+
{ action: "RESUME" }
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Revoke an agent's overlay assignment. Unused grants are cancelled; claimed
|
|
404
|
+
* executions remain visible for reconciliation.
|
|
405
|
+
*/
|
|
406
|
+
async revokeFromAgent(agentId, cardId) {
|
|
407
|
+
return this.client.request(
|
|
408
|
+
"DELETE",
|
|
409
|
+
`/api/agents/${encodeURIComponent(agentId)}/external-cards/${encodeURIComponent(cardId)}`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
/** List overlay requests, including executions that need receipt reconciliation. */
|
|
413
|
+
async listRequests(input = {}) {
|
|
414
|
+
const query = new URLSearchParams();
|
|
415
|
+
if (input.status) query.set("status", input.status);
|
|
416
|
+
if (input.limit !== void 0) query.set("limit", String(input.limit));
|
|
417
|
+
const suffix = query.toString();
|
|
418
|
+
return this.client.request("GET", `/api/external-cards/requests${suffix ? `?${suffix}` : ""}`);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Resolve a claimed execution after checking the customer's processor or
|
|
422
|
+
* receipt. Resolving NOT_EXECUTED releases the reserved policy amount.
|
|
423
|
+
*/
|
|
424
|
+
async resolveRequest(requestId, input) {
|
|
425
|
+
return this.client.request(
|
|
426
|
+
"POST",
|
|
427
|
+
`/api/external-cards/requests/${encodeURIComponent(requestId)}/resolve`,
|
|
428
|
+
input
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
/** Pause the Conto-controlled path and revoke live overlay grants. */
|
|
432
|
+
async pause(cardId) {
|
|
433
|
+
return this.setState(cardId, "PAUSED");
|
|
434
|
+
}
|
|
435
|
+
/** Resume the Conto-controlled path. This does not activate a card at its issuer. */
|
|
436
|
+
async resume(cardId) {
|
|
437
|
+
return this.setState(cardId, "ACTIVE");
|
|
438
|
+
}
|
|
439
|
+
async setState(cardId, state) {
|
|
440
|
+
return this.client.request("PATCH", `/api/external-cards/${encodeURIComponent(cardId)}/state`, {
|
|
441
|
+
state
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
};
|
|
204
445
|
var AgentsClient = class {
|
|
205
446
|
constructor(client) {
|
|
206
447
|
this.client = client;
|
|
@@ -241,19 +482,15 @@ var AgentsClient = class {
|
|
|
241
482
|
}
|
|
242
483
|
/** Freeze an agent, blocking all transactions */
|
|
243
484
|
async freeze(agentId, input) {
|
|
244
|
-
return this.client.request(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
input
|
|
248
|
-
);
|
|
485
|
+
return this.client.request("POST", `/api/agents/${agentId}/freeze`, {
|
|
486
|
+
...input.freezeWallets !== void 0 && { freezeWallets: input.freezeWallets }
|
|
487
|
+
});
|
|
249
488
|
}
|
|
250
489
|
/** Unfreeze an agent, restoring transaction ability */
|
|
251
490
|
async unfreeze(agentId, input) {
|
|
252
|
-
return this.client.request(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
input
|
|
256
|
-
);
|
|
491
|
+
return this.client.request("POST", `/api/agents/${agentId}/unfreeze`, {
|
|
492
|
+
...input.unfreezeWallets !== void 0 && { unfreezeWallets: input.unfreezeWallets }
|
|
493
|
+
});
|
|
257
494
|
}
|
|
258
495
|
/** List wallets linked to an agent */
|
|
259
496
|
async listWallets(agentId) {
|
|
@@ -285,10 +522,9 @@ var AgentsClient = class {
|
|
|
285
522
|
);
|
|
286
523
|
}
|
|
287
524
|
/** Assign a policy to an agent */
|
|
288
|
-
async assignPolicy(agentId, policyId
|
|
525
|
+
async assignPolicy(agentId, policyId) {
|
|
289
526
|
return this.client.request("POST", `/api/agents/${agentId}/policies`, {
|
|
290
|
-
policyId
|
|
291
|
-
...overrides && { overrides }
|
|
527
|
+
policyId
|
|
292
528
|
});
|
|
293
529
|
}
|
|
294
530
|
/** Unassign a policy from an agent */
|
|
@@ -333,7 +569,7 @@ var WalletsClient = class {
|
|
|
333
569
|
const qs = query.toString() ? `?${query.toString()}` : "";
|
|
334
570
|
return this.client.request("GET", `/api/wallets${qs}`);
|
|
335
571
|
}
|
|
336
|
-
/** Create a
|
|
572
|
+
/** Create a managed wallet or register an external address. */
|
|
337
573
|
async create(input) {
|
|
338
574
|
return this.client.request(
|
|
339
575
|
"POST",
|
|
@@ -473,6 +709,60 @@ if (process.env.SENTRY_DSN) {
|
|
|
473
709
|
enableLogs: true
|
|
474
710
|
});
|
|
475
711
|
}
|
|
712
|
+
var ContoError = class extends Error {
|
|
713
|
+
constructor(message, metadata) {
|
|
714
|
+
super(message);
|
|
715
|
+
this.name = "ContoError";
|
|
716
|
+
this.code = metadata.code;
|
|
717
|
+
this.status = metadata.status;
|
|
718
|
+
this.requestId = metadata.requestId;
|
|
719
|
+
this.retryAfter = metadata.retryAfter;
|
|
720
|
+
this.retryGuidance = metadata.retryGuidance;
|
|
721
|
+
this.actionUrl = metadata.actionUrl;
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
var ExternalCardConfirmationPendingError = class extends ContoError {
|
|
725
|
+
constructor(requestId, confirmationInput, receipt) {
|
|
726
|
+
super("External card execution completed but confirmation is pending", {
|
|
727
|
+
code: "CARD_CONFIRMATION_PENDING",
|
|
728
|
+
status: 0,
|
|
729
|
+
requestId,
|
|
730
|
+
retryGuidance: "Persist the receipt and confirmation input, then retry cards.confirm(). Do not execute the card payment again."
|
|
731
|
+
});
|
|
732
|
+
this.name = "ExternalCardConfirmationPendingError";
|
|
733
|
+
this.confirmationInput = confirmationInput;
|
|
734
|
+
this.receipt = receipt;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
function generatedIdempotencyKey(prefix) {
|
|
738
|
+
return `${prefix}_${randomUUID()}`;
|
|
739
|
+
}
|
|
740
|
+
function requireFinancialIdentifier(value, field) {
|
|
741
|
+
if (!value || value.trim().length === 0) {
|
|
742
|
+
throw new ContoError(`${field} is required for this financial write`, {
|
|
743
|
+
code: "IDEMPOTENCY_KEY_REQUIRED",
|
|
744
|
+
status: 0,
|
|
745
|
+
retryGuidance: `Generate a stable unique ${field} and reuse it when reconciling or retrying this operation.`
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
if (value.length > 255) {
|
|
749
|
+
throw new ContoError(`${field} must be 255 characters or fewer`, {
|
|
750
|
+
code: "INVALID_IDEMPOTENCY_KEY",
|
|
751
|
+
status: 0
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
return value;
|
|
755
|
+
}
|
|
756
|
+
function withQuery(path, query) {
|
|
757
|
+
if (!query) return path;
|
|
758
|
+
const searchParams = new URLSearchParams();
|
|
759
|
+
for (const [key, value] of Object.entries(query)) {
|
|
760
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
761
|
+
searchParams.set(key, String(value));
|
|
762
|
+
}
|
|
763
|
+
const queryString = searchParams.toString();
|
|
764
|
+
return queryString ? `${path}?${queryString}` : path;
|
|
765
|
+
}
|
|
476
766
|
var Conto = class {
|
|
477
767
|
constructor(config) {
|
|
478
768
|
if (!config.apiKey) {
|
|
@@ -485,70 +775,222 @@ var Conto = class {
|
|
|
485
775
|
this.baseUrl = (config.baseUrl || "https://conto.finance").replace(/\/$/, "");
|
|
486
776
|
this.timeout = config.timeout || 3e4;
|
|
487
777
|
this.payments = new PaymentsClient(this);
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
492
|
-
try {
|
|
493
|
-
const response = await fetch(url, options);
|
|
494
|
-
if (response.status === 429 || response.status >= 500 && attempt < maxRetries) {
|
|
495
|
-
const retryAfter = response.headers.get("retry-after");
|
|
496
|
-
const delay = retryAfter ? parseInt(retryAfter, 10) * 1e3 : Math.min(1e3 * 2 ** attempt, 1e4);
|
|
497
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
return response;
|
|
501
|
-
} catch (err) {
|
|
502
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
503
|
-
if (attempt < maxRetries) {
|
|
504
|
-
const delay = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
505
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
throw lastError || new Error("Request failed after retries");
|
|
778
|
+
this.cards = new CardsClient(this);
|
|
779
|
+
this.x402 = new X402Client(this);
|
|
780
|
+
this.mpp = new MppClient(this);
|
|
511
781
|
}
|
|
512
782
|
/** Make an authenticated request to the Giro API */
|
|
513
|
-
async request(method, path, body) {
|
|
783
|
+
async request(method, path, body, options = {}) {
|
|
514
784
|
const url = `${this.baseUrl}${path}`;
|
|
515
785
|
const controller = new AbortController();
|
|
516
786
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
787
|
+
const normalizedMethod = method.toUpperCase();
|
|
788
|
+
const retryable = options.idempotent === true || ["GET", "HEAD", "OPTIONS"].includes(normalizedMethod);
|
|
517
789
|
try {
|
|
518
|
-
const response = await
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
790
|
+
const response = await fetchWithRetry(
|
|
791
|
+
url,
|
|
792
|
+
{
|
|
793
|
+
method: normalizedMethod,
|
|
794
|
+
headers: {
|
|
795
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
796
|
+
"Content-Type": "application/json",
|
|
797
|
+
"User-Agent": `@conto_finance/sdk/${import_package_version2.SDK_PACKAGE_VERSION}`
|
|
798
|
+
},
|
|
799
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
800
|
+
signal: controller.signal
|
|
524
801
|
},
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
clearTimeout(timeoutId);
|
|
529
|
-
const data = await response.json();
|
|
802
|
+
{ retryable, maxRetries: options.maxRetries }
|
|
803
|
+
);
|
|
804
|
+
const data = await readResponseData(response);
|
|
530
805
|
if (!response.ok) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
throw error;
|
|
806
|
+
throw new ContoError(
|
|
807
|
+
responseErrorMessage(data, response.status),
|
|
808
|
+
responseErrorMetadata(response, data)
|
|
809
|
+
);
|
|
536
810
|
}
|
|
537
811
|
return data;
|
|
538
812
|
} catch (error) {
|
|
539
|
-
|
|
813
|
+
if (error instanceof ContoError) throw error;
|
|
540
814
|
if (error instanceof Error && error.name === "AbortError") {
|
|
541
|
-
const timeoutError = new
|
|
542
|
-
|
|
543
|
-
|
|
815
|
+
const timeoutError = new ContoError("Request timeout", {
|
|
816
|
+
code: "TIMEOUT",
|
|
817
|
+
status: 0,
|
|
818
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; check operation status before trying again."
|
|
819
|
+
});
|
|
544
820
|
Sentry2.captureException(timeoutError, { extra: { method, path } });
|
|
545
821
|
throw timeoutError;
|
|
546
822
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
823
|
+
const networkError = new ContoError("Network request failed", {
|
|
824
|
+
code: "NETWORK_ERROR",
|
|
825
|
+
status: 0,
|
|
826
|
+
retryGuidance: retryable ? "The request is safe to retry." : "Do not retry automatically; check operation status before trying again."
|
|
827
|
+
});
|
|
828
|
+
Sentry2.captureException(networkError, { extra: { method, path } });
|
|
829
|
+
throw networkError;
|
|
830
|
+
} finally {
|
|
831
|
+
clearTimeout(timeoutId);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
var CardsClient = class {
|
|
836
|
+
constructor(client) {
|
|
837
|
+
this.client = client;
|
|
838
|
+
}
|
|
839
|
+
async approve(input) {
|
|
840
|
+
const idempotencyKey = input.idempotencyKey?.trim();
|
|
841
|
+
if (!idempotencyKey || idempotencyKey.length < 8 || idempotencyKey.length > 128) {
|
|
842
|
+
throw new ContoError("idempotencyKey must contain 8 to 128 characters", {
|
|
843
|
+
code: "INVALID_IDEMPOTENCY_KEY",
|
|
844
|
+
status: 0,
|
|
845
|
+
retryGuidance: "Generate one stable identifier per purchase intent and reuse it for retries."
|
|
846
|
+
});
|
|
551
847
|
}
|
|
848
|
+
return this.client.request(
|
|
849
|
+
"POST",
|
|
850
|
+
"/api/sdk/external-cards/approve",
|
|
851
|
+
{
|
|
852
|
+
...input,
|
|
853
|
+
idempotencyKey,
|
|
854
|
+
currency: input.currency || "USD"
|
|
855
|
+
},
|
|
856
|
+
{ idempotent: true }
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
async confirm(requestId, input) {
|
|
860
|
+
return this.client.request(
|
|
861
|
+
"POST",
|
|
862
|
+
`/api/sdk/external-cards/${encodeURIComponent(requestId)}/confirm`,
|
|
863
|
+
input,
|
|
864
|
+
{ idempotent: true }
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Atomically claim the approved intent before customer code accesses or uses
|
|
869
|
+
* the card credential. A request can be claimed only once.
|
|
870
|
+
*/
|
|
871
|
+
async begin(requestId, input) {
|
|
872
|
+
return this.client.request(
|
|
873
|
+
"POST",
|
|
874
|
+
`/api/sdk/external-cards/${encodeURIComponent(requestId)}/begin`,
|
|
875
|
+
input,
|
|
876
|
+
{ idempotent: true }
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
/** Read execution and reconciliation state without exposing card credentials. */
|
|
880
|
+
async status(requestId) {
|
|
881
|
+
return this.client.request(
|
|
882
|
+
"GET",
|
|
883
|
+
`/api/sdk/external-cards/${encodeURIComponent(requestId)}`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Create a reference payment tool whose executor callback is unreachable
|
|
888
|
+
* until Conto has returned a live approval grant.
|
|
889
|
+
*
|
|
890
|
+
* @deprecated Use the separate `@conto_finance/card-access` product package for
|
|
891
|
+
* customer-hosted card enforcement. This helper remains for compatibility.
|
|
892
|
+
*/
|
|
893
|
+
controlled(executor) {
|
|
894
|
+
return new ControlledExternalCardPaymentTool(this, executor);
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
var ControlledExternalCardPaymentTool = class {
|
|
898
|
+
constructor(cardsClient, executor) {
|
|
899
|
+
this.cardsClient = cardsClient;
|
|
900
|
+
this.executor = executor;
|
|
901
|
+
}
|
|
902
|
+
async pay(input) {
|
|
903
|
+
const approval = await this.cardsClient.approve(input);
|
|
904
|
+
if (!approval.approved || !approval.approvalToken) {
|
|
905
|
+
const executionAlreadyClaimed = approval.decision === "EXECUTING" || approval.decision === "CONFIRMING";
|
|
906
|
+
throw new ContoError("External card payment was not approved", {
|
|
907
|
+
code: executionAlreadyClaimed ? "CARD_EXECUTION_RECONCILIATION_REQUIRED" : approval.decision === "COMPLETED" ? "CARD_PAYMENT_ALREADY_COMPLETED" : approval.decision === "REQUIRES_APPROVAL" ? "REQUIRES_APPROVAL" : "CARD_PAYMENT_DENIED",
|
|
908
|
+
status: executionAlreadyClaimed || approval.decision === "COMPLETED" ? 409 : approval.decision === "REQUIRES_APPROVAL" ? 202 : 403,
|
|
909
|
+
requestId: approval.requestId,
|
|
910
|
+
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."
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
if (!approval.expiresAt || Date.parse(approval.expiresAt) <= Date.now()) {
|
|
914
|
+
throw new ContoError("External card approval has expired", {
|
|
915
|
+
code: "CARD_APPROVAL_EXPIRED",
|
|
916
|
+
status: 400,
|
|
917
|
+
requestId: approval.requestId,
|
|
918
|
+
retryGuidance: "Request a new approval before accessing or using the card credential."
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
const claim = await this.cardsClient.begin(approval.requestId, {
|
|
922
|
+
approvalToken: approval.approvalToken
|
|
923
|
+
});
|
|
924
|
+
if (!claim.claimed) {
|
|
925
|
+
throw new ContoError("External card execution was already claimed", {
|
|
926
|
+
code: "CARD_EXECUTION_RECONCILIATION_REQUIRED",
|
|
927
|
+
status: 409,
|
|
928
|
+
requestId: approval.requestId,
|
|
929
|
+
retryGuidance: "Do not execute the payment again. Check cards.status() and confirm or reconcile the existing request."
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
let execution;
|
|
933
|
+
try {
|
|
934
|
+
execution = await this.executor({
|
|
935
|
+
requestId: approval.requestId,
|
|
936
|
+
cardId: input.cardId,
|
|
937
|
+
amount: input.amount,
|
|
938
|
+
currency: input.currency || "USD",
|
|
939
|
+
merchantName: input.merchantName,
|
|
940
|
+
merchantId: input.merchantId,
|
|
941
|
+
merchantCategory: input.merchantCategory,
|
|
942
|
+
merchantAddress: input.merchantAddress,
|
|
943
|
+
merchantCountry: input.merchantCountry,
|
|
944
|
+
purpose: input.purpose,
|
|
945
|
+
context: input.context
|
|
946
|
+
});
|
|
947
|
+
} catch (error) {
|
|
948
|
+
const outcomeError = new ContoError(
|
|
949
|
+
"External card executor failed after execution was claimed",
|
|
950
|
+
{
|
|
951
|
+
code: "CARD_EXECUTION_OUTCOME_UNKNOWN",
|
|
952
|
+
status: 0,
|
|
953
|
+
requestId: approval.requestId,
|
|
954
|
+
retryGuidance: "Do not execute the payment again. Verify the customer card outcome, then reconcile this request."
|
|
955
|
+
}
|
|
956
|
+
);
|
|
957
|
+
Sentry2.captureException(outcomeError, {
|
|
958
|
+
extra: {
|
|
959
|
+
requestId: approval.requestId,
|
|
960
|
+
executorError: error instanceof Error ? error.message : String(error)
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
throw outcomeError;
|
|
964
|
+
}
|
|
965
|
+
const confirmationInput = {
|
|
966
|
+
approvalToken: approval.approvalToken,
|
|
967
|
+
externalTxId: execution.externalTxId,
|
|
968
|
+
authorizationCode: execution.authorizationCode,
|
|
969
|
+
actualAmount: execution.actualAmount,
|
|
970
|
+
merchantDetails: execution.merchantDetails
|
|
971
|
+
};
|
|
972
|
+
let confirmation;
|
|
973
|
+
try {
|
|
974
|
+
confirmation = await this.cardsClient.confirm(approval.requestId, confirmationInput);
|
|
975
|
+
} catch (error) {
|
|
976
|
+
const pending = new ExternalCardConfirmationPendingError(
|
|
977
|
+
approval.requestId,
|
|
978
|
+
confirmationInput,
|
|
979
|
+
execution.receipt
|
|
980
|
+
);
|
|
981
|
+
Sentry2.captureException(pending, {
|
|
982
|
+
extra: {
|
|
983
|
+
requestId: approval.requestId,
|
|
984
|
+
confirmationError: error instanceof Error ? error.message : String(error)
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
throw pending;
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
approval,
|
|
991
|
+
confirmation,
|
|
992
|
+
receipt: execution.receipt
|
|
993
|
+
};
|
|
552
994
|
}
|
|
553
995
|
};
|
|
554
996
|
var PaymentsClient = class {
|
|
@@ -584,10 +1026,15 @@ var PaymentsClient = class {
|
|
|
584
1026
|
* ```
|
|
585
1027
|
*/
|
|
586
1028
|
async request(input) {
|
|
1029
|
+
const requestInput = {
|
|
1030
|
+
...input,
|
|
1031
|
+
idempotencyKey: input.idempotencyKey ?? generatedIdempotencyKey("conto_payment")
|
|
1032
|
+
};
|
|
587
1033
|
return this.client.request(
|
|
588
1034
|
"POST",
|
|
589
1035
|
"/api/sdk/payments/request",
|
|
590
|
-
|
|
1036
|
+
requestInput,
|
|
1037
|
+
{ idempotent: true }
|
|
591
1038
|
);
|
|
592
1039
|
}
|
|
593
1040
|
/**
|
|
@@ -601,16 +1048,34 @@ var PaymentsClient = class {
|
|
|
601
1048
|
* @example
|
|
602
1049
|
* ```typescript
|
|
603
1050
|
* const result = await conto.payments.execute(request.requestId);
|
|
604
|
-
* console.log(`
|
|
605
|
-
* console.log(`
|
|
1051
|
+
* console.log(`Status: ${result.status}`);
|
|
1052
|
+
* console.log(`Conto transaction: ${result.receipt.transactionId}`);
|
|
1053
|
+
* console.log(`Settlement mode: ${result.receipt.settlementMode}`);
|
|
1054
|
+
* console.log(`Transaction: ${result.receipt.txHash}`);
|
|
1055
|
+
* if (result.receipt.explorerUrl) console.log(`Explorer: ${result.receipt.explorerUrl}`);
|
|
606
1056
|
* ```
|
|
607
1057
|
*/
|
|
608
1058
|
async execute(requestId) {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1059
|
+
try {
|
|
1060
|
+
return await this.client.request(
|
|
1061
|
+
"POST",
|
|
1062
|
+
`/api/sdk/payments/${encodeURIComponent(requestId)}/execute`,
|
|
1063
|
+
{}
|
|
1064
|
+
);
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
if (error instanceof ContoError && error.status === 0) {
|
|
1067
|
+
throw new ContoError(error.message, {
|
|
1068
|
+
code: error.code,
|
|
1069
|
+
status: error.status,
|
|
1070
|
+
requestId: error.requestId,
|
|
1071
|
+
retryAfter: error.retryAfter,
|
|
1072
|
+
retryGuidance: `Check payments.status(${JSON.stringify(
|
|
1073
|
+
requestId
|
|
1074
|
+
)}) before attempting execution again.`
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
throw error;
|
|
1078
|
+
}
|
|
614
1079
|
}
|
|
615
1080
|
/**
|
|
616
1081
|
* Get the status of a payment request.
|
|
@@ -620,8 +1085,8 @@ var PaymentsClient = class {
|
|
|
620
1085
|
* @example
|
|
621
1086
|
* ```typescript
|
|
622
1087
|
* const status = await conto.payments.status(requestId);
|
|
623
|
-
* if (status.transaction?.status === '
|
|
624
|
-
* console.log('Payment confirmed
|
|
1088
|
+
* if (status.transaction?.status === 'completed') {
|
|
1089
|
+
* console.log('Payment confirmed', status.transaction.txHash);
|
|
625
1090
|
* }
|
|
626
1091
|
* ```
|
|
627
1092
|
*/
|
|
@@ -646,7 +1111,9 @@ var PaymentsClient = class {
|
|
|
646
1111
|
* recipientAddress: '0x...',
|
|
647
1112
|
* purpose: 'Service fee'
|
|
648
1113
|
* });
|
|
649
|
-
* console.log(`
|
|
1114
|
+
* console.log(`Payment status: ${result.status}`);
|
|
1115
|
+
* console.log(`Conto transaction: ${result.receipt.transactionId}`);
|
|
1116
|
+
* console.log(`Transaction: ${result.receipt.txHash}`);
|
|
650
1117
|
* } catch (error) {
|
|
651
1118
|
* if (error.code === 'REQUIRES_APPROVAL') {
|
|
652
1119
|
* console.log('Payment needs human approval');
|
|
@@ -660,24 +1127,133 @@ var PaymentsClient = class {
|
|
|
660
1127
|
const { autoExecute: _autoExecute, ...requestInput } = input;
|
|
661
1128
|
const request = await this.request(requestInput);
|
|
662
1129
|
if (request.status === "DENIED") {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1130
|
+
throw new ContoError("Payment was denied.", {
|
|
1131
|
+
code: "PAYMENT_DENIED",
|
|
1132
|
+
status: 403,
|
|
1133
|
+
requestId: request.requestId,
|
|
1134
|
+
retryGuidance: "Review the payment request before trying again."
|
|
1135
|
+
});
|
|
667
1136
|
}
|
|
668
1137
|
if (request.status === "REQUIRES_APPROVAL") {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
1138
|
+
throw new ContoError("Payment requires manual approval", {
|
|
1139
|
+
code: "REQUIRES_APPROVAL",
|
|
1140
|
+
status: 202,
|
|
1141
|
+
requestId: request.requestId,
|
|
1142
|
+
retryGuidance: "Wait for approval, then execute the same request ID."
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
if (request.status === "ACTION_REQUIRED") {
|
|
1146
|
+
throw new ContoError("Payment requires an additional action", {
|
|
1147
|
+
code: "ACTION_REQUIRED",
|
|
1148
|
+
status: 402,
|
|
1149
|
+
requestId: request.requestId,
|
|
1150
|
+
retryGuidance: request.actionUrl ? `Complete the required action at ${request.actionUrl}, then check the payment status.` : "Complete the required action, then check the payment status."
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
if (request.status === "PROCESSING") {
|
|
1154
|
+
throw new ContoError("Payment is processing", {
|
|
1155
|
+
code: "PAYMENT_PROCESSING",
|
|
1156
|
+
status: 202,
|
|
1157
|
+
requestId: request.requestId,
|
|
1158
|
+
retryGuidance: "Check the payment status before taking another payment action."
|
|
1159
|
+
});
|
|
673
1160
|
}
|
|
674
1161
|
return this.execute(request.requestId);
|
|
675
1162
|
}
|
|
676
1163
|
};
|
|
1164
|
+
var X402Client = class {
|
|
1165
|
+
constructor(client) {
|
|
1166
|
+
this.client = client;
|
|
1167
|
+
}
|
|
1168
|
+
async preAuthorize(input) {
|
|
1169
|
+
return this.client.request(
|
|
1170
|
+
"POST",
|
|
1171
|
+
"/api/sdk/x402/pre-authorize",
|
|
1172
|
+
input,
|
|
1173
|
+
{ idempotent: true }
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
async record(input) {
|
|
1177
|
+
const paymentId = requireFinancialIdentifier(input.paymentId, "paymentId");
|
|
1178
|
+
const requestInput = {
|
|
1179
|
+
...input,
|
|
1180
|
+
paymentId
|
|
1181
|
+
};
|
|
1182
|
+
return this.client.request(
|
|
1183
|
+
"POST",
|
|
1184
|
+
"/api/sdk/x402/record",
|
|
1185
|
+
requestInput
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
async budget(options) {
|
|
1189
|
+
return this.client.request(
|
|
1190
|
+
"GET",
|
|
1191
|
+
withQuery("/api/sdk/x402/budget", { sessionId: options?.sessionId }),
|
|
1192
|
+
void 0
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
async services(options) {
|
|
1196
|
+
return this.client.request(
|
|
1197
|
+
"GET",
|
|
1198
|
+
withQuery("/api/sdk/x402/services", {
|
|
1199
|
+
period: options?.period,
|
|
1200
|
+
limit: options?.limit
|
|
1201
|
+
}),
|
|
1202
|
+
void 0
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
var MppClient = class {
|
|
1207
|
+
constructor(client) {
|
|
1208
|
+
this.client = client;
|
|
1209
|
+
}
|
|
1210
|
+
async preAuthorize(input) {
|
|
1211
|
+
return this.client.request(
|
|
1212
|
+
"POST",
|
|
1213
|
+
"/api/sdk/mpp/pre-authorize",
|
|
1214
|
+
input,
|
|
1215
|
+
{ idempotent: true }
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
async record(input) {
|
|
1219
|
+
const credentialId = requireFinancialIdentifier(input.credentialId, "credentialId");
|
|
1220
|
+
const requestInput = {
|
|
1221
|
+
...input,
|
|
1222
|
+
credentialId
|
|
1223
|
+
};
|
|
1224
|
+
return this.client.request(
|
|
1225
|
+
"POST",
|
|
1226
|
+
"/api/sdk/mpp/record",
|
|
1227
|
+
requestInput
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
async budget(options) {
|
|
1231
|
+
return this.client.request(
|
|
1232
|
+
"GET",
|
|
1233
|
+
withQuery("/api/sdk/mpp/budget", { sessionId: options?.sessionId }),
|
|
1234
|
+
void 0
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
async services(options) {
|
|
1238
|
+
return this.client.request(
|
|
1239
|
+
"GET",
|
|
1240
|
+
withQuery("/api/sdk/mpp/services", {
|
|
1241
|
+
period: options?.period,
|
|
1242
|
+
limit: options?.limit
|
|
1243
|
+
}),
|
|
1244
|
+
void 0
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
677
1248
|
var index_default = Conto;
|
|
678
1249
|
export {
|
|
1250
|
+
CardsClient,
|
|
679
1251
|
Conto,
|
|
680
1252
|
ContoAdmin,
|
|
1253
|
+
ContoAdminError,
|
|
1254
|
+
ContoError,
|
|
1255
|
+
ControlledExternalCardPaymentTool,
|
|
1256
|
+
ExternalCardConfirmationPendingError,
|
|
681
1257
|
Conto as Giro,
|
|
682
1258
|
index_default as default
|
|
683
1259
|
};
|