@genesis-tech/genesispay-seller 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +330 -0
- package/LICENSE +21 -0
- package/README.md +429 -0
- package/dist/client.d.ts +275 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +541 -0
- package/dist/client.js.map +1 -0
- package/dist/customers.d.ts +41 -0
- package/dist/customers.d.ts.map +1 -0
- package/dist/customers.js +76 -0
- package/dist/customers.js.map +1 -0
- package/dist/errors.d.ts +63 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +162 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/invoices.d.ts +80 -0
- package/dist/invoices.d.ts.map +1 -0
- package/dist/invoices.js +133 -0
- package/dist/invoices.js.map +1 -0
- package/dist/mandate-gate.d.ts +49 -0
- package/dist/mandate-gate.d.ts.map +1 -0
- package/dist/mandate-gate.js +109 -0
- package/dist/mandate-gate.js.map +1 -0
- package/dist/mandates.d.ts +172 -0
- package/dist/mandates.d.ts.map +1 -0
- package/dist/mandates.js +213 -0
- package/dist/mandates.js.map +1 -0
- package/dist/networks.d.ts +10 -0
- package/dist/networks.d.ts.map +1 -0
- package/dist/networks.js +22 -0
- package/dist/networks.js.map +1 -0
- package/dist/payment-gate.d.ts +67 -0
- package/dist/payment-gate.d.ts.map +1 -0
- package/dist/payment-gate.js +106 -0
- package/dist/payment-gate.js.map +1 -0
- package/dist/peerpay-settlement.d.ts +27 -0
- package/dist/peerpay-settlement.d.ts.map +1 -0
- package/dist/peerpay-settlement.js +100 -0
- package/dist/peerpay-settlement.js.map +1 -0
- package/dist/plans.d.ts +68 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +127 -0
- package/dist/plans.js.map +1 -0
- package/dist/resource.d.ts +47 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/resource.js +71 -0
- package/dist/resource.js.map +1 -0
- package/dist/usdc-amount.d.ts +7 -0
- package/dist/usdc-amount.d.ts.map +1 -0
- package/dist/usdc-amount.js +21 -0
- package/dist/usdc-amount.js.map +1 -0
- package/dist/webhooks.d.ts +277 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +223 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, PAYMENT_SIGNATURE_HEADER, buildPaymentRequiredPayload, decodePaymentSignatureHeader, encodePaymentRequiredHeader, encodeSettlementResponseHeader, validatePaymentSignatureAgainstRequirement, } from "@genesis-tech/genesispay-protocol";
|
|
2
|
+
import { getAddress, isAddress } from "viem";
|
|
3
|
+
import { resolvePaymentGateNetwork } from "./networks.js";
|
|
4
|
+
import { parseUsdcAmountToMinorUnits } from "./usdc-amount.js";
|
|
5
|
+
export function createPaymentGate(config) {
|
|
6
|
+
const network = resolvePaymentGateNetwork(config.network ?? "base-sepolia");
|
|
7
|
+
const amountUsdcMinor = parseUsdcAmountToMinorUnits(config.amountUsdc);
|
|
8
|
+
const payTo = normalizePayToAddress(config.payTo);
|
|
9
|
+
const description = config.description ?? "Payment required";
|
|
10
|
+
function buildPayload(resource) {
|
|
11
|
+
return buildPaymentRequiredPayload({
|
|
12
|
+
amount: config.amountUsdc.trim(),
|
|
13
|
+
amountUsdcMinor,
|
|
14
|
+
asset: "USDC",
|
|
15
|
+
assetAddress: network.usdcAddress,
|
|
16
|
+
chainId: network.chainId,
|
|
17
|
+
network: network.network,
|
|
18
|
+
destination: payTo,
|
|
19
|
+
description,
|
|
20
|
+
resource,
|
|
21
|
+
mimeType: config.mimeType,
|
|
22
|
+
maxTimeoutSeconds: config.maxTimeoutSeconds,
|
|
23
|
+
extra: config.eip712Domain,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function resolveResource(request) {
|
|
27
|
+
if (config.resource) {
|
|
28
|
+
return config.resource;
|
|
29
|
+
}
|
|
30
|
+
const url = new URL(request.url);
|
|
31
|
+
return `${url.origin}${url.pathname}`;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
requirementFor(resource) {
|
|
35
|
+
return buildPayload(resource).accepts[0];
|
|
36
|
+
},
|
|
37
|
+
wrap(handler, options) {
|
|
38
|
+
if (typeof options?.verifySettlement !== "function") {
|
|
39
|
+
throw new Error("gate.wrap requires a verifySettlement hook — use peerPaySettlement({ facilitatorBaseUrl, apiKey }) or provide your own.");
|
|
40
|
+
}
|
|
41
|
+
return async (request, ...args) => {
|
|
42
|
+
const payload = buildPayload(resolveResource(request));
|
|
43
|
+
const paymentSignatureHeader = request.headers.get(PAYMENT_SIGNATURE_HEADER);
|
|
44
|
+
if (!paymentSignatureHeader?.trim()) {
|
|
45
|
+
return paymentRequiredResponse(payload);
|
|
46
|
+
}
|
|
47
|
+
let payment;
|
|
48
|
+
try {
|
|
49
|
+
payment = decodePaymentSignatureHeader(paymentSignatureHeader);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return paymentRequiredResponse(payload, describeError(error));
|
|
53
|
+
}
|
|
54
|
+
const requirement = payload.accepts[0];
|
|
55
|
+
const mismatch = validatePaymentSignatureAgainstRequirement(payment, requirement);
|
|
56
|
+
if (mismatch) {
|
|
57
|
+
return paymentRequiredResponse(payload, mismatch);
|
|
58
|
+
}
|
|
59
|
+
const verification = await options.verifySettlement({
|
|
60
|
+
request,
|
|
61
|
+
paymentSignatureHeader,
|
|
62
|
+
payment,
|
|
63
|
+
requirement,
|
|
64
|
+
});
|
|
65
|
+
if (!verification.ok) {
|
|
66
|
+
return paymentRequiredResponse(payload, verification.errorReason, verification.settlement);
|
|
67
|
+
}
|
|
68
|
+
const response = await handler(request, ...args);
|
|
69
|
+
return withSettlementHeader(response, verification.settlement);
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function paymentRequiredResponse(payload, errorReason, settlement) {
|
|
75
|
+
const body = errorReason
|
|
76
|
+
? { ...payload, error: errorReason }
|
|
77
|
+
: payload;
|
|
78
|
+
const headers = new Headers({
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
"Cache-Control": "no-store",
|
|
81
|
+
[PAYMENT_REQUIRED_HEADER]: encodePaymentRequiredHeader(body),
|
|
82
|
+
});
|
|
83
|
+
if (settlement) {
|
|
84
|
+
headers.set(PAYMENT_RESPONSE_HEADER, encodeSettlementResponseHeader(settlement));
|
|
85
|
+
}
|
|
86
|
+
return new Response(JSON.stringify(body), { status: 402, headers });
|
|
87
|
+
}
|
|
88
|
+
function withSettlementHeader(response, settlement) {
|
|
89
|
+
const headers = new Headers(response.headers);
|
|
90
|
+
headers.set(PAYMENT_RESPONSE_HEADER, encodeSettlementResponseHeader(settlement));
|
|
91
|
+
return new Response(response.body, {
|
|
92
|
+
status: response.status,
|
|
93
|
+
statusText: response.statusText,
|
|
94
|
+
headers,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function normalizePayToAddress(payTo) {
|
|
98
|
+
if (typeof payTo !== "string" || !isAddress(payTo.trim())) {
|
|
99
|
+
throw new Error(`Invalid payTo address "${String(payTo)}": expected an EVM address like 0x1234....`);
|
|
100
|
+
}
|
|
101
|
+
return getAddress(payTo.trim());
|
|
102
|
+
}
|
|
103
|
+
function describeError(error) {
|
|
104
|
+
return error instanceof Error ? error.message : "Malformed PAYMENT-SIGNATURE header.";
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=payment-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-gate.js","sourceRoot":"","sources":["../src/payment-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { VerifySettlement } from "./payment-gate.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default GenesisPay app base URL — the public "development" facilitator. Used
|
|
4
|
+
* when `facilitatorBaseUrl` is omitted so integrators don't have to configure a
|
|
5
|
+
* URL in dev. Set `facilitatorBaseUrl` explicitly for production.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_FACILITATOR_BASE_URL = "https://peerpay-app-development.up.railway.app";
|
|
8
|
+
export type PeerPaySettlementOptions = {
|
|
9
|
+
/**
|
|
10
|
+
* Base URL of the GenesisPay app, e.g. "https://genesispay.example". Defaults to
|
|
11
|
+
* the public development facilitator (DEFAULT_FACILITATOR_BASE_URL) when
|
|
12
|
+
* omitted; set it explicitly in production.
|
|
13
|
+
*/
|
|
14
|
+
facilitatorBaseUrl?: string;
|
|
15
|
+
/** GenesisPay seller API key ("pp_sk_..."). */
|
|
16
|
+
apiKey: string;
|
|
17
|
+
/** Override for testing; defaults to global fetch. */
|
|
18
|
+
fetchFn?: typeof fetch;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Built-in settlement hook that forwards the PAYMENT-SIGNATURE payload to the
|
|
22
|
+
* GenesisPay facilitator (`POST /api/v1/facilitator/settle`). GenesisPay broadcasts
|
|
23
|
+
* the EIP-3009 authorization on-chain (or verifies a payer-provided txHash)
|
|
24
|
+
* and verifies the USDC transfer before reporting success.
|
|
25
|
+
*/
|
|
26
|
+
export declare function peerPaySettlement(options: PeerPaySettlementOptions): VerifySettlement;
|
|
27
|
+
//# sourceMappingURL=peerpay-settlement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peerpay-settlement.d.ts","sourceRoot":"","sources":["../src/peerpay-settlement.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,mDACS,CAAC;AAEnD,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAChC,gBAAgB,CA6DlB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { parseSettlementResponsePayload } from "@genesis-tech/genesispay-protocol";
|
|
2
|
+
/**
|
|
3
|
+
* Default GenesisPay app base URL — the public "development" facilitator. Used
|
|
4
|
+
* when `facilitatorBaseUrl` is omitted so integrators don't have to configure a
|
|
5
|
+
* URL in dev. Set `facilitatorBaseUrl` explicitly for production.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_FACILITATOR_BASE_URL = "https://peerpay-app-development.up.railway.app";
|
|
8
|
+
/**
|
|
9
|
+
* Built-in settlement hook that forwards the PAYMENT-SIGNATURE payload to the
|
|
10
|
+
* GenesisPay facilitator (`POST /api/v1/facilitator/settle`). GenesisPay broadcasts
|
|
11
|
+
* the EIP-3009 authorization on-chain (or verifies a payer-provided txHash)
|
|
12
|
+
* and verifies the USDC transfer before reporting success.
|
|
13
|
+
*/
|
|
14
|
+
export function peerPaySettlement(options) {
|
|
15
|
+
const settleUrl = buildSettleUrl(options.facilitatorBaseUrl?.trim() || DEFAULT_FACILITATOR_BASE_URL);
|
|
16
|
+
const apiKey = options.apiKey?.trim();
|
|
17
|
+
if (!apiKey) {
|
|
18
|
+
throw new Error("peerPaySettlement requires a GenesisPay seller API key (pp_sk_...).");
|
|
19
|
+
}
|
|
20
|
+
const fetchFn = options.fetchFn ?? fetch;
|
|
21
|
+
return async (context) => {
|
|
22
|
+
const { requirement } = context;
|
|
23
|
+
let response;
|
|
24
|
+
try {
|
|
25
|
+
response = await fetchFn(settleUrl, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
Authorization: `Bearer ${apiKey}`,
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
paymentSignature: context.paymentSignatureHeader,
|
|
33
|
+
requirement: {
|
|
34
|
+
resource: requirement.resource,
|
|
35
|
+
network: requirement.network,
|
|
36
|
+
chainId: requirement.chainId,
|
|
37
|
+
assetAddress: requirement.assetAddress,
|
|
38
|
+
amountUsdcMinor: requirement.maxAmountRequired,
|
|
39
|
+
payTo: requirement.payTo,
|
|
40
|
+
description: requirement.description || undefined,
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
errorReason: `Failed to reach the GenesisPay facilitator: ${describeError(error)}`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const body = await readJsonBody(response);
|
|
52
|
+
const settlement = tryParseSettlement(body);
|
|
53
|
+
if (response.ok && settlement?.success) {
|
|
54
|
+
return { ok: true, settlement };
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
errorReason: settlement?.errorReason ??
|
|
59
|
+
readErrorMessage(body) ??
|
|
60
|
+
`GenesisPay facilitator settlement failed with status ${response.status}.`,
|
|
61
|
+
...(settlement ? { settlement } : {}),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function buildSettleUrl(facilitatorBaseUrl) {
|
|
66
|
+
const trimmed = facilitatorBaseUrl?.trim().replace(/\/+$/, "");
|
|
67
|
+
if (!trimmed || !/^https?:\/\//.test(trimmed)) {
|
|
68
|
+
throw new Error(`Invalid facilitatorBaseUrl "${String(facilitatorBaseUrl)}": expected an http(s) URL like "https://genesispay.example".`);
|
|
69
|
+
}
|
|
70
|
+
return `${trimmed}/api/v1/facilitator/settle`;
|
|
71
|
+
}
|
|
72
|
+
async function readJsonBody(response) {
|
|
73
|
+
try {
|
|
74
|
+
return await response.json();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function tryParseSettlement(body) {
|
|
81
|
+
try {
|
|
82
|
+
return parseSettlementResponsePayload(body);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function readErrorMessage(body) {
|
|
89
|
+
if (typeof body === "object" &&
|
|
90
|
+
body !== null &&
|
|
91
|
+
"error" in body &&
|
|
92
|
+
typeof body.error === "string") {
|
|
93
|
+
return body.error;
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
function describeError(error) {
|
|
98
|
+
return error instanceof Error ? error.message : "unknown error";
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=peerpay-settlement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peerpay-settlement.js","sourceRoot":"","sources":["../src/peerpay-settlement.ts"],"names":[],"mappings":"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"}
|
package/dist/plans.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type PeerPayRequest } from "./resource.js";
|
|
2
|
+
export type SubscriptionPlanStatus = "active" | "archived";
|
|
3
|
+
export type SubscriptionPlan = {
|
|
4
|
+
/** Internal id. Address a plan by `publicId` — that is what the routes take. */
|
|
5
|
+
id: string;
|
|
6
|
+
publicId: string;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string | null;
|
|
9
|
+
asset: "USDC" | "EURC";
|
|
10
|
+
/** Decimal amount charged per period, e.g. "9.00". */
|
|
11
|
+
amountPerPeriod: string;
|
|
12
|
+
/** Same amount in 6-decimal minor units, as a string (no precision loss). */
|
|
13
|
+
amountPerPeriodMinor: string;
|
|
14
|
+
periodDays: number;
|
|
15
|
+
/**
|
|
16
|
+
* How many periods of allowance the customer approves up front. The mandate's
|
|
17
|
+
* total allowance is `amountPerPeriodMinor * prepaidCycles`; when it runs out
|
|
18
|
+
* the customer signs again.
|
|
19
|
+
*/
|
|
20
|
+
prepaidCycles: number;
|
|
21
|
+
capPerChargeMinor: string;
|
|
22
|
+
allowanceMinor: string;
|
|
23
|
+
destinationWallet: string;
|
|
24
|
+
chainId: number;
|
|
25
|
+
status: SubscriptionPlanStatus;
|
|
26
|
+
/**
|
|
27
|
+
* The hosted subscribe flow (`/subscribe/:publicId`) — send the customer
|
|
28
|
+
* here. This is the whole reason to call the API rather than build the URL
|
|
29
|
+
* yourself: the host depends on the deployment the key points at.
|
|
30
|
+
*/
|
|
31
|
+
checkoutUrl: string;
|
|
32
|
+
createdAt: string;
|
|
33
|
+
archivedAt: string | null;
|
|
34
|
+
};
|
|
35
|
+
export type PlanCreateInput = {
|
|
36
|
+
title: string;
|
|
37
|
+
/** Decimal amount per period, e.g. "9" or "9.00". */
|
|
38
|
+
amountPerPeriod: string;
|
|
39
|
+
/** Billing period in whole days, e.g. 30. */
|
|
40
|
+
periodDays: number;
|
|
41
|
+
description?: string;
|
|
42
|
+
/** Periods of allowance collected in one signature. Server default applies. */
|
|
43
|
+
prepaidCycles?: number;
|
|
44
|
+
/** Defaults to the account's receiving wallet. */
|
|
45
|
+
destinationWallet?: string;
|
|
46
|
+
/** Defaults to USDC. */
|
|
47
|
+
asset?: "USDC" | "EURC";
|
|
48
|
+
};
|
|
49
|
+
export interface PlansResource {
|
|
50
|
+
create(input: PlanCreateInput): Promise<SubscriptionPlan>;
|
|
51
|
+
list(): Promise<SubscriptionPlan[]>;
|
|
52
|
+
retrieve(publicId: string): Promise<SubscriptionPlan>;
|
|
53
|
+
/**
|
|
54
|
+
* Stops the hosted page from taking new subscribers. Mandates already signed
|
|
55
|
+
* from this plan keep billing until the payer revokes them — archiving is not
|
|
56
|
+
* a cancellation. Archiving twice is a no-op that returns the same plan.
|
|
57
|
+
*/
|
|
58
|
+
archive(publicId: string): Promise<SubscriptionPlan>;
|
|
59
|
+
}
|
|
60
|
+
export declare function createPlansResource(request: PeerPayRequest,
|
|
61
|
+
/**
|
|
62
|
+
* Applies the client's `expectedPayTo` pin to the wallet the server froze
|
|
63
|
+
* onto the plan. A plan's destination is where every future renewal settles,
|
|
64
|
+
* so it is exactly the value the pin exists to guard.
|
|
65
|
+
*/
|
|
66
|
+
assertPinnedDestination?: (wallet: unknown, what: string) => void): PlansResource;
|
|
67
|
+
export declare function toSubscriptionPlan(raw: Record<string, unknown>): SubscriptionPlan;
|
|
68
|
+
//# sourceMappingURL=plans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"AASA,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE3D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,sDAAsD;IACtD,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,sBAAsB,CAAC;IAC/B;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,eAAe,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC1D,IAAI,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACtD;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,cAAc;AACvB;;;;GAIG;AACH,uBAAuB,GAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAe,GAC1E,aAAa,CAwEf;AA6BD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAwBjF"}
|
package/dist/plans.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// `peerpay.plans.*` — subscription plans over `/api/v1/plans`.
|
|
2
|
+
//
|
|
3
|
+
// A plan is the recurring sibling of a checkout link: it describes an amount
|
|
4
|
+
// per period and exposes a hosted `checkoutUrl` the customer signs a mandate
|
|
5
|
+
// on. Everything is scoped to the key's own account, so a plan owned by someone
|
|
6
|
+
// else answers 404 (PeerPayNotFoundError) rather than 403 — the API is no
|
|
7
|
+
// plan-enumeration oracle, and the SDK does not paper over that.
|
|
8
|
+
import { PeerPayConfigError } from "./errors.js";
|
|
9
|
+
import { asRecord, optionalString, requiredString, toAmountString, toCount, oneOf, } from "./resource.js";
|
|
10
|
+
export function createPlansResource(request,
|
|
11
|
+
/**
|
|
12
|
+
* Applies the client's `expectedPayTo` pin to the wallet the server froze
|
|
13
|
+
* onto the plan. A plan's destination is where every future renewal settles,
|
|
14
|
+
* so it is exactly the value the pin exists to guard.
|
|
15
|
+
*/
|
|
16
|
+
assertPinnedDestination = () => { }) {
|
|
17
|
+
return {
|
|
18
|
+
create: async (input) => {
|
|
19
|
+
const body = await request({
|
|
20
|
+
method: "POST",
|
|
21
|
+
path: "/api/v1/plans",
|
|
22
|
+
operation: "POST /api/v1/plans",
|
|
23
|
+
action: "create a subscription plan",
|
|
24
|
+
body: {
|
|
25
|
+
title: input.title,
|
|
26
|
+
amountPerPeriod: input.amountPerPeriod,
|
|
27
|
+
periodDays: input.periodDays,
|
|
28
|
+
...(input.description ? { description: input.description } : {}),
|
|
29
|
+
...(input.prepaidCycles === undefined
|
|
30
|
+
? {}
|
|
31
|
+
: { prepaidCycles: input.prepaidCycles }),
|
|
32
|
+
...(input.destinationWallet
|
|
33
|
+
? { destinationWallet: input.destinationWallet }
|
|
34
|
+
: {}),
|
|
35
|
+
...(input.asset ? { asset: input.asset } : {}),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const plan = requirePlan(body, "POST /api/v1/plans");
|
|
39
|
+
assertPinnedDestination(plan.destinationWallet, "subscription plan");
|
|
40
|
+
return plan;
|
|
41
|
+
},
|
|
42
|
+
list: async () => {
|
|
43
|
+
const body = await request({
|
|
44
|
+
path: "/api/v1/plans",
|
|
45
|
+
operation: "GET /api/v1/plans",
|
|
46
|
+
action: "list subscription plans",
|
|
47
|
+
});
|
|
48
|
+
const plans = asRecord(body)?.plans;
|
|
49
|
+
if (!Array.isArray(plans)) {
|
|
50
|
+
// A list endpoint that answers 200 without a list is a broken contract,
|
|
51
|
+
// not an old backend. Returning [] here would hide it as "no plans yet".
|
|
52
|
+
throw new PeerPayConfigError(`GenesisPay GET /api/v1/plans returned no plans array: ${JSON.stringify(body)}`);
|
|
53
|
+
}
|
|
54
|
+
// Individual entries stay tolerant: one unusable row must not lose the rest.
|
|
55
|
+
return plans
|
|
56
|
+
.map(asRecord)
|
|
57
|
+
.filter((raw) => raw !== null)
|
|
58
|
+
.filter((raw) => typeof raw.publicId === "string" && raw.publicId.length > 0)
|
|
59
|
+
.map(toSubscriptionPlan);
|
|
60
|
+
},
|
|
61
|
+
retrieve: async (publicId) => {
|
|
62
|
+
const id = requirePublicId(publicId, "plans.retrieve");
|
|
63
|
+
const body = await request({
|
|
64
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}`,
|
|
65
|
+
operation: `GET /api/v1/plans/${id}`,
|
|
66
|
+
action: `retrieve subscription plan "${id}"`,
|
|
67
|
+
notFoundMessage: notFoundMessage(id),
|
|
68
|
+
});
|
|
69
|
+
return requirePlan(body, `GET /api/v1/plans/${id}`);
|
|
70
|
+
},
|
|
71
|
+
archive: async (publicId) => {
|
|
72
|
+
const id = requirePublicId(publicId, "plans.archive");
|
|
73
|
+
const body = await request({
|
|
74
|
+
method: "POST",
|
|
75
|
+
path: `/api/v1/plans/${encodeURIComponent(id)}/archive`,
|
|
76
|
+
operation: `POST /api/v1/plans/${id}/archive`,
|
|
77
|
+
action: `archive subscription plan "${id}"`,
|
|
78
|
+
notFoundMessage: notFoundMessage(id),
|
|
79
|
+
});
|
|
80
|
+
return requirePlan(body, `POST /api/v1/plans/${id}/archive`);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function notFoundMessage(id) {
|
|
85
|
+
return (`No GenesisPay subscription plan with publicId "${id}". Check the id and that ` +
|
|
86
|
+
`it belongs to the account this API key authenticates.`);
|
|
87
|
+
}
|
|
88
|
+
function requirePublicId(publicId, method) {
|
|
89
|
+
const id = publicId?.trim();
|
|
90
|
+
if (!id) {
|
|
91
|
+
throw new PeerPayConfigError(`${method}(publicId) requires the publicId returned by plans.create().`);
|
|
92
|
+
}
|
|
93
|
+
return id;
|
|
94
|
+
}
|
|
95
|
+
function requirePlan(body, operation) {
|
|
96
|
+
const raw = asRecord(asRecord(body)?.plan);
|
|
97
|
+
if (!raw || typeof raw.publicId !== "string" || !raw.publicId) {
|
|
98
|
+
throw new PeerPayConfigError(`GenesisPay ${operation} returned no plan: ${JSON.stringify(body)}`);
|
|
99
|
+
}
|
|
100
|
+
return toSubscriptionPlan(raw);
|
|
101
|
+
}
|
|
102
|
+
export function toSubscriptionPlan(raw) {
|
|
103
|
+
return {
|
|
104
|
+
id: requiredString(raw.id),
|
|
105
|
+
publicId: requiredString(raw.publicId),
|
|
106
|
+
title: requiredString(raw.title),
|
|
107
|
+
description: optionalString(raw.description),
|
|
108
|
+
asset: oneOf(raw.asset, ["USDC", "EURC"], "USDC"),
|
|
109
|
+
amountPerPeriod: toAmountString(raw.amountPerPeriod),
|
|
110
|
+
amountPerPeriodMinor: toAmountString(raw.amountPerPeriodMinor),
|
|
111
|
+
periodDays: toCount(raw.periodDays),
|
|
112
|
+
prepaidCycles: toCount(raw.prepaidCycles),
|
|
113
|
+
capPerChargeMinor: toAmountString(raw.capPerChargeMinor),
|
|
114
|
+
allowanceMinor: toAmountString(raw.allowanceMinor),
|
|
115
|
+
destinationWallet: requiredString(raw.destinationWallet),
|
|
116
|
+
chainId: toCount(raw.chainId),
|
|
117
|
+
// Degrade to "archived", not "active": a status this SDK version does not
|
|
118
|
+
// know (a future "paused", "deleted", …) must not read as "keep sending
|
|
119
|
+
// customers to this checkoutUrl". Mirrors mandate status degrading to the
|
|
120
|
+
// inert `pending_permit`.
|
|
121
|
+
status: oneOf(raw.status, ["active", "archived"], "archived"),
|
|
122
|
+
checkoutUrl: requiredString(raw.checkoutUrl),
|
|
123
|
+
createdAt: requiredString(raw.createdAt),
|
|
124
|
+
archivedAt: optionalString(raw.archivedAt),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=plans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plans.js","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type PeerPayRequestOptions = {
|
|
2
|
+
/** Defaults to GET. */
|
|
3
|
+
method?: "GET" | "POST" | "PATCH";
|
|
4
|
+
/** Path starting with "/", already percent-encoded. */
|
|
5
|
+
path: string;
|
|
6
|
+
/** JSON request body. Omit entirely for a GET (no content-type is sent). */
|
|
7
|
+
body?: unknown;
|
|
8
|
+
/** Additional non-secret request headers, e.g. Idempotency-Key. */
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
/** Label used in error messages, e.g. `POST /api/v1/plans`. */
|
|
11
|
+
operation: string;
|
|
12
|
+
/** Replaces the generic 404 text — a 404 usually needs case-specific wording. */
|
|
13
|
+
notFoundMessage?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Next step for the caller, appended to the message. A function receives the
|
|
16
|
+
* HTTP status so one call site can give, say, a 403 its own guidance without
|
|
17
|
+
* hand-rolling an error class next to the shared mapper.
|
|
18
|
+
*/
|
|
19
|
+
hint?: string | ((status: number) => string | undefined);
|
|
20
|
+
/**
|
|
21
|
+
* Filled into "Failed to reach GenesisPay to <action>" when the transport itself
|
|
22
|
+
* fails (DNS, connection refused, abort) — there is no response to map then.
|
|
23
|
+
*/
|
|
24
|
+
action: string;
|
|
25
|
+
};
|
|
26
|
+
/** The client's bound request function, as a resource module sees it. */
|
|
27
|
+
export type PeerPayRequest = (options: PeerPayRequestOptions) => Promise<unknown>;
|
|
28
|
+
/** A non-null, non-array object, or null — the shape a mapper can read fields off. */
|
|
29
|
+
export declare function asRecord(value: unknown): Record<string, unknown> | null;
|
|
30
|
+
export declare function optionalString(value: unknown): string | null;
|
|
31
|
+
export declare function requiredString(value: unknown): string;
|
|
32
|
+
/** Flat `Record<string, string>`; anything else (array, nested object) → null. */
|
|
33
|
+
export declare function optionalMetadata(value: unknown): Record<string, string> | null;
|
|
34
|
+
export declare function oneOf<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T;
|
|
35
|
+
export declare function toCount(value: unknown): number;
|
|
36
|
+
/** Like `toCount`, but a missing/unusable value stays `null` rather than 0. */
|
|
37
|
+
export declare function optionalCount(value: unknown): number | null;
|
|
38
|
+
/**
|
|
39
|
+
* Money crosses the wire as a decimal or minor-unit *string* so no precision is
|
|
40
|
+
* lost. A number is accepted and stringified anyway (a hand-rolled proxy or an
|
|
41
|
+
* older serializer may send one); anything else becomes "" rather than a
|
|
42
|
+
* plausible-looking wrong amount.
|
|
43
|
+
*/
|
|
44
|
+
export declare function toAmountString(value: unknown): string;
|
|
45
|
+
/** Same as `toAmountString`, but keeps "not set" distinguishable from "zero". */
|
|
46
|
+
export declare function optionalAmountString(value: unknown): string | null;
|
|
47
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB;IACvB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;IACzD;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAElF,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAGvE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAE5D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAErD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAM9E;AAED,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EACpC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,QAAQ,EAAE,CAAC,GACV,CAAC,CAIH;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG9C;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI3D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIrD;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIlE"}
|
package/dist/resource.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// What every GenesisPay resource module (checkout, plans, mandates) is built on:
|
|
2
|
+
// one HTTP seam and one set of tolerant wire mappers.
|
|
3
|
+
//
|
|
4
|
+
// The seam exists so a resource module never touches `fetch`, the API key or
|
|
5
|
+
// the base URL — the client owns those and hands down a bound `PeerPayRequest`.
|
|
6
|
+
// That keeps the error mapping in exactly one place (`throwForErrorResponse`,
|
|
7
|
+
// see ./errors.ts): a resource that built its own request could quietly grow a
|
|
8
|
+
// second, divergent notion of what a 422 means.
|
|
9
|
+
//
|
|
10
|
+
// The mappers are deliberately forgiving. Every field arrives as `unknown`
|
|
11
|
+
// because an older backend — one that predates a column this SDK version knows
|
|
12
|
+
// about — must never be able to crash a caller's poll loop or webhook handler.
|
|
13
|
+
// Missing or wrong-typed values degrade to null/0/"" or a named default; they
|
|
14
|
+
// never throw.
|
|
15
|
+
/** A non-null, non-array object, or null — the shape a mapper can read fields off. */
|
|
16
|
+
export function asRecord(value) {
|
|
17
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
18
|
+
return null;
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
export function optionalString(value) {
|
|
22
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
23
|
+
}
|
|
24
|
+
export function requiredString(value) {
|
|
25
|
+
return typeof value === "string" ? value : "";
|
|
26
|
+
}
|
|
27
|
+
/** Flat `Record<string, string>`; anything else (array, nested object) → null. */
|
|
28
|
+
export function optionalMetadata(value) {
|
|
29
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
30
|
+
return null;
|
|
31
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
32
|
+
return entries.length > 0 ? Object.fromEntries(entries) : null;
|
|
33
|
+
}
|
|
34
|
+
export function oneOf(value, allowed, fallback) {
|
|
35
|
+
return typeof value === "string" && allowed.includes(value)
|
|
36
|
+
? value
|
|
37
|
+
: fallback;
|
|
38
|
+
}
|
|
39
|
+
export function toCount(value) {
|
|
40
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
41
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
42
|
+
}
|
|
43
|
+
/** Like `toCount`, but a missing/unusable value stays `null` rather than 0. */
|
|
44
|
+
export function optionalCount(value) {
|
|
45
|
+
if (value === null || value === undefined || value === "")
|
|
46
|
+
return null;
|
|
47
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
48
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Money crosses the wire as a decimal or minor-unit *string* so no precision is
|
|
52
|
+
* lost. A number is accepted and stringified anyway (a hand-rolled proxy or an
|
|
53
|
+
* older serializer may send one); anything else becomes "" rather than a
|
|
54
|
+
* plausible-looking wrong amount.
|
|
55
|
+
*/
|
|
56
|
+
export function toAmountString(value) {
|
|
57
|
+
if (typeof value === "string")
|
|
58
|
+
return value;
|
|
59
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
60
|
+
return String(value);
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
/** Same as `toAmountString`, but keeps "not set" distinguishable from "zero". */
|
|
64
|
+
export function optionalAmountString(value) {
|
|
65
|
+
if (typeof value === "string")
|
|
66
|
+
return value.length > 0 ? value : null;
|
|
67
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
68
|
+
return String(value);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a human-readable decimal USDC amount ("0.10") into integer minor
|
|
3
|
+
* units (6 decimals) as a bigint. Rejects anything that is not a positive
|
|
4
|
+
* amount with at most 6 decimal places.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseUsdcAmountToMinorUnits(amount: string): bigint;
|
|
7
|
+
//# sourceMappingURL=usdc-amount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usdc-amount.d.ts","sourceRoot":"","sources":["../src/usdc-amount.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBlE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const USDC_DECIMALS = 6;
|
|
2
|
+
const USDC_AMOUNT_PATTERN = /^\d+(?:\.\d{1,6})?$/;
|
|
3
|
+
/**
|
|
4
|
+
* Parses a human-readable decimal USDC amount ("0.10") into integer minor
|
|
5
|
+
* units (6 decimals) as a bigint. Rejects anything that is not a positive
|
|
6
|
+
* amount with at most 6 decimal places.
|
|
7
|
+
*/
|
|
8
|
+
export function parseUsdcAmountToMinorUnits(amount) {
|
|
9
|
+
const normalizedAmount = amount.trim();
|
|
10
|
+
if (!USDC_AMOUNT_PATTERN.test(normalizedAmount)) {
|
|
11
|
+
throw new Error(`Invalid USDC amount "${amount}": use a positive decimal with up to 6 decimals, e.g. "0.10".`);
|
|
12
|
+
}
|
|
13
|
+
const [wholePart, fractionalPart = ""] = normalizedAmount.split(".");
|
|
14
|
+
const minorUnits = BigInt(wholePart) * BigInt(10 ** USDC_DECIMALS) +
|
|
15
|
+
BigInt(fractionalPart.padEnd(USDC_DECIMALS, "0"));
|
|
16
|
+
if (minorUnits <= 0n) {
|
|
17
|
+
throw new Error("USDC amount must be greater than zero.");
|
|
18
|
+
}
|
|
19
|
+
return minorUnits;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=usdc-amount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usdc-amount.js","sourceRoot":"","sources":["../src/usdc-amount.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAc;IACxD,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAEvC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,wBAAwB,MAAM,+DAA+D,CAC9F,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,EAAE,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrE,MAAM,UAAU,GACd,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,EAAE,IAAI,aAAa,CAAC;QAC/C,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC;IAEpD,IAAI,UAAU,IAAI,EAAE,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
|