@stripe/link-sdk 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/client.d.ts +4 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +749 -373
- package/dist/index.js.map +1 -1
- package/dist/resources/__tests__/approval-policy.test.d.ts +2 -0
- package/dist/resources/__tests__/approval-policy.test.d.ts.map +1 -0
- package/dist/resources/__tests__/credentials.test.d.ts +2 -0
- package/dist/resources/__tests__/credentials.test.d.ts.map +1 -0
- package/dist/resources/__tests__/holder-jwk.test.d.ts +2 -0
- package/dist/resources/__tests__/holder-jwk.test.d.ts.map +1 -0
- package/dist/resources/__tests__/ucp.test.d.ts +2 -0
- package/dist/resources/__tests__/ucp.test.d.ts.map +1 -0
- package/dist/resources/approval-policy.d.ts +9 -0
- package/dist/resources/approval-policy.d.ts.map +1 -0
- package/dist/resources/base.d.ts +1 -0
- package/dist/resources/base.d.ts.map +1 -1
- package/dist/resources/holder-jwk.d.ts +11 -0
- package/dist/resources/holder-jwk.d.ts.map +1 -0
- package/dist/resources/identity-credentials.d.ts +9 -0
- package/dist/resources/identity-credentials.d.ts.map +1 -0
- package/dist/resources/interfaces.d.ts +75 -8
- package/dist/resources/interfaces.d.ts.map +1 -1
- package/dist/resources/payment-methods.d.ts +3 -1
- package/dist/resources/payment-methods.d.ts.map +1 -1
- package/dist/resources/ucp.d.ts +25 -0
- package/dist/resources/ucp.d.ts.map +1 -0
- package/dist/resources/user-info.d.ts.map +1 -1
- package/dist/types/index.d.ts +132 -48
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/resources/__tests__/summaries.test.d.ts +0 -2
- package/dist/resources/__tests__/summaries.test.d.ts.map +0 -1
- package/dist/resources/summaries.d.ts +0 -10
- package/dist/resources/summaries.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// src/resources/
|
|
2
|
-
import { createHash as createHash2 } from "crypto";
|
|
1
|
+
// src/resources/approval-policy.ts
|
|
3
2
|
import { z } from "zod";
|
|
4
3
|
|
|
5
4
|
// src/errors.ts
|
|
@@ -45,6 +44,224 @@ var LinkApiError = class extends LinkSdkError {
|
|
|
45
44
|
}
|
|
46
45
|
};
|
|
47
46
|
|
|
47
|
+
// src/config.ts
|
|
48
|
+
var DEFAULT_API_BASE_URL = "https://api.link.com";
|
|
49
|
+
function createDefaultHeadersFetch(baseFetch, defaultHeaders) {
|
|
50
|
+
return ((input, init) => {
|
|
51
|
+
const headers = new Headers(init?.headers);
|
|
52
|
+
for (const [key, value] of Object.entries(defaultHeaders)) {
|
|
53
|
+
if (!headers.has(key)) {
|
|
54
|
+
headers.set(key, value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return baseFetch(input, { ...init, headers });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function createDefaultLogger() {
|
|
61
|
+
return {
|
|
62
|
+
debug() {
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function resolveLinkSdkConfig(options, defaults = {}) {
|
|
67
|
+
const verbose = options.verbose ?? false;
|
|
68
|
+
const logger = options.logger ?? createDefaultLogger();
|
|
69
|
+
if (options.accessToken !== void 0 && options.getAccessToken !== void 0) {
|
|
70
|
+
throw new LinkConfigurationError(
|
|
71
|
+
"Pass either `accessToken` or `getAccessToken`, not both."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
let getAccessToken;
|
|
75
|
+
let canRefreshAccessToken;
|
|
76
|
+
if (options.accessToken !== void 0) {
|
|
77
|
+
if (options.accessToken.trim().length === 0) {
|
|
78
|
+
throw new LinkConfigurationError("`accessToken` cannot be empty.");
|
|
79
|
+
}
|
|
80
|
+
const accessToken = options.accessToken;
|
|
81
|
+
getAccessToken = () => accessToken;
|
|
82
|
+
canRefreshAccessToken = false;
|
|
83
|
+
} else if (typeof options.getAccessToken === "function") {
|
|
84
|
+
getAccessToken = options.getAccessToken;
|
|
85
|
+
canRefreshAccessToken = true;
|
|
86
|
+
} else {
|
|
87
|
+
throw new LinkConfigurationError(
|
|
88
|
+
"Pass `accessToken` or `getAccessToken` to the Link client."
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const apiBaseUrl = options.apiBaseUrl ?? defaults.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
92
|
+
const spendRequestBaseUrl = options.spendRequestBaseUrl ?? defaults.spendRequestBaseUrl ?? apiBaseUrl;
|
|
93
|
+
const baseFetch = options.fetch ?? globalThis.fetch;
|
|
94
|
+
const effectiveFetch = options.defaultHeaders && Object.keys(options.defaultHeaders).length > 0 ? createDefaultHeadersFetch(baseFetch, options.defaultHeaders) : baseFetch;
|
|
95
|
+
return {
|
|
96
|
+
verbose,
|
|
97
|
+
getAccessToken,
|
|
98
|
+
canRefreshAccessToken,
|
|
99
|
+
fetch: effectiveFetch,
|
|
100
|
+
apiBaseUrl,
|
|
101
|
+
spendRequestBaseUrl,
|
|
102
|
+
logger
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function requireFetchImplementation(config) {
|
|
106
|
+
if (!config.fetch) {
|
|
107
|
+
throw new LinkConfigurationError(
|
|
108
|
+
"No fetch implementation available. Pass `fetch` in Link SDK options."
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return config.fetch;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/resources/base.ts
|
|
115
|
+
function isRecord(value) {
|
|
116
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
117
|
+
}
|
|
118
|
+
function extractErrorMessage(data, rawBody) {
|
|
119
|
+
if (isRecord(data)) {
|
|
120
|
+
if (typeof data.error === "string") {
|
|
121
|
+
return data.error;
|
|
122
|
+
}
|
|
123
|
+
if (isRecord(data.error)) {
|
|
124
|
+
const nested = data.error;
|
|
125
|
+
if (typeof nested.message === "string") {
|
|
126
|
+
return nested.message;
|
|
127
|
+
}
|
|
128
|
+
if (typeof nested.code === "string") {
|
|
129
|
+
return nested.code;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (typeof data.message === "string") {
|
|
133
|
+
return data.message;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return rawBody || "unknown error";
|
|
137
|
+
}
|
|
138
|
+
var BaseResource = class {
|
|
139
|
+
verbose;
|
|
140
|
+
getAccessToken;
|
|
141
|
+
canRefreshAccessToken;
|
|
142
|
+
fetchImpl;
|
|
143
|
+
endpoint;
|
|
144
|
+
logger;
|
|
145
|
+
constructor(options, endpointPath, base = "api") {
|
|
146
|
+
const config = resolveLinkSdkConfig(options);
|
|
147
|
+
this.verbose = config.verbose;
|
|
148
|
+
this.getAccessToken = config.getAccessToken;
|
|
149
|
+
this.canRefreshAccessToken = config.canRefreshAccessToken;
|
|
150
|
+
this.fetchImpl = requireFetchImplementation(config);
|
|
151
|
+
const baseUrl = base === "spend" ? config.spendRequestBaseUrl : config.apiBaseUrl;
|
|
152
|
+
this.endpoint = `${baseUrl}${endpointPath}`;
|
|
153
|
+
this.logger = config.logger;
|
|
154
|
+
}
|
|
155
|
+
async rawFetch(opts) {
|
|
156
|
+
if (this.verbose) {
|
|
157
|
+
this.logger.debug(`> ${opts.method} ${opts.url}`);
|
|
158
|
+
}
|
|
159
|
+
let response;
|
|
160
|
+
try {
|
|
161
|
+
const init = {
|
|
162
|
+
method: opts.method,
|
|
163
|
+
...opts.headers !== void 0 && { headers: opts.headers },
|
|
164
|
+
...opts.body !== void 0 && { body: opts.body },
|
|
165
|
+
...opts.signal !== void 0 && { signal: opts.signal },
|
|
166
|
+
...opts.redirect !== void 0 && { redirect: opts.redirect }
|
|
167
|
+
};
|
|
168
|
+
response = await this.fetchImpl(opts.url, init);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
throw new LinkTransportError(
|
|
171
|
+
`Request failed: ${opts.method} ${opts.url}`,
|
|
172
|
+
{ cause: error }
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const rawBody = await response.text();
|
|
176
|
+
let data = null;
|
|
177
|
+
try {
|
|
178
|
+
data = JSON.parse(rawBody);
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
if (this.verbose) {
|
|
182
|
+
this.logger.debug(`< ${response.status} ${response.statusText}`);
|
|
183
|
+
}
|
|
184
|
+
return { status: response.status, data, rawBody };
|
|
185
|
+
}
|
|
186
|
+
async apiFetch(opts) {
|
|
187
|
+
const token = await this.getAccessToken();
|
|
188
|
+
const authedOpts = {
|
|
189
|
+
...opts,
|
|
190
|
+
headers: {
|
|
191
|
+
...opts.headers,
|
|
192
|
+
Authorization: `Bearer ${token}`
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const res = await this.rawFetch(authedOpts);
|
|
196
|
+
if (res.status === 401 && this.canRefreshAccessToken) {
|
|
197
|
+
const refreshedToken = await this.getAccessToken({ forceRefresh: true });
|
|
198
|
+
return this.rawFetch({
|
|
199
|
+
...opts,
|
|
200
|
+
headers: {
|
|
201
|
+
...opts.headers,
|
|
202
|
+
Authorization: `Bearer ${refreshedToken}`
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return res;
|
|
207
|
+
}
|
|
208
|
+
throwApiError(operation, status, data, rawBody, cause) {
|
|
209
|
+
const msg = extractErrorMessage(data, rawBody);
|
|
210
|
+
throw new LinkApiError(`Failed to ${operation} (${status}): ${msg}`, {
|
|
211
|
+
status,
|
|
212
|
+
rawBody,
|
|
213
|
+
details: data,
|
|
214
|
+
cause
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
parseResponse(operation, status, parser) {
|
|
218
|
+
try {
|
|
219
|
+
return parser();
|
|
220
|
+
} catch (error) {
|
|
221
|
+
throw new LinkResponseError(operation, status, { cause: error });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// src/resources/approval-policy.ts
|
|
227
|
+
var approvalPolicySchema = z.looseObject({
|
|
228
|
+
rules: z.array(
|
|
229
|
+
z.looseObject({
|
|
230
|
+
action: z.string(),
|
|
231
|
+
limits: z.looseObject({
|
|
232
|
+
per_purchase: z.looseObject({
|
|
233
|
+
amount: z.number().int(),
|
|
234
|
+
currency: z.string()
|
|
235
|
+
})
|
|
236
|
+
}),
|
|
237
|
+
allowed_payment_methods: z.array(z.string()).optional()
|
|
238
|
+
})
|
|
239
|
+
)
|
|
240
|
+
});
|
|
241
|
+
var ApprovalPolicyResource = class extends BaseResource {
|
|
242
|
+
constructor(options) {
|
|
243
|
+
super(options, "/approval-policy");
|
|
244
|
+
}
|
|
245
|
+
async retrieve() {
|
|
246
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
247
|
+
method: "GET",
|
|
248
|
+
url: this.endpoint
|
|
249
|
+
});
|
|
250
|
+
if (status < 200 || status >= 300) {
|
|
251
|
+
this.throwApiError("retrieve approval policy", status, data, rawBody);
|
|
252
|
+
}
|
|
253
|
+
return this.parseResponse(
|
|
254
|
+
"retrieve approval policy",
|
|
255
|
+
status,
|
|
256
|
+
() => approvalPolicySchema.parse(data)
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/resources/attestations.ts
|
|
262
|
+
import { createHash as createHash2 } from "crypto";
|
|
263
|
+
import { z as z2 } from "zod";
|
|
264
|
+
|
|
48
265
|
// src/resources/attestations-crypto.ts
|
|
49
266
|
import {
|
|
50
267
|
createHash,
|
|
@@ -385,196 +602,23 @@ function computeChallengeDigest(tokenType, issuerName) {
|
|
|
385
602
|
}
|
|
386
603
|
const challenge = Buffer.alloc(
|
|
387
604
|
2 + 2 + issuerBytes.length + 1 + 2 + originBytes.length
|
|
388
|
-
);
|
|
389
|
-
let offset = 0;
|
|
390
|
-
challenge.writeUInt16BE(tokenType, offset);
|
|
391
|
-
offset += 2;
|
|
392
|
-
challenge.writeUInt16BE(issuerBytes.length, offset);
|
|
393
|
-
offset += 2;
|
|
394
|
-
issuerBytes.copy(challenge, offset);
|
|
395
|
-
offset += issuerBytes.length;
|
|
396
|
-
challenge.writeUInt8(0, offset);
|
|
397
|
-
offset += 1;
|
|
398
|
-
challenge.writeUInt16BE(originBytes.length, offset);
|
|
399
|
-
offset += 2;
|
|
400
|
-
if (originBytes.length > 0) {
|
|
401
|
-
originBytes.copy(challenge, offset);
|
|
402
|
-
}
|
|
403
|
-
return new Uint8Array(createHash("sha256").update(challenge).digest());
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// src/config.ts
|
|
407
|
-
var DEFAULT_API_BASE_URL = "https://api.link.com";
|
|
408
|
-
function createDefaultHeadersFetch(baseFetch, defaultHeaders) {
|
|
409
|
-
return ((input, init) => {
|
|
410
|
-
const headers = new Headers(init?.headers);
|
|
411
|
-
for (const [key, value] of Object.entries(defaultHeaders)) {
|
|
412
|
-
if (!headers.has(key)) {
|
|
413
|
-
headers.set(key, value);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
return baseFetch(input, { ...init, headers });
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
function createDefaultLogger() {
|
|
420
|
-
return {
|
|
421
|
-
debug() {
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
function resolveLinkSdkConfig(options, defaults = {}) {
|
|
426
|
-
const verbose = options.verbose ?? false;
|
|
427
|
-
const logger = options.logger ?? createDefaultLogger();
|
|
428
|
-
if (options.accessToken !== void 0 && options.getAccessToken !== void 0) {
|
|
429
|
-
throw new LinkConfigurationError(
|
|
430
|
-
"Pass either `accessToken` or `getAccessToken`, not both."
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
let getAccessToken;
|
|
434
|
-
let canRefreshAccessToken;
|
|
435
|
-
if (options.accessToken !== void 0) {
|
|
436
|
-
if (options.accessToken.trim().length === 0) {
|
|
437
|
-
throw new LinkConfigurationError("`accessToken` cannot be empty.");
|
|
438
|
-
}
|
|
439
|
-
const accessToken = options.accessToken;
|
|
440
|
-
getAccessToken = () => accessToken;
|
|
441
|
-
canRefreshAccessToken = false;
|
|
442
|
-
} else if (typeof options.getAccessToken === "function") {
|
|
443
|
-
getAccessToken = options.getAccessToken;
|
|
444
|
-
canRefreshAccessToken = true;
|
|
445
|
-
} else {
|
|
446
|
-
throw new LinkConfigurationError(
|
|
447
|
-
"Pass `accessToken` or `getAccessToken` to the Link client."
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
const apiBaseUrl = options.apiBaseUrl ?? defaults.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
451
|
-
const spendRequestBaseUrl = options.spendRequestBaseUrl ?? defaults.spendRequestBaseUrl ?? apiBaseUrl;
|
|
452
|
-
const baseFetch = options.fetch ?? globalThis.fetch;
|
|
453
|
-
const effectiveFetch = options.defaultHeaders && Object.keys(options.defaultHeaders).length > 0 ? createDefaultHeadersFetch(baseFetch, options.defaultHeaders) : baseFetch;
|
|
454
|
-
return {
|
|
455
|
-
verbose,
|
|
456
|
-
getAccessToken,
|
|
457
|
-
canRefreshAccessToken,
|
|
458
|
-
fetch: effectiveFetch,
|
|
459
|
-
apiBaseUrl,
|
|
460
|
-
spendRequestBaseUrl,
|
|
461
|
-
logger
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
function requireFetchImplementation(config) {
|
|
465
|
-
if (!config.fetch) {
|
|
466
|
-
throw new LinkConfigurationError(
|
|
467
|
-
"No fetch implementation available. Pass `fetch` in Link SDK options."
|
|
468
|
-
);
|
|
469
|
-
}
|
|
470
|
-
return config.fetch;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// src/resources/base.ts
|
|
474
|
-
function isRecord(value) {
|
|
475
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
476
|
-
}
|
|
477
|
-
function extractErrorMessage(data, rawBody) {
|
|
478
|
-
if (isRecord(data)) {
|
|
479
|
-
if (typeof data.error === "string") {
|
|
480
|
-
return data.error;
|
|
481
|
-
}
|
|
482
|
-
if (isRecord(data.error)) {
|
|
483
|
-
const nested = data.error;
|
|
484
|
-
if (typeof nested.message === "string") {
|
|
485
|
-
return nested.message;
|
|
486
|
-
}
|
|
487
|
-
if (typeof nested.code === "string") {
|
|
488
|
-
return nested.code;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
if (typeof data.message === "string") {
|
|
492
|
-
return data.message;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
return rawBody || "unknown error";
|
|
496
|
-
}
|
|
497
|
-
var BaseResource = class {
|
|
498
|
-
verbose;
|
|
499
|
-
getAccessToken;
|
|
500
|
-
canRefreshAccessToken;
|
|
501
|
-
fetchImpl;
|
|
502
|
-
endpoint;
|
|
503
|
-
logger;
|
|
504
|
-
constructor(options, endpointPath, base = "api") {
|
|
505
|
-
const config = resolveLinkSdkConfig(options);
|
|
506
|
-
this.verbose = config.verbose;
|
|
507
|
-
this.getAccessToken = config.getAccessToken;
|
|
508
|
-
this.canRefreshAccessToken = config.canRefreshAccessToken;
|
|
509
|
-
this.fetchImpl = requireFetchImplementation(config);
|
|
510
|
-
const baseUrl = base === "spend" ? config.spendRequestBaseUrl : config.apiBaseUrl;
|
|
511
|
-
this.endpoint = `${baseUrl}${endpointPath}`;
|
|
512
|
-
this.logger = config.logger;
|
|
513
|
-
}
|
|
514
|
-
async rawFetch(opts) {
|
|
515
|
-
if (this.verbose) {
|
|
516
|
-
this.logger.debug(`> ${opts.method} ${opts.url}`);
|
|
517
|
-
}
|
|
518
|
-
let response;
|
|
519
|
-
try {
|
|
520
|
-
const init = {
|
|
521
|
-
method: opts.method,
|
|
522
|
-
...opts.headers !== void 0 && { headers: opts.headers },
|
|
523
|
-
...opts.body !== void 0 && { body: opts.body },
|
|
524
|
-
...opts.signal !== void 0 && { signal: opts.signal }
|
|
525
|
-
};
|
|
526
|
-
response = await this.fetchImpl(opts.url, init);
|
|
527
|
-
} catch (error) {
|
|
528
|
-
throw new LinkTransportError(
|
|
529
|
-
`Request failed: ${opts.method} ${opts.url}`,
|
|
530
|
-
{ cause: error }
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
const rawBody = await response.text();
|
|
534
|
-
let data = null;
|
|
535
|
-
try {
|
|
536
|
-
data = JSON.parse(rawBody);
|
|
537
|
-
} catch {
|
|
538
|
-
}
|
|
539
|
-
if (this.verbose) {
|
|
540
|
-
this.logger.debug(`< ${response.status} ${response.statusText}`);
|
|
541
|
-
}
|
|
542
|
-
return { status: response.status, data, rawBody };
|
|
543
|
-
}
|
|
544
|
-
async apiFetch(opts) {
|
|
545
|
-
const token = await this.getAccessToken();
|
|
546
|
-
const authedOpts = {
|
|
547
|
-
...opts,
|
|
548
|
-
headers: {
|
|
549
|
-
...opts.headers,
|
|
550
|
-
Authorization: `Bearer ${token}`
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
const res = await this.rawFetch(authedOpts);
|
|
554
|
-
if (res.status === 401 && this.canRefreshAccessToken) {
|
|
555
|
-
const refreshedToken = await this.getAccessToken({ forceRefresh: true });
|
|
556
|
-
authedOpts.headers.Authorization = `Bearer ${refreshedToken}`;
|
|
557
|
-
return this.rawFetch(authedOpts);
|
|
558
|
-
}
|
|
559
|
-
return res;
|
|
560
|
-
}
|
|
561
|
-
throwApiError(operation, status, data, rawBody, cause) {
|
|
562
|
-
const msg = extractErrorMessage(data, rawBody);
|
|
563
|
-
throw new LinkApiError(`Failed to ${operation} (${status}): ${msg}`, {
|
|
564
|
-
status,
|
|
565
|
-
rawBody,
|
|
566
|
-
details: data,
|
|
567
|
-
cause
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
parseResponse(operation, status, parser) {
|
|
571
|
-
try {
|
|
572
|
-
return parser();
|
|
573
|
-
} catch (error) {
|
|
574
|
-
throw new LinkResponseError(operation, status, { cause: error });
|
|
575
|
-
}
|
|
605
|
+
);
|
|
606
|
+
let offset = 0;
|
|
607
|
+
challenge.writeUInt16BE(tokenType, offset);
|
|
608
|
+
offset += 2;
|
|
609
|
+
challenge.writeUInt16BE(issuerBytes.length, offset);
|
|
610
|
+
offset += 2;
|
|
611
|
+
issuerBytes.copy(challenge, offset);
|
|
612
|
+
offset += issuerBytes.length;
|
|
613
|
+
challenge.writeUInt8(0, offset);
|
|
614
|
+
offset += 1;
|
|
615
|
+
challenge.writeUInt16BE(originBytes.length, offset);
|
|
616
|
+
offset += 2;
|
|
617
|
+
if (originBytes.length > 0) {
|
|
618
|
+
originBytes.copy(challenge, offset);
|
|
576
619
|
}
|
|
577
|
-
|
|
620
|
+
return new Uint8Array(createHash("sha256").update(challenge).digest());
|
|
621
|
+
}
|
|
578
622
|
|
|
579
623
|
// src/resources/attestations.ts
|
|
580
624
|
var TOKEN_TYPE_BLIND_RSA = 2;
|
|
@@ -583,17 +627,17 @@ var CONTENT_TYPE_TOKEN_RESPONSE = "application/private-token-generic-batch-respo
|
|
|
583
627
|
var LINK_ISSUER = "https://api.link.com";
|
|
584
628
|
var LINK_ISSUER_HOSTNAME = "api.link.com";
|
|
585
629
|
var LINK_ISSUER_METADATA_URL = `${LINK_ISSUER}/.well-known/aap-issuer`;
|
|
586
|
-
var issuerMetadataSchema =
|
|
587
|
-
issuer:
|
|
588
|
-
token_issuance_endpoint:
|
|
589
|
-
token_keys:
|
|
630
|
+
var issuerMetadataSchema = z2.looseObject({
|
|
631
|
+
issuer: z2.literal(LINK_ISSUER),
|
|
632
|
+
token_issuance_endpoint: z2.string(),
|
|
633
|
+
token_keys: z2.string()
|
|
590
634
|
});
|
|
591
|
-
var tokenKeyDirectorySchema =
|
|
592
|
-
"token-keys":
|
|
593
|
-
|
|
594
|
-
"token-type":
|
|
595
|
-
"token-key":
|
|
596
|
-
"not-before":
|
|
635
|
+
var tokenKeyDirectorySchema = z2.looseObject({
|
|
636
|
+
"token-keys": z2.array(
|
|
637
|
+
z2.looseObject({
|
|
638
|
+
"token-type": z2.number(),
|
|
639
|
+
"token-key": z2.string(),
|
|
640
|
+
"not-before": z2.number().int().nonnegative().optional()
|
|
597
641
|
})
|
|
598
642
|
)
|
|
599
643
|
});
|
|
@@ -906,20 +950,20 @@ var AttestationsResource = class extends BaseResource {
|
|
|
906
950
|
};
|
|
907
951
|
|
|
908
952
|
// src/resources/balances.ts
|
|
909
|
-
import { z as
|
|
910
|
-
var currencyAmountsSchema =
|
|
911
|
-
var balanceSchema =
|
|
912
|
-
source_id:
|
|
913
|
-
type:
|
|
914
|
-
cash:
|
|
915
|
-
credit:
|
|
916
|
-
current:
|
|
917
|
-
currency:
|
|
918
|
-
as_of:
|
|
953
|
+
import { z as z3 } from "zod";
|
|
954
|
+
var currencyAmountsSchema = z3.record(z3.string(), z3.number());
|
|
955
|
+
var balanceSchema = z3.looseObject({
|
|
956
|
+
source_id: z3.string(),
|
|
957
|
+
type: z3.enum(["cash", "credit"]),
|
|
958
|
+
cash: z3.looseObject({ available: currencyAmountsSchema }).nullable().optional(),
|
|
959
|
+
credit: z3.looseObject({ used: currencyAmountsSchema.nullable() }).nullable().optional(),
|
|
960
|
+
current: z3.number(),
|
|
961
|
+
currency: z3.string(),
|
|
962
|
+
as_of: z3.string()
|
|
919
963
|
});
|
|
920
|
-
var balancesPageSchema =
|
|
921
|
-
data:
|
|
922
|
-
has_more:
|
|
964
|
+
var balancesPageSchema = z3.looseObject({
|
|
965
|
+
data: z3.array(balanceSchema),
|
|
966
|
+
has_more: z3.boolean().optional()
|
|
923
967
|
});
|
|
924
968
|
var BalancesResource = class extends BaseResource {
|
|
925
969
|
constructor(options) {
|
|
@@ -959,17 +1003,150 @@ var BalancesResource = class extends BaseResource {
|
|
|
959
1003
|
}
|
|
960
1004
|
};
|
|
961
1005
|
|
|
1006
|
+
// src/resources/identity-credentials.ts
|
|
1007
|
+
import { z as z4 } from "zod";
|
|
1008
|
+
|
|
1009
|
+
// src/resources/holder-jwk.ts
|
|
1010
|
+
import { createHash as createHash3 } from "crypto";
|
|
1011
|
+
function isRecord2(value) {
|
|
1012
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1013
|
+
}
|
|
1014
|
+
function holderJwkThumbprint(jwk) {
|
|
1015
|
+
const canonical = JSON.stringify({
|
|
1016
|
+
crv: jwk.crv,
|
|
1017
|
+
kty: jwk.kty,
|
|
1018
|
+
x: jwk.x
|
|
1019
|
+
});
|
|
1020
|
+
return createHash3("sha256").update(canonical).digest("base64url");
|
|
1021
|
+
}
|
|
1022
|
+
function holderJwksEqual(left, right) {
|
|
1023
|
+
return holderJwkThumbprint(left) === holderJwkThumbprint(right);
|
|
1024
|
+
}
|
|
1025
|
+
function parseHolderPublicJwk(value) {
|
|
1026
|
+
if (!isRecord2(value)) {
|
|
1027
|
+
throw new LinkConfigurationError("Holder public key must be a JWK object");
|
|
1028
|
+
}
|
|
1029
|
+
if (typeof value.kty !== "string") {
|
|
1030
|
+
throw new LinkConfigurationError("Holder public key is missing kty");
|
|
1031
|
+
}
|
|
1032
|
+
if (value.kty !== "OKP" || value.crv !== "Ed25519" || typeof value.x !== "string" || value.x.length === 0) {
|
|
1033
|
+
throw new LinkConfigurationError(
|
|
1034
|
+
"Holder public key must be an Ed25519 OKP JWK with an x member"
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
return { kty: "OKP", crv: "Ed25519", x: value.x };
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// src/resources/identity-credentials.ts
|
|
1041
|
+
var LINK_ISSUER2 = "https://api.link.com";
|
|
1042
|
+
var LINK_ISSUER_METADATA_URL2 = `${LINK_ISSUER2}/.well-known/aap-issuer`;
|
|
1043
|
+
var identityCredentialIssuerMetadataSchema = z4.looseObject({
|
|
1044
|
+
issuer: z4.literal(LINK_ISSUER2),
|
|
1045
|
+
credential_endpoint: z4.string()
|
|
1046
|
+
});
|
|
1047
|
+
var issueIdentityCredentialResponseSchema = z4.looseObject({
|
|
1048
|
+
credential: z4.string(),
|
|
1049
|
+
issuer: z4.literal(LINK_ISSUER2),
|
|
1050
|
+
expires_at: z4.string()
|
|
1051
|
+
});
|
|
1052
|
+
function requireLinkEndpoint2(value, field) {
|
|
1053
|
+
let url;
|
|
1054
|
+
try {
|
|
1055
|
+
url = new URL(value);
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
throw new TypeError(`${field} is not a valid URL`, { cause: error });
|
|
1058
|
+
}
|
|
1059
|
+
if (url.protocol !== "https:" || url.origin !== LINK_ISSUER2 || url.username || url.password) {
|
|
1060
|
+
throw new TypeError(`${field} must be an HTTPS URL on ${LINK_ISSUER2}`);
|
|
1061
|
+
}
|
|
1062
|
+
return url.href;
|
|
1063
|
+
}
|
|
1064
|
+
var IdentityCredentialsResource = class extends BaseResource {
|
|
1065
|
+
constructor(options) {
|
|
1066
|
+
super(options, "");
|
|
1067
|
+
}
|
|
1068
|
+
async discoverCredentialEndpoint() {
|
|
1069
|
+
const { status, data, rawBody } = await this.rawFetch({
|
|
1070
|
+
method: "GET",
|
|
1071
|
+
url: LINK_ISSUER_METADATA_URL2,
|
|
1072
|
+
redirect: "manual"
|
|
1073
|
+
});
|
|
1074
|
+
if (status >= 300 && status < 400) {
|
|
1075
|
+
throw new LinkApiError(
|
|
1076
|
+
`Refused redirect while fetching identity credential issuer metadata (${status})`,
|
|
1077
|
+
{ status, rawBody }
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
if (status < 200 || status >= 300) {
|
|
1081
|
+
this.throwApiError(
|
|
1082
|
+
"fetch identity credential issuer metadata",
|
|
1083
|
+
status,
|
|
1084
|
+
data,
|
|
1085
|
+
rawBody
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
const metadata = this.parseResponse(
|
|
1089
|
+
"parse identity credential issuer metadata",
|
|
1090
|
+
status,
|
|
1091
|
+
() => identityCredentialIssuerMetadataSchema.parse(data)
|
|
1092
|
+
);
|
|
1093
|
+
return this.parseResponse(
|
|
1094
|
+
"validate identity credential issuer metadata",
|
|
1095
|
+
status,
|
|
1096
|
+
() => requireLinkEndpoint2(
|
|
1097
|
+
metadata.credential_endpoint,
|
|
1098
|
+
"credential_endpoint"
|
|
1099
|
+
)
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
async issue(params) {
|
|
1103
|
+
const publicJwk = parseHolderPublicJwk(params.cnf.jwk);
|
|
1104
|
+
const endpoint = await this.discoverCredentialEndpoint();
|
|
1105
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
1106
|
+
method: "POST",
|
|
1107
|
+
url: endpoint,
|
|
1108
|
+
redirect: "manual",
|
|
1109
|
+
headers: {
|
|
1110
|
+
"Content-Type": "application/json",
|
|
1111
|
+
Accept: "application/json"
|
|
1112
|
+
},
|
|
1113
|
+
body: JSON.stringify({ cnf: { jwk: publicJwk } })
|
|
1114
|
+
});
|
|
1115
|
+
if (status >= 300 && status < 400) {
|
|
1116
|
+
throw new LinkApiError(
|
|
1117
|
+
`Refused redirect while issuing identity credential (${status})`,
|
|
1118
|
+
{ status, rawBody }
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
if (status < 200 || status >= 300) {
|
|
1122
|
+
this.throwApiError("issue identity credential", status, data, rawBody);
|
|
1123
|
+
}
|
|
1124
|
+
return this.parseResponse(
|
|
1125
|
+
"issue identity credential",
|
|
1126
|
+
status,
|
|
1127
|
+
() => issueIdentityCredentialResponseSchema.parse(data)
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
|
|
962
1132
|
// src/resources/payment-methods.ts
|
|
963
|
-
import { z as
|
|
964
|
-
var
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
nickname: z3.optional(z3.string().nullable())
|
|
1133
|
+
import { z as z5 } from "zod";
|
|
1134
|
+
var balanceDetailsSchema = z5.looseObject({
|
|
1135
|
+
available_balance: z5.looseObject({
|
|
1136
|
+
amount: z5.number().int(),
|
|
1137
|
+
currency: z5.string()
|
|
1138
|
+
}).optional()
|
|
970
1139
|
});
|
|
971
|
-
var
|
|
972
|
-
|
|
1140
|
+
var paymentMethodSchema = z5.looseObject({
|
|
1141
|
+
id: z5.string(),
|
|
1142
|
+
type: z5.string(),
|
|
1143
|
+
is_default: z5.boolean(),
|
|
1144
|
+
name: z5.string(),
|
|
1145
|
+
nickname: z5.optional(z5.string().nullable()),
|
|
1146
|
+
balance_details: balanceDetailsSchema.nullable().optional()
|
|
1147
|
+
});
|
|
1148
|
+
var paymentMethodsResponseSchema = z5.looseObject({
|
|
1149
|
+
payment_details: z5.array(paymentMethodSchema)
|
|
973
1150
|
});
|
|
974
1151
|
var PaymentMethodsResource = class extends BaseResource {
|
|
975
1152
|
constructor(options) {
|
|
@@ -989,17 +1166,48 @@ var PaymentMethodsResource = class extends BaseResource {
|
|
|
989
1166
|
() => paymentMethodsResponseSchema.parse(data).payment_details
|
|
990
1167
|
);
|
|
991
1168
|
}
|
|
1169
|
+
async retrieve(id) {
|
|
1170
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
1171
|
+
method: "GET",
|
|
1172
|
+
url: `${this.endpoint}/${encodeURIComponent(id)}`
|
|
1173
|
+
});
|
|
1174
|
+
if (status === 404) return null;
|
|
1175
|
+
if (status < 200 || status >= 300) {
|
|
1176
|
+
this.throwApiError("retrieve payment method", status, data, rawBody);
|
|
1177
|
+
}
|
|
1178
|
+
return this.parseResponse(
|
|
1179
|
+
"retrieve payment method",
|
|
1180
|
+
status,
|
|
1181
|
+
() => paymentMethodSchema.parse(data)
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
async update(id, params) {
|
|
1185
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
1186
|
+
method: "POST",
|
|
1187
|
+
url: `${this.endpoint}/${encodeURIComponent(id)}`,
|
|
1188
|
+
headers: { "Content-Type": "application/json" },
|
|
1189
|
+
body: JSON.stringify({ nickname: params.nickname })
|
|
1190
|
+
});
|
|
1191
|
+
if (status < 200 || status >= 300) {
|
|
1192
|
+
this.throwApiError("update payment method", status, data, rawBody);
|
|
1193
|
+
}
|
|
1194
|
+
return this.parseResponse(
|
|
1195
|
+
"update payment method",
|
|
1196
|
+
status,
|
|
1197
|
+
() => paymentMethodSchema.parse(data)
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
992
1200
|
};
|
|
993
1201
|
|
|
994
1202
|
// src/resources/report.ts
|
|
995
|
-
import { z as
|
|
996
|
-
var reportRecordSchema =
|
|
997
|
-
object:
|
|
998
|
-
created_at:
|
|
999
|
-
domain:
|
|
1000
|
-
outcome:
|
|
1001
|
-
spend_request_id:
|
|
1002
|
-
status:
|
|
1203
|
+
import { z as z6 } from "zod";
|
|
1204
|
+
var reportRecordSchema = z6.looseObject({
|
|
1205
|
+
object: z6.string(),
|
|
1206
|
+
created_at: z6.string(),
|
|
1207
|
+
domain: z6.string(),
|
|
1208
|
+
outcome: z6.string(),
|
|
1209
|
+
spend_request_id: z6.string(),
|
|
1210
|
+
status: z6.string()
|
|
1003
1211
|
});
|
|
1004
1212
|
var ReportResource = class extends BaseResource {
|
|
1005
1213
|
constructor(options) {
|
|
@@ -1024,15 +1232,15 @@ var ReportResource = class extends BaseResource {
|
|
|
1024
1232
|
};
|
|
1025
1233
|
|
|
1026
1234
|
// src/resources/shipping-address.ts
|
|
1027
|
-
import { z as
|
|
1028
|
-
var shippingAddressSchema =
|
|
1029
|
-
id:
|
|
1030
|
-
is_default:
|
|
1031
|
-
nickname:
|
|
1032
|
-
address:
|
|
1235
|
+
import { z as z7 } from "zod";
|
|
1236
|
+
var shippingAddressSchema = z7.looseObject({
|
|
1237
|
+
id: z7.string(),
|
|
1238
|
+
is_default: z7.boolean(),
|
|
1239
|
+
nickname: z7.optional(z7.string().nullable()),
|
|
1240
|
+
address: z7.looseObject({}).nullable()
|
|
1033
1241
|
});
|
|
1034
|
-
var shippingAddressesResponseSchema =
|
|
1035
|
-
shipping_addresses:
|
|
1242
|
+
var shippingAddressesResponseSchema = z7.looseObject({
|
|
1243
|
+
shipping_addresses: z7.array(shippingAddressSchema)
|
|
1036
1244
|
});
|
|
1037
1245
|
var ShippingAddressResource = class extends BaseResource {
|
|
1038
1246
|
constructor(options) {
|
|
@@ -1055,21 +1263,21 @@ var ShippingAddressResource = class extends BaseResource {
|
|
|
1055
1263
|
};
|
|
1056
1264
|
|
|
1057
1265
|
// src/resources/sources.ts
|
|
1058
|
-
import { z as
|
|
1059
|
-
var nullableRecordSchema =
|
|
1060
|
-
var sourceSchema =
|
|
1061
|
-
id:
|
|
1062
|
-
name:
|
|
1063
|
-
type:
|
|
1266
|
+
import { z as z8 } from "zod";
|
|
1267
|
+
var nullableRecordSchema = z8.record(z8.string(), z8.unknown()).nullable().optional();
|
|
1268
|
+
var sourceSchema = z8.looseObject({
|
|
1269
|
+
id: z8.string().nullable().optional(),
|
|
1270
|
+
name: z8.string().nullable().optional(),
|
|
1271
|
+
type: z8.string().nullable().optional(),
|
|
1064
1272
|
capabilities: nullableRecordSchema,
|
|
1065
1273
|
external_connection: nullableRecordSchema,
|
|
1066
|
-
granted_actions:
|
|
1274
|
+
granted_actions: z8.array(z8.string()).nullable().optional(),
|
|
1067
1275
|
bank_account: nullableRecordSchema,
|
|
1068
1276
|
card: nullableRecordSchema
|
|
1069
1277
|
});
|
|
1070
|
-
var sourcesPageSchema =
|
|
1071
|
-
data:
|
|
1072
|
-
has_more:
|
|
1278
|
+
var sourcesPageSchema = z8.looseObject({
|
|
1279
|
+
data: z8.array(sourceSchema),
|
|
1280
|
+
has_more: z8.boolean().optional()
|
|
1073
1281
|
});
|
|
1074
1282
|
var SourcesResource = class extends BaseResource {
|
|
1075
1283
|
constructor(options) {
|
|
@@ -1105,34 +1313,34 @@ var SourcesResource = class extends BaseResource {
|
|
|
1105
1313
|
};
|
|
1106
1314
|
|
|
1107
1315
|
// src/resources/spend-request.ts
|
|
1108
|
-
import { z as
|
|
1109
|
-
var sharedPaymentTokenSchema =
|
|
1110
|
-
|
|
1111
|
-
|
|
1316
|
+
import { z as z9 } from "zod";
|
|
1317
|
+
var sharedPaymentTokenSchema = z9.union([
|
|
1318
|
+
z9.string().transform((id) => ({ id })),
|
|
1319
|
+
z9.looseObject({ id: z9.string() })
|
|
1112
1320
|
]);
|
|
1113
|
-
var spendRequestSchema =
|
|
1114
|
-
id:
|
|
1115
|
-
payment_details:
|
|
1116
|
-
status:
|
|
1117
|
-
line_items:
|
|
1118
|
-
totals:
|
|
1119
|
-
created_at:
|
|
1120
|
-
updated_at:
|
|
1321
|
+
var spendRequestSchema = z9.looseObject({
|
|
1322
|
+
id: z9.string(),
|
|
1323
|
+
payment_details: z9.string().optional(),
|
|
1324
|
+
status: z9.string(),
|
|
1325
|
+
line_items: z9.array(z9.unknown()).optional(),
|
|
1326
|
+
totals: z9.array(z9.unknown()).optional(),
|
|
1327
|
+
created_at: z9.string(),
|
|
1328
|
+
updated_at: z9.string(),
|
|
1121
1329
|
shared_payment_token: sharedPaymentTokenSchema.nullable().optional()
|
|
1122
1330
|
});
|
|
1123
|
-
var spendRequestsResponseSchema =
|
|
1124
|
-
data:
|
|
1331
|
+
var spendRequestsResponseSchema = z9.looseObject({
|
|
1332
|
+
data: z9.array(spendRequestSchema)
|
|
1125
1333
|
});
|
|
1126
|
-
var requestApprovalResponseSchema =
|
|
1127
|
-
id:
|
|
1128
|
-
approval_link:
|
|
1334
|
+
var requestApprovalResponseSchema = z9.looseObject({
|
|
1335
|
+
id: z9.string(),
|
|
1336
|
+
approval_link: z9.string()
|
|
1129
1337
|
}).transform(({ approval_link, ...response }) => ({
|
|
1130
1338
|
...response,
|
|
1131
1339
|
approval_url: approval_link
|
|
1132
1340
|
}));
|
|
1133
|
-
var duplicateSpendRequestErrorSchema =
|
|
1134
|
-
error:
|
|
1135
|
-
duplicate_spend_request:
|
|
1341
|
+
var duplicateSpendRequestErrorSchema = z9.looseObject({
|
|
1342
|
+
error: z9.looseObject({
|
|
1343
|
+
duplicate_spend_request: z9.unknown().optional()
|
|
1136
1344
|
})
|
|
1137
1345
|
});
|
|
1138
1346
|
function parseSpendRequest(value) {
|
|
@@ -1251,83 +1459,24 @@ var SpendRequestResource = class extends BaseResource {
|
|
|
1251
1459
|
}
|
|
1252
1460
|
};
|
|
1253
1461
|
|
|
1254
|
-
// src/resources/summaries.ts
|
|
1255
|
-
import { z as z8 } from "zod";
|
|
1256
|
-
var summaryValueSchema = z8.discriminatedUnion("unit", [
|
|
1257
|
-
z8.looseObject({ unit: z8.literal("count"), count: z8.number() }),
|
|
1258
|
-
z8.looseObject({
|
|
1259
|
-
unit: z8.literal("payment_volume"),
|
|
1260
|
-
amount: z8.number(),
|
|
1261
|
-
currency: z8.string()
|
|
1262
|
-
})
|
|
1263
|
-
]);
|
|
1264
|
-
var summaryDataValueSchema = z8.discriminatedUnion("unit", [
|
|
1265
|
-
z8.looseObject({ unit: z8.literal("count"), amount: z8.number() }),
|
|
1266
|
-
z8.looseObject({
|
|
1267
|
-
unit: z8.literal("payment_volume"),
|
|
1268
|
-
amount: z8.number(),
|
|
1269
|
-
currency: z8.string()
|
|
1270
|
-
})
|
|
1271
|
-
]);
|
|
1272
|
-
var summarySchema = z8.looseObject({
|
|
1273
|
-
id: z8.string(),
|
|
1274
|
-
description: z8.string(),
|
|
1275
|
-
created_at: z8.string().nullable().optional(),
|
|
1276
|
-
status: z8.enum(["ready", "pending", "no_data"]),
|
|
1277
|
-
entries: z8.array(z8.looseObject({ label: z8.string(), value: summaryValueSchema })).default([]),
|
|
1278
|
-
as_of: z8.number().optional(),
|
|
1279
|
-
data: z8.array(z8.looseObject({ label: z8.string(), value: summaryDataValueSchema })).nullable().optional()
|
|
1280
|
-
});
|
|
1281
|
-
var summariesPageSchema = z8.looseObject({
|
|
1282
|
-
data: z8.array(summarySchema),
|
|
1283
|
-
has_more: z8.boolean().optional()
|
|
1284
|
-
});
|
|
1285
|
-
var SummariesResource = class extends BaseResource {
|
|
1286
|
-
constructor(options) {
|
|
1287
|
-
super(options, "/summaries");
|
|
1288
|
-
}
|
|
1289
|
-
buildUrl(params) {
|
|
1290
|
-
const url = new URL(this.endpoint);
|
|
1291
|
-
if (params.starting_after !== void 0)
|
|
1292
|
-
url.searchParams.set("starting_after", params.starting_after);
|
|
1293
|
-
if (params.summaries !== void 0)
|
|
1294
|
-
for (const summary of params.summaries)
|
|
1295
|
-
url.searchParams.append("summaries[]", summary);
|
|
1296
|
-
return url.toString();
|
|
1297
|
-
}
|
|
1298
|
-
async list(params = {}) {
|
|
1299
|
-
const { status, data, rawBody } = await this.apiFetch({
|
|
1300
|
-
method: "GET",
|
|
1301
|
-
url: this.buildUrl(params)
|
|
1302
|
-
});
|
|
1303
|
-
if (status < 200 || status >= 300)
|
|
1304
|
-
this.throwApiError("list summaries", status, data, rawBody);
|
|
1305
|
-
return this.parseResponse(
|
|
1306
|
-
"list summaries",
|
|
1307
|
-
status,
|
|
1308
|
-
() => summariesPageSchema.parse(data)
|
|
1309
|
-
);
|
|
1310
|
-
}
|
|
1311
|
-
};
|
|
1312
|
-
|
|
1313
1462
|
// src/resources/transactions.ts
|
|
1314
|
-
import { z as
|
|
1315
|
-
var transactionSchema =
|
|
1316
|
-
id:
|
|
1317
|
-
source_id:
|
|
1318
|
-
amount:
|
|
1319
|
-
currency:
|
|
1320
|
-
created_date:
|
|
1321
|
-
description:
|
|
1322
|
-
origin:
|
|
1323
|
-
category:
|
|
1324
|
-
status:
|
|
1463
|
+
import { z as z10 } from "zod";
|
|
1464
|
+
var transactionSchema = z10.looseObject({
|
|
1465
|
+
id: z10.string(),
|
|
1466
|
+
source_id: z10.string().nullable(),
|
|
1467
|
+
amount: z10.number(),
|
|
1468
|
+
currency: z10.string(),
|
|
1469
|
+
created_date: z10.string(),
|
|
1470
|
+
description: z10.string(),
|
|
1471
|
+
origin: z10.enum(["link", "external_connection"]),
|
|
1472
|
+
category: z10.string().nullable(),
|
|
1473
|
+
status: z10.string()
|
|
1325
1474
|
});
|
|
1326
|
-
var transactionsPageSchema =
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
data:
|
|
1330
|
-
has_more:
|
|
1475
|
+
var transactionsPageSchema = z10.union([
|
|
1476
|
+
z10.array(transactionSchema).transform((data) => ({ data })),
|
|
1477
|
+
z10.looseObject({
|
|
1478
|
+
data: z10.array(transactionSchema),
|
|
1479
|
+
has_more: z10.boolean().optional()
|
|
1331
1480
|
})
|
|
1332
1481
|
]);
|
|
1333
1482
|
var TransactionsResource = class extends BaseResource {
|
|
@@ -1380,53 +1529,273 @@ var TransactionsResource = class extends BaseResource {
|
|
|
1380
1529
|
}
|
|
1381
1530
|
};
|
|
1382
1531
|
|
|
1532
|
+
// src/resources/ucp.ts
|
|
1533
|
+
function extractApiError(data, rawBody) {
|
|
1534
|
+
if (data && typeof data === "object") {
|
|
1535
|
+
const body = data;
|
|
1536
|
+
if (body.error && typeof body.error === "object") {
|
|
1537
|
+
const err = body.error;
|
|
1538
|
+
if (typeof err.message === "string") return err.message;
|
|
1539
|
+
}
|
|
1540
|
+
if (typeof body.error === "string") return body.error;
|
|
1541
|
+
if (typeof body.message === "string") return body.message;
|
|
1542
|
+
}
|
|
1543
|
+
return rawBody || "unknown error";
|
|
1544
|
+
}
|
|
1545
|
+
function normalizeSearchResult(data) {
|
|
1546
|
+
const body = data ?? {};
|
|
1547
|
+
const items = Array.isArray(body.data) ? body.data : [];
|
|
1548
|
+
return { ...body, data: items };
|
|
1549
|
+
}
|
|
1550
|
+
function normalizeCheckout(data) {
|
|
1551
|
+
const body = data ?? {};
|
|
1552
|
+
return { ...body, id: String(body.id ?? "") };
|
|
1553
|
+
}
|
|
1554
|
+
var UcpResource = class {
|
|
1555
|
+
verbose;
|
|
1556
|
+
getAccessToken;
|
|
1557
|
+
fetchImpl;
|
|
1558
|
+
ucpEndpoint;
|
|
1559
|
+
logger;
|
|
1560
|
+
constructor(options) {
|
|
1561
|
+
const config = resolveLinkSdkConfig(options);
|
|
1562
|
+
this.verbose = config.verbose;
|
|
1563
|
+
this.getAccessToken = config.getAccessToken;
|
|
1564
|
+
this.fetchImpl = requireFetchImplementation(config);
|
|
1565
|
+
this.ucpEndpoint = `${config.apiBaseUrl}/ucp`;
|
|
1566
|
+
this.logger = config.logger;
|
|
1567
|
+
}
|
|
1568
|
+
async rawFetch(opts) {
|
|
1569
|
+
if (this.verbose) {
|
|
1570
|
+
const redactedHeaders = { ...opts.headers };
|
|
1571
|
+
if (redactedHeaders.Authorization)
|
|
1572
|
+
redactedHeaders.Authorization = "Bearer <redacted>";
|
|
1573
|
+
this.logger.debug(`> ${opts.method} ${opts.url}`);
|
|
1574
|
+
this.logger.debug(` Headers: ${JSON.stringify(redactedHeaders)}`);
|
|
1575
|
+
if (opts.body) this.logger.debug(opts.body);
|
|
1576
|
+
}
|
|
1577
|
+
const fetchOpts = {
|
|
1578
|
+
method: opts.method,
|
|
1579
|
+
...opts.headers ? { headers: opts.headers } : {}
|
|
1580
|
+
};
|
|
1581
|
+
if (opts.body) fetchOpts.body = opts.body;
|
|
1582
|
+
let response;
|
|
1583
|
+
try {
|
|
1584
|
+
response = await this.fetchImpl(opts.url, fetchOpts);
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
throw new LinkTransportError(
|
|
1587
|
+
`Request failed: ${opts.method} ${opts.url}`,
|
|
1588
|
+
{
|
|
1589
|
+
cause: error
|
|
1590
|
+
}
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
const rawBody = await response.text();
|
|
1594
|
+
let data = null;
|
|
1595
|
+
try {
|
|
1596
|
+
data = JSON.parse(rawBody);
|
|
1597
|
+
} catch {
|
|
1598
|
+
}
|
|
1599
|
+
if (this.verbose) {
|
|
1600
|
+
this.logger.debug(`< ${response.status} ${response.statusText}`);
|
|
1601
|
+
response.headers.forEach((value, key) => {
|
|
1602
|
+
this.logger.debug(` ${key}: ${value}`);
|
|
1603
|
+
});
|
|
1604
|
+
this.logger.debug(JSON.stringify(data, null, 2) ?? rawBody);
|
|
1605
|
+
}
|
|
1606
|
+
return { status: response.status, data, rawBody };
|
|
1607
|
+
}
|
|
1608
|
+
/** Injects the Bearer token; retries once on 401 after refreshing. */
|
|
1609
|
+
async apiFetch(opts) {
|
|
1610
|
+
const token = await this.getAccessToken();
|
|
1611
|
+
const authedOpts = {
|
|
1612
|
+
...opts,
|
|
1613
|
+
headers: { ...opts.headers, Authorization: `Bearer ${token}` }
|
|
1614
|
+
};
|
|
1615
|
+
const res = await this.rawFetch(authedOpts);
|
|
1616
|
+
if (res.status === 401) {
|
|
1617
|
+
const refreshedToken = await this.getAccessToken({ forceRefresh: true });
|
|
1618
|
+
authedOpts.headers.Authorization = `Bearer ${refreshedToken}`;
|
|
1619
|
+
return this.rawFetch(authedOpts);
|
|
1620
|
+
}
|
|
1621
|
+
return res;
|
|
1622
|
+
}
|
|
1623
|
+
buildSearchUrl(params) {
|
|
1624
|
+
const url = new URL(`${this.ucpEndpoint}/catalog/search`);
|
|
1625
|
+
const setIf = (key, value) => {
|
|
1626
|
+
if (value !== void 0) url.searchParams.set(key, String(value));
|
|
1627
|
+
};
|
|
1628
|
+
setIf("query", params.query);
|
|
1629
|
+
setIf("profile_id", params.profile_id);
|
|
1630
|
+
setIf("sku", params.sku);
|
|
1631
|
+
setIf("price_min", params.price_min);
|
|
1632
|
+
setIf("price_max", params.price_max);
|
|
1633
|
+
setIf("currency", params.currency);
|
|
1634
|
+
setIf("availability", params.availability);
|
|
1635
|
+
setIf("sort", params.sort);
|
|
1636
|
+
setIf("group_by", params.group_by);
|
|
1637
|
+
setIf("limit", params.limit);
|
|
1638
|
+
setIf("offset", params.offset);
|
|
1639
|
+
if (params.include_facets) url.searchParams.set("include_facets", "true");
|
|
1640
|
+
const arrays = [
|
|
1641
|
+
["brand", params.brand],
|
|
1642
|
+
["category", params.category],
|
|
1643
|
+
["color", params.color],
|
|
1644
|
+
["size", params.size],
|
|
1645
|
+
["material", params.material],
|
|
1646
|
+
["gender", params.gender],
|
|
1647
|
+
["condition", params.condition]
|
|
1648
|
+
];
|
|
1649
|
+
for (const [key, values] of arrays) {
|
|
1650
|
+
if (values)
|
|
1651
|
+
for (const value of values) url.searchParams.append(`${key}[]`, value);
|
|
1652
|
+
}
|
|
1653
|
+
if (params.test) url.searchParams.set("test", "true");
|
|
1654
|
+
return url.toString();
|
|
1655
|
+
}
|
|
1656
|
+
async searchCatalog(params) {
|
|
1657
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
1658
|
+
method: "GET",
|
|
1659
|
+
url: this.buildSearchUrl(params)
|
|
1660
|
+
});
|
|
1661
|
+
if (status < 200 || status >= 300) {
|
|
1662
|
+
throw new LinkApiError(
|
|
1663
|
+
`Failed to search UCP catalog (${status}): ${extractApiError(data, rawBody)}`,
|
|
1664
|
+
{ status, rawBody, details: data }
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
return normalizeSearchResult(data);
|
|
1668
|
+
}
|
|
1669
|
+
async createCheckout(params) {
|
|
1670
|
+
const body = {
|
|
1671
|
+
profile_id: params.profile_id,
|
|
1672
|
+
line_items: params.line_items
|
|
1673
|
+
};
|
|
1674
|
+
if (params.currency !== void 0) body.currency = params.currency;
|
|
1675
|
+
if (params.fulfillment_details !== void 0)
|
|
1676
|
+
body.fulfillment_details = params.fulfillment_details;
|
|
1677
|
+
if (params.test) body.test = true;
|
|
1678
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
1679
|
+
method: "POST",
|
|
1680
|
+
url: `${this.ucpEndpoint}/checkout`,
|
|
1681
|
+
headers: { "Content-Type": "application/json" },
|
|
1682
|
+
body: JSON.stringify(body)
|
|
1683
|
+
});
|
|
1684
|
+
if (status < 200 || status >= 300) {
|
|
1685
|
+
throw new LinkApiError(
|
|
1686
|
+
`Failed to create UCP checkout (${status}): ${extractApiError(data, rawBody)}`,
|
|
1687
|
+
{ status, rawBody, details: data }
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
return normalizeCheckout(data);
|
|
1691
|
+
}
|
|
1692
|
+
async completeCheckout(id, params) {
|
|
1693
|
+
const body = {
|
|
1694
|
+
spend_request_id: params.spend_request_id,
|
|
1695
|
+
profile_id: params.profile_id
|
|
1696
|
+
};
|
|
1697
|
+
if (params.test) body.test = true;
|
|
1698
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
1699
|
+
method: "POST",
|
|
1700
|
+
url: `${this.ucpEndpoint}/checkout/${encodeURIComponent(id)}/complete`,
|
|
1701
|
+
headers: { "Content-Type": "application/json" },
|
|
1702
|
+
body: JSON.stringify(body)
|
|
1703
|
+
});
|
|
1704
|
+
if (status < 200 || status >= 300) {
|
|
1705
|
+
throw new LinkApiError(
|
|
1706
|
+
`Failed to complete UCP checkout (${status}): ${extractApiError(data, rawBody)}`,
|
|
1707
|
+
{ status, rawBody, details: data }
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
return normalizeCheckout(data);
|
|
1711
|
+
}
|
|
1712
|
+
async retrieveCheckout(id, params) {
|
|
1713
|
+
const url = new URL(
|
|
1714
|
+
`${this.ucpEndpoint}/checkout/${encodeURIComponent(id)}`
|
|
1715
|
+
);
|
|
1716
|
+
url.searchParams.set("spend_request_id", params.spend_request_id);
|
|
1717
|
+
if (params.test) url.searchParams.set("test", "true");
|
|
1718
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
1719
|
+
method: "GET",
|
|
1720
|
+
url: url.toString()
|
|
1721
|
+
});
|
|
1722
|
+
if (status < 200 || status >= 300) {
|
|
1723
|
+
throw new LinkApiError(
|
|
1724
|
+
`Failed to retrieve UCP checkout (${status}): ${extractApiError(data, rawBody)}`,
|
|
1725
|
+
{ status, rawBody, details: data }
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
return normalizeCheckout(data);
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1383
1732
|
// src/resources/user-info.ts
|
|
1384
|
-
import { z as
|
|
1385
|
-
var rollingSpendLimitSchema =
|
|
1386
|
-
limit:
|
|
1387
|
-
used:
|
|
1388
|
-
remaining:
|
|
1733
|
+
import { z as z11 } from "zod";
|
|
1734
|
+
var rollingSpendLimitSchema = z11.object({
|
|
1735
|
+
limit: z11.number().int().nullable(),
|
|
1736
|
+
used: z11.number().int(),
|
|
1737
|
+
remaining: z11.number().int().nullable()
|
|
1389
1738
|
});
|
|
1390
|
-
var agentWalletSpendLimitsSchema =
|
|
1391
|
-
per_transaction:
|
|
1392
|
-
limit:
|
|
1739
|
+
var agentWalletSpendLimitsSchema = z11.object({
|
|
1740
|
+
per_transaction: z11.object({
|
|
1741
|
+
limit: z11.number().int().nullable()
|
|
1393
1742
|
}),
|
|
1394
1743
|
daily: rollingSpendLimitSchema,
|
|
1395
1744
|
thirty_day: rollingSpendLimitSchema
|
|
1396
1745
|
});
|
|
1397
|
-
var agentWalletVerificationRequirementSchema =
|
|
1398
|
-
status:
|
|
1746
|
+
var agentWalletVerificationRequirementSchema = z11.object({
|
|
1747
|
+
status: z11.enum([
|
|
1399
1748
|
"not_required",
|
|
1400
1749
|
"ssn_verification",
|
|
1401
1750
|
"identity_verification",
|
|
1402
1751
|
"contact_support",
|
|
1403
1752
|
"complete"
|
|
1404
1753
|
]),
|
|
1405
|
-
action_url:
|
|
1754
|
+
action_url: z11.string().nullable()
|
|
1406
1755
|
});
|
|
1407
|
-
var
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1756
|
+
var userInfoAddressSchema = z11.object({
|
|
1757
|
+
line1: z11.string().nullable(),
|
|
1758
|
+
line2: z11.string().nullable().optional(),
|
|
1759
|
+
city: z11.string().nullable(),
|
|
1760
|
+
state: z11.string().nullable(),
|
|
1761
|
+
postal_code: z11.string().nullable(),
|
|
1762
|
+
country: z11.string().nullable()
|
|
1763
|
+
}).transform(({ line2, ...address }) => ({
|
|
1764
|
+
...address,
|
|
1765
|
+
...line2 === void 0 ? {} : { line2 }
|
|
1766
|
+
}));
|
|
1767
|
+
var userInfoSchema = z11.looseObject({
|
|
1768
|
+
id: z11.string().min(1).optional(),
|
|
1769
|
+
email: z11.string().nullable().optional(),
|
|
1770
|
+
name: z11.string().nullable().optional(),
|
|
1771
|
+
first_name: z11.string().nullable().optional(),
|
|
1772
|
+
last_name: z11.string().nullable().optional(),
|
|
1773
|
+
phone: z11.string().nullable().optional(),
|
|
1774
|
+
address: userInfoAddressSchema.nullable().optional(),
|
|
1775
|
+
eligible_for_balance: z11.boolean().optional(),
|
|
1413
1776
|
agent_wallet_spend_limits: agentWalletSpendLimitsSchema.optional(),
|
|
1414
1777
|
agent_wallet_step_up: agentWalletVerificationRequirementSchema.optional()
|
|
1415
1778
|
}).transform(
|
|
1416
1779
|
({
|
|
1780
|
+
id,
|
|
1417
1781
|
email,
|
|
1418
1782
|
name,
|
|
1419
1783
|
first_name,
|
|
1420
1784
|
last_name,
|
|
1421
1785
|
phone,
|
|
1786
|
+
address,
|
|
1787
|
+
eligible_for_balance,
|
|
1422
1788
|
agent_wallet_spend_limits,
|
|
1423
1789
|
agent_wallet_step_up
|
|
1424
1790
|
}) => ({
|
|
1791
|
+
...id === void 0 ? {} : { id },
|
|
1425
1792
|
email: email ?? null,
|
|
1426
1793
|
name: name ?? null,
|
|
1427
1794
|
first_name: first_name ?? null,
|
|
1428
1795
|
last_name: last_name ?? null,
|
|
1429
1796
|
phone: phone ?? null,
|
|
1797
|
+
...address === void 0 ? {} : { address },
|
|
1798
|
+
...eligible_for_balance === void 0 ? {} : { eligible_for_balance },
|
|
1430
1799
|
...agent_wallet_spend_limits === void 0 ? {} : { agent_wallet_spend_limits },
|
|
1431
1800
|
...agent_wallet_step_up === void 0 ? {} : { agent_wallet_verification_requirement: agent_wallet_step_up }
|
|
1432
1801
|
})
|
|
@@ -1452,16 +1821,16 @@ var UserInfoResource = class extends BaseResource {
|
|
|
1452
1821
|
};
|
|
1453
1822
|
|
|
1454
1823
|
// src/resources/web-bot-auth.ts
|
|
1455
|
-
import { z as
|
|
1824
|
+
import { z as z12 } from "zod";
|
|
1456
1825
|
var EXPIRY_BUFFER_MS = 3e4;
|
|
1457
|
-
var webBotAuthBlockSchema =
|
|
1458
|
-
signature:
|
|
1459
|
-
signature_input:
|
|
1460
|
-
signature_agent:
|
|
1461
|
-
authority:
|
|
1462
|
-
expires_at:
|
|
1826
|
+
var webBotAuthBlockSchema = z12.looseObject({
|
|
1827
|
+
signature: z12.string(),
|
|
1828
|
+
signature_input: z12.string(),
|
|
1829
|
+
signature_agent: z12.string(),
|
|
1830
|
+
authority: z12.string(),
|
|
1831
|
+
expires_at: z12.string()
|
|
1463
1832
|
});
|
|
1464
|
-
var webBotAuthResponseSchema =
|
|
1833
|
+
var webBotAuthResponseSchema = z12.looseObject({
|
|
1465
1834
|
web_bot_auth: webBotAuthBlockSchema
|
|
1466
1835
|
});
|
|
1467
1836
|
var WebBotAuthResource = class extends BaseResource {
|
|
@@ -1508,28 +1877,32 @@ var WebBotAuthResource = class extends BaseResource {
|
|
|
1508
1877
|
// src/client.ts
|
|
1509
1878
|
var Link = class {
|
|
1510
1879
|
attestations;
|
|
1880
|
+
identityCredentials;
|
|
1511
1881
|
spendRequests;
|
|
1512
1882
|
paymentMethods;
|
|
1513
1883
|
shippingAddresses;
|
|
1514
1884
|
userInfo;
|
|
1885
|
+
approvalPolicy;
|
|
1515
1886
|
transactions;
|
|
1516
1887
|
sources;
|
|
1517
1888
|
balances;
|
|
1518
|
-
summaries;
|
|
1519
1889
|
webBotAuth;
|
|
1520
1890
|
reports;
|
|
1891
|
+
ucp;
|
|
1521
1892
|
constructor(options) {
|
|
1522
1893
|
this.attestations = new AttestationsResource(options);
|
|
1894
|
+
this.identityCredentials = new IdentityCredentialsResource(options);
|
|
1523
1895
|
this.spendRequests = new SpendRequestResource(options);
|
|
1524
1896
|
this.paymentMethods = new PaymentMethodsResource(options);
|
|
1525
1897
|
this.shippingAddresses = new ShippingAddressResource(options);
|
|
1526
1898
|
this.userInfo = new UserInfoResource(options);
|
|
1899
|
+
this.approvalPolicy = new ApprovalPolicyResource(options);
|
|
1527
1900
|
this.transactions = new TransactionsResource(options);
|
|
1528
1901
|
this.sources = new SourcesResource(options);
|
|
1529
1902
|
this.balances = new BalancesResource(options);
|
|
1530
|
-
this.summaries = new SummariesResource(options);
|
|
1531
1903
|
this.webBotAuth = new WebBotAuthResource(options);
|
|
1532
1904
|
this.reports = new ReportResource(options);
|
|
1905
|
+
this.ucp = new UcpResource(options);
|
|
1533
1906
|
}
|
|
1534
1907
|
};
|
|
1535
1908
|
var client_default = Link;
|
|
@@ -1554,6 +1927,7 @@ var REPORT_TAGS = [
|
|
|
1554
1927
|
];
|
|
1555
1928
|
var REPORT_ATTEMPT_TRACE_MAX_LENGTH = 8e3;
|
|
1556
1929
|
export {
|
|
1930
|
+
ApprovalPolicyResource,
|
|
1557
1931
|
AttestationsResource,
|
|
1558
1932
|
Link,
|
|
1559
1933
|
LinkApiError,
|
|
@@ -1564,8 +1938,10 @@ export {
|
|
|
1564
1938
|
REPORT_ATTEMPT_TRACE_MAX_LENGTH,
|
|
1565
1939
|
REPORT_OUTCOMES,
|
|
1566
1940
|
REPORT_TAGS,
|
|
1567
|
-
SummariesResource,
|
|
1568
1941
|
client_default as default,
|
|
1569
|
-
getDuplicateSpendRequest
|
|
1942
|
+
getDuplicateSpendRequest,
|
|
1943
|
+
holderJwkThumbprint,
|
|
1944
|
+
holderJwksEqual,
|
|
1945
|
+
parseHolderPublicJwk
|
|
1570
1946
|
};
|
|
1571
1947
|
//# sourceMappingURL=index.js.map
|