@fragmentpay/server 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -8
- package/dist/index.d.mts +120 -7
- package/dist/index.d.ts +120 -7
- package/dist/index.js +169 -32
- package/dist/index.mjs +168 -32
- package/dist/openapi-client.d.mts +1201 -0
- package/dist/openapi-client.d.ts +1201 -0
- package/dist/openapi-client.js +54 -0
- package/dist/openapi-client.mjs +19 -0
- package/dist/openapi.d.ts +1161 -0
- package/package.json +40 -7
package/dist/index.mjs
CHANGED
|
@@ -24,7 +24,8 @@ var v = {
|
|
|
24
24
|
},
|
|
25
25
|
num(path, val, opts = {}) {
|
|
26
26
|
const n = typeof val === "string" ? Number(val) : val;
|
|
27
|
-
if (typeof n !== "number" || !Number.isFinite(n))
|
|
27
|
+
if (typeof n !== "number" || !Number.isFinite(n))
|
|
28
|
+
return fail(path, `expected finite number, got ${JSON.stringify(val)}`);
|
|
28
29
|
if (opts.int && !Number.isInteger(n)) fail(path, "expected integer");
|
|
29
30
|
if (opts.min != null && n < opts.min) fail(path, `min ${opts.min}`);
|
|
30
31
|
if (opts.max != null && n > opts.max) fail(path, `max ${opts.max}`);
|
|
@@ -64,7 +65,15 @@ var v = {
|
|
|
64
65
|
return v.str(path, val, { max: 254, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ });
|
|
65
66
|
}
|
|
66
67
|
};
|
|
67
|
-
var CHAINS = [
|
|
68
|
+
var CHAINS = [
|
|
69
|
+
"solana",
|
|
70
|
+
"ethereum",
|
|
71
|
+
"base",
|
|
72
|
+
"arbitrum",
|
|
73
|
+
"optimism",
|
|
74
|
+
"polygon",
|
|
75
|
+
"bnb"
|
|
76
|
+
];
|
|
68
77
|
var INTENT_STATUSES = [
|
|
69
78
|
"created",
|
|
70
79
|
"requires_payment",
|
|
@@ -79,6 +88,13 @@ var INTENT_STATUSES = [
|
|
|
79
88
|
"refunded"
|
|
80
89
|
];
|
|
81
90
|
var PAYOUT_STATUSES = ["queued", "pending", "paid", "failed", "paused"];
|
|
91
|
+
var REFUND_STATUSES = [
|
|
92
|
+
"pending",
|
|
93
|
+
"processing",
|
|
94
|
+
"retry_scheduled",
|
|
95
|
+
"succeeded",
|
|
96
|
+
"failed"
|
|
97
|
+
];
|
|
82
98
|
var WEBHOOK_EVENTS = [
|
|
83
99
|
"payment_intent.created",
|
|
84
100
|
"payment_intent.quoted",
|
|
@@ -100,6 +116,18 @@ var WEBHOOK_EVENTS = [
|
|
|
100
116
|
];
|
|
101
117
|
|
|
102
118
|
// src/index.ts
|
|
119
|
+
function refundPhase(status) {
|
|
120
|
+
switch (status) {
|
|
121
|
+
case "succeeded":
|
|
122
|
+
return "completed";
|
|
123
|
+
case "processing":
|
|
124
|
+
return "sent";
|
|
125
|
+
case "failed":
|
|
126
|
+
return "failed";
|
|
127
|
+
default:
|
|
128
|
+
return "queued";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
103
131
|
var FragError = class extends Error {
|
|
104
132
|
status;
|
|
105
133
|
code;
|
|
@@ -121,20 +149,26 @@ var FragmentPay = class {
|
|
|
121
149
|
webhookEndpoints;
|
|
122
150
|
webhooks;
|
|
123
151
|
tokens;
|
|
152
|
+
checkout;
|
|
124
153
|
#opts;
|
|
125
154
|
constructor(opts) {
|
|
126
155
|
if (!opts || typeof opts !== "object") {
|
|
127
156
|
throw new FragValidationError("options", "expected FragOptions object");
|
|
128
157
|
}
|
|
129
158
|
if (typeof opts.apiKey !== "string" || !/^sk_(test|live)_[A-Za-z0-9_-]{8,}$/.test(opts.apiKey)) {
|
|
130
|
-
throw new FragValidationError(
|
|
159
|
+
throw new FragValidationError(
|
|
160
|
+
"options.apiKey",
|
|
161
|
+
"must match sk_test_\u2026 or sk_live_\u2026 (min 8 chars after prefix)"
|
|
162
|
+
);
|
|
131
163
|
}
|
|
132
164
|
if (opts.baseUrl !== void 0) v.url("options.baseUrl", opts.baseUrl);
|
|
133
|
-
if (opts.timeout !== void 0)
|
|
134
|
-
|
|
165
|
+
if (opts.timeout !== void 0)
|
|
166
|
+
v.num("options.timeout", opts.timeout, { min: 0, max: 6e5, int: true });
|
|
167
|
+
if (opts.retries !== void 0)
|
|
168
|
+
v.num("options.retries", opts.retries, { min: 0, max: 10, int: true });
|
|
135
169
|
this.#opts = {
|
|
136
170
|
apiKey: opts.apiKey,
|
|
137
|
-
baseUrl: (opts.baseUrl ?? "https://
|
|
171
|
+
baseUrl: (opts.baseUrl ?? "https://frag.cash").replace(/\/$/, ""),
|
|
138
172
|
fetch: opts.fetch ?? globalThis.fetch.bind(globalThis),
|
|
139
173
|
timeout: opts.timeout ?? 3e4,
|
|
140
174
|
retries: opts.retries ?? 2,
|
|
@@ -146,8 +180,16 @@ var FragmentPay = class {
|
|
|
146
180
|
this.payouts = new PayoutResource(req);
|
|
147
181
|
this.webhookEndpoints = new WebhookEndpointResource(req);
|
|
148
182
|
this.tokens = new TokenResource(req);
|
|
183
|
+
this.checkout = new PublicCheckoutResource(req);
|
|
149
184
|
this.webhooks = new WebhookHelpers();
|
|
150
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Unauthenticated health probe against the API host. Safe to call from
|
|
188
|
+
* uptime monitors and CI. Returns `{ status: "ok" | "degraded" | "down", ... }`.
|
|
189
|
+
*/
|
|
190
|
+
health() {
|
|
191
|
+
return this.#req(`/api/public/health`, { skipAuth: true });
|
|
192
|
+
}
|
|
151
193
|
setDebug(on) {
|
|
152
194
|
this.#opts.debug = on;
|
|
153
195
|
}
|
|
@@ -157,7 +199,7 @@ var FragmentPay = class {
|
|
|
157
199
|
).toString() : "";
|
|
158
200
|
const url = `${this.#opts.baseUrl}${path}${query}`;
|
|
159
201
|
const headers = new Headers(init.headers);
|
|
160
|
-
headers.set("authorization", `Bearer ${this.#opts.apiKey}`);
|
|
202
|
+
if (!init.skipAuth) headers.set("authorization", `Bearer ${this.#opts.apiKey}`);
|
|
161
203
|
if (init.body && !headers.has("content-type")) headers.set("content-type", "application/json");
|
|
162
204
|
if (init.idempotencyKey) headers.set("idempotency-key", init.idempotencyKey);
|
|
163
205
|
const maxAttempts = Math.max(1, this.#opts.retries + 1);
|
|
@@ -221,16 +263,35 @@ var PaymentIntentResource = class {
|
|
|
221
263
|
}
|
|
222
264
|
req;
|
|
223
265
|
create(input) {
|
|
224
|
-
if (!input || typeof input !== "object")
|
|
266
|
+
if (!input || typeof input !== "object")
|
|
267
|
+
throw new FragValidationError("createIntent", "expected object");
|
|
225
268
|
const amount = v.num("createIntent.amount", input.amount, { min: 0.01, max: 1e7 });
|
|
226
|
-
const currency = v.optStr("createIntent.currency", input.currency, {
|
|
269
|
+
const currency = v.optStr("createIntent.currency", input.currency, {
|
|
270
|
+
min: 3,
|
|
271
|
+
max: 8,
|
|
272
|
+
pattern: /^[A-Z]{3,8}$/
|
|
273
|
+
}) ?? "USD";
|
|
227
274
|
if (input.settlement !== void 0) v.obj("createIntent.settlement", input.settlement);
|
|
228
275
|
const settlementChain = input.settlement?.chain ? v.enum("createIntent.settlement.chain", input.settlement.chain, CHAINS) : void 0;
|
|
229
|
-
const settlementToken = v.optStr("createIntent.settlement.token", input.settlement?.token, {
|
|
230
|
-
|
|
276
|
+
const settlementToken = v.optStr("createIntent.settlement.token", input.settlement?.token, {
|
|
277
|
+
min: 1,
|
|
278
|
+
max: 64
|
|
279
|
+
});
|
|
280
|
+
const settlementAddress = v.optStr(
|
|
281
|
+
"createIntent.settlement.address",
|
|
282
|
+
input.settlement?.address,
|
|
283
|
+
{ min: 26, max: 128 }
|
|
284
|
+
);
|
|
231
285
|
const customerEmail = input.customerEmail !== void 0 ? v.email("createIntent.customerEmail", input.customerEmail) : void 0;
|
|
232
|
-
const expiresInSeconds = v.optNum("createIntent.expiresInSeconds", input.expiresInSeconds, {
|
|
233
|
-
|
|
286
|
+
const expiresInSeconds = v.optNum("createIntent.expiresInSeconds", input.expiresInSeconds, {
|
|
287
|
+
min: 60,
|
|
288
|
+
max: 86400,
|
|
289
|
+
int: true
|
|
290
|
+
});
|
|
291
|
+
const idempotencyKey = v.optStr("createIntent.idempotencyKey", input.idempotencyKey, {
|
|
292
|
+
min: 8,
|
|
293
|
+
max: 128
|
|
294
|
+
});
|
|
234
295
|
const successUrl = input.successUrl !== void 0 ? v.url("createIntent.successUrl", input.successUrl) : void 0;
|
|
235
296
|
const cancelUrl = input.cancelUrl !== void 0 ? v.url("createIntent.cancelUrl", input.cancelUrl) : void 0;
|
|
236
297
|
if (input.metadata !== void 0) v.obj("createIntent.metadata", input.metadata);
|
|
@@ -264,7 +325,10 @@ var PaymentIntentResource = class {
|
|
|
264
325
|
}
|
|
265
326
|
cancel(id) {
|
|
266
327
|
v.id("paymentIntents.cancel.id", id);
|
|
267
|
-
return this.req(
|
|
328
|
+
return this.req(
|
|
329
|
+
`/api/public/v1/payment-intents/${encodeURIComponent(id)}/cancel`,
|
|
330
|
+
{ method: "POST" }
|
|
331
|
+
);
|
|
268
332
|
}
|
|
269
333
|
transactions(id) {
|
|
270
334
|
v.id("paymentIntents.transactions.id", id);
|
|
@@ -285,10 +349,11 @@ var RefundResource = class {
|
|
|
285
349
|
}
|
|
286
350
|
req;
|
|
287
351
|
create(input) {
|
|
288
|
-
if (!input || typeof input !== "object")
|
|
352
|
+
if (!input || typeof input !== "object")
|
|
353
|
+
throw new FragValidationError("refunds.create", "expected object");
|
|
289
354
|
v.id("refunds.create.paymentIntentId", input.paymentIntentId);
|
|
290
355
|
v.num("refunds.create.amountUsd", input.amountUsd, { min: 0.01, max: 1e7 });
|
|
291
|
-
v.
|
|
356
|
+
v.optStr("refunds.create.destination", input.destination, { min: 26, max: 128 });
|
|
292
357
|
v.optStr("refunds.create.reason", input.reason, { max: 500 });
|
|
293
358
|
v.optStr("refunds.create.idempotencyKey", input.idempotencyKey, { min: 8, max: 128 });
|
|
294
359
|
return this.req("/api/public/v1/refunds", {
|
|
@@ -307,12 +372,48 @@ var RefundResource = class {
|
|
|
307
372
|
return this.req(`/api/public/v1/refunds/${encodeURIComponent(id)}`);
|
|
308
373
|
}
|
|
309
374
|
list(params = {}) {
|
|
310
|
-
if (params.paymentIntentId !== void 0)
|
|
375
|
+
if (params.paymentIntentId !== void 0)
|
|
376
|
+
v.id("refunds.list.paymentIntentId", params.paymentIntentId);
|
|
377
|
+
v.optEnum("refunds.list.status", params.status, REFUND_STATUSES);
|
|
311
378
|
v.optNum("refunds.list.limit", params.limit, { min: 1, max: 100, int: true });
|
|
312
379
|
return this.req(`/api/public/v1/refunds`, {
|
|
313
|
-
query: {
|
|
380
|
+
query: {
|
|
381
|
+
payment_intent_id: params.paymentIntentId,
|
|
382
|
+
status: params.status,
|
|
383
|
+
limit: params.limit
|
|
384
|
+
}
|
|
314
385
|
});
|
|
315
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Fetch the payer-facing safe status view for a refund. UNAUTHENTICATED —
|
|
389
|
+
* safe to expose in a customer-facing page or email. Redacts the destination
|
|
390
|
+
* address (only the last 6 chars) and omits merchant PII. The returned
|
|
391
|
+
* `phase` field is a stable 4-bucket categorization
|
|
392
|
+
* (`queued | sent | completed | failed`) computed on the client for
|
|
393
|
+
* convenient status pills.
|
|
394
|
+
*/
|
|
395
|
+
async status(id) {
|
|
396
|
+
v.id("refunds.status.id", id);
|
|
397
|
+
const raw = await this.req(
|
|
398
|
+
`/api/public/refunds/${encodeURIComponent(id)}/status`,
|
|
399
|
+
{ skipAuth: true }
|
|
400
|
+
);
|
|
401
|
+
return { ...raw, phase: refundPhase(raw.status) };
|
|
402
|
+
}
|
|
403
|
+
/** Client-side helper: bucket a raw {@link RefundStatus} to a UI phase. */
|
|
404
|
+
phase(status) {
|
|
405
|
+
return refundPhase(status);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
var PublicCheckoutResource = class {
|
|
409
|
+
constructor(req) {
|
|
410
|
+
this.req = req;
|
|
411
|
+
}
|
|
412
|
+
req;
|
|
413
|
+
retrieve(id) {
|
|
414
|
+
v.id("checkout.retrieve.id", id);
|
|
415
|
+
return this.req(`/api/public/v1/checkout/${encodeURIComponent(id)}`, { skipAuth: true });
|
|
416
|
+
}
|
|
316
417
|
};
|
|
317
418
|
var PayoutResource = class {
|
|
318
419
|
constructor(req) {
|
|
@@ -325,10 +426,15 @@ var PayoutResource = class {
|
|
|
325
426
|
}
|
|
326
427
|
list(params = {}) {
|
|
327
428
|
v.optEnum("payouts.list.status", params.status, PAYOUT_STATUSES);
|
|
328
|
-
if (params.paymentIntentId !== void 0)
|
|
429
|
+
if (params.paymentIntentId !== void 0)
|
|
430
|
+
v.id("payouts.list.paymentIntentId", params.paymentIntentId);
|
|
329
431
|
v.optNum("payouts.list.limit", params.limit, { min: 1, max: 100, int: true });
|
|
330
432
|
return this.req(`/api/public/v1/payouts`, {
|
|
331
|
-
query: {
|
|
433
|
+
query: {
|
|
434
|
+
status: params.status,
|
|
435
|
+
payment_intent_id: params.paymentIntentId,
|
|
436
|
+
limit: params.limit
|
|
437
|
+
}
|
|
332
438
|
});
|
|
333
439
|
}
|
|
334
440
|
replay(id) {
|
|
@@ -342,12 +448,18 @@ var WebhookEndpointResource = class {
|
|
|
342
448
|
}
|
|
343
449
|
req;
|
|
344
450
|
create(input) {
|
|
345
|
-
if (!input || typeof input !== "object")
|
|
451
|
+
if (!input || typeof input !== "object")
|
|
452
|
+
throw new FragValidationError("webhookEndpoints.create", "expected object");
|
|
346
453
|
v.url("webhookEndpoints.create.url", input.url);
|
|
347
454
|
if (!Array.isArray(input.events) || input.events.length === 0) {
|
|
348
|
-
throw new FragValidationError(
|
|
455
|
+
throw new FragValidationError(
|
|
456
|
+
"webhookEndpoints.create.events",
|
|
457
|
+
"at least one event required"
|
|
458
|
+
);
|
|
349
459
|
}
|
|
350
|
-
input.events.forEach(
|
|
460
|
+
input.events.forEach(
|
|
461
|
+
(e, i) => v.enum(`webhookEndpoints.create.events[${i}]`, e, WEBHOOK_EVENTS)
|
|
462
|
+
);
|
|
351
463
|
return this.req("/api/public/v1/webhook-endpoints", {
|
|
352
464
|
method: "POST",
|
|
353
465
|
body: JSON.stringify(input)
|
|
@@ -364,8 +476,11 @@ var WebhookEndpointResource = class {
|
|
|
364
476
|
v.id("webhookEndpoints.update.id", id);
|
|
365
477
|
if (patch.url !== void 0) v.url("webhookEndpoints.update.url", patch.url);
|
|
366
478
|
if (patch.events !== void 0) {
|
|
367
|
-
if (!Array.isArray(patch.events))
|
|
368
|
-
|
|
479
|
+
if (!Array.isArray(patch.events))
|
|
480
|
+
throw new FragValidationError("webhookEndpoints.update.events", "expected array");
|
|
481
|
+
patch.events.forEach(
|
|
482
|
+
(e, i) => v.enum(`webhookEndpoints.update.events[${i}]`, e, WEBHOOK_EVENTS)
|
|
483
|
+
);
|
|
369
484
|
}
|
|
370
485
|
return this.req(`/api/public/v1/webhook-endpoints/${encodeURIComponent(id)}`, {
|
|
371
486
|
method: "PATCH",
|
|
@@ -374,11 +489,16 @@ var WebhookEndpointResource = class {
|
|
|
374
489
|
}
|
|
375
490
|
delete(id) {
|
|
376
491
|
v.id("webhookEndpoints.delete.id", id);
|
|
377
|
-
return this.req(`/api/public/v1/webhook-endpoints/${encodeURIComponent(id)}`, {
|
|
492
|
+
return this.req(`/api/public/v1/webhook-endpoints/${encodeURIComponent(id)}`, {
|
|
493
|
+
method: "DELETE"
|
|
494
|
+
});
|
|
378
495
|
}
|
|
379
496
|
rotateSecret(id) {
|
|
380
497
|
v.id("webhookEndpoints.rotateSecret.id", id);
|
|
381
|
-
return this.req(
|
|
498
|
+
return this.req(
|
|
499
|
+
`/api/public/v1/webhook-endpoints/${encodeURIComponent(id)}/rotate-secret`,
|
|
500
|
+
{ method: "POST" }
|
|
501
|
+
);
|
|
382
502
|
}
|
|
383
503
|
};
|
|
384
504
|
var TokenResource = class {
|
|
@@ -408,19 +528,34 @@ var WebhookHelpers = class {
|
|
|
408
528
|
}
|
|
409
529
|
};
|
|
410
530
|
async function verifyWebhook(input) {
|
|
411
|
-
if (!input || typeof input !== "object")
|
|
531
|
+
if (!input || typeof input !== "object")
|
|
532
|
+
throw new FragValidationError("verifyWebhook", "expected object");
|
|
412
533
|
v.str("verifyWebhook.payload", input.payload, { min: 1 });
|
|
413
534
|
v.str("verifyWebhook.secret", input.secret, { min: 8 });
|
|
414
|
-
if (input.tolerance !== void 0)
|
|
535
|
+
if (input.tolerance !== void 0)
|
|
536
|
+
v.num("verifyWebhook.tolerance", input.tolerance, { min: 0, max: 3600, int: true });
|
|
415
537
|
const helper = new WebhookHelpers();
|
|
416
|
-
const ok = await helper.verify(
|
|
417
|
-
|
|
538
|
+
const ok = await helper.verify(
|
|
539
|
+
input.payload,
|
|
540
|
+
input.signature ?? null,
|
|
541
|
+
input.secret,
|
|
542
|
+
input.tolerance
|
|
543
|
+
);
|
|
544
|
+
if (!ok)
|
|
545
|
+
throw new FragError(401, "invalid_signature", "webhook signature verification failed", null);
|
|
418
546
|
return helper.parse(input.payload);
|
|
419
547
|
}
|
|
420
548
|
var Frag = class extends FragmentPay {
|
|
421
549
|
constructor(opts) {
|
|
422
550
|
const apiKey = "secretKey" in opts ? opts.secretKey : opts.apiKey;
|
|
423
|
-
super({
|
|
551
|
+
super({
|
|
552
|
+
apiKey,
|
|
553
|
+
baseUrl: opts.baseUrl,
|
|
554
|
+
fetch: opts.fetch,
|
|
555
|
+
timeout: opts.timeout,
|
|
556
|
+
retries: opts.retries,
|
|
557
|
+
debug: opts.debug
|
|
558
|
+
});
|
|
424
559
|
}
|
|
425
560
|
};
|
|
426
561
|
async function hmacHex(secret, message) {
|
|
@@ -445,5 +580,6 @@ export {
|
|
|
445
580
|
FragError,
|
|
446
581
|
FragValidationError,
|
|
447
582
|
FragmentPay,
|
|
583
|
+
refundPhase,
|
|
448
584
|
verifyWebhook
|
|
449
585
|
};
|