paysafe-sdk 1.0.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/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/index.cjs +1572 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1211 -0
- package/dist/index.d.ts +1211 -0
- package/dist/index.js +1548 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1572 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/errors.ts
|
|
4
|
+
var RETRYABLE_API_CODES = /* @__PURE__ */ new Set(["3028", "5000", "5001", "5002"]);
|
|
5
|
+
var RETRYABLE_HTTP_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
6
|
+
var PaysafeError = class _PaysafeError extends Error {
|
|
7
|
+
kind;
|
|
8
|
+
code;
|
|
9
|
+
httpStatus;
|
|
10
|
+
fieldErrors;
|
|
11
|
+
details;
|
|
12
|
+
retryable;
|
|
13
|
+
constructor(init) {
|
|
14
|
+
super(formatMessage(init));
|
|
15
|
+
this.name = "PaysafeError";
|
|
16
|
+
this.kind = init.kind;
|
|
17
|
+
this.code = init.code;
|
|
18
|
+
this.httpStatus = init.httpStatus;
|
|
19
|
+
this.fieldErrors = init.fieldErrors ?? [];
|
|
20
|
+
this.details = init.details;
|
|
21
|
+
this.retryable = init.retryable ?? false;
|
|
22
|
+
if (init.cause !== void 0) {
|
|
23
|
+
this.cause = init.cause;
|
|
24
|
+
}
|
|
25
|
+
Object.setPrototypeOf(this, _PaysafeError.prototype);
|
|
26
|
+
}
|
|
27
|
+
/** Whether the SDK's retry policy should attempt this request again. */
|
|
28
|
+
isRetryable() {
|
|
29
|
+
return this.retryable;
|
|
30
|
+
}
|
|
31
|
+
static fromApiResponse(body, httpStatus) {
|
|
32
|
+
const errObj = isRecord(body.error) ? body.error : void 0;
|
|
33
|
+
const code = errObj ? stringify(errObj.code) : "";
|
|
34
|
+
let message = "Unknown API error";
|
|
35
|
+
if (errObj && typeof errObj.message === "string" && errObj.message !== "") {
|
|
36
|
+
message = errObj.message;
|
|
37
|
+
} else if (typeof body.message === "string" && body.message !== "") {
|
|
38
|
+
message = body.message;
|
|
39
|
+
}
|
|
40
|
+
let fieldErrors = [];
|
|
41
|
+
if (errObj && Array.isArray(errObj.fieldErrors)) {
|
|
42
|
+
fieldErrors = errObj.fieldErrors.filter(isRecord).map((fe) => ({ field: stringify(fe.field), error: stringify(fe.error) }));
|
|
43
|
+
}
|
|
44
|
+
return new _PaysafeError({
|
|
45
|
+
kind: "api_error",
|
|
46
|
+
code,
|
|
47
|
+
message,
|
|
48
|
+
httpStatus,
|
|
49
|
+
fieldErrors,
|
|
50
|
+
details: body,
|
|
51
|
+
retryable: RETRYABLE_API_CODES.has(code) || RETRYABLE_HTTP_STATUSES.has(httpStatus)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
static httpError(cause, timedOut) {
|
|
55
|
+
if (timedOut) {
|
|
56
|
+
return new _PaysafeError({
|
|
57
|
+
kind: "timeout",
|
|
58
|
+
message: "Request timed out",
|
|
59
|
+
retryable: true,
|
|
60
|
+
cause
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return new _PaysafeError({
|
|
64
|
+
kind: "http_error",
|
|
65
|
+
message: `HTTP transport error: ${describeCause(cause)}`,
|
|
66
|
+
retryable: false,
|
|
67
|
+
cause
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
static rateLimited() {
|
|
71
|
+
return new _PaysafeError({
|
|
72
|
+
kind: "rate_limited",
|
|
73
|
+
message: "Rate limit exceeded. Reduce request frequency.",
|
|
74
|
+
retryable: true
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
static invalidParams(message) {
|
|
78
|
+
return new _PaysafeError({ kind: "invalid_params", message, retryable: false });
|
|
79
|
+
}
|
|
80
|
+
static invalidConfig(message) {
|
|
81
|
+
return new _PaysafeError({ kind: "invalid_config", message, retryable: false });
|
|
82
|
+
}
|
|
83
|
+
static webhookMismatch() {
|
|
84
|
+
return new _PaysafeError({
|
|
85
|
+
kind: "webhook_signature_mismatch",
|
|
86
|
+
message: "Webhook HMAC-SHA256 signature does not match.",
|
|
87
|
+
retryable: false
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
static decodeError(cause) {
|
|
91
|
+
return new _PaysafeError({
|
|
92
|
+
kind: "decode_error",
|
|
93
|
+
message: `Failed to decode response: ${describeCause(cause)}`,
|
|
94
|
+
retryable: false,
|
|
95
|
+
cause
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
static contextCanceled(cause) {
|
|
99
|
+
return new _PaysafeError({
|
|
100
|
+
kind: "context_canceled",
|
|
101
|
+
message: `Request aborted: ${describeCause(cause)}`,
|
|
102
|
+
retryable: false,
|
|
103
|
+
cause
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function formatMessage(init) {
|
|
108
|
+
return init.code ? `paysafe: ${init.kind} [${init.code}]: ${init.message}` : `paysafe: ${init.kind}: ${init.message}`;
|
|
109
|
+
}
|
|
110
|
+
function isRecord(value) {
|
|
111
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
112
|
+
}
|
|
113
|
+
function stringify(value) {
|
|
114
|
+
if (typeof value === "string") return value;
|
|
115
|
+
if (value === null || value === void 0) return "";
|
|
116
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
117
|
+
return String(value);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return JSON.stringify(value) ?? "";
|
|
121
|
+
} catch {
|
|
122
|
+
return "";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function describeCause(cause) {
|
|
126
|
+
if (cause instanceof Error) return cause.message;
|
|
127
|
+
return String(cause);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/core/config.ts
|
|
131
|
+
var BASE_URLS = {
|
|
132
|
+
test: "https://api.test.paysafe.com",
|
|
133
|
+
production: "https://api.paysafe.com"
|
|
134
|
+
};
|
|
135
|
+
var PAYMENTS_PATH = "/paymenthub/v1";
|
|
136
|
+
var SCHEDULER_PATH = "/subscriptionsplans/v1";
|
|
137
|
+
var APPLICATIONS_PATH = "/merchant/v1";
|
|
138
|
+
var FX_RATES_PATH = "/fxrates/v1";
|
|
139
|
+
var BANK_ACCOUNT_VALIDATOR_PATH = "/bankaccountvalidator/v1";
|
|
140
|
+
var CUSTOMER_IDENTIFICATION_PATH = "/customeridentification/v1";
|
|
141
|
+
var ResolvedConfig = class {
|
|
142
|
+
username;
|
|
143
|
+
password;
|
|
144
|
+
environment;
|
|
145
|
+
accountId;
|
|
146
|
+
baseUrlOverride;
|
|
147
|
+
timeoutMs;
|
|
148
|
+
maxRetries;
|
|
149
|
+
retryBaseDelayMs;
|
|
150
|
+
rateLimit;
|
|
151
|
+
fetchImpl;
|
|
152
|
+
telemetryPrefix;
|
|
153
|
+
constructor(opts) {
|
|
154
|
+
this.username = opts.username;
|
|
155
|
+
this.password = opts.password;
|
|
156
|
+
this.environment = opts.environment ?? "test";
|
|
157
|
+
this.accountId = opts.accountId;
|
|
158
|
+
this.baseUrlOverride = opts.baseUrlOverride;
|
|
159
|
+
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
160
|
+
this.maxRetries = opts.maxRetries ?? 3;
|
|
161
|
+
this.retryBaseDelayMs = opts.retryBaseDelayMs ?? 500;
|
|
162
|
+
this.rateLimit = opts.rateLimit ?? { limit: 100, windowMs: 1e3 };
|
|
163
|
+
this.telemetryPrefix = opts.telemetryPrefix ?? "paysafe";
|
|
164
|
+
const fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
165
|
+
if (typeof fetchImpl !== "function") {
|
|
166
|
+
throw PaysafeError.invalidConfig(
|
|
167
|
+
"No fetch implementation available. Provide one via `fetch` in PaysafeClientOptions (the global fetch is unavailable in this runtime)."
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
this.fetchImpl = fetchImpl.bind(globalThis);
|
|
171
|
+
this.validate();
|
|
172
|
+
}
|
|
173
|
+
validate() {
|
|
174
|
+
if (!this.username) {
|
|
175
|
+
throw PaysafeError.invalidConfig("username is required");
|
|
176
|
+
}
|
|
177
|
+
if (!this.password) {
|
|
178
|
+
throw PaysafeError.invalidConfig("password is required");
|
|
179
|
+
}
|
|
180
|
+
if (this.environment !== "test" && this.environment !== "production") {
|
|
181
|
+
throw PaysafeError.invalidConfig('environment must be "test" or "production"');
|
|
182
|
+
}
|
|
183
|
+
if (this.maxRetries < 0) {
|
|
184
|
+
throw PaysafeError.invalidConfig("maxRetries must be >= 0");
|
|
185
|
+
}
|
|
186
|
+
if (this.rateLimit.limit <= 0 || this.rateLimit.windowMs <= 0) {
|
|
187
|
+
throw PaysafeError.invalidConfig("rateLimit must have a positive limit and windowMs");
|
|
188
|
+
}
|
|
189
|
+
if (this.timeoutMs <= 0) {
|
|
190
|
+
throw PaysafeError.invalidConfig("timeoutMs must be > 0");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
baseUrl() {
|
|
194
|
+
return this.baseUrlOverride ?? BASE_URLS[this.environment];
|
|
195
|
+
}
|
|
196
|
+
paymentsUrl() {
|
|
197
|
+
return this.baseUrl() + PAYMENTS_PATH;
|
|
198
|
+
}
|
|
199
|
+
schedulerUrl() {
|
|
200
|
+
return this.baseUrl() + SCHEDULER_PATH;
|
|
201
|
+
}
|
|
202
|
+
applicationsUrl() {
|
|
203
|
+
return this.baseUrl() + APPLICATIONS_PATH;
|
|
204
|
+
}
|
|
205
|
+
fxRatesUrl() {
|
|
206
|
+
return this.baseUrl() + FX_RATES_PATH;
|
|
207
|
+
}
|
|
208
|
+
bankAccountValidatorUrl() {
|
|
209
|
+
return this.baseUrl() + BANK_ACCOUNT_VALIDATOR_PATH;
|
|
210
|
+
}
|
|
211
|
+
customerIdentificationUrl() {
|
|
212
|
+
return this.baseUrl() + CUSTOMER_IDENTIFICATION_PATH;
|
|
213
|
+
}
|
|
214
|
+
/** Computes the Base64-encoded HTTP Basic Auth header value. */
|
|
215
|
+
authHeader() {
|
|
216
|
+
const raw = `${this.username}:${this.password}`;
|
|
217
|
+
return `Basic ${base64Encode(raw)}`;
|
|
218
|
+
}
|
|
219
|
+
/** Returns `override` if non-empty, otherwise the configured default account ID. */
|
|
220
|
+
resolveAccountId(override) {
|
|
221
|
+
if (override) return override;
|
|
222
|
+
if (this.accountId) return this.accountId;
|
|
223
|
+
throw PaysafeError.invalidParams(
|
|
224
|
+
"accountId is required: pass it explicitly or set a default via PaysafeClientOptions.accountId"
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
function base64Encode(raw) {
|
|
229
|
+
if (typeof btoa === "function") {
|
|
230
|
+
return btoa(raw);
|
|
231
|
+
}
|
|
232
|
+
return Buffer.from(raw, "utf-8").toString("base64");
|
|
233
|
+
}
|
|
234
|
+
function configFromEnv(overrides = {}) {
|
|
235
|
+
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
236
|
+
if (!env) {
|
|
237
|
+
throw PaysafeError.invalidConfig(
|
|
238
|
+
"configFromEnv() requires a Node.js-like `process.env`; pass options explicitly in this runtime."
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
const base = {
|
|
242
|
+
username: env.PAYSAFE_USERNAME ?? "",
|
|
243
|
+
password: env.PAYSAFE_PASSWORD ?? ""
|
|
244
|
+
};
|
|
245
|
+
if (env.PAYSAFE_ENVIRONMENT === "production") {
|
|
246
|
+
base.environment = "production";
|
|
247
|
+
}
|
|
248
|
+
if (env.PAYSAFE_ACCOUNT_ID) {
|
|
249
|
+
base.accountId = env.PAYSAFE_ACCOUNT_ID;
|
|
250
|
+
}
|
|
251
|
+
return { ...base, ...overrides };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/core/telemetry.ts
|
|
255
|
+
function hasStatusCode(value) {
|
|
256
|
+
return typeof value === "object" && value !== null && "httpStatus" in value;
|
|
257
|
+
}
|
|
258
|
+
async function span(hook, meta, fn) {
|
|
259
|
+
const start = Date.now();
|
|
260
|
+
hook?.onStart?.(meta);
|
|
261
|
+
try {
|
|
262
|
+
const result = await fn();
|
|
263
|
+
hook?.onStop?.({
|
|
264
|
+
...meta,
|
|
265
|
+
durationMs: Date.now() - start,
|
|
266
|
+
httpStatus: 0,
|
|
267
|
+
ok: true
|
|
268
|
+
});
|
|
269
|
+
return result;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
const httpStatus = hasStatusCode(error) ? error.httpStatus ?? 0 : 0;
|
|
272
|
+
hook?.onStop?.({
|
|
273
|
+
...meta,
|
|
274
|
+
durationMs: Date.now() - start,
|
|
275
|
+
httpStatus,
|
|
276
|
+
ok: false,
|
|
277
|
+
error
|
|
278
|
+
});
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/core/http-client.ts
|
|
284
|
+
var SDK_USER_AGENT = "paysafe-sdk/1.0.0 (+https://github.com/iamkanishka/paysafe-sdk)";
|
|
285
|
+
var MAX_JITTER_MS = 100;
|
|
286
|
+
function backoffDelayMs(baseMs, attempt) {
|
|
287
|
+
const multiplier = 2 ** attempt;
|
|
288
|
+
const jitter = Math.random() * MAX_JITTER_MS;
|
|
289
|
+
return baseMs * multiplier + jitter;
|
|
290
|
+
}
|
|
291
|
+
function sleep(ms, signal) {
|
|
292
|
+
return new Promise((resolve, reject) => {
|
|
293
|
+
const timer = setTimeout(resolve, ms);
|
|
294
|
+
signal?.addEventListener(
|
|
295
|
+
"abort",
|
|
296
|
+
() => {
|
|
297
|
+
clearTimeout(timer);
|
|
298
|
+
reject(PaysafeError.contextCanceled(signal.reason));
|
|
299
|
+
},
|
|
300
|
+
{ once: true }
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
async function decodeErrorBody(response) {
|
|
305
|
+
const text = await response.text();
|
|
306
|
+
if (!text) return {};
|
|
307
|
+
try {
|
|
308
|
+
const parsed = JSON.parse(text);
|
|
309
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
310
|
+
return parsed;
|
|
311
|
+
}
|
|
312
|
+
return { message: text };
|
|
313
|
+
} catch {
|
|
314
|
+
return { message: text };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
var HttpTransport = class {
|
|
318
|
+
constructor(cfg, limiter, hook) {
|
|
319
|
+
this.cfg = cfg;
|
|
320
|
+
this.limiter = limiter;
|
|
321
|
+
this.hook = hook;
|
|
322
|
+
}
|
|
323
|
+
cfg;
|
|
324
|
+
limiter;
|
|
325
|
+
hook;
|
|
326
|
+
/**
|
|
327
|
+
* Performs the HTTP round trip and returns the raw response body as
|
|
328
|
+
* text. Used directly for endpoints with no JSON object response body
|
|
329
|
+
* (e.g. `DELETE`).
|
|
330
|
+
*/
|
|
331
|
+
async raw(options) {
|
|
332
|
+
const meta = {
|
|
333
|
+
prefix: this.cfg.telemetryPrefix,
|
|
334
|
+
api: options.api,
|
|
335
|
+
method: options.method,
|
|
336
|
+
path: options.url,
|
|
337
|
+
operation: options.operation
|
|
338
|
+
};
|
|
339
|
+
return span(this.hook, meta, async () => {
|
|
340
|
+
this.limiter.allow(this.cfg.username, this.cfg.rateLimit.limit, this.cfg.rateLimit.windowMs);
|
|
341
|
+
return this.doWithRetry(options, 0);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Performs the HTTP round trip and decodes the JSON response body as
|
|
346
|
+
* `T`. This is the common case used by nearly every resource method.
|
|
347
|
+
*/
|
|
348
|
+
async do(options) {
|
|
349
|
+
const raw = await this.raw(options);
|
|
350
|
+
if (!raw) {
|
|
351
|
+
return {};
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
return JSON.parse(raw);
|
|
355
|
+
} catch (cause) {
|
|
356
|
+
throw PaysafeError.decodeError(cause);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
async doWithRetry(options, attempt) {
|
|
360
|
+
try {
|
|
361
|
+
return await this.doOnce(options);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
const retryable = err instanceof PaysafeError && err.isRetryable();
|
|
364
|
+
if (retryable && attempt < this.cfg.maxRetries) {
|
|
365
|
+
const delay = backoffDelayMs(this.cfg.retryBaseDelayMs, attempt);
|
|
366
|
+
await sleep(delay, options.signal);
|
|
367
|
+
return this.doWithRetry(options, attempt + 1);
|
|
368
|
+
}
|
|
369
|
+
throw err;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async doOnce(options) {
|
|
373
|
+
const controller = new AbortController();
|
|
374
|
+
const timeoutTimer = setTimeout(
|
|
375
|
+
() => controller.abort(new Error("timeout")),
|
|
376
|
+
this.cfg.timeoutMs
|
|
377
|
+
);
|
|
378
|
+
if (options.signal) {
|
|
379
|
+
if (options.signal.aborted) {
|
|
380
|
+
controller.abort(options.signal.reason);
|
|
381
|
+
} else {
|
|
382
|
+
options.signal.addEventListener("abort", () => controller.abort(options.signal?.reason), {
|
|
383
|
+
once: true
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const headers = {
|
|
388
|
+
Authorization: this.cfg.authHeader(),
|
|
389
|
+
Accept: "application/json",
|
|
390
|
+
"User-Agent": SDK_USER_AGENT
|
|
391
|
+
};
|
|
392
|
+
let requestBody;
|
|
393
|
+
if (options.body !== void 0) {
|
|
394
|
+
try {
|
|
395
|
+
requestBody = JSON.stringify(options.body);
|
|
396
|
+
} catch (cause) {
|
|
397
|
+
clearTimeout(timeoutTimer);
|
|
398
|
+
throw PaysafeError.invalidParams(`failed to encode request body: ${String(cause)}`);
|
|
399
|
+
}
|
|
400
|
+
headers["Content-Type"] = "application/json";
|
|
401
|
+
}
|
|
402
|
+
let response;
|
|
403
|
+
try {
|
|
404
|
+
response = await this.cfg.fetchImpl(options.url, {
|
|
405
|
+
method: options.method,
|
|
406
|
+
headers,
|
|
407
|
+
body: requestBody,
|
|
408
|
+
signal: controller.signal
|
|
409
|
+
});
|
|
410
|
+
} catch (cause) {
|
|
411
|
+
clearTimeout(timeoutTimer);
|
|
412
|
+
const timedOut = controller.signal.aborted;
|
|
413
|
+
throw PaysafeError.httpError(cause, timedOut);
|
|
414
|
+
}
|
|
415
|
+
clearTimeout(timeoutTimer);
|
|
416
|
+
if (response.ok) {
|
|
417
|
+
return response.text();
|
|
418
|
+
}
|
|
419
|
+
const errorBody = await decodeErrorBody(response);
|
|
420
|
+
throw PaysafeError.fromApiResponse(errorBody, response.status);
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// src/core/rate-limiter.ts
|
|
425
|
+
function newBucket(limit, windowMs) {
|
|
426
|
+
return {
|
|
427
|
+
capacity: limit,
|
|
428
|
+
tokens: limit,
|
|
429
|
+
refillRatePerMs: limit / windowMs,
|
|
430
|
+
updatedAt: Date.now()
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function tryConsume(bucket) {
|
|
434
|
+
const now = Date.now();
|
|
435
|
+
const elapsedMs = now - bucket.updatedAt;
|
|
436
|
+
bucket.updatedAt = now;
|
|
437
|
+
bucket.tokens = Math.min(bucket.capacity, bucket.tokens + elapsedMs * bucket.refillRatePerMs);
|
|
438
|
+
if (bucket.tokens >= 1) {
|
|
439
|
+
bucket.tokens -= 1;
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
var RateLimiter = class {
|
|
445
|
+
buckets = /* @__PURE__ */ new Map();
|
|
446
|
+
bucketFor(key, limit, windowMs) {
|
|
447
|
+
let bucket = this.buckets.get(key);
|
|
448
|
+
if (!bucket) {
|
|
449
|
+
bucket = newBucket(limit, windowMs);
|
|
450
|
+
this.buckets.set(key, bucket);
|
|
451
|
+
}
|
|
452
|
+
return bucket;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Checks (and, if permitted, consumes) one token for `key`. Throws a
|
|
456
|
+
* {@link PaysafeError} with `kind === "rate_limited"` if the bucket is
|
|
457
|
+
* exhausted.
|
|
458
|
+
*/
|
|
459
|
+
allow(key, limit, windowMs) {
|
|
460
|
+
const window = windowMs > 0 ? windowMs : 1e3;
|
|
461
|
+
const bucket = this.bucketFor(key, limit, window);
|
|
462
|
+
if (!tryConsume(bucket)) {
|
|
463
|
+
throw PaysafeError.rateLimited();
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// src/types/shared.ts
|
|
469
|
+
function findLinkByRel(links, rel) {
|
|
470
|
+
return links?.find((link) => link.rel === rel)?.href;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/resources/payment-handles.ts
|
|
474
|
+
var API_NAME = "payments";
|
|
475
|
+
var PaymentHandlesResource = class {
|
|
476
|
+
constructor(cfg, transport) {
|
|
477
|
+
this.cfg = cfg;
|
|
478
|
+
this.transport = transport;
|
|
479
|
+
}
|
|
480
|
+
cfg;
|
|
481
|
+
transport;
|
|
482
|
+
/**
|
|
483
|
+
* Tokenizes a payment instrument, returning a Payment Handle. If
|
|
484
|
+
* `req.accountId` is omitted, the client's default account ID is used.
|
|
485
|
+
*/
|
|
486
|
+
async create(req, signal) {
|
|
487
|
+
const accountId = req.accountId ?? this.cfg.resolveAccountId();
|
|
488
|
+
const body = { ...req, accountId };
|
|
489
|
+
const url = `${this.cfg.paymentsUrl()}/paymenthandles`;
|
|
490
|
+
return this.transport.do({
|
|
491
|
+
api: API_NAME,
|
|
492
|
+
operation: "paymentHandles.create",
|
|
493
|
+
method: "POST",
|
|
494
|
+
url,
|
|
495
|
+
body,
|
|
496
|
+
signal
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Retrieves a Payment Handle by ID. Useful for polling status if
|
|
501
|
+
* webhooks are not received; always verify via `get` after receiving a
|
|
502
|
+
* webhook before trusting the status.
|
|
503
|
+
*/
|
|
504
|
+
async get(paymentHandleId, signal) {
|
|
505
|
+
const url = `${this.cfg.paymentsUrl()}/paymenthandles/${encodeURIComponent(paymentHandleId)}`;
|
|
506
|
+
return this.transport.do({
|
|
507
|
+
api: API_NAME,
|
|
508
|
+
operation: "paymentHandles.get",
|
|
509
|
+
method: "GET",
|
|
510
|
+
url,
|
|
511
|
+
signal
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
function paymentHandleRedirectUrl(handle) {
|
|
516
|
+
if (handle.action !== "REDIRECT") return void 0;
|
|
517
|
+
return findLinkByRel(handle.links, "payment_redirect");
|
|
518
|
+
}
|
|
519
|
+
function paymentHandleRequiresAction(handle) {
|
|
520
|
+
return handle.action === "REDIRECT";
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/resources/payments.ts
|
|
524
|
+
var API_NAME2 = "payments";
|
|
525
|
+
var PaymentsResource = class {
|
|
526
|
+
constructor(cfg, transport) {
|
|
527
|
+
this.cfg = cfg;
|
|
528
|
+
this.transport = transport;
|
|
529
|
+
}
|
|
530
|
+
cfg;
|
|
531
|
+
transport;
|
|
532
|
+
/** Creates a payment using a `paymentHandleToken` from a `PAYABLE` Payment Handle. */
|
|
533
|
+
async create(req, signal) {
|
|
534
|
+
const url = `${this.cfg.paymentsUrl()}/payments`;
|
|
535
|
+
return this.transport.do({
|
|
536
|
+
api: API_NAME2,
|
|
537
|
+
operation: "payments.create",
|
|
538
|
+
method: "POST",
|
|
539
|
+
url,
|
|
540
|
+
body: req,
|
|
541
|
+
signal
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
/** Retrieves a payment by ID. */
|
|
545
|
+
async get(paymentId, signal) {
|
|
546
|
+
const url = `${this.cfg.paymentsUrl()}/payments/${encodeURIComponent(paymentId)}`;
|
|
547
|
+
return this.transport.do({
|
|
548
|
+
api: API_NAME2,
|
|
549
|
+
operation: "payments.get",
|
|
550
|
+
method: "GET",
|
|
551
|
+
url,
|
|
552
|
+
signal
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
/** Returns payments matching the given filter. */
|
|
556
|
+
async list(filter = {}, signal) {
|
|
557
|
+
const query = new URLSearchParams();
|
|
558
|
+
if (filter.merchantRefNum) query.set("merchantRefNum", filter.merchantRefNum);
|
|
559
|
+
if (filter.startDate) query.set("startDate", filter.startDate);
|
|
560
|
+
if (filter.endDate) query.set("endDate", filter.endDate);
|
|
561
|
+
if (filter.limit !== void 0) query.set("limit", String(filter.limit));
|
|
562
|
+
if (filter.offset !== void 0) query.set("offset", String(filter.offset));
|
|
563
|
+
const qs = query.toString();
|
|
564
|
+
const url = `${this.cfg.paymentsUrl()}/payments${qs ? `?${qs}` : ""}`;
|
|
565
|
+
const resp = await this.transport.do({
|
|
566
|
+
api: API_NAME2,
|
|
567
|
+
operation: "payments.list",
|
|
568
|
+
method: "GET",
|
|
569
|
+
url,
|
|
570
|
+
signal
|
|
571
|
+
});
|
|
572
|
+
return resp.payments;
|
|
573
|
+
}
|
|
574
|
+
/** Cancels an authorized (not-yet-settled) payment. */
|
|
575
|
+
async cancel(paymentId, signal) {
|
|
576
|
+
const url = `${this.cfg.paymentsUrl()}/payments/${encodeURIComponent(paymentId)}/cancels`;
|
|
577
|
+
return this.transport.do({
|
|
578
|
+
api: API_NAME2,
|
|
579
|
+
operation: "payments.cancel",
|
|
580
|
+
method: "POST",
|
|
581
|
+
url,
|
|
582
|
+
body: {},
|
|
583
|
+
signal
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
// src/resources/settlements.ts
|
|
589
|
+
var API_NAME3 = "payments";
|
|
590
|
+
var SettlementsResource = class {
|
|
591
|
+
constructor(cfg, transport) {
|
|
592
|
+
this.cfg = cfg;
|
|
593
|
+
this.transport = transport;
|
|
594
|
+
}
|
|
595
|
+
cfg;
|
|
596
|
+
transport;
|
|
597
|
+
/** Settles (captures) an authorized payment by payment ID. */
|
|
598
|
+
async create(paymentId, req, signal) {
|
|
599
|
+
const url = `${this.cfg.paymentsUrl()}/payments/${encodeURIComponent(paymentId)}/settlements`;
|
|
600
|
+
return this.transport.do({
|
|
601
|
+
api: API_NAME3,
|
|
602
|
+
operation: "settlements.create",
|
|
603
|
+
method: "POST",
|
|
604
|
+
url,
|
|
605
|
+
body: req,
|
|
606
|
+
signal
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/** Retrieves a settlement by ID. */
|
|
610
|
+
async get(settlementId, signal) {
|
|
611
|
+
const url = `${this.cfg.paymentsUrl()}/settlements/${encodeURIComponent(settlementId)}`;
|
|
612
|
+
return this.transport.do({
|
|
613
|
+
api: API_NAME3,
|
|
614
|
+
operation: "settlements.get",
|
|
615
|
+
method: "GET",
|
|
616
|
+
url,
|
|
617
|
+
signal
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
/** Cancels a pending settlement. */
|
|
621
|
+
async cancel(settlementId, signal) {
|
|
622
|
+
const url = `${this.cfg.paymentsUrl()}/settlements/${encodeURIComponent(settlementId)}/cancels`;
|
|
623
|
+
return this.transport.do({
|
|
624
|
+
api: API_NAME3,
|
|
625
|
+
operation: "settlements.cancel",
|
|
626
|
+
method: "POST",
|
|
627
|
+
url,
|
|
628
|
+
body: {},
|
|
629
|
+
signal
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
// src/resources/refunds.ts
|
|
635
|
+
var API_NAME4 = "payments";
|
|
636
|
+
var RefundsResource = class {
|
|
637
|
+
constructor(cfg, transport) {
|
|
638
|
+
this.cfg = cfg;
|
|
639
|
+
this.transport = transport;
|
|
640
|
+
}
|
|
641
|
+
cfg;
|
|
642
|
+
transport;
|
|
643
|
+
/** Issues a refund for a completed payment or settlement. */
|
|
644
|
+
async create(settlementId, req, signal) {
|
|
645
|
+
const url = `${this.cfg.paymentsUrl()}/settlements/${encodeURIComponent(settlementId)}/refunds`;
|
|
646
|
+
return this.transport.do({
|
|
647
|
+
api: API_NAME4,
|
|
648
|
+
operation: "refunds.create",
|
|
649
|
+
method: "POST",
|
|
650
|
+
url,
|
|
651
|
+
body: req,
|
|
652
|
+
signal
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
/** Retrieves a refund by ID. */
|
|
656
|
+
async get(refundId, signal) {
|
|
657
|
+
const url = `${this.cfg.paymentsUrl()}/refunds/${encodeURIComponent(refundId)}`;
|
|
658
|
+
return this.transport.do({
|
|
659
|
+
api: API_NAME4,
|
|
660
|
+
operation: "refunds.get",
|
|
661
|
+
method: "GET",
|
|
662
|
+
url,
|
|
663
|
+
signal
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
/** Cancels a pending refund. */
|
|
667
|
+
async cancel(refundId, signal) {
|
|
668
|
+
const url = `${this.cfg.paymentsUrl()}/refunds/${encodeURIComponent(refundId)}/cancels`;
|
|
669
|
+
return this.transport.do({
|
|
670
|
+
api: API_NAME4,
|
|
671
|
+
operation: "refunds.cancel",
|
|
672
|
+
method: "POST",
|
|
673
|
+
url,
|
|
674
|
+
body: {},
|
|
675
|
+
signal
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// src/resources/payouts.ts
|
|
681
|
+
var API_NAME5 = "payments";
|
|
682
|
+
var PayoutsResource = class {
|
|
683
|
+
constructor(cfg, transport) {
|
|
684
|
+
this.cfg = cfg;
|
|
685
|
+
this.transport = transport;
|
|
686
|
+
}
|
|
687
|
+
cfg;
|
|
688
|
+
transport;
|
|
689
|
+
/**
|
|
690
|
+
* Creates a standalone credit (payout for non-iGaming merchants). The
|
|
691
|
+
* Payment Handle must have `transactionType: "STANDALONE_CREDIT"`.
|
|
692
|
+
*/
|
|
693
|
+
async standaloneCredit(req, signal) {
|
|
694
|
+
const accountId = req.accountId ?? this.cfg.resolveAccountId();
|
|
695
|
+
const body = { ...req, accountId };
|
|
696
|
+
const url = `${this.cfg.paymentsUrl()}/standalonecredits`;
|
|
697
|
+
return this.transport.do({
|
|
698
|
+
api: API_NAME5,
|
|
699
|
+
operation: "payouts.standaloneCredit",
|
|
700
|
+
method: "POST",
|
|
701
|
+
url,
|
|
702
|
+
body,
|
|
703
|
+
signal
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Creates an original credit (payout for iGaming merchants). The
|
|
708
|
+
* Payment Handle must have `transactionType: "ORIGINAL_CREDIT"`.
|
|
709
|
+
*/
|
|
710
|
+
async originalCredit(req, signal) {
|
|
711
|
+
const accountId = req.accountId ?? this.cfg.resolveAccountId();
|
|
712
|
+
const body = { ...req, accountId };
|
|
713
|
+
const url = `${this.cfg.paymentsUrl()}/originalcredits`;
|
|
714
|
+
return this.transport.do({
|
|
715
|
+
api: API_NAME5,
|
|
716
|
+
operation: "payouts.originalCredit",
|
|
717
|
+
method: "POST",
|
|
718
|
+
url,
|
|
719
|
+
body,
|
|
720
|
+
signal
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
/** Retrieves a standalone credit by ID. */
|
|
724
|
+
async getStandaloneCredit(creditId, signal) {
|
|
725
|
+
const url = `${this.cfg.paymentsUrl()}/standalonecredits/${encodeURIComponent(creditId)}`;
|
|
726
|
+
return this.transport.do({
|
|
727
|
+
api: API_NAME5,
|
|
728
|
+
operation: "payouts.getStandaloneCredit",
|
|
729
|
+
method: "GET",
|
|
730
|
+
url,
|
|
731
|
+
signal
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
/** Retrieves an original credit by ID. */
|
|
735
|
+
async getOriginalCredit(creditId, signal) {
|
|
736
|
+
const url = `${this.cfg.paymentsUrl()}/originalcredits/${encodeURIComponent(creditId)}`;
|
|
737
|
+
return this.transport.do({
|
|
738
|
+
api: API_NAME5,
|
|
739
|
+
operation: "payouts.getOriginalCredit",
|
|
740
|
+
method: "GET",
|
|
741
|
+
url,
|
|
742
|
+
signal
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
/** Cancels a standalone credit. */
|
|
746
|
+
async cancelStandaloneCredit(creditId, signal) {
|
|
747
|
+
const url = `${this.cfg.paymentsUrl()}/standalonecredits/${encodeURIComponent(creditId)}/cancels`;
|
|
748
|
+
return this.transport.do({
|
|
749
|
+
api: API_NAME5,
|
|
750
|
+
operation: "payouts.cancelStandaloneCredit",
|
|
751
|
+
method: "POST",
|
|
752
|
+
url,
|
|
753
|
+
body: {},
|
|
754
|
+
signal
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
// src/resources/verifications.ts
|
|
760
|
+
var API_NAME6 = "payments";
|
|
761
|
+
var VerificationsResource = class {
|
|
762
|
+
constructor(cfg, transport) {
|
|
763
|
+
this.cfg = cfg;
|
|
764
|
+
this.transport = transport;
|
|
765
|
+
}
|
|
766
|
+
cfg;
|
|
767
|
+
transport;
|
|
768
|
+
/** Creates a card verification. */
|
|
769
|
+
async create(req, signal) {
|
|
770
|
+
const url = `${this.cfg.paymentsUrl()}/verifications`;
|
|
771
|
+
return this.transport.do({
|
|
772
|
+
api: API_NAME6,
|
|
773
|
+
operation: "verifications.create",
|
|
774
|
+
method: "POST",
|
|
775
|
+
url,
|
|
776
|
+
body: req,
|
|
777
|
+
signal
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
/** Retrieves a verification by ID. */
|
|
781
|
+
async get(verificationId, signal) {
|
|
782
|
+
const url = `${this.cfg.paymentsUrl()}/verifications/${encodeURIComponent(verificationId)}`;
|
|
783
|
+
return this.transport.do({
|
|
784
|
+
api: API_NAME6,
|
|
785
|
+
operation: "verifications.get",
|
|
786
|
+
method: "GET",
|
|
787
|
+
url,
|
|
788
|
+
signal
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
// src/resources/customers.ts
|
|
794
|
+
var API_NAME7 = "payments";
|
|
795
|
+
var CustomersResource = class {
|
|
796
|
+
constructor(cfg, transport) {
|
|
797
|
+
this.cfg = cfg;
|
|
798
|
+
this.transport = transport;
|
|
799
|
+
}
|
|
800
|
+
cfg;
|
|
801
|
+
transport;
|
|
802
|
+
/** Creates a customer profile in the vault. */
|
|
803
|
+
async create(req, signal) {
|
|
804
|
+
const accountId = req.accountId ?? this.cfg.resolveAccountId();
|
|
805
|
+
const body = { ...req, accountId };
|
|
806
|
+
const url = `${this.cfg.paymentsUrl()}/customers`;
|
|
807
|
+
return this.transport.do({
|
|
808
|
+
api: API_NAME7,
|
|
809
|
+
operation: "customers.create",
|
|
810
|
+
method: "POST",
|
|
811
|
+
url,
|
|
812
|
+
body,
|
|
813
|
+
signal
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
/** Retrieves a customer profile by Paysafe's customer ID. */
|
|
817
|
+
async get(customerId, signal) {
|
|
818
|
+
const url = `${this.cfg.paymentsUrl()}/customers/${encodeURIComponent(customerId)}`;
|
|
819
|
+
return this.transport.do({
|
|
820
|
+
api: API_NAME7,
|
|
821
|
+
operation: "customers.get",
|
|
822
|
+
method: "GET",
|
|
823
|
+
url,
|
|
824
|
+
signal
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
/** Retrieves a customer profile by your own `merchantCustomerId`. */
|
|
828
|
+
async getByMerchantCustomerId(merchantCustomerId, signal) {
|
|
829
|
+
const url = `${this.cfg.paymentsUrl()}/customers?merchantCustomerId=${encodeURIComponent(merchantCustomerId)}`;
|
|
830
|
+
return this.transport.do({
|
|
831
|
+
api: API_NAME7,
|
|
832
|
+
operation: "customers.getByMerchantCustomerId",
|
|
833
|
+
method: "GET",
|
|
834
|
+
url,
|
|
835
|
+
signal
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
/** Updates a customer profile. Only the fields set in `req` are changed server-side. */
|
|
839
|
+
async update(customerId, req, signal) {
|
|
840
|
+
const url = `${this.cfg.paymentsUrl()}/customers/${encodeURIComponent(customerId)}`;
|
|
841
|
+
return this.transport.do({
|
|
842
|
+
api: API_NAME7,
|
|
843
|
+
operation: "customers.update",
|
|
844
|
+
method: "PUT",
|
|
845
|
+
url,
|
|
846
|
+
body: req,
|
|
847
|
+
signal
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
/** Deletes a customer profile. */
|
|
851
|
+
async delete(customerId, signal) {
|
|
852
|
+
const url = `${this.cfg.paymentsUrl()}/customers/${encodeURIComponent(customerId)}`;
|
|
853
|
+
await this.transport.raw({
|
|
854
|
+
api: API_NAME7,
|
|
855
|
+
operation: "customers.delete",
|
|
856
|
+
method: "DELETE",
|
|
857
|
+
url,
|
|
858
|
+
signal
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Converts a single-use token (SUT) returned from an initial payment
|
|
863
|
+
* into a multi-use token (MUT) attached to the customer profile,
|
|
864
|
+
* enabling saved-card/recurring flows.
|
|
865
|
+
*/
|
|
866
|
+
async createPaymentHandle(customerId, req, signal) {
|
|
867
|
+
const url = `${this.cfg.paymentsUrl()}/customers/${encodeURIComponent(customerId)}/paymenthandles`;
|
|
868
|
+
return this.transport.do({
|
|
869
|
+
api: API_NAME7,
|
|
870
|
+
operation: "customers.createPaymentHandle",
|
|
871
|
+
method: "POST",
|
|
872
|
+
url,
|
|
873
|
+
body: req,
|
|
874
|
+
signal
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
/** Lists all saved payment handles (saved instruments) for a customer. */
|
|
878
|
+
async listPaymentHandles(customerId, signal) {
|
|
879
|
+
const url = `${this.cfg.paymentsUrl()}/customers/${encodeURIComponent(customerId)}/paymenthandles`;
|
|
880
|
+
const resp = await this.transport.do({
|
|
881
|
+
api: API_NAME7,
|
|
882
|
+
operation: "customers.listPaymentHandles",
|
|
883
|
+
method: "GET",
|
|
884
|
+
url,
|
|
885
|
+
signal
|
|
886
|
+
});
|
|
887
|
+
return resp.paymentHandles;
|
|
888
|
+
}
|
|
889
|
+
/** Deletes a specific saved payment handle for a customer. */
|
|
890
|
+
async deletePaymentHandle(customerId, handleId, signal) {
|
|
891
|
+
const url = `${this.cfg.paymentsUrl()}/customers/${encodeURIComponent(customerId)}/paymenthandles/${encodeURIComponent(handleId)}`;
|
|
892
|
+
await this.transport.raw({
|
|
893
|
+
api: API_NAME7,
|
|
894
|
+
operation: "customers.deletePaymentHandle",
|
|
895
|
+
method: "DELETE",
|
|
896
|
+
url,
|
|
897
|
+
signal
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Tokenizes the customer's entire saved profile — profile details,
|
|
902
|
+
* saved cards, addresses, and bank mandates — into one short-lived
|
|
903
|
+
* token valid for 900 seconds. Used to display a returning customer's
|
|
904
|
+
* saved instruments, or to re-collect a one-time CVV for a saved card.
|
|
905
|
+
*/
|
|
906
|
+
async createSingleUseCustomerToken(customerId, signal) {
|
|
907
|
+
const url = `${this.cfg.paymentsUrl()}/customers/${encodeURIComponent(customerId)}/singleusecustomertokens`;
|
|
908
|
+
return this.transport.do({
|
|
909
|
+
api: API_NAME7,
|
|
910
|
+
operation: "customers.createSingleUseCustomerToken",
|
|
911
|
+
method: "POST",
|
|
912
|
+
url,
|
|
913
|
+
body: {},
|
|
914
|
+
signal
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
// src/resources/applications.ts
|
|
920
|
+
var API_NAME8 = "applications";
|
|
921
|
+
var ApplicationsResource = class {
|
|
922
|
+
constructor(cfg, transport) {
|
|
923
|
+
this.cfg = cfg;
|
|
924
|
+
this.transport = transport;
|
|
925
|
+
}
|
|
926
|
+
cfg;
|
|
927
|
+
transport;
|
|
928
|
+
/** Creates a draft merchant application. */
|
|
929
|
+
async create(req, signal) {
|
|
930
|
+
const url = `${this.cfg.applicationsUrl()}/applications`;
|
|
931
|
+
return this.transport.do({
|
|
932
|
+
api: API_NAME8,
|
|
933
|
+
operation: "applications.create",
|
|
934
|
+
method: "POST",
|
|
935
|
+
url,
|
|
936
|
+
body: req,
|
|
937
|
+
signal
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
/** Retrieves an application by ID. */
|
|
941
|
+
async get(applicationId, signal) {
|
|
942
|
+
const url = `${this.cfg.applicationsUrl()}/applications/${encodeURIComponent(applicationId)}`;
|
|
943
|
+
return this.transport.do({
|
|
944
|
+
api: API_NAME8,
|
|
945
|
+
operation: "applications.get",
|
|
946
|
+
method: "GET",
|
|
947
|
+
url,
|
|
948
|
+
signal
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
/** Updates a draft application. Only possible before `submit` is called. */
|
|
952
|
+
async update(applicationId, req, signal) {
|
|
953
|
+
const url = `${this.cfg.applicationsUrl()}/applications/${encodeURIComponent(applicationId)}`;
|
|
954
|
+
return this.transport.do({
|
|
955
|
+
api: API_NAME8,
|
|
956
|
+
operation: "applications.update",
|
|
957
|
+
method: "PUT",
|
|
958
|
+
url,
|
|
959
|
+
body: req,
|
|
960
|
+
signal
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Sends the application to Paysafe for review. After this call
|
|
965
|
+
* succeeds, the application can no longer be modified via the API.
|
|
966
|
+
*/
|
|
967
|
+
async submit(applicationId, signal) {
|
|
968
|
+
const url = `${this.cfg.applicationsUrl()}/applications/${encodeURIComponent(applicationId)}`;
|
|
969
|
+
return this.transport.do({
|
|
970
|
+
api: API_NAME8,
|
|
971
|
+
operation: "applications.submit",
|
|
972
|
+
method: "PATCH",
|
|
973
|
+
url,
|
|
974
|
+
body: {},
|
|
975
|
+
signal
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
/** Retrieves the terms the merchant must accept before processing can begin. */
|
|
979
|
+
async getTermsAndConditions(applicationId, signal) {
|
|
980
|
+
const url = `${this.cfg.applicationsUrl()}/applications/${encodeURIComponent(applicationId)}/termsandconditions`;
|
|
981
|
+
return this.transport.do({
|
|
982
|
+
api: API_NAME8,
|
|
983
|
+
operation: "applications.getTermsAndConditions",
|
|
984
|
+
method: "GET",
|
|
985
|
+
url,
|
|
986
|
+
signal
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Attaches a supporting document to an application (e.g. requested by
|
|
991
|
+
* a credit underwriter during review). Documents can be uploaded before
|
|
992
|
+
* or after submission.
|
|
993
|
+
*/
|
|
994
|
+
async uploadDocument(applicationId, req, signal) {
|
|
995
|
+
const url = `${this.cfg.applicationsUrl()}/applications/${encodeURIComponent(applicationId)}/documents`;
|
|
996
|
+
return this.transport.do({
|
|
997
|
+
api: API_NAME8,
|
|
998
|
+
operation: "applications.uploadDocument",
|
|
999
|
+
method: "POST",
|
|
1000
|
+
url,
|
|
1001
|
+
body: req,
|
|
1002
|
+
signal
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
// src/resources/plans.ts
|
|
1008
|
+
var API_NAME9 = "scheduler";
|
|
1009
|
+
var PlansResource = class {
|
|
1010
|
+
constructor(cfg, transport) {
|
|
1011
|
+
this.cfg = cfg;
|
|
1012
|
+
this.transport = transport;
|
|
1013
|
+
}
|
|
1014
|
+
cfg;
|
|
1015
|
+
transport;
|
|
1016
|
+
/**
|
|
1017
|
+
* Creates a recurring billing plan under `accountId` (or the client's
|
|
1018
|
+
* default account ID if `accountId` is omitted).
|
|
1019
|
+
*/
|
|
1020
|
+
async create(accountId, req, signal) {
|
|
1021
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1022
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/plans`;
|
|
1023
|
+
return this.transport.do({
|
|
1024
|
+
api: API_NAME9,
|
|
1025
|
+
operation: "plans.create",
|
|
1026
|
+
method: "POST",
|
|
1027
|
+
url,
|
|
1028
|
+
body: req,
|
|
1029
|
+
signal
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
/** Retrieves a plan by ID. */
|
|
1033
|
+
async get(accountId, planId, signal) {
|
|
1034
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1035
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/plans/${encodeURIComponent(planId)}`;
|
|
1036
|
+
return this.transport.do({
|
|
1037
|
+
api: API_NAME9,
|
|
1038
|
+
operation: "plans.get",
|
|
1039
|
+
method: "GET",
|
|
1040
|
+
url,
|
|
1041
|
+
signal
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
/** Lists all billing plans for an account. */
|
|
1045
|
+
async list(accountId, signal) {
|
|
1046
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1047
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/plans`;
|
|
1048
|
+
const resp = await this.transport.do({
|
|
1049
|
+
api: API_NAME9,
|
|
1050
|
+
operation: "plans.list",
|
|
1051
|
+
method: "GET",
|
|
1052
|
+
url,
|
|
1053
|
+
signal
|
|
1054
|
+
});
|
|
1055
|
+
return resp.plans;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Updates a plan's mutable fields. Keep amount increases below 20% to
|
|
1059
|
+
* comply with card scheme guidelines. Discontinuing a plan prevents new
|
|
1060
|
+
* subscriptions but does not affect existing ones.
|
|
1061
|
+
*/
|
|
1062
|
+
async update(accountId, planId, req, signal) {
|
|
1063
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1064
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/plans/${encodeURIComponent(planId)}`;
|
|
1065
|
+
return this.transport.do({
|
|
1066
|
+
api: API_NAME9,
|
|
1067
|
+
operation: "plans.update",
|
|
1068
|
+
method: "PATCH",
|
|
1069
|
+
url,
|
|
1070
|
+
body: req,
|
|
1071
|
+
signal
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
/** Discontinues a plan. */
|
|
1075
|
+
async delete(accountId, planId, signal) {
|
|
1076
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1077
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/plans/${encodeURIComponent(planId)}`;
|
|
1078
|
+
await this.transport.raw({
|
|
1079
|
+
api: API_NAME9,
|
|
1080
|
+
operation: "plans.delete",
|
|
1081
|
+
method: "DELETE",
|
|
1082
|
+
url,
|
|
1083
|
+
signal
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// src/resources/subscriptions.ts
|
|
1089
|
+
var API_NAME10 = "scheduler";
|
|
1090
|
+
var SubscriptionsResource = class {
|
|
1091
|
+
constructor(cfg, transport) {
|
|
1092
|
+
this.cfg = cfg;
|
|
1093
|
+
this.transport = transport;
|
|
1094
|
+
}
|
|
1095
|
+
cfg;
|
|
1096
|
+
transport;
|
|
1097
|
+
/** Creates a subscription for a customer under a plan. */
|
|
1098
|
+
async create(accountId, req, signal) {
|
|
1099
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1100
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/subscriptions`;
|
|
1101
|
+
return this.transport.do({
|
|
1102
|
+
api: API_NAME10,
|
|
1103
|
+
operation: "subscriptions.create",
|
|
1104
|
+
method: "POST",
|
|
1105
|
+
url,
|
|
1106
|
+
body: req,
|
|
1107
|
+
signal
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
/** Retrieves a subscription by ID. */
|
|
1111
|
+
async get(accountId, subscriptionId, signal) {
|
|
1112
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1113
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/subscriptions/${encodeURIComponent(subscriptionId)}`;
|
|
1114
|
+
return this.transport.do({
|
|
1115
|
+
api: API_NAME10,
|
|
1116
|
+
operation: "subscriptions.get",
|
|
1117
|
+
method: "GET",
|
|
1118
|
+
url,
|
|
1119
|
+
signal
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
/** Lists subscriptions matching the given filter. */
|
|
1123
|
+
async list(accountId, filter = {}, signal) {
|
|
1124
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1125
|
+
const query = new URLSearchParams();
|
|
1126
|
+
if (filter.planId) query.set("planId", filter.planId);
|
|
1127
|
+
if (filter.status) query.set("status", filter.status);
|
|
1128
|
+
if (filter.merchantCustomerId) query.set("merchantCustomerId", filter.merchantCustomerId);
|
|
1129
|
+
if (filter.limit !== void 0) query.set("limit", String(filter.limit));
|
|
1130
|
+
if (filter.offset !== void 0) query.set("offset", String(filter.offset));
|
|
1131
|
+
const qs = query.toString();
|
|
1132
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/subscriptions${qs ? `?${qs}` : ""}`;
|
|
1133
|
+
const resp = await this.transport.do({
|
|
1134
|
+
api: API_NAME10,
|
|
1135
|
+
operation: "subscriptions.list",
|
|
1136
|
+
method: "GET",
|
|
1137
|
+
url,
|
|
1138
|
+
signal
|
|
1139
|
+
});
|
|
1140
|
+
return resp.subscriptions;
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Updates a subscription: extend cycles (`numCycles`), apply a discount
|
|
1144
|
+
* (`discountAmount`/`discountNumPayments`), or change `status` directly.
|
|
1145
|
+
* Prefer `cancel`/`suspend`/`reactivate` for status transitions.
|
|
1146
|
+
*/
|
|
1147
|
+
async update(accountId, subscriptionId, req, signal) {
|
|
1148
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1149
|
+
const url = `${this.cfg.schedulerUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/subscriptions/${encodeURIComponent(subscriptionId)}`;
|
|
1150
|
+
return this.transport.do({
|
|
1151
|
+
api: API_NAME10,
|
|
1152
|
+
operation: "subscriptions.update",
|
|
1153
|
+
method: "PATCH",
|
|
1154
|
+
url,
|
|
1155
|
+
body: req,
|
|
1156
|
+
signal
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
/** Cancels a subscription immediately. */
|
|
1160
|
+
async cancel(accountId, subscriptionId, signal) {
|
|
1161
|
+
return this.update(accountId, subscriptionId, { status: "CANCELLED" }, signal);
|
|
1162
|
+
}
|
|
1163
|
+
/** Pauses billing on a subscription; it can later be reactivated. */
|
|
1164
|
+
async suspend(accountId, subscriptionId, signal) {
|
|
1165
|
+
return this.update(accountId, subscriptionId, { status: "SUSPENDED" }, signal);
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Re-activates a suspended subscription. The last failed payment is
|
|
1169
|
+
* retried; subsequent cycles resume on their original schedule.
|
|
1170
|
+
*/
|
|
1171
|
+
async reactivate(accountId, subscriptionId, signal) {
|
|
1172
|
+
return this.update(accountId, subscriptionId, { status: "ACTIVE" }, signal);
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
// src/resources/fx-rates.ts
|
|
1177
|
+
var API_NAME11 = "fx_rates";
|
|
1178
|
+
var FxRatesResource = class {
|
|
1179
|
+
constructor(cfg, transport) {
|
|
1180
|
+
this.cfg = cfg;
|
|
1181
|
+
this.transport = transport;
|
|
1182
|
+
}
|
|
1183
|
+
cfg;
|
|
1184
|
+
transport;
|
|
1185
|
+
/** Gets a guaranteed FX rate for a currency pair. */
|
|
1186
|
+
async getRate(accountId, req, signal) {
|
|
1187
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1188
|
+
const url = `${this.cfg.fxRatesUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/fxrates`;
|
|
1189
|
+
return this.transport.do({
|
|
1190
|
+
api: API_NAME11,
|
|
1191
|
+
operation: "fxRates.getRate",
|
|
1192
|
+
method: "POST",
|
|
1193
|
+
url,
|
|
1194
|
+
body: req,
|
|
1195
|
+
signal
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
/** Retrieves a specific FX rate by quote ID. */
|
|
1199
|
+
async getQuote(accountId, quoteId, signal) {
|
|
1200
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1201
|
+
const url = `${this.cfg.fxRatesUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/fxrates/${encodeURIComponent(quoteId)}`;
|
|
1202
|
+
return this.transport.do({
|
|
1203
|
+
api: API_NAME11,
|
|
1204
|
+
operation: "fxRates.getQuote",
|
|
1205
|
+
method: "GET",
|
|
1206
|
+
url,
|
|
1207
|
+
signal
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
|
|
1212
|
+
// src/resources/customer-identity.ts
|
|
1213
|
+
var API_NAME12 = "customer_identity";
|
|
1214
|
+
var CustomerIdentityResource = class {
|
|
1215
|
+
constructor(cfg, transport) {
|
|
1216
|
+
this.cfg = cfg;
|
|
1217
|
+
this.transport = transport;
|
|
1218
|
+
}
|
|
1219
|
+
cfg;
|
|
1220
|
+
transport;
|
|
1221
|
+
/** Submits an identity verification request. */
|
|
1222
|
+
async verify(req, signal) {
|
|
1223
|
+
const url = `${this.cfg.customerIdentificationUrl()}/identityprofiles`;
|
|
1224
|
+
return this.transport.do({
|
|
1225
|
+
api: API_NAME12,
|
|
1226
|
+
operation: "customerIdentity.verify",
|
|
1227
|
+
method: "POST",
|
|
1228
|
+
url,
|
|
1229
|
+
body: req,
|
|
1230
|
+
signal
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
/** Retrieves a previously submitted identity profile by ID. */
|
|
1234
|
+
async get(profileId, signal) {
|
|
1235
|
+
const url = `${this.cfg.customerIdentificationUrl()}/identityprofiles/${encodeURIComponent(profileId)}`;
|
|
1236
|
+
return this.transport.do({
|
|
1237
|
+
api: API_NAME12,
|
|
1238
|
+
operation: "customerIdentity.get",
|
|
1239
|
+
method: "GET",
|
|
1240
|
+
url,
|
|
1241
|
+
signal
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Reruns a failed identity check. Only rerun a check whose `decision`
|
|
1246
|
+
* was `"ERROR"` — this indicates a transient downstream provider
|
|
1247
|
+
* issue. Do not rerun a check whose `decision` was `"FAIL"`;
|
|
1248
|
+
* resubmitting returns the same `"FAIL"` result again.
|
|
1249
|
+
*/
|
|
1250
|
+
async rerun(profileId, signal) {
|
|
1251
|
+
const url = `${this.cfg.customerIdentificationUrl()}/identityprofiles/${encodeURIComponent(profileId)}/rerunfailedchecks`;
|
|
1252
|
+
return this.transport.do({
|
|
1253
|
+
api: API_NAME12,
|
|
1254
|
+
operation: "customerIdentity.rerun",
|
|
1255
|
+
method: "POST",
|
|
1256
|
+
url,
|
|
1257
|
+
body: {},
|
|
1258
|
+
signal
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
// src/resources/bank-account-validation.ts
|
|
1264
|
+
var BankAccountValidationResource = class {
|
|
1265
|
+
constructor(cfg, transport) {
|
|
1266
|
+
this.cfg = cfg;
|
|
1267
|
+
this.transport = transport;
|
|
1268
|
+
}
|
|
1269
|
+
cfg;
|
|
1270
|
+
transport;
|
|
1271
|
+
/**
|
|
1272
|
+
* Starts a bank account verification session. Redirect the customer to
|
|
1273
|
+
* {@link bankVerificationRedirectUrl} to complete the flow; there is no
|
|
1274
|
+
* synchronous result.
|
|
1275
|
+
*/
|
|
1276
|
+
async create(accountId, req, signal) {
|
|
1277
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1278
|
+
const url = `${this.cfg.bankAccountValidatorUrl()}/accounts/${encodeURIComponent(resolvedAccountId)}/verifications`;
|
|
1279
|
+
return this.transport.do({
|
|
1280
|
+
api: "bank_account_validation",
|
|
1281
|
+
operation: "bankAccountValidation.create",
|
|
1282
|
+
method: "POST",
|
|
1283
|
+
url,
|
|
1284
|
+
body: req,
|
|
1285
|
+
signal
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Looks up a bank account verification by ID. Returns validated
|
|
1290
|
+
* account details and a single-use payment token once the customer has
|
|
1291
|
+
* completed the redirect flow.
|
|
1292
|
+
*/
|
|
1293
|
+
async get(verificationId, signal) {
|
|
1294
|
+
const url = `${this.cfg.bankAccountValidatorUrl()}/verifications/${encodeURIComponent(verificationId)}`;
|
|
1295
|
+
return this.transport.do({
|
|
1296
|
+
api: "bank_account_validation",
|
|
1297
|
+
operation: "bankAccountValidation.get",
|
|
1298
|
+
method: "GET",
|
|
1299
|
+
url,
|
|
1300
|
+
signal
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
var InteracVerificationResource = class {
|
|
1305
|
+
constructor(cfg, transport) {
|
|
1306
|
+
this.cfg = cfg;
|
|
1307
|
+
this.transport = transport;
|
|
1308
|
+
}
|
|
1309
|
+
cfg;
|
|
1310
|
+
transport;
|
|
1311
|
+
/** Starts an Interac AML Assist verification session. */
|
|
1312
|
+
async create(accountId, req, signal) {
|
|
1313
|
+
const resolvedAccountId = this.cfg.resolveAccountId(accountId);
|
|
1314
|
+
const url = `${this.cfg.bankAccountValidatorUrl()}/verifiedme/accounts/${encodeURIComponent(resolvedAccountId)}/verifications`;
|
|
1315
|
+
return this.transport.do({
|
|
1316
|
+
api: "interac_verification",
|
|
1317
|
+
operation: "interacVerification.create",
|
|
1318
|
+
method: "POST",
|
|
1319
|
+
url,
|
|
1320
|
+
body: req,
|
|
1321
|
+
signal
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Fetches the user's verified information once they have completed the
|
|
1326
|
+
* redirect flow on Interac's bank page.
|
|
1327
|
+
*/
|
|
1328
|
+
async get(verificationId, signal) {
|
|
1329
|
+
const url = `${this.cfg.bankAccountValidatorUrl()}/verifiedme/verifications/${encodeURIComponent(verificationId)}`;
|
|
1330
|
+
return this.transport.do({
|
|
1331
|
+
api: "interac_verification",
|
|
1332
|
+
operation: "interacVerification.get",
|
|
1333
|
+
method: "GET",
|
|
1334
|
+
url,
|
|
1335
|
+
signal
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
function bankVerificationRedirectUrl(verification) {
|
|
1340
|
+
return findLinkByRel(verification.links, "redirect_bank_validation");
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// src/core/webhook-crypto.ts
|
|
1344
|
+
var encoder = new TextEncoder();
|
|
1345
|
+
function isNodeRuntime() {
|
|
1346
|
+
return typeof process !== "undefined" && typeof process.versions === "object" && typeof process.versions.node === "string";
|
|
1347
|
+
}
|
|
1348
|
+
async function hmacSha256Base64Node(key, data) {
|
|
1349
|
+
const nodeCrypto = await import('crypto');
|
|
1350
|
+
const mac = nodeCrypto.createHmac("sha256", key);
|
|
1351
|
+
mac.update(data);
|
|
1352
|
+
return mac.digest("base64");
|
|
1353
|
+
}
|
|
1354
|
+
async function hmacSha256Base64WebCrypto(key, data) {
|
|
1355
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1356
|
+
if (!subtle) {
|
|
1357
|
+
throw new Error(
|
|
1358
|
+
"No HMAC implementation available: neither node:crypto nor globalThis.crypto.subtle is present in this runtime."
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
const cryptoKey = await subtle.importKey(
|
|
1362
|
+
"raw",
|
|
1363
|
+
encoder.encode(key),
|
|
1364
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1365
|
+
false,
|
|
1366
|
+
["sign"]
|
|
1367
|
+
);
|
|
1368
|
+
const signature = await subtle.sign("HMAC", cryptoKey, data);
|
|
1369
|
+
return bufferToBase64(new Uint8Array(signature));
|
|
1370
|
+
}
|
|
1371
|
+
function bufferToBase64(bytes) {
|
|
1372
|
+
if (typeof Buffer !== "undefined") {
|
|
1373
|
+
return Buffer.from(bytes).toString("base64");
|
|
1374
|
+
}
|
|
1375
|
+
let binary = "";
|
|
1376
|
+
for (const byte of bytes) {
|
|
1377
|
+
binary += String.fromCharCode(byte);
|
|
1378
|
+
}
|
|
1379
|
+
return btoa(binary);
|
|
1380
|
+
}
|
|
1381
|
+
async function hmacSha256Base64(key, data) {
|
|
1382
|
+
const bytes = typeof data === "string" ? encoder.encode(data) : data;
|
|
1383
|
+
return isNodeRuntime() ? hmacSha256Base64Node(key, bytes) : hmacSha256Base64WebCrypto(key, bytes);
|
|
1384
|
+
}
|
|
1385
|
+
async function timingSafeStringEqual(a, b) {
|
|
1386
|
+
if (isNodeRuntime()) {
|
|
1387
|
+
const nodeCrypto = await import('crypto');
|
|
1388
|
+
const bufA = Buffer.from(a, "utf-8");
|
|
1389
|
+
const bufB = Buffer.from(b, "utf-8");
|
|
1390
|
+
if (bufA.length !== bufB.length) {
|
|
1391
|
+
nodeCrypto.timingSafeEqual(bufA, bufA);
|
|
1392
|
+
return false;
|
|
1393
|
+
}
|
|
1394
|
+
return nodeCrypto.timingSafeEqual(bufA, bufB);
|
|
1395
|
+
}
|
|
1396
|
+
const bytesA = encoder.encode(a);
|
|
1397
|
+
const bytesB = encoder.encode(b);
|
|
1398
|
+
const length = Math.max(bytesA.length, bytesB.length);
|
|
1399
|
+
let mismatch = bytesA.length === bytesB.length ? 0 : 1;
|
|
1400
|
+
for (let i = 0; i < length; i++) {
|
|
1401
|
+
const byteA = bytesA[i] ?? 0;
|
|
1402
|
+
const byteB = bytesB[i] ?? 0;
|
|
1403
|
+
mismatch |= byteA ^ byteB;
|
|
1404
|
+
}
|
|
1405
|
+
return mismatch === 0;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// src/resources/webhooks.ts
|
|
1409
|
+
var WebhooksResource = class {
|
|
1410
|
+
/**
|
|
1411
|
+
* Verifies the HMAC-SHA256 signature of `rawBody` against `signature`
|
|
1412
|
+
* using `hmacKey`, then decodes the payload into a
|
|
1413
|
+
* {@link PaysafeWebhookEvent}.
|
|
1414
|
+
*
|
|
1415
|
+
* @param rawBody - The exact, unmodified request body bytes/text as received.
|
|
1416
|
+
* @param signature - The value of the `Signature` HTTP header.
|
|
1417
|
+
* @param hmacKey - The merchant's webhook HMAC key from the Paysafe portal.
|
|
1418
|
+
*
|
|
1419
|
+
* Verification uses a constant-time comparison to prevent timing
|
|
1420
|
+
* attacks.
|
|
1421
|
+
*/
|
|
1422
|
+
async verifyAndParse(rawBody, signature, hmacKey) {
|
|
1423
|
+
await this.verifySignature(rawBody, signature, hmacKey);
|
|
1424
|
+
return this.parse(rawBody);
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Verifies the webhook signature without parsing the body. Resolves on
|
|
1428
|
+
* success, or throws a {@link PaysafeError} with
|
|
1429
|
+
* `kind === "webhook_signature_mismatch"` on failure.
|
|
1430
|
+
*/
|
|
1431
|
+
async verifySignature(rawBody, signature, hmacKey) {
|
|
1432
|
+
const expected = await hmacSha256Base64(hmacKey, rawBody);
|
|
1433
|
+
const matches = await timingSafeStringEqual(expected, signature);
|
|
1434
|
+
if (!matches) {
|
|
1435
|
+
throw PaysafeError.webhookMismatch();
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Decodes a webhook body that has already been verified (e.g. by a
|
|
1440
|
+
* signature-checking middleware) into a {@link PaysafeWebhookEvent}.
|
|
1441
|
+
*/
|
|
1442
|
+
parse(rawBody) {
|
|
1443
|
+
const text = typeof rawBody === "string" ? rawBody : new TextDecoder().decode(rawBody);
|
|
1444
|
+
try {
|
|
1445
|
+
return JSON.parse(text);
|
|
1446
|
+
} catch (cause) {
|
|
1447
|
+
throw PaysafeError.decodeError(cause);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Parses an already-decoded object into a {@link PaysafeWebhookEvent},
|
|
1452
|
+
* useful when the caller's web framework has already deserialized the
|
|
1453
|
+
* JSON body.
|
|
1454
|
+
*/
|
|
1455
|
+
parseObject(obj) {
|
|
1456
|
+
return obj;
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
function webhookTopic(event) {
|
|
1460
|
+
const name = event.eventName;
|
|
1461
|
+
if (name.startsWith("PAYMENT_HANDLE_")) return "payment_handle";
|
|
1462
|
+
if (name.startsWith("PAYMENT_")) return "payment";
|
|
1463
|
+
if (name.startsWith("SETTLEMENT_")) return "settlement";
|
|
1464
|
+
if (name.startsWith("REFUND_")) return "refund";
|
|
1465
|
+
if (name.startsWith("STANDALONE_CREDIT_") || name.startsWith("ORIGINAL_CREDIT_")) return "payout";
|
|
1466
|
+
if (name.startsWith("SUBSCRIPTION_")) return "subscription";
|
|
1467
|
+
if (name.startsWith("ACCOUNT_UPDATER_")) return "account_updater";
|
|
1468
|
+
return "unknown";
|
|
1469
|
+
}
|
|
1470
|
+
var PAYMENT_HANDLE_STATUS_BY_EVENT = {
|
|
1471
|
+
PAYMENT_HANDLE_INITIATED: "INITIATED",
|
|
1472
|
+
PAYMENT_HANDLE_PROCESSING: "PROCESSING",
|
|
1473
|
+
PAYMENT_HANDLE_PAYABLE: "PAYABLE",
|
|
1474
|
+
PAYMENT_HANDLE_COMPLETED: "COMPLETED",
|
|
1475
|
+
PAYMENT_HANDLE_FAILED: "FAILED",
|
|
1476
|
+
PAYMENT_HANDLE_EXPIRED: "EXPIRED",
|
|
1477
|
+
PAYMENT_HANDLE_CANCELLED: "CANCELLED"
|
|
1478
|
+
};
|
|
1479
|
+
function webhookPaymentHandleStatus(event) {
|
|
1480
|
+
return PAYMENT_HANDLE_STATUS_BY_EVENT[event.eventName];
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// src/client.ts
|
|
1484
|
+
var PaysafeClient = class _PaysafeClient {
|
|
1485
|
+
cfg;
|
|
1486
|
+
// Payments API
|
|
1487
|
+
paymentHandles;
|
|
1488
|
+
payments;
|
|
1489
|
+
settlements;
|
|
1490
|
+
refunds;
|
|
1491
|
+
payouts;
|
|
1492
|
+
verifications;
|
|
1493
|
+
customers;
|
|
1494
|
+
// Payment Scheduler API
|
|
1495
|
+
plans;
|
|
1496
|
+
subscriptions;
|
|
1497
|
+
// Applications (Onboarding) API
|
|
1498
|
+
applications;
|
|
1499
|
+
// Value Added Services
|
|
1500
|
+
fxRates;
|
|
1501
|
+
customerIdentity;
|
|
1502
|
+
bankAccountValidation;
|
|
1503
|
+
interacVerification;
|
|
1504
|
+
// Webhooks
|
|
1505
|
+
webhooks;
|
|
1506
|
+
constructor(options, telemetryHook) {
|
|
1507
|
+
this.cfg = new ResolvedConfig(options);
|
|
1508
|
+
const limiter = new RateLimiter();
|
|
1509
|
+
const transport = new HttpTransport(this.cfg, limiter, telemetryHook);
|
|
1510
|
+
this.paymentHandles = new PaymentHandlesResource(this.cfg, transport);
|
|
1511
|
+
this.payments = new PaymentsResource(this.cfg, transport);
|
|
1512
|
+
this.settlements = new SettlementsResource(this.cfg, transport);
|
|
1513
|
+
this.refunds = new RefundsResource(this.cfg, transport);
|
|
1514
|
+
this.payouts = new PayoutsResource(this.cfg, transport);
|
|
1515
|
+
this.verifications = new VerificationsResource(this.cfg, transport);
|
|
1516
|
+
this.customers = new CustomersResource(this.cfg, transport);
|
|
1517
|
+
this.plans = new PlansResource(this.cfg, transport);
|
|
1518
|
+
this.subscriptions = new SubscriptionsResource(this.cfg, transport);
|
|
1519
|
+
this.applications = new ApplicationsResource(this.cfg, transport);
|
|
1520
|
+
this.fxRates = new FxRatesResource(this.cfg, transport);
|
|
1521
|
+
this.customerIdentity = new CustomerIdentityResource(this.cfg, transport);
|
|
1522
|
+
this.bankAccountValidation = new BankAccountValidationResource(this.cfg, transport);
|
|
1523
|
+
this.interacVerification = new InteracVerificationResource(this.cfg, transport);
|
|
1524
|
+
this.webhooks = new WebhooksResource();
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Builds a `PaysafeClient` from `PAYSAFE_USERNAME`, `PAYSAFE_PASSWORD`,
|
|
1528
|
+
* `PAYSAFE_ENVIRONMENT`, and `PAYSAFE_ACCOUNT_ID` environment variables
|
|
1529
|
+
* (Node.js only), merged with any additional `overrides` (last-wins).
|
|
1530
|
+
*/
|
|
1531
|
+
static fromEnv(overrides = {}, telemetryHook) {
|
|
1532
|
+
return new _PaysafeClient(configFromEnv(overrides), telemetryHook);
|
|
1533
|
+
}
|
|
1534
|
+
/** The client's configured default account ID, if set. */
|
|
1535
|
+
get accountId() {
|
|
1536
|
+
return this.cfg.accountId;
|
|
1537
|
+
}
|
|
1538
|
+
/** The client's configured environment (`"test"` or `"production"`). */
|
|
1539
|
+
get environment() {
|
|
1540
|
+
return this.cfg.environment;
|
|
1541
|
+
}
|
|
1542
|
+
/** The resolved base URL the client sends requests to. */
|
|
1543
|
+
get baseUrl() {
|
|
1544
|
+
return this.cfg.baseUrl();
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
|
|
1548
|
+
exports.ApplicationsResource = ApplicationsResource;
|
|
1549
|
+
exports.BankAccountValidationResource = BankAccountValidationResource;
|
|
1550
|
+
exports.CustomerIdentityResource = CustomerIdentityResource;
|
|
1551
|
+
exports.CustomersResource = CustomersResource;
|
|
1552
|
+
exports.FxRatesResource = FxRatesResource;
|
|
1553
|
+
exports.InteracVerificationResource = InteracVerificationResource;
|
|
1554
|
+
exports.PaymentHandlesResource = PaymentHandlesResource;
|
|
1555
|
+
exports.PaymentsResource = PaymentsResource;
|
|
1556
|
+
exports.PayoutsResource = PayoutsResource;
|
|
1557
|
+
exports.PaysafeClient = PaysafeClient;
|
|
1558
|
+
exports.PaysafeError = PaysafeError;
|
|
1559
|
+
exports.PlansResource = PlansResource;
|
|
1560
|
+
exports.RefundsResource = RefundsResource;
|
|
1561
|
+
exports.SettlementsResource = SettlementsResource;
|
|
1562
|
+
exports.SubscriptionsResource = SubscriptionsResource;
|
|
1563
|
+
exports.VerificationsResource = VerificationsResource;
|
|
1564
|
+
exports.WebhooksResource = WebhooksResource;
|
|
1565
|
+
exports.bankVerificationRedirectUrl = bankVerificationRedirectUrl;
|
|
1566
|
+
exports.findLinkByRel = findLinkByRel;
|
|
1567
|
+
exports.paymentHandleRedirectUrl = paymentHandleRedirectUrl;
|
|
1568
|
+
exports.paymentHandleRequiresAction = paymentHandleRequiresAction;
|
|
1569
|
+
exports.webhookPaymentHandleStatus = webhookPaymentHandleStatus;
|
|
1570
|
+
exports.webhookTopic = webhookTopic;
|
|
1571
|
+
//# sourceMappingURL=index.cjs.map
|
|
1572
|
+
//# sourceMappingURL=index.cjs.map
|