@conto_finance/sdk 0.6.0-beta.1 → 0.6.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -60
- package/dist/index.d.mts +98 -4
- package/dist/index.d.ts +98 -4
- package/dist/index.js +154 -18
- package/dist/index.mjs +153 -18
- package/dist/link-testing.d.mts +15 -3
- package/dist/link-testing.d.ts +15 -3
- package/dist/link-testing.js +66 -45
- package/dist/link-testing.mjs +66 -45
- package/package.json +2 -2
package/dist/link-testing.js
CHANGED
|
@@ -35,6 +35,27 @@ function canonical(value) {
|
|
|
35
35
|
function fail(code, message, status = 409) {
|
|
36
36
|
throw new import_index.ContoError(message, { code, status });
|
|
37
37
|
}
|
|
38
|
+
var LIVE_API_ERRORS = {
|
|
39
|
+
POLICY_VIOLATION: {
|
|
40
|
+
status: 403,
|
|
41
|
+
message: "The request is not permitted.",
|
|
42
|
+
retryGuidance: "Verify credentials and permissions before trying again."
|
|
43
|
+
},
|
|
44
|
+
NOT_FOUND: {
|
|
45
|
+
status: 404,
|
|
46
|
+
message: "The requested resource was not found.",
|
|
47
|
+
retryGuidance: "Verify the resource identifier before trying again."
|
|
48
|
+
},
|
|
49
|
+
CONFLICT: {
|
|
50
|
+
status: 409,
|
|
51
|
+
message: "The request conflicts with the current resource state.",
|
|
52
|
+
retryGuidance: "Check the current resource state before trying again."
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
function liveApiError(code) {
|
|
56
|
+
const { status, message, retryGuidance } = LIVE_API_ERRORS[code];
|
|
57
|
+
throw new import_index.ContoError(message, { code, status, retryGuidance });
|
|
58
|
+
}
|
|
38
59
|
function createLinkTestClient(options = {}) {
|
|
39
60
|
const scenario = options.scenario ?? "approved";
|
|
40
61
|
const maximumAmountMinor = options.maximumAmountMinor ?? 5e3;
|
|
@@ -50,36 +71,37 @@ function createLinkTestClient(options = {}) {
|
|
|
50
71
|
const sessionId = (0, import_node_crypto.randomUUID)();
|
|
51
72
|
const read = (id) => {
|
|
52
73
|
const record = records.get(id);
|
|
53
|
-
if (!record)
|
|
54
|
-
return fail(
|
|
55
|
-
"SIMULATED_EXECUTION_NOT_FOUND",
|
|
56
|
-
"No such execution in this local simulation",
|
|
57
|
-
404
|
|
58
|
-
);
|
|
74
|
+
if (!record) return liveApiError("NOT_FOUND");
|
|
59
75
|
return record;
|
|
60
76
|
};
|
|
61
77
|
const response = (id) => ({
|
|
62
78
|
execution: structuredClone(read(id))
|
|
63
79
|
});
|
|
64
|
-
const setStatus = (id, status) => {
|
|
80
|
+
const setStatus = (id, status, providerStatus, evidence = {}) => {
|
|
65
81
|
const record = read(id);
|
|
66
82
|
record.status = status;
|
|
67
|
-
record.
|
|
68
|
-
record.
|
|
69
|
-
record.evidence.providerStatus = record.providerStatus;
|
|
83
|
+
record.providerStatus = `simulated_${providerStatus}`;
|
|
84
|
+
Object.assign(record.evidence, evidence, { status, providerStatus: record.providerStatus });
|
|
70
85
|
delete record.approvalUrl;
|
|
71
86
|
return response(id);
|
|
72
87
|
};
|
|
73
88
|
const setup = async (_input = {}) => ({
|
|
74
89
|
version: "conto.link-setup.v1",
|
|
75
90
|
mode: "SIMULATED",
|
|
76
|
-
|
|
91
|
+
// No live setting was inspected, so the simulator never reports CONFIGURED.
|
|
92
|
+
configurationStatus: "INCOMPLETE",
|
|
77
93
|
checks: [
|
|
78
94
|
{
|
|
79
95
|
code: "LOCAL_SIMULATION",
|
|
80
96
|
status: "WARNING",
|
|
81
97
|
actor: "developer",
|
|
82
98
|
message: "Local workflow fixture only. No real Link account, Conto policy, checkout, budget, or settlement is tested."
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
code: "LIVE_SETUP_NOT_INSPECTED",
|
|
102
|
+
status: "BLOCKED",
|
|
103
|
+
actor: "developer",
|
|
104
|
+
message: "The live Link configuration was not inspected. Call link.setup() on a live Conto client before a real payment."
|
|
83
105
|
}
|
|
84
106
|
],
|
|
85
107
|
missingScopes: [],
|
|
@@ -98,7 +120,7 @@ function createLinkTestClient(options = {}) {
|
|
|
98
120
|
id: "simulated_method",
|
|
99
121
|
type: "SIMULATED",
|
|
100
122
|
isDefault: true,
|
|
101
|
-
displayLabel: "Simulated funding source
|
|
123
|
+
displayLabel: "Simulated funding source (no funds)",
|
|
102
124
|
eligibleCredentialTypes: ["LINK_PAY_TOKEN"]
|
|
103
125
|
}
|
|
104
126
|
]
|
|
@@ -121,16 +143,11 @@ function createLinkTestClient(options = {}) {
|
|
|
121
143
|
const fingerprint = canonical({ ...input, idempotencyKey: key });
|
|
122
144
|
const prior = intents.get(key);
|
|
123
145
|
if (prior) {
|
|
124
|
-
if (prior.fingerprint !== fingerprint)
|
|
125
|
-
return fail("IDEMPOTENCY_CONFLICT", "The key belongs to a different simulated purchase");
|
|
146
|
+
if (prior.fingerprint !== fingerprint) return liveApiError("CONFLICT");
|
|
126
147
|
return response(prior.id);
|
|
127
148
|
}
|
|
128
149
|
if (scenario === "denied" || input.amountMinor > maximumAmountMinor) {
|
|
129
|
-
return
|
|
130
|
-
"COMMERCE_POLICY_DENIED",
|
|
131
|
-
"The local simulated policy denied this purchase",
|
|
132
|
-
403
|
|
133
|
-
);
|
|
150
|
+
return liveApiError("POLICY_VIOLATION");
|
|
134
151
|
}
|
|
135
152
|
if (records.size >= 1e3)
|
|
136
153
|
return fail(
|
|
@@ -139,7 +156,9 @@ function createLinkTestClient(options = {}) {
|
|
|
139
156
|
);
|
|
140
157
|
const id = `sim_link_${sessionId}_${++sequence}`;
|
|
141
158
|
const status = "AWAITING_APPROVAL";
|
|
159
|
+
const providerStatus = "simulated_pending_approval";
|
|
142
160
|
const evidence = {
|
|
161
|
+
mode: "SIMULATED",
|
|
143
162
|
schemaVersion: "conto.commerce-evidence.v1",
|
|
144
163
|
traceId: `sim_trace_${sequence}`,
|
|
145
164
|
executionId: id,
|
|
@@ -161,9 +180,10 @@ function createLinkTestClient(options = {}) {
|
|
|
161
180
|
status,
|
|
162
181
|
deliveryMode: "SERVER_ONLY_ARTIFACT",
|
|
163
182
|
artifactKind: "LINK_PAY_TOKEN",
|
|
164
|
-
providerStatus
|
|
183
|
+
providerStatus
|
|
165
184
|
};
|
|
166
185
|
records.set(id, {
|
|
186
|
+
mode: "SIMULATED",
|
|
167
187
|
id,
|
|
168
188
|
intentId: evidence.intentId,
|
|
169
189
|
organizationId: evidence.organizationId,
|
|
@@ -173,10 +193,10 @@ function createLinkTestClient(options = {}) {
|
|
|
173
193
|
executionChannel: "BROWSER",
|
|
174
194
|
checkoutMethod: "LINK",
|
|
175
195
|
status,
|
|
176
|
-
providerStatus
|
|
196
|
+
providerStatus,
|
|
177
197
|
artifactKind: "LINK_PAY_TOKEN",
|
|
178
198
|
deliveryMode: "SERVER_ONLY_ARTIFACT",
|
|
179
|
-
// Reserved .invalid domain. It cannot approve anything
|
|
199
|
+
// Reserved .invalid domain. It cannot approve anything. Call the test-only approve() method.
|
|
180
200
|
approvalUrl: `https://link-simulation.invalid/approve/${id}`,
|
|
181
201
|
amountMinor: input.amountMinor,
|
|
182
202
|
currency: input.currency,
|
|
@@ -191,37 +211,36 @@ function createLinkTestClient(options = {}) {
|
|
|
191
211
|
async getCommerceExecution(id) {
|
|
192
212
|
return response(id);
|
|
193
213
|
},
|
|
214
|
+
// Like a live refresh, this observes the provider and never submits a payment.
|
|
215
|
+
// A submitted simulated checkout is observed as succeeded on the next refresh.
|
|
194
216
|
async refreshCommerceExecution(id) {
|
|
217
|
+
const { status, providerStatus } = read(id);
|
|
218
|
+
if (status === "PROCESSING" && providerStatus === "simulated_checkout_submitted")
|
|
219
|
+
return setStatus(id, "SETTLED", "succeeded");
|
|
195
220
|
return response(id);
|
|
196
221
|
},
|
|
197
222
|
async executeCommerceExecution(id) {
|
|
198
|
-
if (read(id).status !== "READY")
|
|
199
|
-
return fail("COMMERCE_EXECUTION_NOT_READY", "The simulated payment is not ready");
|
|
223
|
+
if (read(id).status !== "READY") return liveApiError("CONFLICT");
|
|
200
224
|
if (scenario === "interrupted") {
|
|
201
|
-
setStatus(id, "NEEDS_RECONCILIATION");
|
|
202
|
-
|
|
203
|
-
"SIMULATED_EXECUTION_INTERRUPTED",
|
|
204
|
-
"Simulated lost execution response; inspect the existing execution",
|
|
205
|
-
0
|
|
206
|
-
);
|
|
225
|
+
setStatus(id, "NEEDS_RECONCILIATION", "checkout_outcome_unknown");
|
|
226
|
+
throw new import_index.ContoError("Network request failed", { code: "NETWORK_ERROR", status: 0 });
|
|
207
227
|
}
|
|
208
228
|
return {
|
|
209
|
-
...setStatus(id, "
|
|
229
|
+
...setStatus(id, "PROCESSING", "checkout_submitted", {
|
|
230
|
+
checkoutReference: id.replace(/^sim_link_/, "sim_checkout_")
|
|
231
|
+
}),
|
|
210
232
|
checkout: {
|
|
211
|
-
submitted:
|
|
212
|
-
|
|
213
|
-
|
|
233
|
+
submitted: true,
|
|
234
|
+
deliveryMode: "SERVER_ONLY_ARTIFACT",
|
|
235
|
+
accepted: true
|
|
214
236
|
}
|
|
215
237
|
};
|
|
216
238
|
},
|
|
217
239
|
async cancelCommerceExecution(id) {
|
|
218
240
|
if (["PROCESSING", "NEEDS_RECONCILIATION", "COMPLETED", "SETTLED"].includes(read(id).status)) {
|
|
219
|
-
return
|
|
220
|
-
"SIMULATED_CANCELLATION_UNAVAILABLE",
|
|
221
|
-
"This simulated execution must be reconciled and cannot be cancelled"
|
|
222
|
-
);
|
|
241
|
+
return liveApiError("CONFLICT");
|
|
223
242
|
}
|
|
224
|
-
return setStatus(id, "CANCELLED");
|
|
243
|
+
return setStatus(id, "CANCELLED", "canceled");
|
|
225
244
|
}
|
|
226
245
|
};
|
|
227
246
|
const client = new import_index.LinkClient(transport, {
|
|
@@ -236,23 +255,25 @@ function createLinkTestClient(options = {}) {
|
|
|
236
255
|
throw Object.assign(safe, { mode: "SIMULATED", settlementVerified: false });
|
|
237
256
|
}
|
|
238
257
|
};
|
|
258
|
+
const payment = (operation) => marked(operation);
|
|
239
259
|
return {
|
|
240
260
|
mode: "SIMULATED",
|
|
241
261
|
link: {
|
|
242
262
|
setup: (input) => marked(() => client.setup(input)),
|
|
243
263
|
paymentMethods: () => marked(() => client.paymentMethods()),
|
|
244
|
-
pay: (input) =>
|
|
245
|
-
resume: (id) =>
|
|
246
|
-
status: (id) =>
|
|
247
|
-
cancel: (id) =>
|
|
264
|
+
pay: (input) => payment(() => client.pay(input)),
|
|
265
|
+
resume: (id) => payment(() => client.resume(id)),
|
|
266
|
+
status: (id) => payment(() => client.status(id)),
|
|
267
|
+
cancel: (id) => payment(() => client.cancel(id)),
|
|
248
268
|
/** Test-only owner action. Never available on the live Link client. */
|
|
249
|
-
approve: (id) =>
|
|
269
|
+
approve: (id) => payment(async () => {
|
|
250
270
|
if (read(id).status !== "AWAITING_APPROVAL")
|
|
251
271
|
return fail(
|
|
252
272
|
"SIMULATED_APPROVAL_UNAVAILABLE",
|
|
253
273
|
"This simulation is not awaiting approval"
|
|
254
274
|
);
|
|
255
|
-
|
|
275
|
+
if (scenario === "expired") setStatus(id, "EXPIRED", "expired");
|
|
276
|
+
else setStatus(id, "READY", "approved");
|
|
256
277
|
return client.status(id);
|
|
257
278
|
})
|
|
258
279
|
}
|
package/dist/link-testing.mjs
CHANGED
|
@@ -16,6 +16,27 @@ function canonical(value) {
|
|
|
16
16
|
function fail(code, message, status = 409) {
|
|
17
17
|
throw new ContoError(message, { code, status });
|
|
18
18
|
}
|
|
19
|
+
var LIVE_API_ERRORS = {
|
|
20
|
+
POLICY_VIOLATION: {
|
|
21
|
+
status: 403,
|
|
22
|
+
message: "The request is not permitted.",
|
|
23
|
+
retryGuidance: "Verify credentials and permissions before trying again."
|
|
24
|
+
},
|
|
25
|
+
NOT_FOUND: {
|
|
26
|
+
status: 404,
|
|
27
|
+
message: "The requested resource was not found.",
|
|
28
|
+
retryGuidance: "Verify the resource identifier before trying again."
|
|
29
|
+
},
|
|
30
|
+
CONFLICT: {
|
|
31
|
+
status: 409,
|
|
32
|
+
message: "The request conflicts with the current resource state.",
|
|
33
|
+
retryGuidance: "Check the current resource state before trying again."
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
function liveApiError(code) {
|
|
37
|
+
const { status, message, retryGuidance } = LIVE_API_ERRORS[code];
|
|
38
|
+
throw new ContoError(message, { code, status, retryGuidance });
|
|
39
|
+
}
|
|
19
40
|
function createLinkTestClient(options = {}) {
|
|
20
41
|
const scenario = options.scenario ?? "approved";
|
|
21
42
|
const maximumAmountMinor = options.maximumAmountMinor ?? 5e3;
|
|
@@ -31,36 +52,37 @@ function createLinkTestClient(options = {}) {
|
|
|
31
52
|
const sessionId = randomUUID();
|
|
32
53
|
const read = (id) => {
|
|
33
54
|
const record = records.get(id);
|
|
34
|
-
if (!record)
|
|
35
|
-
return fail(
|
|
36
|
-
"SIMULATED_EXECUTION_NOT_FOUND",
|
|
37
|
-
"No such execution in this local simulation",
|
|
38
|
-
404
|
|
39
|
-
);
|
|
55
|
+
if (!record) return liveApiError("NOT_FOUND");
|
|
40
56
|
return record;
|
|
41
57
|
};
|
|
42
58
|
const response = (id) => ({
|
|
43
59
|
execution: structuredClone(read(id))
|
|
44
60
|
});
|
|
45
|
-
const setStatus = (id, status) => {
|
|
61
|
+
const setStatus = (id, status, providerStatus, evidence = {}) => {
|
|
46
62
|
const record = read(id);
|
|
47
63
|
record.status = status;
|
|
48
|
-
record.
|
|
49
|
-
record.
|
|
50
|
-
record.evidence.providerStatus = record.providerStatus;
|
|
64
|
+
record.providerStatus = `simulated_${providerStatus}`;
|
|
65
|
+
Object.assign(record.evidence, evidence, { status, providerStatus: record.providerStatus });
|
|
51
66
|
delete record.approvalUrl;
|
|
52
67
|
return response(id);
|
|
53
68
|
};
|
|
54
69
|
const setup = async (_input = {}) => ({
|
|
55
70
|
version: "conto.link-setup.v1",
|
|
56
71
|
mode: "SIMULATED",
|
|
57
|
-
|
|
72
|
+
// No live setting was inspected, so the simulator never reports CONFIGURED.
|
|
73
|
+
configurationStatus: "INCOMPLETE",
|
|
58
74
|
checks: [
|
|
59
75
|
{
|
|
60
76
|
code: "LOCAL_SIMULATION",
|
|
61
77
|
status: "WARNING",
|
|
62
78
|
actor: "developer",
|
|
63
79
|
message: "Local workflow fixture only. No real Link account, Conto policy, checkout, budget, or settlement is tested."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
code: "LIVE_SETUP_NOT_INSPECTED",
|
|
83
|
+
status: "BLOCKED",
|
|
84
|
+
actor: "developer",
|
|
85
|
+
message: "The live Link configuration was not inspected. Call link.setup() on a live Conto client before a real payment."
|
|
64
86
|
}
|
|
65
87
|
],
|
|
66
88
|
missingScopes: [],
|
|
@@ -79,7 +101,7 @@ function createLinkTestClient(options = {}) {
|
|
|
79
101
|
id: "simulated_method",
|
|
80
102
|
type: "SIMULATED",
|
|
81
103
|
isDefault: true,
|
|
82
|
-
displayLabel: "Simulated funding source
|
|
104
|
+
displayLabel: "Simulated funding source (no funds)",
|
|
83
105
|
eligibleCredentialTypes: ["LINK_PAY_TOKEN"]
|
|
84
106
|
}
|
|
85
107
|
]
|
|
@@ -102,16 +124,11 @@ function createLinkTestClient(options = {}) {
|
|
|
102
124
|
const fingerprint = canonical({ ...input, idempotencyKey: key });
|
|
103
125
|
const prior = intents.get(key);
|
|
104
126
|
if (prior) {
|
|
105
|
-
if (prior.fingerprint !== fingerprint)
|
|
106
|
-
return fail("IDEMPOTENCY_CONFLICT", "The key belongs to a different simulated purchase");
|
|
127
|
+
if (prior.fingerprint !== fingerprint) return liveApiError("CONFLICT");
|
|
107
128
|
return response(prior.id);
|
|
108
129
|
}
|
|
109
130
|
if (scenario === "denied" || input.amountMinor > maximumAmountMinor) {
|
|
110
|
-
return
|
|
111
|
-
"COMMERCE_POLICY_DENIED",
|
|
112
|
-
"The local simulated policy denied this purchase",
|
|
113
|
-
403
|
|
114
|
-
);
|
|
131
|
+
return liveApiError("POLICY_VIOLATION");
|
|
115
132
|
}
|
|
116
133
|
if (records.size >= 1e3)
|
|
117
134
|
return fail(
|
|
@@ -120,7 +137,9 @@ function createLinkTestClient(options = {}) {
|
|
|
120
137
|
);
|
|
121
138
|
const id = `sim_link_${sessionId}_${++sequence}`;
|
|
122
139
|
const status = "AWAITING_APPROVAL";
|
|
140
|
+
const providerStatus = "simulated_pending_approval";
|
|
123
141
|
const evidence = {
|
|
142
|
+
mode: "SIMULATED",
|
|
124
143
|
schemaVersion: "conto.commerce-evidence.v1",
|
|
125
144
|
traceId: `sim_trace_${sequence}`,
|
|
126
145
|
executionId: id,
|
|
@@ -142,9 +161,10 @@ function createLinkTestClient(options = {}) {
|
|
|
142
161
|
status,
|
|
143
162
|
deliveryMode: "SERVER_ONLY_ARTIFACT",
|
|
144
163
|
artifactKind: "LINK_PAY_TOKEN",
|
|
145
|
-
providerStatus
|
|
164
|
+
providerStatus
|
|
146
165
|
};
|
|
147
166
|
records.set(id, {
|
|
167
|
+
mode: "SIMULATED",
|
|
148
168
|
id,
|
|
149
169
|
intentId: evidence.intentId,
|
|
150
170
|
organizationId: evidence.organizationId,
|
|
@@ -154,10 +174,10 @@ function createLinkTestClient(options = {}) {
|
|
|
154
174
|
executionChannel: "BROWSER",
|
|
155
175
|
checkoutMethod: "LINK",
|
|
156
176
|
status,
|
|
157
|
-
providerStatus
|
|
177
|
+
providerStatus,
|
|
158
178
|
artifactKind: "LINK_PAY_TOKEN",
|
|
159
179
|
deliveryMode: "SERVER_ONLY_ARTIFACT",
|
|
160
|
-
// Reserved .invalid domain. It cannot approve anything
|
|
180
|
+
// Reserved .invalid domain. It cannot approve anything. Call the test-only approve() method.
|
|
161
181
|
approvalUrl: `https://link-simulation.invalid/approve/${id}`,
|
|
162
182
|
amountMinor: input.amountMinor,
|
|
163
183
|
currency: input.currency,
|
|
@@ -172,37 +192,36 @@ function createLinkTestClient(options = {}) {
|
|
|
172
192
|
async getCommerceExecution(id) {
|
|
173
193
|
return response(id);
|
|
174
194
|
},
|
|
195
|
+
// Like a live refresh, this observes the provider and never submits a payment.
|
|
196
|
+
// A submitted simulated checkout is observed as succeeded on the next refresh.
|
|
175
197
|
async refreshCommerceExecution(id) {
|
|
198
|
+
const { status, providerStatus } = read(id);
|
|
199
|
+
if (status === "PROCESSING" && providerStatus === "simulated_checkout_submitted")
|
|
200
|
+
return setStatus(id, "SETTLED", "succeeded");
|
|
176
201
|
return response(id);
|
|
177
202
|
},
|
|
178
203
|
async executeCommerceExecution(id) {
|
|
179
|
-
if (read(id).status !== "READY")
|
|
180
|
-
return fail("COMMERCE_EXECUTION_NOT_READY", "The simulated payment is not ready");
|
|
204
|
+
if (read(id).status !== "READY") return liveApiError("CONFLICT");
|
|
181
205
|
if (scenario === "interrupted") {
|
|
182
|
-
setStatus(id, "NEEDS_RECONCILIATION");
|
|
183
|
-
|
|
184
|
-
"SIMULATED_EXECUTION_INTERRUPTED",
|
|
185
|
-
"Simulated lost execution response; inspect the existing execution",
|
|
186
|
-
0
|
|
187
|
-
);
|
|
206
|
+
setStatus(id, "NEEDS_RECONCILIATION", "checkout_outcome_unknown");
|
|
207
|
+
throw new ContoError("Network request failed", { code: "NETWORK_ERROR", status: 0 });
|
|
188
208
|
}
|
|
189
209
|
return {
|
|
190
|
-
...setStatus(id, "
|
|
210
|
+
...setStatus(id, "PROCESSING", "checkout_submitted", {
|
|
211
|
+
checkoutReference: id.replace(/^sim_link_/, "sim_checkout_")
|
|
212
|
+
}),
|
|
191
213
|
checkout: {
|
|
192
|
-
submitted:
|
|
193
|
-
|
|
194
|
-
|
|
214
|
+
submitted: true,
|
|
215
|
+
deliveryMode: "SERVER_ONLY_ARTIFACT",
|
|
216
|
+
accepted: true
|
|
195
217
|
}
|
|
196
218
|
};
|
|
197
219
|
},
|
|
198
220
|
async cancelCommerceExecution(id) {
|
|
199
221
|
if (["PROCESSING", "NEEDS_RECONCILIATION", "COMPLETED", "SETTLED"].includes(read(id).status)) {
|
|
200
|
-
return
|
|
201
|
-
"SIMULATED_CANCELLATION_UNAVAILABLE",
|
|
202
|
-
"This simulated execution must be reconciled and cannot be cancelled"
|
|
203
|
-
);
|
|
222
|
+
return liveApiError("CONFLICT");
|
|
204
223
|
}
|
|
205
|
-
return setStatus(id, "CANCELLED");
|
|
224
|
+
return setStatus(id, "CANCELLED", "canceled");
|
|
206
225
|
}
|
|
207
226
|
};
|
|
208
227
|
const client = new LinkClient(transport, {
|
|
@@ -217,23 +236,25 @@ function createLinkTestClient(options = {}) {
|
|
|
217
236
|
throw Object.assign(safe, { mode: "SIMULATED", settlementVerified: false });
|
|
218
237
|
}
|
|
219
238
|
};
|
|
239
|
+
const payment = (operation) => marked(operation);
|
|
220
240
|
return {
|
|
221
241
|
mode: "SIMULATED",
|
|
222
242
|
link: {
|
|
223
243
|
setup: (input) => marked(() => client.setup(input)),
|
|
224
244
|
paymentMethods: () => marked(() => client.paymentMethods()),
|
|
225
|
-
pay: (input) =>
|
|
226
|
-
resume: (id) =>
|
|
227
|
-
status: (id) =>
|
|
228
|
-
cancel: (id) =>
|
|
245
|
+
pay: (input) => payment(() => client.pay(input)),
|
|
246
|
+
resume: (id) => payment(() => client.resume(id)),
|
|
247
|
+
status: (id) => payment(() => client.status(id)),
|
|
248
|
+
cancel: (id) => payment(() => client.cancel(id)),
|
|
229
249
|
/** Test-only owner action. Never available on the live Link client. */
|
|
230
|
-
approve: (id) =>
|
|
250
|
+
approve: (id) => payment(async () => {
|
|
231
251
|
if (read(id).status !== "AWAITING_APPROVAL")
|
|
232
252
|
return fail(
|
|
233
253
|
"SIMULATED_APPROVAL_UNAVAILABLE",
|
|
234
254
|
"This simulation is not awaiting approval"
|
|
235
255
|
);
|
|
236
|
-
|
|
256
|
+
if (scenario === "expired") setStatus(id, "EXPIRED", "expired");
|
|
257
|
+
else setStatus(id, "READY", "approved");
|
|
237
258
|
return client.status(id);
|
|
238
259
|
})
|
|
239
260
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conto_finance/sdk",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.2",
|
|
4
4
|
"description": "Conto SDK for AI agent payment authorization",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"dev": "tsup --watch",
|
|
48
48
|
"lint": "tsc --noEmit",
|
|
49
49
|
"test": "npm run lint && npm run test:contract && npm run test:consumer",
|
|
50
|
-
"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 test/link.test.mjs test/link-testing.test.mjs",
|
|
50
|
+
"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 test/link.test.mjs test/link-testing.test.mjs test/checkout.test.mjs",
|
|
51
51
|
"test:consumer": "npm run build && node --test test/consumer.test.mjs",
|
|
52
52
|
"verify:release": "node scripts/verify-release.mjs",
|
|
53
53
|
"prepublishOnly": "npm run verify:release && npm run lint && npm run test:contract && npm run test:consumer"
|