@stripe/link-sdk 0.2.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/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/index.d.ts +413 -0
- package/dist/index.js +773 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
import { ZodError, prettifyError } from "zod";
|
|
3
|
+
var LinkSdkError = class extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message, { cause: options?.cause });
|
|
7
|
+
this.name = new.target.name;
|
|
8
|
+
this.code = options?.code ?? "sdk_error";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var LinkResponseError = class extends LinkSdkError {
|
|
12
|
+
status;
|
|
13
|
+
constructor(operation, status, options) {
|
|
14
|
+
const detail = options?.cause instanceof ZodError ? `: ${prettifyError(options.cause)}` : options?.cause instanceof Error ? `: ${options.cause.message}` : "";
|
|
15
|
+
super(
|
|
16
|
+
`Invalid response while attempting to ${operation} (${status})${detail}`,
|
|
17
|
+
{ code: "invalid_response", cause: options?.cause }
|
|
18
|
+
);
|
|
19
|
+
this.status = status;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var LinkConfigurationError = class extends LinkSdkError {
|
|
23
|
+
constructor(message, options) {
|
|
24
|
+
super(message, { code: "configuration_error", ...options });
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var LinkTransportError = class extends LinkSdkError {
|
|
28
|
+
constructor(message, options) {
|
|
29
|
+
super(message, { code: "transport_error", ...options });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var LinkApiError = class extends LinkSdkError {
|
|
33
|
+
status;
|
|
34
|
+
rawBody;
|
|
35
|
+
details;
|
|
36
|
+
constructor(message, options) {
|
|
37
|
+
super(message, { code: options.code ?? "api_error", cause: options.cause });
|
|
38
|
+
this.status = options.status;
|
|
39
|
+
this.rawBody = options.rawBody;
|
|
40
|
+
this.details = options.details;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/config.ts
|
|
45
|
+
var DEFAULT_API_BASE_URL = "https://api.link.com";
|
|
46
|
+
function createDefaultHeadersFetch(baseFetch, defaultHeaders) {
|
|
47
|
+
return ((input, init) => {
|
|
48
|
+
const headers = new Headers(init?.headers);
|
|
49
|
+
for (const [key, value] of Object.entries(defaultHeaders)) {
|
|
50
|
+
if (!headers.has(key)) {
|
|
51
|
+
headers.set(key, value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return baseFetch(input, { ...init, headers });
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function createDefaultLogger() {
|
|
58
|
+
return {
|
|
59
|
+
debug() {
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function resolveLinkSdkConfig(options, defaults = {}) {
|
|
64
|
+
const verbose = options.verbose ?? false;
|
|
65
|
+
const logger = options.logger ?? createDefaultLogger();
|
|
66
|
+
if (options.accessToken !== void 0 && options.getAccessToken !== void 0) {
|
|
67
|
+
throw new LinkConfigurationError(
|
|
68
|
+
"Pass either `accessToken` or `getAccessToken`, not both."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
let getAccessToken;
|
|
72
|
+
let canRefreshAccessToken;
|
|
73
|
+
if (options.accessToken !== void 0) {
|
|
74
|
+
if (options.accessToken.trim().length === 0) {
|
|
75
|
+
throw new LinkConfigurationError("`accessToken` cannot be empty.");
|
|
76
|
+
}
|
|
77
|
+
const accessToken = options.accessToken;
|
|
78
|
+
getAccessToken = () => accessToken;
|
|
79
|
+
canRefreshAccessToken = false;
|
|
80
|
+
} else if (typeof options.getAccessToken === "function") {
|
|
81
|
+
getAccessToken = options.getAccessToken;
|
|
82
|
+
canRefreshAccessToken = true;
|
|
83
|
+
} else {
|
|
84
|
+
throw new LinkConfigurationError(
|
|
85
|
+
"Pass `accessToken` or `getAccessToken` to the Link client."
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const apiBaseUrl = options.apiBaseUrl ?? defaults.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
89
|
+
const spendRequestBaseUrl = options.spendRequestBaseUrl ?? defaults.spendRequestBaseUrl ?? apiBaseUrl;
|
|
90
|
+
const baseFetch = options.fetch ?? globalThis.fetch;
|
|
91
|
+
const effectiveFetch = options.defaultHeaders && Object.keys(options.defaultHeaders).length > 0 ? createDefaultHeadersFetch(baseFetch, options.defaultHeaders) : baseFetch;
|
|
92
|
+
return {
|
|
93
|
+
verbose,
|
|
94
|
+
getAccessToken,
|
|
95
|
+
canRefreshAccessToken,
|
|
96
|
+
fetch: effectiveFetch,
|
|
97
|
+
apiBaseUrl,
|
|
98
|
+
spendRequestBaseUrl,
|
|
99
|
+
logger
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function requireFetchImplementation(config) {
|
|
103
|
+
if (!config.fetch) {
|
|
104
|
+
throw new LinkConfigurationError(
|
|
105
|
+
"No fetch implementation available. Pass `fetch` in Link SDK options."
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return config.fetch;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/resources/base.ts
|
|
112
|
+
function isRecord(value) {
|
|
113
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
114
|
+
}
|
|
115
|
+
function extractErrorMessage(data, rawBody) {
|
|
116
|
+
if (isRecord(data)) {
|
|
117
|
+
if (typeof data.error === "string") {
|
|
118
|
+
return data.error;
|
|
119
|
+
}
|
|
120
|
+
if (isRecord(data.error)) {
|
|
121
|
+
const nested = data.error;
|
|
122
|
+
if (typeof nested.message === "string") {
|
|
123
|
+
return nested.message;
|
|
124
|
+
}
|
|
125
|
+
if (typeof nested.code === "string") {
|
|
126
|
+
return nested.code;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (typeof data.message === "string") {
|
|
130
|
+
return data.message;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return rawBody || "unknown error";
|
|
134
|
+
}
|
|
135
|
+
var BaseResource = class {
|
|
136
|
+
verbose;
|
|
137
|
+
getAccessToken;
|
|
138
|
+
canRefreshAccessToken;
|
|
139
|
+
fetchImpl;
|
|
140
|
+
endpoint;
|
|
141
|
+
logger;
|
|
142
|
+
constructor(options, endpointPath, base = "api") {
|
|
143
|
+
const config = resolveLinkSdkConfig(options);
|
|
144
|
+
this.verbose = config.verbose;
|
|
145
|
+
this.getAccessToken = config.getAccessToken;
|
|
146
|
+
this.canRefreshAccessToken = config.canRefreshAccessToken;
|
|
147
|
+
this.fetchImpl = requireFetchImplementation(config);
|
|
148
|
+
const baseUrl = base === "spend" ? config.spendRequestBaseUrl : config.apiBaseUrl;
|
|
149
|
+
this.endpoint = `${baseUrl}${endpointPath}`;
|
|
150
|
+
this.logger = config.logger;
|
|
151
|
+
}
|
|
152
|
+
async rawFetch(opts) {
|
|
153
|
+
if (this.verbose) {
|
|
154
|
+
this.logger.debug(`> ${opts.method} ${opts.url}`);
|
|
155
|
+
}
|
|
156
|
+
let response;
|
|
157
|
+
try {
|
|
158
|
+
const init = {
|
|
159
|
+
method: opts.method,
|
|
160
|
+
...opts.headers !== void 0 && { headers: opts.headers },
|
|
161
|
+
...opts.body !== void 0 && { body: opts.body },
|
|
162
|
+
...opts.signal !== void 0 && { signal: opts.signal }
|
|
163
|
+
};
|
|
164
|
+
response = await this.fetchImpl(opts.url, init);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw new LinkTransportError(
|
|
167
|
+
`Request failed: ${opts.method} ${opts.url}`,
|
|
168
|
+
{ cause: error }
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const rawBody = await response.text();
|
|
172
|
+
let data = null;
|
|
173
|
+
try {
|
|
174
|
+
data = JSON.parse(rawBody);
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
if (this.verbose) {
|
|
178
|
+
this.logger.debug(`< ${response.status} ${response.statusText}`);
|
|
179
|
+
}
|
|
180
|
+
return { status: response.status, data, rawBody };
|
|
181
|
+
}
|
|
182
|
+
async apiFetch(opts) {
|
|
183
|
+
const token = await this.getAccessToken();
|
|
184
|
+
const authedOpts = {
|
|
185
|
+
...opts,
|
|
186
|
+
headers: {
|
|
187
|
+
...opts.headers,
|
|
188
|
+
Authorization: `Bearer ${token}`
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const res = await this.rawFetch(authedOpts);
|
|
192
|
+
if (res.status === 401 && this.canRefreshAccessToken) {
|
|
193
|
+
const refreshedToken = await this.getAccessToken({ forceRefresh: true });
|
|
194
|
+
authedOpts.headers.Authorization = `Bearer ${refreshedToken}`;
|
|
195
|
+
return this.rawFetch(authedOpts);
|
|
196
|
+
}
|
|
197
|
+
return res;
|
|
198
|
+
}
|
|
199
|
+
throwApiError(operation, status, data, rawBody, cause) {
|
|
200
|
+
const msg = extractErrorMessage(data, rawBody);
|
|
201
|
+
throw new LinkApiError(`Failed to ${operation} (${status}): ${msg}`, {
|
|
202
|
+
status,
|
|
203
|
+
rawBody,
|
|
204
|
+
details: data,
|
|
205
|
+
cause
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
parseResponse(operation, status, parser) {
|
|
209
|
+
try {
|
|
210
|
+
return parser();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new LinkResponseError(operation, status, { cause: error });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// src/resources/balances.ts
|
|
218
|
+
import { z } from "zod";
|
|
219
|
+
var currencyAmountsSchema = z.record(z.string(), z.number());
|
|
220
|
+
var balanceSchema = z.looseObject({
|
|
221
|
+
source_id: z.string(),
|
|
222
|
+
type: z.enum(["cash", "credit"]),
|
|
223
|
+
cash: z.looseObject({ available: currencyAmountsSchema }).nullable().optional(),
|
|
224
|
+
credit: z.looseObject({ used: currencyAmountsSchema }).nullable().optional(),
|
|
225
|
+
current: z.number(),
|
|
226
|
+
currency: z.string(),
|
|
227
|
+
as_of: z.string()
|
|
228
|
+
});
|
|
229
|
+
var balancesPageSchema = z.looseObject({
|
|
230
|
+
data: z.array(balanceSchema),
|
|
231
|
+
has_more: z.boolean().optional()
|
|
232
|
+
});
|
|
233
|
+
var BalancesResource = class extends BaseResource {
|
|
234
|
+
constructor(options) {
|
|
235
|
+
super(options, "/balances");
|
|
236
|
+
}
|
|
237
|
+
buildUrl(params) {
|
|
238
|
+
const url = new URL(this.endpoint);
|
|
239
|
+
if (params.sources !== void 0) {
|
|
240
|
+
for (const source of params.sources) {
|
|
241
|
+
url.searchParams.append("sources[]", source);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (params.limit !== void 0) {
|
|
245
|
+
url.searchParams.set("limit", String(params.limit));
|
|
246
|
+
}
|
|
247
|
+
if (params.starting_after !== void 0) {
|
|
248
|
+
url.searchParams.set("starting_after", params.starting_after);
|
|
249
|
+
}
|
|
250
|
+
if (params.ending_before !== void 0) {
|
|
251
|
+
url.searchParams.set("ending_before", params.ending_before);
|
|
252
|
+
}
|
|
253
|
+
return url.toString();
|
|
254
|
+
}
|
|
255
|
+
async list(params = {}) {
|
|
256
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
257
|
+
method: "GET",
|
|
258
|
+
url: this.buildUrl(params)
|
|
259
|
+
});
|
|
260
|
+
if (status < 200 || status >= 300) {
|
|
261
|
+
this.throwApiError("list balances", status, data, rawBody);
|
|
262
|
+
}
|
|
263
|
+
return this.parseResponse(
|
|
264
|
+
"list balances",
|
|
265
|
+
status,
|
|
266
|
+
() => balancesPageSchema.parse(data)
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/resources/payment-methods.ts
|
|
272
|
+
import { z as z2 } from "zod";
|
|
273
|
+
var paymentMethodSchema = z2.looseObject({
|
|
274
|
+
id: z2.string(),
|
|
275
|
+
type: z2.string(),
|
|
276
|
+
is_default: z2.boolean()
|
|
277
|
+
});
|
|
278
|
+
var paymentMethodsResponseSchema = z2.looseObject({
|
|
279
|
+
payment_details: z2.array(paymentMethodSchema)
|
|
280
|
+
});
|
|
281
|
+
var PaymentMethodsResource = class extends BaseResource {
|
|
282
|
+
constructor(options) {
|
|
283
|
+
super(options, "/payment-details");
|
|
284
|
+
}
|
|
285
|
+
async list() {
|
|
286
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
287
|
+
method: "GET",
|
|
288
|
+
url: this.endpoint
|
|
289
|
+
});
|
|
290
|
+
if (status < 200 || status >= 300) {
|
|
291
|
+
this.throwApiError("list payment methods", status, data, rawBody);
|
|
292
|
+
}
|
|
293
|
+
return this.parseResponse(
|
|
294
|
+
"list payment methods",
|
|
295
|
+
status,
|
|
296
|
+
() => paymentMethodsResponseSchema.parse(data).payment_details
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// src/resources/report.ts
|
|
302
|
+
import { z as z3 } from "zod";
|
|
303
|
+
var reportRecordSchema = z3.looseObject({
|
|
304
|
+
object: z3.string(),
|
|
305
|
+
created_at: z3.string(),
|
|
306
|
+
domain: z3.string(),
|
|
307
|
+
outcome: z3.string(),
|
|
308
|
+
spend_request_id: z3.string(),
|
|
309
|
+
status: z3.string()
|
|
310
|
+
});
|
|
311
|
+
var ReportResource = class extends BaseResource {
|
|
312
|
+
constructor(options) {
|
|
313
|
+
super(options, "/agent_observations");
|
|
314
|
+
}
|
|
315
|
+
async create(params) {
|
|
316
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
317
|
+
method: "POST",
|
|
318
|
+
url: this.endpoint,
|
|
319
|
+
headers: { "Content-Type": "application/json" },
|
|
320
|
+
body: JSON.stringify(params)
|
|
321
|
+
});
|
|
322
|
+
if (status < 200 || status >= 300) {
|
|
323
|
+
this.throwApiError("create report", status, data, rawBody);
|
|
324
|
+
}
|
|
325
|
+
return this.parseResponse(
|
|
326
|
+
"create report",
|
|
327
|
+
status,
|
|
328
|
+
() => reportRecordSchema.parse(data)
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// src/resources/shipping-address.ts
|
|
334
|
+
import { z as z4 } from "zod";
|
|
335
|
+
var shippingAddressSchema = z4.looseObject({
|
|
336
|
+
id: z4.string(),
|
|
337
|
+
is_default: z4.boolean(),
|
|
338
|
+
nickname: z4.string().nullable(),
|
|
339
|
+
address: z4.looseObject({}).nullable()
|
|
340
|
+
});
|
|
341
|
+
var shippingAddressesResponseSchema = z4.looseObject({
|
|
342
|
+
shipping_addresses: z4.array(shippingAddressSchema)
|
|
343
|
+
});
|
|
344
|
+
var ShippingAddressResource = class extends BaseResource {
|
|
345
|
+
constructor(options) {
|
|
346
|
+
super(options, "/shipping_addresses");
|
|
347
|
+
}
|
|
348
|
+
async list() {
|
|
349
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
350
|
+
method: "GET",
|
|
351
|
+
url: this.endpoint
|
|
352
|
+
});
|
|
353
|
+
if (status < 200 || status >= 300) {
|
|
354
|
+
this.throwApiError("list shipping addresses", status, data, rawBody);
|
|
355
|
+
}
|
|
356
|
+
return this.parseResponse(
|
|
357
|
+
"list shipping addresses",
|
|
358
|
+
status,
|
|
359
|
+
() => shippingAddressesResponseSchema.parse(data).shipping_addresses
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/resources/sources.ts
|
|
365
|
+
import { z as z5 } from "zod";
|
|
366
|
+
var nullableRecordSchema = z5.record(z5.string(), z5.unknown()).nullable().optional();
|
|
367
|
+
var sourceSchema = z5.looseObject({
|
|
368
|
+
id: z5.string().nullable().optional(),
|
|
369
|
+
name: z5.string().nullable().optional(),
|
|
370
|
+
type: z5.string().nullable().optional(),
|
|
371
|
+
capabilities: nullableRecordSchema,
|
|
372
|
+
external_connection: nullableRecordSchema,
|
|
373
|
+
granted_actions: z5.array(z5.string()).nullable().optional(),
|
|
374
|
+
bank_account: nullableRecordSchema,
|
|
375
|
+
card: nullableRecordSchema
|
|
376
|
+
});
|
|
377
|
+
var sourcesPageSchema = z5.looseObject({
|
|
378
|
+
data: z5.array(sourceSchema),
|
|
379
|
+
has_more: z5.boolean().optional()
|
|
380
|
+
});
|
|
381
|
+
var SourcesResource = class extends BaseResource {
|
|
382
|
+
constructor(options) {
|
|
383
|
+
super(options, "/sources");
|
|
384
|
+
}
|
|
385
|
+
buildUrl(params) {
|
|
386
|
+
const url = new URL(this.endpoint);
|
|
387
|
+
if (params.limit !== void 0) {
|
|
388
|
+
url.searchParams.set("limit", String(params.limit));
|
|
389
|
+
}
|
|
390
|
+
if (params.starting_after !== void 0) {
|
|
391
|
+
url.searchParams.set("starting_after", params.starting_after);
|
|
392
|
+
}
|
|
393
|
+
if (params.ending_before !== void 0) {
|
|
394
|
+
url.searchParams.set("ending_before", params.ending_before);
|
|
395
|
+
}
|
|
396
|
+
return url.toString();
|
|
397
|
+
}
|
|
398
|
+
async list(params = {}) {
|
|
399
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
400
|
+
method: "GET",
|
|
401
|
+
url: this.buildUrl(params)
|
|
402
|
+
});
|
|
403
|
+
if (status < 200 || status >= 300) {
|
|
404
|
+
this.throwApiError("list sources", status, data, rawBody);
|
|
405
|
+
}
|
|
406
|
+
return this.parseResponse(
|
|
407
|
+
"list sources",
|
|
408
|
+
status,
|
|
409
|
+
() => sourcesPageSchema.parse(data)
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// src/resources/spend-request.ts
|
|
415
|
+
import { z as z6 } from "zod";
|
|
416
|
+
var sharedPaymentTokenSchema = z6.union([
|
|
417
|
+
z6.string().transform((id) => ({ id })),
|
|
418
|
+
z6.looseObject({ id: z6.string() })
|
|
419
|
+
]);
|
|
420
|
+
var spendRequestSchema = z6.looseObject({
|
|
421
|
+
id: z6.string(),
|
|
422
|
+
payment_details: z6.string().optional(),
|
|
423
|
+
status: z6.string(),
|
|
424
|
+
line_items: z6.array(z6.unknown()).optional(),
|
|
425
|
+
totals: z6.array(z6.unknown()).optional(),
|
|
426
|
+
created_at: z6.string(),
|
|
427
|
+
updated_at: z6.string(),
|
|
428
|
+
shared_payment_token: sharedPaymentTokenSchema.nullable().optional()
|
|
429
|
+
});
|
|
430
|
+
var spendRequestsResponseSchema = z6.looseObject({
|
|
431
|
+
data: z6.array(spendRequestSchema)
|
|
432
|
+
});
|
|
433
|
+
var requestApprovalResponseSchema = z6.looseObject({
|
|
434
|
+
id: z6.string(),
|
|
435
|
+
approval_link: z6.string()
|
|
436
|
+
}).transform(({ approval_link, ...response }) => ({
|
|
437
|
+
...response,
|
|
438
|
+
approval_url: approval_link
|
|
439
|
+
}));
|
|
440
|
+
var duplicateSpendRequestErrorSchema = z6.looseObject({
|
|
441
|
+
error: z6.looseObject({
|
|
442
|
+
duplicate_spend_request: z6.unknown().optional()
|
|
443
|
+
})
|
|
444
|
+
});
|
|
445
|
+
function parseSpendRequest(value) {
|
|
446
|
+
return spendRequestSchema.parse(value);
|
|
447
|
+
}
|
|
448
|
+
function getDuplicateSpendRequest(error) {
|
|
449
|
+
if (!(error instanceof LinkApiError)) return null;
|
|
450
|
+
const details = duplicateSpendRequestErrorSchema.safeParse(error.details);
|
|
451
|
+
if (!details.success) return null;
|
|
452
|
+
const duplicate = spendRequestSchema.safeParse(
|
|
453
|
+
details.data.error.duplicate_spend_request
|
|
454
|
+
);
|
|
455
|
+
return duplicate.success ? duplicate.data : null;
|
|
456
|
+
}
|
|
457
|
+
var SpendRequestResource = class extends BaseResource {
|
|
458
|
+
constructor(options) {
|
|
459
|
+
super(options, "/spend_requests", "spend");
|
|
460
|
+
}
|
|
461
|
+
async list(opts) {
|
|
462
|
+
const url = opts?.includeHistory ? `${this.endpoint}?include_history=true` : this.endpoint;
|
|
463
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
464
|
+
method: "GET",
|
|
465
|
+
url
|
|
466
|
+
});
|
|
467
|
+
if (status < 200 || status >= 300) {
|
|
468
|
+
this.throwApiError("list spend requests", status, data, rawBody);
|
|
469
|
+
}
|
|
470
|
+
return this.parseResponse(
|
|
471
|
+
"list spend requests",
|
|
472
|
+
status,
|
|
473
|
+
() => spendRequestsResponseSchema.parse(data).data
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
async create(params) {
|
|
477
|
+
const { approve, ...body } = params;
|
|
478
|
+
const url = approve ? `${this.endpoint}/create_delegated` : this.endpoint;
|
|
479
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
480
|
+
method: "POST",
|
|
481
|
+
url,
|
|
482
|
+
headers: { "Content-Type": "application/json" },
|
|
483
|
+
body: JSON.stringify(body)
|
|
484
|
+
});
|
|
485
|
+
if (status < 200 || status >= 300) {
|
|
486
|
+
this.throwApiError("create spend request", status, data, rawBody);
|
|
487
|
+
}
|
|
488
|
+
return this.parseResponse(
|
|
489
|
+
"create spend request",
|
|
490
|
+
status,
|
|
491
|
+
() => parseSpendRequest(data)
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
async update(id, params) {
|
|
495
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
496
|
+
method: "POST",
|
|
497
|
+
url: `${this.endpoint}/${id}`,
|
|
498
|
+
headers: { "Content-Type": "application/json" },
|
|
499
|
+
body: JSON.stringify(params)
|
|
500
|
+
});
|
|
501
|
+
if (status < 200 || status >= 300) {
|
|
502
|
+
this.throwApiError("update spend request", status, data, rawBody);
|
|
503
|
+
}
|
|
504
|
+
return this.parseResponse(
|
|
505
|
+
"update spend request",
|
|
506
|
+
status,
|
|
507
|
+
() => parseSpendRequest(data)
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
async cancel(id) {
|
|
511
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
512
|
+
method: "POST",
|
|
513
|
+
url: `${this.endpoint}/${id}/cancel`
|
|
514
|
+
});
|
|
515
|
+
if (status < 200 || status >= 300) {
|
|
516
|
+
this.throwApiError("cancel spend request", status, data, rawBody);
|
|
517
|
+
}
|
|
518
|
+
return this.parseResponse(
|
|
519
|
+
"cancel spend request",
|
|
520
|
+
status,
|
|
521
|
+
() => parseSpendRequest(data)
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
async requestApproval(id) {
|
|
525
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
526
|
+
method: "POST",
|
|
527
|
+
url: `${this.endpoint}/${id}/request_approval`
|
|
528
|
+
});
|
|
529
|
+
if (status < 200 || status >= 300) {
|
|
530
|
+
this.throwApiError("request approval", status, data, rawBody);
|
|
531
|
+
}
|
|
532
|
+
return this.parseResponse(
|
|
533
|
+
"request approval",
|
|
534
|
+
status,
|
|
535
|
+
() => requestApprovalResponseSchema.parse(data)
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
async retrieve(id, opts) {
|
|
539
|
+
const url = new URL(`${this.endpoint}/${id}`);
|
|
540
|
+
if (opts?.include?.length) {
|
|
541
|
+
url.searchParams.set("include", opts.include.join(","));
|
|
542
|
+
}
|
|
543
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
544
|
+
method: "GET",
|
|
545
|
+
url: url.toString()
|
|
546
|
+
});
|
|
547
|
+
if (status === 404) return null;
|
|
548
|
+
if (status < 200 || status >= 300) {
|
|
549
|
+
this.throwApiError("retrieve spend request", status, data, rawBody);
|
|
550
|
+
}
|
|
551
|
+
return this.parseResponse(
|
|
552
|
+
"retrieve spend request",
|
|
553
|
+
status,
|
|
554
|
+
() => parseSpendRequest(data)
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// src/resources/transactions.ts
|
|
560
|
+
import { z as z7 } from "zod";
|
|
561
|
+
var transactionSchema = z7.looseObject({
|
|
562
|
+
id: z7.string(),
|
|
563
|
+
source_id: z7.string().nullable(),
|
|
564
|
+
amount: z7.number(),
|
|
565
|
+
currency: z7.string(),
|
|
566
|
+
created_date: z7.string(),
|
|
567
|
+
description: z7.string(),
|
|
568
|
+
origin: z7.enum(["link", "external_connection"]),
|
|
569
|
+
category: z7.string().nullable(),
|
|
570
|
+
status: z7.string()
|
|
571
|
+
});
|
|
572
|
+
var transactionsPageSchema = z7.union([
|
|
573
|
+
z7.array(transactionSchema).transform((data) => ({ data })),
|
|
574
|
+
z7.looseObject({
|
|
575
|
+
data: z7.array(transactionSchema),
|
|
576
|
+
has_more: z7.boolean().optional()
|
|
577
|
+
})
|
|
578
|
+
]);
|
|
579
|
+
var TransactionsResource = class extends BaseResource {
|
|
580
|
+
constructor(options) {
|
|
581
|
+
super(options, "/transactions");
|
|
582
|
+
}
|
|
583
|
+
buildUrl(params) {
|
|
584
|
+
const url = new URL(this.endpoint);
|
|
585
|
+
if (params.limit !== void 0) {
|
|
586
|
+
url.searchParams.set("limit", String(params.limit));
|
|
587
|
+
}
|
|
588
|
+
if (params.starting_after !== void 0) {
|
|
589
|
+
url.searchParams.set("starting_after", params.starting_after);
|
|
590
|
+
}
|
|
591
|
+
if (params.ending_before !== void 0) {
|
|
592
|
+
url.searchParams.set("ending_before", params.ending_before);
|
|
593
|
+
}
|
|
594
|
+
if (params.start_date !== void 0) {
|
|
595
|
+
url.searchParams.set("date_start", params.start_date);
|
|
596
|
+
}
|
|
597
|
+
if (params.end_date !== void 0) {
|
|
598
|
+
url.searchParams.set("date_end", params.end_date);
|
|
599
|
+
}
|
|
600
|
+
if (params.category !== void 0) {
|
|
601
|
+
url.searchParams.set("category", params.category);
|
|
602
|
+
}
|
|
603
|
+
if (params.origin !== void 0) {
|
|
604
|
+
url.searchParams.set("origin", params.origin);
|
|
605
|
+
}
|
|
606
|
+
if (params.sources !== void 0) {
|
|
607
|
+
for (const source of params.sources) {
|
|
608
|
+
url.searchParams.append("sources[]", source);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return url.toString();
|
|
612
|
+
}
|
|
613
|
+
async list(params = {}) {
|
|
614
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
615
|
+
method: "GET",
|
|
616
|
+
url: this.buildUrl(params)
|
|
617
|
+
});
|
|
618
|
+
if (status < 200 || status >= 300) {
|
|
619
|
+
this.throwApiError("list transactions", status, data, rawBody);
|
|
620
|
+
}
|
|
621
|
+
return this.parseResponse(
|
|
622
|
+
"list transactions",
|
|
623
|
+
status,
|
|
624
|
+
() => transactionsPageSchema.parse(data)
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
// src/resources/user-info.ts
|
|
630
|
+
import { z as z8 } from "zod";
|
|
631
|
+
var userInfoSchema = z8.looseObject({
|
|
632
|
+
email: z8.string().nullable().optional(),
|
|
633
|
+
name: z8.string().nullable().optional(),
|
|
634
|
+
first_name: z8.string().nullable().optional(),
|
|
635
|
+
last_name: z8.string().nullable().optional(),
|
|
636
|
+
phone: z8.string().nullable().optional()
|
|
637
|
+
}).transform(({ email, name, first_name, last_name, phone }) => ({
|
|
638
|
+
email: email ?? null,
|
|
639
|
+
name: name ?? null,
|
|
640
|
+
first_name: first_name ?? null,
|
|
641
|
+
last_name: last_name ?? null,
|
|
642
|
+
phone: phone ?? null
|
|
643
|
+
}));
|
|
644
|
+
var UserInfoResource = class extends BaseResource {
|
|
645
|
+
constructor(options) {
|
|
646
|
+
super(options, "/userinfo");
|
|
647
|
+
}
|
|
648
|
+
async retrieve() {
|
|
649
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
650
|
+
method: "GET",
|
|
651
|
+
url: this.endpoint
|
|
652
|
+
});
|
|
653
|
+
if (status < 200 || status >= 300) {
|
|
654
|
+
this.throwApiError("retrieve user info", status, data, rawBody);
|
|
655
|
+
}
|
|
656
|
+
return this.parseResponse(
|
|
657
|
+
"retrieve user info",
|
|
658
|
+
status,
|
|
659
|
+
() => userInfoSchema.parse(data)
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
// src/resources/web-bot-auth.ts
|
|
665
|
+
import { z as z9 } from "zod";
|
|
666
|
+
var EXPIRY_BUFFER_MS = 3e4;
|
|
667
|
+
var webBotAuthBlockSchema = z9.looseObject({
|
|
668
|
+
signature: z9.string(),
|
|
669
|
+
signature_input: z9.string(),
|
|
670
|
+
signature_agent: z9.string(),
|
|
671
|
+
authority: z9.string(),
|
|
672
|
+
expires_at: z9.string()
|
|
673
|
+
});
|
|
674
|
+
var webBotAuthResponseSchema = z9.looseObject({
|
|
675
|
+
web_bot_auth: webBotAuthBlockSchema
|
|
676
|
+
});
|
|
677
|
+
var WebBotAuthResource = class extends BaseResource {
|
|
678
|
+
cache = /* @__PURE__ */ new Map();
|
|
679
|
+
constructor(options) {
|
|
680
|
+
super(options, "/web_bot_auth/sign");
|
|
681
|
+
}
|
|
682
|
+
async signUrl(url) {
|
|
683
|
+
let authority;
|
|
684
|
+
try {
|
|
685
|
+
authority = new URL(url).hostname;
|
|
686
|
+
} catch (error) {
|
|
687
|
+
throw new LinkSdkError(`Invalid URL: ${url}`, { cause: error });
|
|
688
|
+
}
|
|
689
|
+
const cached = this.cache.get(authority);
|
|
690
|
+
if (cached && Date.now() < cached.expiresAt - EXPIRY_BUFFER_MS) {
|
|
691
|
+
return cached.block;
|
|
692
|
+
}
|
|
693
|
+
const { status, data, rawBody } = await this.apiFetch({
|
|
694
|
+
method: "POST",
|
|
695
|
+
url: this.endpoint,
|
|
696
|
+
headers: { "Content-Type": "application/json" },
|
|
697
|
+
body: JSON.stringify({ url })
|
|
698
|
+
});
|
|
699
|
+
if (status < 200 || status >= 300) {
|
|
700
|
+
this.throwApiError("get web bot auth headers", status, data, rawBody);
|
|
701
|
+
}
|
|
702
|
+
const webBotAuth = this.parseResponse(
|
|
703
|
+
"get web bot auth headers",
|
|
704
|
+
status,
|
|
705
|
+
() => webBotAuthResponseSchema.parse(data).web_bot_auth
|
|
706
|
+
);
|
|
707
|
+
const expiresAt = Date.parse(webBotAuth.expires_at);
|
|
708
|
+
if (Number.isNaN(expiresAt)) {
|
|
709
|
+
throw new LinkSdkError(
|
|
710
|
+
`Credentials response has invalid expires_at: ${webBotAuth.expires_at}`
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
this.cache.set(authority, { block: webBotAuth, expiresAt });
|
|
714
|
+
return webBotAuth;
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
// src/client.ts
|
|
719
|
+
var Link = class {
|
|
720
|
+
spendRequests;
|
|
721
|
+
paymentMethods;
|
|
722
|
+
shippingAddresses;
|
|
723
|
+
userInfo;
|
|
724
|
+
transactions;
|
|
725
|
+
sources;
|
|
726
|
+
balances;
|
|
727
|
+
webBotAuth;
|
|
728
|
+
reports;
|
|
729
|
+
constructor(options) {
|
|
730
|
+
this.spendRequests = new SpendRequestResource(options);
|
|
731
|
+
this.paymentMethods = new PaymentMethodsResource(options);
|
|
732
|
+
this.shippingAddresses = new ShippingAddressResource(options);
|
|
733
|
+
this.userInfo = new UserInfoResource(options);
|
|
734
|
+
this.transactions = new TransactionsResource(options);
|
|
735
|
+
this.sources = new SourcesResource(options);
|
|
736
|
+
this.balances = new BalancesResource(options);
|
|
737
|
+
this.webBotAuth = new WebBotAuthResource(options);
|
|
738
|
+
this.reports = new ReportResource(options);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
var client_default = Link;
|
|
742
|
+
|
|
743
|
+
// src/resources/interfaces.ts
|
|
744
|
+
var REPORT_OUTCOMES = ["success", "blocked", "abandoned"];
|
|
745
|
+
var REPORT_TAGS = [
|
|
746
|
+
"stripe_checkout",
|
|
747
|
+
"captcha",
|
|
748
|
+
"anti_bot_script",
|
|
749
|
+
"cdn_block",
|
|
750
|
+
"waf_block",
|
|
751
|
+
"dns_block",
|
|
752
|
+
"rate_limited",
|
|
753
|
+
"login_required",
|
|
754
|
+
"3ds_challenge",
|
|
755
|
+
"page_inaccessible",
|
|
756
|
+
"timeout",
|
|
757
|
+
"site_error",
|
|
758
|
+
"payment_declined",
|
|
759
|
+
"other"
|
|
760
|
+
];
|
|
761
|
+
export {
|
|
762
|
+
Link,
|
|
763
|
+
LinkApiError,
|
|
764
|
+
LinkConfigurationError,
|
|
765
|
+
LinkResponseError,
|
|
766
|
+
LinkSdkError,
|
|
767
|
+
LinkTransportError,
|
|
768
|
+
REPORT_OUTCOMES,
|
|
769
|
+
REPORT_TAGS,
|
|
770
|
+
client_default as default,
|
|
771
|
+
getDuplicateSpendRequest
|
|
772
|
+
};
|
|
773
|
+
//# sourceMappingURL=index.js.map
|