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