@klappay/types 2.0.4 → 3.0.1

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/dist/index.mjs CHANGED
@@ -30,11 +30,26 @@ var ApiKeyScopeSchema = z3.enum([
30
30
  "metrics:charges:read",
31
31
  "metrics:transactions:read",
32
32
  "metrics:distributions:read",
33
- "sandbox:trigger"
33
+ "sandbox:trigger",
34
+ "charges:split_write",
35
+ "recipients:read",
36
+ "recipients:write",
37
+ "recipients:manage_payout"
34
38
  ]).describe(
35
- "What an API key is allowed to do, independent of `tenantId`/`environment` (which scope *whose* data, not *what actions*). A key with none of these can still authenticate but every scoped route rejects it with `403 insufficient_scope`. `metrics:read` alone grants every metrics resource; `metrics:{resource}:read` grants only that one \u2014 a key can hold either or both."
39
+ "What an API key is allowed to do, independent of `tenantId`/`environment` (which scope *whose* data, not *what actions*). A key with none of these can still authenticate but every scoped route rejects it with `403 insufficient_scope`. `metrics:read` alone grants every metrics resource; `metrics:{resource}:read` grants only that one \u2014 a key can hold either or both. `charges:split_write` is required on top of `charges:write` whenever a charge request includes `splitRecipients` \u2014 a key without it can create ordinary charges but never redirect part of the payout. `recipients:write` registers/revokes recipients (addresses eligible to be *referenced* in a split); `recipients:manage_payout` is separate and strictly more sensitive \u2014 it is what lets a recipient actually become an API key's `payoutAddress`, and should be granted only to a key that already went through out-of-band approval for that (Dashboard's own internal key, never a merchant-facing or third-party integration key like a marketplace's). `charges:split_write` can never be combined with `recipients:write`/`recipients:manage_payout` on the same key (see `CONFLICTING_SCOPE_PAIRS`) \u2014 Core rejects such a key outright, before any route runs."
36
40
  );
37
41
  var API_KEY_SCOPES = ApiKeyScopeSchema.options;
42
+ var CONFLICTING_SCOPE_PAIRS = [
43
+ ["charges:split_write", "recipients:write"],
44
+ ["charges:split_write", "recipients:manage_payout"]
45
+ ];
46
+ function findConflictingScopes(scopes) {
47
+ const held = new Set(scopes);
48
+ for (const [a, b] of CONFLICTING_SCOPE_PAIRS) {
49
+ if (held.has(a) && held.has(b)) return [a, b];
50
+ }
51
+ return null;
52
+ }
38
53
 
39
54
  // src/networks.ts
40
55
  import { z as z4 } from "zod";
@@ -133,51 +148,82 @@ var TOKEN_ADDRESSES = {
133
148
  }
134
149
  };
135
150
 
151
+ // src/alt-tokens.ts
152
+ import { z as z7 } from "zod";
153
+ var AltTokenSchema = z7.enum(["ETH", "BNB", "MATIC", "AVAX", "BTC"]).describe(
154
+ "A non-stablecoin cryptocurrency Klappay trusts as swap input for a charge, via the 0x Swap API \u2014 swapped to one of the charge's `acceptedPayments` tokens before it ever reaches the merchant, so the merchant always receives USDC/USDT regardless of what the payer sent. Only a network's own native currency, plus `BTC` (wrapped) on the networks with deep, reputably-custodied liquidity, is trusted today (see `ALT_TOKEN_ADDRESSES`) \u2014 never assume every value here is available on every network."
155
+ );
156
+ var ALT_TOKEN_DECIMALS = {
157
+ ETH: 18,
158
+ BNB: 18,
159
+ MATIC: 18,
160
+ AVAX: 18,
161
+ BTC: 8
162
+ };
163
+ var ALT_TOKEN_ADDRESSES = {
164
+ base: { ETH: "native", BTC: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf" },
165
+ optimism: { ETH: "native", BTC: "0x68f180fcCe6836688e9084f035309E29Bf0A2095" },
166
+ ethereum: { ETH: "native", BTC: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" },
167
+ arbitrum: { ETH: "native", BTC: "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f" },
168
+ polygon: { MATIC: "native", BTC: "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6" },
169
+ avalanche: { AVAX: "native" },
170
+ bnb: { BNB: "native" }
171
+ };
172
+ function listAltTokensForNetworks(networks) {
173
+ const found = /* @__PURE__ */ new Set();
174
+ for (const network of networks) {
175
+ for (const token of Object.keys(ALT_TOKEN_ADDRESSES[network] ?? {})) {
176
+ found.add(token);
177
+ }
178
+ }
179
+ return [...found];
180
+ }
181
+
136
182
  // src/charges.ts
137
- import { z as z8 } from "zod";
183
+ import { z as z9 } from "zod";
138
184
 
139
185
  // src/checkout-metadata.ts
140
- import { z as z7 } from "zod";
186
+ import { z as z8 } from "zod";
141
187
  var CHECKOUT_PRODUCTS_MAX = 20;
142
- var CheckoutProductSchema = z7.object({
143
- name: z7.string().min(1).max(200).describe("What the payer is buying, shown as-is on the hosted checkout page."),
144
- quantity: z7.number().int().positive().max(9999).optional().describe("How many of this item. Omit for a single, unquantified item."),
145
- imageUrl: z7.string().url().max(2048).refine((value) => /^https?:\/\//.test(value), "must use http or https").optional().describe(
188
+ var CheckoutProductSchema = z8.object({
189
+ name: z8.string().min(1).max(200).describe("What the payer is buying, shown as-is on the hosted checkout page."),
190
+ quantity: z8.number().int().positive().max(9999).optional().describe("How many of this item. Omit for a single, unquantified item."),
191
+ imageUrl: z8.string().url().max(2048).refine((value) => /^https?:\/\//.test(value), "must use http or https").optional().describe(
146
192
  "Product image, fetched only by the payer's own browser \u2014 Klappay never fetches it server-side. Must be `http(s)`."
147
193
  )
148
194
  });
149
- var KlappayCheckoutMetadataSchema = z7.object({
150
- products: z7.array(CheckoutProductSchema).max(CHECKOUT_PRODUCTS_MAX).optional().describe(
195
+ var KlappayCheckoutMetadataSchema = z8.object({
196
+ products: z8.array(CheckoutProductSchema).max(CHECKOUT_PRODUCTS_MAX).optional().describe(
151
197
  `What the payer is buying, shown on the hosted checkout page \u2014 up to ${CHECKOUT_PRODUCTS_MAX} items. Purely informational: never validated against \`amount\`, never used by any payment or distribution logic.`
152
198
  )
153
199
  }).describe(
154
200
  "Reserved for Klappay \u2014 the one namespace inside `metadata` whose format is defined and enforced by Klappay, not by you. A `metadata.klappay` that does not match this shape is rejected outright (`400 validation_error`), unlike every other key in `metadata`, which accepts absolutely anything and never fails validation."
155
201
  );
156
- var MetadataWithKlappaySchema = z7.object({ klappay: KlappayCheckoutMetadataSchema.optional() }).catchall(z7.unknown()).describe(
202
+ var MetadataWithKlappaySchema = z8.object({ klappay: KlappayCheckoutMetadataSchema.optional() }).catchall(z8.unknown()).describe(
157
203
  "Arbitrary key/value data, returned as-is on every read. Put whatever you want in here \u2014 none of it is validated, except the `klappay` key, which is reserved for Klappay: if present, it must match `KlappayCheckoutMetadataSchema` exactly, or the whole request is rejected with `400 validation_error`."
158
204
  );
159
205
 
160
206
  // src/charges.ts
161
- var ChargeStatusSchema = z8.enum(["pending", "partially_paid", "confirmed", "expired", "underpaid"]).describe(
207
+ var ChargeStatusSchema = z9.enum(["pending", "partially_paid", "confirmed", "expired", "underpaid"]).describe(
162
208
  "Payment progress, from the payer side. `pending`: created, nothing received yet. `partially_paid`: some funds received, less than `amount`. `confirmed`: full amount received (or more \u2014 see `isOverpaid`). `expired`: `expiresAt` passed with zero funds received. `underpaid`: `expiresAt` passed while `partially_paid`. Every status is reached automatically, on its own timeline \u2014 there is no merchant-initiated cancellation. This never reflects whether funds actually reached the merchant \u2014 see `settlementStatus` for that."
163
209
  );
164
- var SettlementStatusSchema = z8.enum(["pending", "completed", "failed"]).describe(
210
+ var SettlementStatusSchema = z9.enum(["pending", "completed", "failed"]).describe(
165
211
  "Progress of the payout to the merchant's wallet, a separate step from `status` \u2014 `status: confirmed` only means the payment was detected on-chain, not that the merchant has been paid yet. `pending`: payment detected, payout not yet attempted. `completed`: the merchant's wallet has the funds. `failed`: the payout attempt failed and retries were exhausted (rare; contact support). `null` on the parent `Charge` means no payout has been attempted yet \u2014 nothing has been received, or the charge is still in progress."
166
212
  );
167
213
  var CHARGE_EXPIRES_IN_MIN_SECONDS = 60;
168
214
  var CHARGE_EXPIRES_IN_MAX_SECONDS = 3600;
169
215
  var CHARGE_ACCEPTED_PAYMENTS_MAX = 14;
170
- var AcceptedPaymentSchema = z8.object({
216
+ var AcceptedPaymentSchema = z9.object({
171
217
  token: TokenSchema,
172
218
  network: NetworkSchema
173
219
  });
174
- var AcceptedPaymentsSchema = z8.array(AcceptedPaymentSchema).min(1, "At least one accepted payment is required.").max(CHARGE_ACCEPTED_PAYMENTS_MAX).superRefine((pairs, ctx) => {
220
+ var AcceptedPaymentsSchema = z9.array(AcceptedPaymentSchema).min(1, "At least one accepted payment is required.").max(CHARGE_ACCEPTED_PAYMENTS_MAX).superRefine((pairs, ctx) => {
175
221
  const seen = /* @__PURE__ */ new Set();
176
222
  pairs.forEach((pair, index) => {
177
223
  const key = `${pair.token}:${pair.network}`;
178
224
  if (seen.has(key)) {
179
225
  ctx.addIssue({
180
- code: z8.ZodIssueCode.custom,
226
+ code: z9.ZodIssueCode.custom,
181
227
  message: `Duplicate accepted payment: ${pair.token} on ${pair.network}.`,
182
228
  path: [index]
183
229
  });
@@ -185,7 +231,7 @@ var AcceptedPaymentsSchema = z8.array(AcceptedPaymentSchema).min(1, "At least on
185
231
  seen.add(key);
186
232
  if (!OPERATIONAL_NETWORKS.includes(pair.network)) {
187
233
  ctx.addIssue({
188
- code: z8.ZodIssueCode.custom,
234
+ code: z9.ZodIssueCode.custom,
189
235
  message: `Network "${pair.network}" isn't live yet \u2014 only ${OPERATIONAL_NETWORKS.join(", ")} today.`,
190
236
  path: [index, "network"]
191
237
  });
@@ -195,105 +241,118 @@ var AcceptedPaymentsSchema = z8.array(AcceptedPaymentSchema).min(1, "At least on
195
241
  `Every \`(token, network)\` pair the payer is allowed to pay with \u2014 at least one, up to ${CHARGE_ACCEPTED_PAYMENTS_MAX}. This list is also the only restriction knob: the payer can use any combination of the pairs listed here, and every transfer on one of them is credited and sums toward the charge total (see \`paidWith\`) \u2014 e.g. a charge accepting USDC and USDT can be confirmed by $9 in USDC plus $1 in USDT, or by USDC arriving on two different accepted networks. To require payment in one specific token on one specific network, list only that single pair \u2014 a transfer on any pair not in this list is still recorded (for audit) but never credited. Each network must be live (see \`GET /v1/networks\` for the current matrix) \u2014 an unconfigured \`(token, network)\` combination for your environment is rejected with \`422 token_not_supported\`.`
196
242
  );
197
243
  var CHARGE_SPLIT_RECIPIENTS_MAX = 5;
198
- var SplitRecipientSchema = z8.object({
199
- address: z8.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be a 20-byte hex address").describe("EVM address to send a slice of this charge to."),
200
- percent: z8.number().positive().max(100).describe(
244
+ var SplitRecipientSchema = z9.object({
245
+ address: z9.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be a 20-byte hex address").describe("EVM address to send a slice of this charge to."),
246
+ percent: z9.number().positive().max(100).describe(
201
247
  "Percent of *your own* net share (i.e. of `100 - feePercent`, not of the charge's gross `amount`) to route to this address instead of your own payout wallet. Klappay's fee is computed on the gross amount first and is never diluted by how you choose to split what's left \u2014 see `docs/payments.md`'s \"Settling the payout\" section for the exact math."
202
248
  ),
203
- label: z8.string().min(1).max(64).optional().describe(
249
+ label: z9.string().min(1).max(64).optional().describe(
204
250
  'Free-form label for your own bookkeeping (e.g. `"supplier"`, `"sales rep"`) \u2014 echoed back unchanged, never interpreted by Klappay.'
205
251
  )
206
252
  });
207
- var SplitRecipientsSchema = z8.array(SplitRecipientSchema).max(CHARGE_SPLIT_RECIPIENTS_MAX).superRefine((recipients, ctx) => {
253
+ var SplitRecipientInputSchema = z9.object({
254
+ recipientId: z9.string().describe(
255
+ "id of a `Recipient` you already registered via `POST /v1/recipients` (not a raw address) \u2014 see `recipients:write`/`charges:split_write` scopes. A leaked `charges:write`-only key can never redirect payout to a brand new address this way, only reference one already trusted."
256
+ ),
257
+ percent: z9.number().positive().max(100).describe(
258
+ "Percent of *your own* net share (i.e. of `100 - feePercent`, not of the charge's gross `amount`) to route to this recipient instead of your own payout wallet. Klappay's fee is computed on the gross amount first and is never diluted by how you choose to split what's left \u2014 see `docs/payments.md`'s \"Settling the payout\" section for the exact math."
259
+ ),
260
+ label: z9.string().min(1).max(64).optional().describe(
261
+ 'Free-form label for your own bookkeeping (e.g. `"supplier"`, `"sales rep"`) \u2014 echoed back unchanged, never interpreted by Klappay. Independent of the label the recipient was registered with.'
262
+ )
263
+ });
264
+ var SplitRecipientsInputSchema = z9.array(SplitRecipientInputSchema).max(CHARGE_SPLIT_RECIPIENTS_MAX).superRefine((recipients, ctx) => {
208
265
  const seen = /* @__PURE__ */ new Set();
209
266
  recipients.forEach((recipient, index) => {
210
- const key = recipient.address.toLowerCase();
211
- if (seen.has(key)) {
267
+ if (seen.has(recipient.recipientId)) {
212
268
  ctx.addIssue({
213
- code: z8.ZodIssueCode.custom,
214
- message: `Duplicate split recipient address: ${recipient.address}.`,
215
- path: [index, "address"]
269
+ code: z9.ZodIssueCode.custom,
270
+ message: `Duplicate split recipientId: ${recipient.recipientId}.`,
271
+ path: [index, "recipientId"]
216
272
  });
217
273
  }
218
- seen.add(key);
274
+ seen.add(recipient.recipientId);
219
275
  });
220
276
  }).describe(
221
- `Optional extra recipients for this charge's split \u2014 e.g. a supplier or the sales rep who closed the deal \u2014 up to ${CHARGE_SPLIT_RECIPIENTS_MAX}. Frozen at creation exactly like everything else that shapes the split address; cannot be changed afterward. The sum of every \`percent\` here must fit within \`100 - feePercent\` (your own net share) \u2014 a request that doesn't is rejected with \`422 split_recipients_exceed_available_percent\`.`
277
+ `Optional extra recipients for this charge's split \u2014 e.g. a supplier or the sales rep who closed the deal \u2014 up to ${CHARGE_SPLIT_RECIPIENTS_MAX}, each referenced by \`recipientId\` (see \`POST /v1/recipients\`), never a raw address. Requires the \`charges:split_write\` scope in addition to \`charges:write\`. Frozen at creation exactly like everything else that shapes the split address; cannot be changed afterward. The sum of every \`percent\` here must fit within \`100 - feePercent\` (your own net share) \u2014 a request that doesn't is rejected with \`422 split_recipients_exceed_available_percent\`.`
222
278
  );
223
279
  var CHARGE_AMOUNT_MAX = 999999999999;
224
- var CreateChargeSchema = z8.object({
225
- amount: z8.number().positive().max(CHARGE_AMOUNT_MAX).describe(
280
+ var CreateChargeSchema = z9.object({
281
+ amount: z9.number().positive().max(CHARGE_AMOUNT_MAX).describe(
226
282
  "Amount to charge, in `currency` units (e.g. `49.9` = $49.90) \u2014 up to 6 decimal places; anything more precise is silently truncated. Required \u2014 every charge has a target amount, the first credited transfer that reaches it confirms the charge."
227
283
  ),
228
- currency: z8.literal("USD").default("USD").describe("Always `USD` today \u2014 the only supported currency."),
284
+ currency: z9.literal("USD").default("USD").describe("Always `USD` today \u2014 the only supported currency."),
229
285
  acceptedPayments: AcceptedPaymentsSchema,
230
- expiresIn: z8.number().int().min(CHARGE_EXPIRES_IN_MIN_SECONDS).max(CHARGE_EXPIRES_IN_MAX_SECONDS).describe(
286
+ expiresIn: z9.number().int().min(CHARGE_EXPIRES_IN_MIN_SECONDS).max(CHARGE_EXPIRES_IN_MAX_SECONDS).describe(
231
287
  "Seconds, not minutes or milliseconds \u2014 how long the charge stays open before it expires. Required, min 60, max 3600 (60 minutes) \u2014 sized off the slowest chain Klappay supports today (Ethereum mainnet, where a safely-confirmed transfer takes up to ~15 minutes), leaving real margin for payer-side delay (gas spikes, wallet friction) on top of that. Cannot be extended or shortened after creation."
232
288
  ),
233
- idempotencyKey: z8.string().min(1).max(255).optional().describe(
289
+ idempotencyKey: z9.string().min(1).max(255).optional().describe(
234
290
  "Scoped to your tenant. Replaying the same key returns the original charge unchanged instead of creating a duplicate \u2014 safe to retry a request after a timeout without double-charging."
235
291
  ),
236
- externalRef: z8.string().min(1).max(255).optional().describe(
292
+ externalRef: z9.string().min(1).max(255).optional().describe(
237
293
  "An opaque correlation id from your own system (e.g. an order id) \u2014 echoed back on the charge and in every webhook payload. Not interpreted or validated by Klappay."
238
294
  ),
239
- source: z8.string().min(1).max(64).optional().describe(
295
+ source: z9.string().min(1).max(64).optional().describe(
240
296
  'Free-form label for what created this charge (e.g. `"checkout"`, `"invoice"`) \u2014 useful if you create charges from more than one flow and want to tell them apart later. Not a fixed enum; use whatever values make sense to you.'
241
297
  ),
242
298
  metadata: MetadataWithKlappaySchema.optional(),
243
- redirectUrl: z8.string().url().refine((value) => /^https?:\/\//.test(value), "must use http or https").optional().describe(
299
+ redirectUrl: z9.string().url().refine((value) => /^https?:\/\//.test(value), "must use http or https").optional().describe(
244
300
  "Where to send the payer once this charge resolves, if you use Klappay's hosted checkout page (see `checkoutUrl` on the read shape) \u2014 ignored otherwise. Must be `http(s)` \u2014 a browser will navigate here, so `javascript:`/`data:` and other non-navigational schemes are rejected. Otherwise not validated beyond being well-formed; what happens at that destination is yours to build."
245
301
  ),
246
- splitRecipients: SplitRecipientsSchema.optional()
302
+ splitRecipients: SplitRecipientsInputSchema.optional()
247
303
  });
248
- var ChargeSchema = z8.object({
249
- id: z8.string().describe("Klappay-generated id, e.g. `ch_...`. Use this to look up the charge later."),
250
- amount: z8.number().describe("The amount originally requested, in `currency` units (up to 6 decimal places)."),
251
- amountReceived: z8.number().nullable().describe(
304
+ var ChargeSchema = z9.object({
305
+ id: z9.string().describe("Klappay-generated id, e.g. `ch_...`. Use this to look up the charge later."),
306
+ amount: z9.number().describe("The amount originally requested, in `currency` units (up to 6 decimal places)."),
307
+ amountReceived: z9.number().nullable().describe(
252
308
  "Cumulative amount actually received on-chain so far, in `currency` units (up to 6 decimal places). `null` until the first transfer arrives. Can exceed `amount` \u2014 see `isOverpaid`."
253
309
  ),
254
- isOverpaid: z8.boolean().describe(
310
+ isOverpaid: z9.boolean().describe(
255
311
  "`true` if `amountReceived` ended up greater than `amount`. Klappay never refunds the difference automatically \u2014 see the docs for why."
256
312
  ),
257
- currency: z8.string().describe("Always `USD` today \u2014 the only supported currency."),
258
- acceptedPayments: z8.array(AcceptedPaymentSchema).describe(
313
+ currency: z9.string().describe("Always `USD` today \u2014 the only supported currency."),
314
+ acceptedPayments: z9.array(AcceptedPaymentSchema).describe(
259
315
  "Every `(token, network)` pair this charge was configured to accept, unchanged after creation."
260
316
  ),
261
- paidWith: z8.array(AcceptedPaymentSchema).describe(
317
+ paidWith: z9.array(AcceptedPaymentSchema).describe(
262
318
  "Every distinct `(token, network)` pair that has actually contributed a credited transfer so far \u2014 empty until the first one arrives. Can hold more than one entry: a charge accepting several pairs can be paid across a combination of them, and every entry here sums toward `amountReceived`."
263
319
  ),
264
- address: z8.string().describe(
320
+ swapAlternatives: z9.array(AltTokenSchema).describe(
321
+ "Non-stablecoin cryptocurrencies the payer can pay with instead, via `POST /v1/charges/{id}/quote` \u2014 derived from the networks in `acceptedPayments` (e.g. a charge accepting USDC on Base lists `ETH` here, since Base's native currency is trusted as swap input). Recomputed on every read against Klappay's current trusted list, not frozen at creation \u2014 empty if this charge's networks have no trusted alt-token, or if swap-to-pay isn't configured on this deployment."
322
+ ),
323
+ address: z9.string().describe(
265
324
  "The on-chain address the payer must send funds to \u2014 identical across every accepted network (0xSplits addresses are chain-agnostic). Unique per charge, predicted at creation time \u2014 funds sent here go directly to the merchant, Klappay never custodies them."
266
325
  ),
267
326
  status: ChargeStatusSchema,
268
327
  settlementStatus: SettlementStatusSchema.nullable(),
269
328
  environment: EnvironmentSchema,
270
- apiKeyId: z8.string().nullable().describe(
329
+ apiKeyId: z9.string().nullable().describe(
271
330
  "Which of your API keys created this charge. `null` for a charge created before this field existed."
272
331
  ),
273
- txHash: z8.string().nullable().describe(
332
+ txHash: z9.string().nullable().describe(
274
333
  "Transaction hash of the most recent transfer detected for this charge. `null` until a payment is detected."
275
334
  ),
276
- externalRef: z8.string().nullable(),
277
- source: z8.string().nullable(),
335
+ externalRef: z9.string().nullable(),
336
+ source: z9.string().nullable(),
278
337
  metadata: MetadataWithKlappaySchema.nullable(),
279
- redirectUrl: z8.string().nullable().describe("Echoes the `redirectUrl` set at creation, if any. `null` if none was set."),
280
- checkoutUrl: z8.string().nullable().describe(
338
+ redirectUrl: z9.string().nullable().describe("Echoes the `redirectUrl` set at creation, if any. `null` if none was set."),
339
+ checkoutUrl: z9.string().nullable().describe(
281
340
  "Link to Klappay's hosted checkout page for this charge. `null` if this deployment has no hosted checkout configured \u2014 build your own payment UI from `address`/`acceptedPayments` instead."
282
341
  ),
283
- splitRecipients: z8.array(SplitRecipientSchema).describe("Echoes whatever extra split recipients were set at creation \u2014 empty array if none."),
284
- createdAt: z8.string().datetime(),
285
- expiresAt: z8.string().datetime().describe(
342
+ splitRecipients: z9.array(SplitRecipientSchema).describe("Echoes whatever extra split recipients were set at creation \u2014 empty array if none."),
343
+ createdAt: z9.string().datetime(),
344
+ expiresAt: z9.string().datetime().describe(
286
345
  "When this charge stops accepting payment, if still `pending`/`partially_paid` by then."
287
346
  ),
288
- confirmedAt: z8.string().datetime().nullable().describe("When `status` first reached `confirmed`. `null` until then."),
289
- settledAt: z8.string().datetime().nullable().describe(
347
+ confirmedAt: z9.string().datetime().nullable().describe("When `status` first reached `confirmed`. `null` until then."),
348
+ settledAt: z9.string().datetime().nullable().describe(
290
349
  "When `settlementStatus` first reached `completed` \u2014 the merchant's wallet actually has the funds. `null` until then, including while `settlementStatus` is `pending`/`failed`."
291
350
  ),
292
- lastActivityAt: z8.string().datetime().describe(
351
+ lastActivityAt: z9.string().datetime().describe(
293
352
  "When a transfer was last credited toward this charge, or `createdAt` if none has arrived yet."
294
353
  )
295
354
  });
296
- var ListChargesSchema = z8.object({
355
+ var ListChargesSchema = z9.object({
297
356
  status: ChargeStatusSchema.optional(),
298
357
  token: TokenSchema.optional().describe(
299
358
  "Filters on `paidWith.token` \u2014 the pair actually paid, not accepted."
@@ -302,13 +361,13 @@ var ListChargesSchema = z8.object({
302
361
  "Filters on `paidWith.network` \u2014 the pair actually paid, not accepted."
303
362
  ),
304
363
  environment: EnvironmentSchema.optional(),
305
- since: z8.string().datetime().optional().describe(
364
+ since: z9.string().datetime().optional().describe(
306
365
  "Only return charges created at or after this timestamp (filters on `createdAt`, not on when the status last changed). If polling as a fallback for missed webhooks, use a window at least as wide as the longest `expiresIn` your charges use, or you can miss a long-lived charge that changed status outside a narrower window."
307
366
  ),
308
- isOverpaid: z8.enum(["true", "false"]).transform((v) => v === "true").optional()
367
+ isOverpaid: z9.enum(["true", "false"]).transform((v) => v === "true").optional()
309
368
  }).extend(PaginationQuerySchema.shape);
310
369
  var PaginatedChargesSchema = paginatedSchema(ChargeSchema);
311
- var GetChargeQrCodeQuerySchema = z8.object({
370
+ var GetChargeQrCodeQuerySchema = z9.object({
312
371
  token: TokenSchema.optional().describe(
313
372
  "Which accepted `(token, network)` pair to encode in the QR \u2014 required if `acceptedPayments` has more than one pair, since there is no single unambiguous default to fall back to. Ignored (and unnecessary) when the charge accepts exactly one pair."
314
373
  ),
@@ -316,61 +375,61 @@ var GetChargeQrCodeQuerySchema = z8.object({
316
375
  });
317
376
 
318
377
  // src/distributions.ts
319
- import { z as z9 } from "zod";
320
- var SplitDistributionStatusSchema = z9.enum(["pending", "processing", "completed", "failed"]).describe(
378
+ import { z as z10 } from "zod";
379
+ var SplitDistributionStatusSchema = z10.enum(["pending", "processing", "completed", "failed"]).describe(
321
380
  "Status of one payout attempt to the merchant, for a single `(token, network)` pair \u2014 a charge that settles across more than one pair has one of these per pair. `pending`: queued, not yet claimed by a distributor. `processing`: a distributor (Klappay's own worker, or anyone racing to call `distribute()` first, see `PendingDistributionSchema`) has claimed it and is submitting the on-chain transaction. `completed`: the merchant's wallet has the funds. `failed`: every automatic retry was exhausted."
322
381
  );
323
- var PendingDistributionRecipientSchema = z9.object({
324
- address: z9.string().describe("On-chain recipient address."),
325
- percentAllocation: z9.number().describe("This recipient's share of the split, as a percentage (e.g. `99.0991` = 99.0991%).")
382
+ var PendingDistributionRecipientSchema = z10.object({
383
+ address: z10.string().describe("On-chain recipient address."),
384
+ percentAllocation: z10.number().describe("This recipient's share of the split, as a percentage (e.g. `99.0991` = 99.0991%).")
326
385
  });
327
- var PendingDistributionSchema = z9.object({
328
- splitAddress: z9.string().describe("The on-chain 0xSplits address to call `distribute()` on."),
386
+ var PendingDistributionSchema = z10.object({
387
+ splitAddress: z10.string().describe("The on-chain 0xSplits address to call `distribute()` on."),
329
388
  network: NetworkSchema,
330
389
  token: TokenSchema,
331
- recipients: z9.array(PendingDistributionRecipientSchema).describe(
390
+ recipients: z10.array(PendingDistributionRecipientSchema).describe(
332
391
  "The exact recipient list to pass to `distribute()` \u2014 the split contract only stores a hash of this config, so the caller must supply the identical array to prove it matches. Always present, never reconstructed from partial data."
333
392
  ),
334
- distributorFeePercent: z9.number().describe(
393
+ distributorFeePercent: z10.number().describe(
335
394
  "Percentage of the split balance paid to whoever calls `distribute()` first (e.g. `0.1` = 0.1%). Frozen at charge creation, same for every distribution today."
336
395
  ),
337
- estimatedRewardAmount: z9.number().describe(
396
+ estimatedRewardAmount: z10.number().describe(
338
397
  "Estimate only, in the charge's `currency` units, based on the amount Klappay detected on-chain \u2014 not a live read of the split's current balance. Read the balance yourself before submitting a transaction; a stale estimate is harmless (see the docs), never a reason to skip that check."
339
398
  ),
340
- availableSince: z9.string().datetime().describe("When this distribution entered its grace period."),
341
- graceEndsAt: z9.string().datetime().describe(
399
+ availableSince: z10.string().datetime().describe("When this distribution entered its grace period."),
400
+ graceEndsAt: z10.string().datetime().describe(
342
401
  "When Klappay's own worker may claim this distribution. Racing to call `distribute()` after this timestamp is possible but increasingly likely to lose to the worker."
343
402
  )
344
403
  });
345
404
  var PaginatedPendingDistributionsSchema = paginatedSchema(PendingDistributionSchema);
346
- var ListenPendingDistributionsQuerySchema = z9.object({
347
- limit: z9.coerce.number().int().min(0).max(PAGINATION_LIMIT_MAX).default(0).describe(
405
+ var ListenPendingDistributionsQuerySchema = z10.object({
406
+ limit: z10.coerce.number().int().min(0).max(PAGINATION_LIMIT_MAX).default(0).describe(
348
407
  "How many currently-claimable distributions to emit as an initial snapshot right after connecting \u2014 each as a synthetic `distribution.available` event \u2014 before continuing with real-time deltas. `0` (the default, same as omitting it) sends no snapshot at all, matching this endpoint's original behavior: connect first, then call `GET /v1/distributions/pending` yourself to bootstrap. Not a page \u2014 there is no cursor for this snapshot, so if more than `limit` are claimable at connect time, the excess is simply not sent; call `GET /v1/distributions/pending` directly for a complete, paginated listing."
349
408
  )
350
409
  });
351
- var PendingDistributionEventSchema = z9.discriminatedUnion("type", [
352
- z9.object({
353
- type: z9.literal("distribution.available"),
410
+ var PendingDistributionEventSchema = z10.discriminatedUnion("type", [
411
+ z10.object({
412
+ type: z10.literal("distribution.available"),
354
413
  distribution: PendingDistributionSchema
355
414
  }),
356
- z9.object({
357
- type: z9.literal("distribution.claimed"),
358
- splitAddress: z9.string().describe("No longer claimable \u2014 either settled by someone, or picked up by the worker.")
415
+ z10.object({
416
+ type: z10.literal("distribution.claimed"),
417
+ splitAddress: z10.string().describe("No longer claimable \u2014 either settled by someone, or picked up by the worker.")
359
418
  })
360
419
  ]);
361
420
 
362
421
  // src/metrics.ts
363
- import { z as z10 } from "zod";
364
- var MetricsResourceSchema = z10.enum(["charges", "transactions", "distributions"]).describe(
422
+ import { z as z11 } from "zod";
423
+ var MetricsResourceSchema = z11.enum(["charges", "transactions", "distributions"]).describe(
365
424
  "Which underlying dataset to query. `charges`: one row per charge. `transactions`: one row per detected on-chain transfer \u2014 a charge paid in installments has more than one. `distributions`: one row per payout attempt to the merchant, one per `(token, network)` pair a charge settled across."
366
425
  );
367
- var MetricsAggregationSchema = z10.enum(["count", "sum", "avg", "min", "max"]).describe(
426
+ var MetricsAggregationSchema = z11.enum(["count", "sum", "avg", "min", "max"]).describe(
368
427
  "`count` counts matching rows and never takes `field`. `sum`/`avg`/`min`/`max` require `field` to be set to one of the resource\u2019s numeric fields."
369
428
  );
370
- var MetricsFilterOperatorSchema = z10.enum(["eq", "neq", "in", "gt", "gte", "lt", "lte"]).describe(
429
+ var MetricsFilterOperatorSchema = z11.enum(["eq", "neq", "in", "gt", "gte", "lt", "lte"]).describe(
371
430
  "`in` expects an array value (max 50 entries); every other operator expects a single scalar."
372
431
  );
373
- var MetricsDateGranularitySchema = z10.enum(["day", "week", "month", "year"]).describe(
432
+ var MetricsDateGranularitySchema = z11.enum(["day", "week", "month", "year"]).describe(
374
433
  "Bucket width for a `date_bucket` `groupBy` entry \u2014 Postgres `date_trunc` semantics (UTC)."
375
434
  );
376
435
  var metricsQueryEnvironmentSchema = EnvironmentSchema.describe(
@@ -383,151 +442,151 @@ var METRICS_QUERY_MAX_GROUP_BY = 3;
383
442
  var METRICS_QUERY_MAX_FILTERS = 20;
384
443
  var METRICS_QUERY_MAX_METRICS = 10;
385
444
  var METRIC_ALIAS_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
386
- var metricAliasSchema = z10.string().min(1).max(64).regex(
445
+ var metricAliasSchema = z11.string().min(1).max(64).regex(
387
446
  METRIC_ALIAS_PATTERN,
388
447
  "Must start with a letter or underscore, and contain only letters, digits, and underscores \u2014 this becomes a SQL column alias."
389
448
  ).optional();
390
- var MetricsFilterValueSchema = z10.union([
391
- z10.string().max(255),
392
- z10.number(),
393
- z10.boolean(),
394
- z10.array(z10.union([z10.string().max(255), z10.number()])).min(1).max(50)
449
+ var MetricsFilterValueSchema = z11.union([
450
+ z11.string().max(255),
451
+ z11.number(),
452
+ z11.boolean(),
453
+ z11.array(z11.union([z11.string().max(255), z11.number()])).min(1).max(50)
395
454
  ]);
396
- var orderBySchema = z10.object({
397
- key: z10.string().min(1).max(64).regex(
455
+ var orderBySchema = z11.object({
456
+ key: z11.string().min(1).max(64).regex(
398
457
  METRIC_ALIAS_PATTERN,
399
458
  "Must start with a letter or underscore, and contain only letters, digits, and underscores \u2014 every valid output column name already looks like this."
400
459
  ).describe(
401
460
  "An output column name from this same query \u2014 either a `groupBy` field name, or a metric\u2019s `alias` (or its default name: `${aggregation}` for `count`, `${aggregation}_${field}` otherwise, e.g. `sum_amount`). Must match `^[a-zA-Z_][a-zA-Z0-9_]*$` \u2014 every real output column name already does, so this only ever rejects a value that could never have been one."
402
461
  ),
403
- direction: z10.enum(["asc", "desc"])
462
+ direction: z11.enum(["asc", "desc"])
404
463
  }).describe(
405
464
  "Sort the result rows by any output column \u2014 including the bucket field itself for a `date_bucket` query (e.g. `createdAt`). Omit to get ascending-by-bucket order for a date-bucketed query, or implementation-defined (not guaranteed stable) order otherwise."
406
465
  );
407
- var limitSchema = z10.number().int().min(1).max(METRICS_QUERY_MAX_ROW_LIMIT).default(METRICS_QUERY_DEFAULT_ROW_LIMIT).describe(
466
+ var limitSchema = z11.number().int().min(1).max(METRICS_QUERY_MAX_ROW_LIMIT).default(METRICS_QUERY_DEFAULT_ROW_LIMIT).describe(
408
467
  `Max rows to return, ${1}\u2013${METRICS_QUERY_MAX_ROW_LIMIT}, default ${METRICS_QUERY_DEFAULT_ROW_LIMIT}. If more rows matched, \`meta.truncated\` is \`true\` on the response \u2014 narrow the query instead of just raising this.`
409
468
  );
410
- var ChargesQueryFieldSchema = z10.enum(["status", "source", "apiKeyId", "currency", "isOverpaid", "externalRef"]).describe(
469
+ var ChargesQueryFieldSchema = z11.enum(["status", "source", "apiKeyId", "currency", "isOverpaid", "externalRef"]).describe(
411
470
  "A `Charge` field to filter or group by \u2014 see `ChargeStatusSchema` for `status`'s own possible values (charges.md). `source`/`externalRef` are free-form strings your own integration set at creation, not a fixed enum."
412
471
  );
413
- var ChargesMetricFieldSchema = z10.enum(["amount", "amountReceived", "feePercent"]).describe(
472
+ var ChargesMetricFieldSchema = z11.enum(["amount", "amountReceived", "feePercent"]).describe(
414
473
  "A `Charge` numeric field to aggregate. `amount`/`amountReceived` are decimal currency amounts (requested vs. actually received \u2014 see `charges.md`). `feePercent` is the platform fee frozen on the charge at creation, e.g. `1.5` means 1.5%."
415
474
  );
416
- var ChargesDateFieldSchema = z10.enum(["createdAt", "confirmedAt", "lastActivityAt", "expiresAt"]).describe(
475
+ var ChargesDateFieldSchema = z11.enum(["createdAt", "confirmedAt", "lastActivityAt", "expiresAt"]).describe(
417
476
  "A `Charge` timestamp to filter/bucket by. `confirmedAt` is `null` until the charge reaches `confirmed` \u2014 a `dateRange`/`date_bucket` on it implicitly excludes every charge that never confirmed. `expiresAt` is always present (set at creation), useful for e.g. finding charges expiring soon or measuring how close to expiry charges typically resolve."
418
477
  );
419
- var ChargesFilterSchema = z10.object({
478
+ var ChargesFilterSchema = z11.object({
420
479
  field: ChargesQueryFieldSchema,
421
480
  operator: MetricsFilterOperatorSchema,
422
481
  value: MetricsFilterValueSchema
423
482
  });
424
- var ChargesGroupBySchema = z10.union([
425
- z10.object({ type: z10.literal("field"), field: ChargesQueryFieldSchema }),
426
- z10.object({
427
- type: z10.literal("date_bucket"),
483
+ var ChargesGroupBySchema = z11.union([
484
+ z11.object({ type: z11.literal("field"), field: ChargesQueryFieldSchema }),
485
+ z11.object({
486
+ type: z11.literal("date_bucket"),
428
487
  field: ChargesDateFieldSchema,
429
488
  granularity: MetricsDateGranularitySchema
430
489
  })
431
490
  ]);
432
- var ChargesMetricSchema = z10.object({
491
+ var ChargesMetricSchema = z11.object({
433
492
  aggregation: MetricsAggregationSchema,
434
493
  field: ChargesMetricFieldSchema.optional(),
435
494
  alias: metricAliasSchema
436
495
  });
437
- var ChargesMetricsQuerySchema = z10.object({
438
- resource: z10.literal("charges"),
496
+ var ChargesMetricsQuerySchema = z11.object({
497
+ resource: z11.literal("charges"),
439
498
  environment: metricsQueryEnvironmentSchema,
440
- dateRange: z10.object({
499
+ dateRange: z11.object({
441
500
  field: ChargesDateFieldSchema,
442
- from: z10.string().max(64).datetime(),
443
- to: z10.string().max(64).datetime()
501
+ from: z11.string().max(64).datetime(),
502
+ to: z11.string().max(64).datetime()
444
503
  }),
445
- groupBy: z10.array(ChargesGroupBySchema).max(METRICS_QUERY_MAX_GROUP_BY).default([]),
446
- metrics: z10.array(ChargesMetricSchema).min(1).max(METRICS_QUERY_MAX_METRICS),
447
- filters: z10.array(ChargesFilterSchema).max(METRICS_QUERY_MAX_FILTERS).default([]),
504
+ groupBy: z11.array(ChargesGroupBySchema).max(METRICS_QUERY_MAX_GROUP_BY).default([]),
505
+ metrics: z11.array(ChargesMetricSchema).min(1).max(METRICS_QUERY_MAX_METRICS),
506
+ filters: z11.array(ChargesFilterSchema).max(METRICS_QUERY_MAX_FILTERS).default([]),
448
507
  orderBy: orderBySchema.optional(),
449
508
  limit: limitSchema
450
509
  });
451
- var TransactionsQueryFieldSchema = z10.enum(["network", "token", "source", "causedTransition"]).describe(
510
+ var TransactionsQueryFieldSchema = z11.enum(["network", "token", "source", "causedTransition"]).describe(
452
511
  "A `Transaction` field to filter or group by \u2014 see `NetworkSchema`/`TokenSchema`/`TransactionSourceSchema` for their possible values. `causedTransition` is `true` only for the transfer(s) that actually flipped the charge's `status` \u2014 a charge paid in installments can have more than one; filtering/grouping on it excludes no-op duplicate transfers (see `TimelineEvent.causedTransition` in charges.md for the full explanation)."
453
512
  );
454
- var TransactionsMetricFieldSchema = z10.enum(["amount"]).describe("The transfer amount, in the charge's `currency` units.");
455
- var TransactionsDateFieldSchema = z10.enum(["detectedAt"]).describe("When Klappay detected this transfer on-chain (not when it was mined).");
456
- var TransactionsFilterSchema = z10.object({
513
+ var TransactionsMetricFieldSchema = z11.enum(["amount"]).describe("The transfer amount, in the charge's `currency` units.");
514
+ var TransactionsDateFieldSchema = z11.enum(["detectedAt"]).describe("When Klappay detected this transfer on-chain (not when it was mined).");
515
+ var TransactionsFilterSchema = z11.object({
457
516
  field: TransactionsQueryFieldSchema,
458
517
  operator: MetricsFilterOperatorSchema,
459
518
  value: MetricsFilterValueSchema
460
519
  });
461
- var TransactionsGroupBySchema = z10.union([
462
- z10.object({ type: z10.literal("field"), field: TransactionsQueryFieldSchema }),
463
- z10.object({
464
- type: z10.literal("date_bucket"),
520
+ var TransactionsGroupBySchema = z11.union([
521
+ z11.object({ type: z11.literal("field"), field: TransactionsQueryFieldSchema }),
522
+ z11.object({
523
+ type: z11.literal("date_bucket"),
465
524
  field: TransactionsDateFieldSchema,
466
525
  granularity: MetricsDateGranularitySchema
467
526
  })
468
527
  ]);
469
- var TransactionsMetricSchema = z10.object({
528
+ var TransactionsMetricSchema = z11.object({
470
529
  aggregation: MetricsAggregationSchema,
471
530
  field: TransactionsMetricFieldSchema.optional(),
472
531
  alias: metricAliasSchema
473
532
  });
474
- var TransactionsMetricsQuerySchema = z10.object({
475
- resource: z10.literal("transactions"),
533
+ var TransactionsMetricsQuerySchema = z11.object({
534
+ resource: z11.literal("transactions"),
476
535
  environment: metricsQueryEnvironmentSchema,
477
- dateRange: z10.object({
536
+ dateRange: z11.object({
478
537
  field: TransactionsDateFieldSchema,
479
- from: z10.string().max(64).datetime(),
480
- to: z10.string().max(64).datetime()
538
+ from: z11.string().max(64).datetime(),
539
+ to: z11.string().max(64).datetime()
481
540
  }),
482
- groupBy: z10.array(TransactionsGroupBySchema).max(METRICS_QUERY_MAX_GROUP_BY).default([]),
483
- metrics: z10.array(TransactionsMetricSchema).min(1).max(METRICS_QUERY_MAX_METRICS),
484
- filters: z10.array(TransactionsFilterSchema).max(METRICS_QUERY_MAX_FILTERS).default([]),
541
+ groupBy: z11.array(TransactionsGroupBySchema).max(METRICS_QUERY_MAX_GROUP_BY).default([]),
542
+ metrics: z11.array(TransactionsMetricSchema).min(1).max(METRICS_QUERY_MAX_METRICS),
543
+ filters: z11.array(TransactionsFilterSchema).max(METRICS_QUERY_MAX_FILTERS).default([]),
485
544
  orderBy: orderBySchema.optional(),
486
545
  limit: limitSchema
487
546
  });
488
- var DistributionsQueryFieldSchema = z10.enum(["status", "network", "token", "distributorAddress"]).describe(
547
+ var DistributionsQueryFieldSchema = z11.enum(["status", "network", "token", "distributorAddress"]).describe(
489
548
  "A `SplitDistribution` field to filter or group by \u2014 see `SplitDistributionStatusSchema`/`NetworkSchema`/`TokenSchema` for their possible values. `distributorAddress` is the public on-chain address that actually called `distribute()` for a `completed` distribution \u2014 Klappay's own operator address if settled by Klappay's own worker, a community keeper's address if settled externally (or `null` on the rare case that lookup failed), `null` for every non-`completed` status."
490
549
  );
491
- var DistributionsMetricFieldSchema = z10.enum(["attempts"]).describe(
550
+ var DistributionsMetricFieldSchema = z11.enum(["attempts"]).describe(
492
551
  "How many times a payout was attempted for this settlement so far \u2014 incremented on every attempt, whether it succeeded or is being retried after failing. A high `attempts` alongside `status: 'failed'` means every automatic retry was exhausted."
493
552
  );
494
- var DistributionsDateFieldSchema = z10.enum(["createdAt", "processingStartedAt", "completedAt"]).describe(
553
+ var DistributionsDateFieldSchema = z11.enum(["createdAt", "processingStartedAt", "completedAt"]).describe(
495
554
  "`createdAt`: when this settlement was queued. `processingStartedAt`: when a worker began its most recent attempt at the payout \u2014 `null` until the first attempt, then overwritten on every subsequent retry, so it reflects the *latest* attempt's start, not the first. `completedAt`: when it actually paid out \u2014 `null` until `status` reaches `completed`, so a `dateRange`/`date_bucket` on it implicitly excludes every distribution still pending/processing/failed."
496
555
  );
497
- var DistributionsFilterSchema = z10.object({
556
+ var DistributionsFilterSchema = z11.object({
498
557
  field: DistributionsQueryFieldSchema,
499
558
  operator: MetricsFilterOperatorSchema,
500
559
  value: MetricsFilterValueSchema
501
560
  });
502
- var DistributionsGroupBySchema = z10.union([
503
- z10.object({ type: z10.literal("field"), field: DistributionsQueryFieldSchema }),
504
- z10.object({
505
- type: z10.literal("date_bucket"),
561
+ var DistributionsGroupBySchema = z11.union([
562
+ z11.object({ type: z11.literal("field"), field: DistributionsQueryFieldSchema }),
563
+ z11.object({
564
+ type: z11.literal("date_bucket"),
506
565
  field: DistributionsDateFieldSchema,
507
566
  granularity: MetricsDateGranularitySchema
508
567
  })
509
568
  ]);
510
- var DistributionsMetricSchema = z10.object({
569
+ var DistributionsMetricSchema = z11.object({
511
570
  aggregation: MetricsAggregationSchema,
512
571
  field: DistributionsMetricFieldSchema.optional(),
513
572
  alias: metricAliasSchema
514
573
  });
515
- var DistributionsMetricsQuerySchema = z10.object({
516
- resource: z10.literal("distributions"),
574
+ var DistributionsMetricsQuerySchema = z11.object({
575
+ resource: z11.literal("distributions"),
517
576
  environment: metricsQueryEnvironmentSchema,
518
- dateRange: z10.object({
577
+ dateRange: z11.object({
519
578
  field: DistributionsDateFieldSchema,
520
- from: z10.string().max(64).datetime(),
521
- to: z10.string().max(64).datetime()
579
+ from: z11.string().max(64).datetime(),
580
+ to: z11.string().max(64).datetime()
522
581
  }),
523
- groupBy: z10.array(DistributionsGroupBySchema).max(METRICS_QUERY_MAX_GROUP_BY).default([]),
524
- metrics: z10.array(DistributionsMetricSchema).min(1).max(METRICS_QUERY_MAX_METRICS),
525
- filters: z10.array(DistributionsFilterSchema).max(METRICS_QUERY_MAX_FILTERS).default([]),
582
+ groupBy: z11.array(DistributionsGroupBySchema).max(METRICS_QUERY_MAX_GROUP_BY).default([]),
583
+ metrics: z11.array(DistributionsMetricSchema).min(1).max(METRICS_QUERY_MAX_METRICS),
584
+ filters: z11.array(DistributionsFilterSchema).max(METRICS_QUERY_MAX_FILTERS).default([]),
526
585
  orderBy: orderBySchema.optional(),
527
586
  limit: limitSchema
528
587
  });
529
588
  var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
530
- var MetricsQuerySchema = z10.discriminatedUnion("resource", [
589
+ var MetricsQuerySchema = z11.discriminatedUnion("resource", [
531
590
  ChargesMetricsQuerySchema,
532
591
  TransactionsMetricsQuerySchema,
533
592
  DistributionsMetricsQuerySchema
@@ -536,7 +595,7 @@ var MetricsQuerySchema = z10.discriminatedUnion("resource", [
536
595
  const to = new Date(input.dateRange.to);
537
596
  if (from >= to) {
538
597
  ctx.addIssue({
539
- code: z10.ZodIssueCode.custom,
598
+ code: z11.ZodIssueCode.custom,
540
599
  message: "`dateRange.from` must be before `dateRange.to`.",
541
600
  path: ["dateRange", "from"]
542
601
  });
@@ -544,7 +603,7 @@ var MetricsQuerySchema = z10.discriminatedUnion("resource", [
544
603
  const spanDays = (to.getTime() - from.getTime()) / ONE_DAY_MS;
545
604
  if (spanDays > MAX_METRICS_QUERY_DATE_RANGE_DAYS) {
546
605
  ctx.addIssue({
547
- code: z10.ZodIssueCode.custom,
606
+ code: z11.ZodIssueCode.custom,
548
607
  message: `\`dateRange\` cannot span more than ${MAX_METRICS_QUERY_DATE_RANGE_DAYS} days.`,
549
608
  path: ["dateRange", "to"]
550
609
  });
@@ -552,7 +611,7 @@ var MetricsQuerySchema = z10.discriminatedUnion("resource", [
552
611
  const dateBucketCount = input.groupBy.filter((entry) => entry.type === "date_bucket").length;
553
612
  if (dateBucketCount > 1) {
554
613
  ctx.addIssue({
555
- code: z10.ZodIssueCode.custom,
614
+ code: z11.ZodIssueCode.custom,
556
615
  message: "At most one `date_bucket` entry is allowed in `groupBy`.",
557
616
  path: ["groupBy"]
558
617
  });
@@ -560,7 +619,7 @@ var MetricsQuerySchema = z10.discriminatedUnion("resource", [
560
619
  input.metrics.forEach((metric, index) => {
561
620
  if (metric.aggregation !== "count" && metric.field === void 0) {
562
621
  ctx.addIssue({
563
- code: z10.ZodIssueCode.custom,
622
+ code: z11.ZodIssueCode.custom,
564
623
  message: "`field` is required unless `aggregation` is `count`.",
565
624
  path: ["metrics", index, "field"]
566
625
  });
@@ -569,7 +628,7 @@ var MetricsQuerySchema = z10.discriminatedUnion("resource", [
569
628
  const aliases = input.metrics.map((metric) => metric.alias).filter((alias) => alias !== void 0);
570
629
  if (new Set(aliases).size !== aliases.length) {
571
630
  ctx.addIssue({
572
- code: z10.ZodIssueCode.custom,
631
+ code: z11.ZodIssueCode.custom,
573
632
  message: "Every `metrics[].alias` must be unique.",
574
633
  path: ["metrics"]
575
634
  });
@@ -578,32 +637,32 @@ var MetricsQuerySchema = z10.discriminatedUnion("resource", [
578
637
  input.metrics.forEach((metric, index) => {
579
638
  if (metric.alias !== void 0 && reservedNames.has(metric.alias)) {
580
639
  ctx.addIssue({
581
- code: z10.ZodIssueCode.custom,
640
+ code: z11.ZodIssueCode.custom,
582
641
  message: `\`alias\` "${metric.alias}" collides with a \`groupBy\` field name (or the reserved word "bucket") \u2014 choose a different alias.`,
583
642
  path: ["metrics", index, "alias"]
584
643
  });
585
644
  }
586
645
  });
587
646
  });
588
- var MetricsQueryResultRowSchema = z10.record(
589
- z10.string(),
590
- z10.union([z10.string(), z10.number(), z10.boolean(), z10.null()])
647
+ var MetricsQueryResultRowSchema = z11.record(
648
+ z11.string(),
649
+ z11.union([z11.string(), z11.number(), z11.boolean(), z11.null()])
591
650
  );
592
- var MetricsQueryResultSchema = z10.object({
593
- data: z10.array(MetricsQueryResultRowSchema),
594
- meta: z10.object({
651
+ var MetricsQueryResultSchema = z11.object({
652
+ data: z11.array(MetricsQueryResultRowSchema),
653
+ meta: z11.object({
595
654
  resource: MetricsResourceSchema,
596
655
  environment: EnvironmentSchema,
597
- rowCount: z10.number().int().describe("Number of rows in `data`."),
598
- truncated: z10.boolean().describe(
656
+ rowCount: z11.number().int().describe("Number of rows in `data`."),
657
+ truncated: z11.boolean().describe(
599
658
  "`true` if more rows matched than `limit` allowed \u2014 `data` holds only the first `limit`."
600
659
  )
601
660
  })
602
661
  });
603
662
 
604
663
  // src/webhook-events.ts
605
- import { z as z11 } from "zod";
606
- var ChargeWebhookEventTypeSchema = z11.enum([
664
+ import { z as z12 } from "zod";
665
+ var ChargeWebhookEventTypeSchema = z12.enum([
607
666
  "charge.created",
608
667
  "charge.partially_paid",
609
668
  "charge.confirmed",
@@ -615,14 +674,14 @@ var ChargeWebhookEventTypeSchema = z11.enum([
615
674
  ]).describe(
616
675
  'Note the distinction between `charge.confirmed` and `charge.settled`: `confirmed` means the payment was detected on-chain; `settled` means the merchant\'s wallet actually received the funds \u2014 a separate, later step. Subscribe to `confirmed` if you only need "will I get paid," or `settled` if you need "has the money actually arrived." `charge.overpaid` fires alongside `charge.confirmed`/`charge.partially_paid` whenever the cumulative amount received ends up above `amount` (see `Charge.isOverpaid`). Every event in this category carries the full `Charge` object as `data`.'
617
676
  );
618
- var WebhookDeliveryEventTypeSchema = z11.enum(["webhook.delivery_failed", "webhook.delivery_recovered", "webhook.endpoint_unhealthy"]).describe(
677
+ var WebhookDeliveryEventTypeSchema = z12.enum(["webhook.delivery_failed", "webhook.delivery_recovered", "webhook.endpoint_unhealthy"]).describe(
619
678
  "Meta-events about the health of your own webhook endpoints \u2014 useful for monitoring without polling `GET /v1/webhooks/{id}/deliveries`. `webhook.endpoint_unhealthy` fires once when a webhook's failure rate over the trailing 24h crosses 20%, and `webhook.delivery_recovered` fires once when a delivery to that webhook next succeeds. `data` for every event in this category: `{ webhookId, url, failureRatio? }` (`failureRatio` only present on `webhook.endpoint_unhealthy`)."
620
679
  );
621
- var WebhookEventTypeSchema = z11.union([
680
+ var WebhookEventTypeSchema = z12.union([
622
681
  ChargeWebhookEventTypeSchema,
623
682
  WebhookDeliveryEventTypeSchema
624
683
  ]);
625
- var WebhookCategorySchema = z11.enum(["payments", "webhooks"]).describe(
684
+ var WebhookCategorySchema = z12.enum(["payments", "webhooks"]).describe(
626
685
  "Subscribe to every event in a category via `eventCategories` instead of listing events one by one \u2014 new events added to a category later arrive automatically, no subscription update needed."
627
686
  );
628
687
  function buildCategoryMap() {
@@ -650,78 +709,101 @@ var TriggerableChargeEventSchema = ChargeWebhookEventTypeSchema.exclude([
650
709
  );
651
710
 
652
711
  // src/webhooks.ts
653
- import { z as z12 } from "zod";
712
+ import { z as z13 } from "zod";
654
713
  var WEBHOOK_EVENTS_WILDCARD = "*";
655
- var CreateWebhookSchema = z12.object({
656
- url: z12.string().max(2048).url().describe(
714
+ var CreateWebhookSchema = z13.object({
715
+ url: z13.string().max(2048).url().describe(
657
716
  "Must be HTTPS and resolve to a public address \u2014 private/internal IPs are rejected."
658
717
  ),
659
- events: z12.array(z12.union([WebhookEventTypeSchema, z12.literal(WEBHOOK_EVENTS_WILDCARD)])).max(Object.keys(EVENT_CATEGORY_MAP).length + 1).default([]).describe(
718
+ events: z13.array(z13.union([WebhookEventTypeSchema, z13.literal(WEBHOOK_EVENTS_WILDCARD)])).max(Object.keys(EVENT_CATEGORY_MAP).length + 1).default([]).describe(
660
719
  'Individual event types to receive, or `"*"` for every event (combine with `excludeEvents` to opt back out of specific ones). Omit in favor of `eventCategories` if you want whole categories instead.'
661
720
  ),
662
- eventCategories: z12.array(WebhookCategorySchema).max(WebhookCategorySchema.options.length).default([]).describe(
721
+ eventCategories: z13.array(WebhookCategorySchema).max(WebhookCategorySchema.options.length).default([]).describe(
663
722
  "Subscribe to every event in these categories. At least one of `events` or `eventCategories` is required."
664
723
  ),
665
- excludeEvents: z12.array(WebhookEventTypeSchema).max(Object.keys(EVENT_CATEGORY_MAP).length).default([]).describe(
724
+ excludeEvents: z13.array(WebhookEventTypeSchema).max(Object.keys(EVENT_CATEGORY_MAP).length).default([]).describe(
666
725
  'Event types to exclude even if selected via `events: ["*"]` or `eventCategories`.'
667
726
  )
668
727
  }).refine((v) => v.events.length > 0 || v.eventCategories.length > 0, {
669
728
  message: "must select at least one event via `events` or `eventCategories`",
670
729
  path: ["events"]
671
730
  });
672
- var WebhookSchema = z12.object({
673
- id: z12.string(),
731
+ var WebhookSchema = z13.object({
732
+ id: z13.string(),
674
733
  environment: EnvironmentSchema.nullable().describe(
675
734
  "Which environment's API key created this webhook \u2014 `live` or `test`. Every event is only ever delivered to a webhook whose `environment` matches the event's own (or to a webhook with `environment: null`, which receives every environment \u2014 the case for every webhook created before this field existed)."
676
735
  ),
677
- url: z12.string(),
678
- events: z12.array(WebhookEventTypeSchema),
679
- eventCategories: z12.array(WebhookCategorySchema),
680
- excludeEvents: z12.array(WebhookEventTypeSchema),
681
- isWildcard: z12.boolean(),
682
- secret: z12.string().describe(
736
+ url: z13.string(),
737
+ events: z13.array(WebhookEventTypeSchema),
738
+ eventCategories: z13.array(WebhookCategorySchema),
739
+ excludeEvents: z13.array(WebhookEventTypeSchema),
740
+ isWildcard: z13.boolean(),
741
+ secret: z13.string().describe(
683
742
  "The signing secret, used to verify the `X-Klappay-Signature` header on every delivery. Returned in full only this once \u2014 store it now, it is not recoverable afterward. Header format: `t=<unix-seconds>,v1=<hex-encoded HMAC-SHA256>`. Compute the expected signature as `HMAC-SHA256(secret, \"${t}.${raw request body}\")` (hex-encoded) and compare it to `v1` using a constant-time comparison; as a replay-protection measure, also reject if `t` is too far from the current time \u2014 Klappay does not enforce or check any particular tolerance server-side, so the exact threshold is entirely the receiver's own policy call. An official SDK's `constructEvent()`/`verifySignature()` do this for you, defaulting to a 300-second tolerance, overridable via `constructEvent`'s `toleranceSeconds` option \u2014 see github.com/klappay for available SDKs."
684
743
  ),
685
- createdAt: z12.string().datetime()
744
+ createdAt: z13.string().datetime()
686
745
  });
687
746
  var WebhookListItemSchema = WebhookSchema.omit({ secret: true }).extend({
688
- hint: z12.string().describe("A truncated, safe-to-display form of the secret (e.g. `whsec_...ab12`).")
747
+ hint: z13.string().describe("A truncated, safe-to-display form of the secret (e.g. `whsec_...ab12`).")
689
748
  });
690
- var WebhookPayloadSchema = z12.object({
691
- id: z12.string().describe(
749
+ var WebhookPayloadSchema = z13.object({
750
+ id: z13.string().describe(
692
751
  "Unique id for this specific delivery \u2014 also sent as the `X-Klappay-Delivery` header."
693
752
  ),
694
753
  event: WebhookEventTypeSchema,
695
- createdAt: z12.string().datetime(),
696
- data: z12.unknown().describe(
754
+ createdAt: z13.string().datetime(),
755
+ data: z13.unknown().describe(
697
756
  "Event-specific data. Charge events (`charge.*`) carry the full `Charge` object; webhook-delivery events carry a smaller, event-specific object \u2014 see `WebhookEventDataMap`/`TypedWebhookPayload` for the exact shape per event, or docs/webhooks.md."
698
757
  )
699
758
  });
700
- var WebhookDeliveryStatusSchema = z12.enum(["pending", "delivered", "failed"]);
701
- var WebhookDeliverySchema = z12.object({
702
- id: z12.string(),
703
- webhookId: z12.string(),
759
+ var WebhookDeliveryStatusSchema = z13.enum(["pending", "delivered", "failed"]);
760
+ var WebhookDeliverySchema = z13.object({
761
+ id: z13.string(),
762
+ webhookId: z13.string(),
704
763
  event: WebhookEventTypeSchema,
705
764
  status: WebhookDeliveryStatusSchema.describe(
706
765
  "`pending`: still retrying. `delivered`: got a 2xx response. `failed`: retries exhausted (5 attempts over ~24h) \u2014 use `POST /v1/webhooks/{id}/deliveries/{deliveryId}/retry` to try again manually."
707
766
  ),
708
- attempts: z12.number(),
709
- responseCode: z12.number().nullable().describe(
767
+ attempts: z13.number(),
768
+ responseCode: z13.number().nullable().describe(
710
769
  "HTTP status your endpoint returned on the most recent attempt. `null` if every attempt failed to connect at all."
711
770
  ),
712
- nextRetryAt: z12.string().datetime().nullable(),
713
- deliveredAt: z12.string().datetime().nullable(),
714
- createdAt: z12.string().datetime()
771
+ nextRetryAt: z13.string().datetime().nullable(),
772
+ deliveredAt: z13.string().datetime().nullable(),
773
+ createdAt: z13.string().datetime()
715
774
  });
716
775
  var ListWebhookDeliveriesSchema = PaginationQuerySchema;
717
776
  var PaginatedWebhookDeliveriesSchema = paginatedSchema(WebhookDeliverySchema);
718
777
 
778
+ // src/recipients.ts
779
+ import { z as z14 } from "zod";
780
+ var EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
781
+ var CreateRecipientSchema = z14.object({
782
+ address: z14.string().regex(EVM_ADDRESS_REGEX, "must be a 20-byte hex address").describe("EVM address to register as a trusted split recipient for your organization."),
783
+ label: z14.string().min(1).max(64).optional().describe('Free-form label for your own bookkeeping (e.g. `"supplier"`) \u2014 never interpreted.')
784
+ });
785
+ var RecipientSchema = z14.object({
786
+ id: z14.string().describe(
787
+ "Klappay-generated id, e.g. `rc_...` \u2014 this, not the raw address, is what a charge's `splitRecipients[].recipientId` references."
788
+ ),
789
+ environment: EnvironmentSchema,
790
+ address: z14.string(),
791
+ label: z14.string().nullable(),
792
+ payout: z14.boolean().describe(
793
+ "Whether this recipient is eligible to be used as an API key's `payoutAddress` (in addition to being referenceable in a split, which every non-revoked recipient already is). Set via `PATCH /v1/recipients/{id}` \u2014 requires the `recipients:manage_payout` scope, deliberately separate from `recipients:write`."
794
+ ),
795
+ createdAt: z14.string().datetime()
796
+ });
797
+ var SetRecipientPayoutSchema = z14.object({
798
+ payout: z14.boolean().describe("New payout-eligibility value for this recipient.")
799
+ });
800
+
719
801
  // src/timeline.ts
720
- import { z as z13 } from "zod";
721
- var TransactionSourceSchema = z13.enum(["moralis_webhook", "reconciliation_job", "sandbox"]).describe(
802
+ import { z as z15 } from "zod";
803
+ var TransactionSourceSchema = z15.enum(["moralis_webhook", "reconciliation_job", "sandbox"]).describe(
722
804
  "How this transfer was detected: `moralis_webhook` (the normal path), `reconciliation_job` (a fallback poller caught it after the webhook was missed or delayed), or `sandbox` (simulated via `POST /v1/sandbox/charges/{id}/trigger`, no real on-chain transfer)."
723
805
  );
724
- var TimelineEventTypeSchema = z13.enum([
806
+ var TimelineEventTypeSchema = z15.enum([
725
807
  "charge.created",
726
808
  "charge.expired",
727
809
  "transaction.detected",
@@ -732,11 +814,11 @@ var TimelineEventTypeSchema = z13.enum([
732
814
  ]).describe(
733
815
  "`charge.created`: the charge was created. `charge.expired`: `expiresAt` passed with no full payment. `transaction.detected`: a raw on-chain transfer was seen (see the `event`-shaped fields below for details \u2014 a charge can have more than one, e.g. a partial payment followed by the rest). `split.distributed`: a payout to the merchant completed on-chain, for one contributing `(token, network)` pair \u2014 a charge settled across more than one pair emits one of these per pair (see the `token`/`network` fields below). `webhook.dispatched`/`webhook.delivered`/`webhook.failed`: one specific delivery *attempt* for one webhook subscription \u2014 `failed` here means this single attempt failed, not that all retries were exhausted (see `WebhookDeliveryStatusSchema` for the exhausted-all-retries state)."
734
816
  );
735
- var TimelineEventSchema = z13.object({
817
+ var TimelineEventSchema = z15.object({
736
818
  type: TimelineEventTypeSchema,
737
- at: z13.string().datetime(),
738
- txHash: z13.string().optional().describe("Present for `transaction.detected` and `split.distributed` events only."),
739
- amount: z13.number().optional().describe(
819
+ at: z15.string().datetime(),
820
+ txHash: z15.string().optional().describe("Present for `transaction.detected` and `split.distributed` events only."),
821
+ amount: z15.number().optional().describe(
740
822
  "Present for `transaction.detected` events only \u2014 the amount that specific transfer carried."
741
823
  ),
742
824
  source: TransactionSourceSchema.optional().describe(
@@ -748,55 +830,111 @@ var TimelineEventSchema = z13.object({
748
830
  network: NetworkSchema.optional().describe(
749
831
  "Present for `transaction.detected` and `split.distributed` events \u2014 which network this specific transfer, or settlement, used."
750
832
  ),
751
- causedTransition: z13.boolean().optional().describe(
833
+ causedTransition: z15.boolean().optional().describe(
752
834
  "Present for `transaction.detected` events only. `true` if this specific transfer changed the charge's status (e.g. PENDING\u2192CONFIRMED) \u2014 a charge paid in installments can have more than one such event."
753
835
  ),
754
836
  event: WebhookEventTypeSchema.optional().describe(
755
837
  "Present for `webhook.*` events only \u2014 which event type this delivery was for."
756
838
  ),
757
- responseCode: z13.number().nullable().optional().describe(
839
+ responseCode: z15.number().nullable().optional().describe(
758
840
  "Present for `webhook.*` events only \u2014 HTTP status your endpoint returned, or `null` if the request never connected."
759
841
  ),
760
- attempts: z13.number().optional().describe(
842
+ attempts: z15.number().optional().describe(
761
843
  "Present for `webhook.*` events only \u2014 how many delivery attempts have been made so far."
762
844
  )
763
845
  });
764
846
 
765
847
  // src/health.ts
766
- import { z as z14 } from "zod";
767
- var HealthSchema = z14.object({
768
- status: z14.enum(["ok", "error"]).describe(
848
+ import { z as z16 } from "zod";
849
+ var HealthSchema = z16.object({
850
+ status: z16.enum(["ok", "error"]).describe(
769
851
  "`error` when the database connectivity check fails \u2014 the HTTP status code mirrors this (503 instead of 200), so a plain uptime check (not just a JSON-aware one) still catches a DB outage."
770
852
  ),
771
- version: z14.string(),
772
- timestamp: z14.string().datetime(),
773
- db: z14.enum(["ok", "error"]).describe("Result of a real database connectivity check, not just a process-alive check."),
774
- pendingWebhooks: z14.number().describe("Count of webhook deliveries still awaiting a successful attempt."),
775
- oldestPendingChargeAgeSeconds: z14.number().nullable().describe("Age of the oldest still-unpaid charge, in seconds. `null` if there are none."),
776
- lastMoralisEventAgeSeconds: z14.number().nullable().describe(
853
+ version: z16.string(),
854
+ timestamp: z16.string().datetime(),
855
+ db: z16.enum(["ok", "error"]).describe("Result of a real database connectivity check, not just a process-alive check."),
856
+ pendingWebhooks: z16.number().describe("Count of webhook deliveries still awaiting a successful attempt."),
857
+ oldestPendingChargeAgeSeconds: z16.number().nullable().describe("Age of the oldest still-unpaid charge, in seconds. `null` if there are none."),
858
+ lastMoralisEventAgeSeconds: z16.number().nullable().describe(
777
859
  "Seconds since the last on-chain payment notification was received \u2014 a cheap signal for whether payment detection is currently working. `null` if none have ever been received."
778
860
  )
779
861
  });
780
862
 
781
863
  // src/sandbox.ts
782
- import { z as z15 } from "zod";
783
- var SandboxTriggerSchema = z15.object({
864
+ import { z as z17 } from "zod";
865
+ var SandboxTriggerSchema = z17.object({
784
866
  event: TriggerableChargeEventSchema,
785
- amount: z15.number().positive().max(CHARGE_AMOUNT_MAX).optional().describe(
867
+ amount: z17.number().positive().max(CHARGE_AMOUNT_MAX).optional().describe(
786
868
  "Used with `charge.partially_paid` (amount to simulate as received so far \u2014 must be less than the charge amount, defaults to half of it if omitted) and with `charge.overpaid` (amount received \u2014 must be greater than the charge amount, defaults to 1.5x it if omitted). Ignored for every other event."
787
869
  )
788
870
  });
789
871
 
790
872
  // src/capabilities.ts
791
- import { z as z16 } from "zod";
792
- var CapabilitiesSchema = z16.object({
793
- acceptedPayments: z16.array(AcceptedPaymentSchema).describe(
873
+ import { z as z18 } from "zod";
874
+ var CapabilitiesSchema = z18.object({
875
+ acceptedPayments: z18.array(AcceptedPaymentSchema).describe(
794
876
  "Every `(token, network)` pair actually configured for your environment right now \u2014 read straight from the same lookup `POST /v1/charges` validates `acceptedPayments` against, so it can never list a pair that charge creation would then reject. Use this to build a picker UI instead of hardcoding the matrix client-side."
795
877
  )
796
878
  });
879
+
880
+ // src/swap.ts
881
+ import { z as z19 } from "zod";
882
+ var CreateSwapQuoteSchema = z19.object({
883
+ inputToken: AltTokenSchema.describe(
884
+ "Which alt-cryptocurrency the payer wants to send \u2014 must be one of this charge's `swapAlternatives`, or `422 token_not_supported`."
885
+ ),
886
+ inputNetwork: NetworkSchema.describe(
887
+ "Which network the payer will send `inputToken` on. Also picks which of this charge's `acceptedPayments` pairs the swap resolves to \u2014 a charge accepting USDC on both Base and Optimism resolves to whichever `inputNetwork` you pass. If the charge accepts more than one token on that same network, Klappay breaks the tie using its own trust ranking for that network (e.g. USDT over USDC on BNB Chain, where \"USDC\" is a third-party Binance-Peg token, not Circle's) \u2014 never a token the charge doesn't actually accept."
888
+ ),
889
+ takerAddress: z19.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be a 20-byte hex address").describe(
890
+ "The payer's own wallet address \u2014 the account that will sign and submit the swap transaction. Not validated against anything else; any well-formed address is accepted, since Klappay never custodies these funds."
891
+ )
892
+ });
893
+ var SwapQuoteSchema = z19.object({
894
+ inputToken: AltTokenSchema,
895
+ inputNetwork: NetworkSchema,
896
+ inputAmount: z19.number().describe(
897
+ "The ceiling of `inputToken` the payer needs available to sign for, in whole units (not wei/base units) \u2014 not necessarily the exact final cost. Any `inputToken` beyond what the swap actually needs (price moved favorably, less slippage than budgeted) is swapped back and refunded to the payer automatically, in the same transaction \u2014 never a separate step or a Klappay-side refund."
898
+ ),
899
+ outputToken: TokenSchema.describe(
900
+ "Which of this charge's `acceptedPayments` tokens the swap resolves to."
901
+ ),
902
+ outputNetwork: NetworkSchema,
903
+ outputAmount: z19.number().describe(
904
+ "The exact remaining amount owed on this charge (`amount - amountReceived`), in `currency` units \u2014 always what the merchant's split address receives, regardless of `inputAmount`."
905
+ ),
906
+ fees: z19.object({
907
+ klappayFee: z19.number().describe(
908
+ "Klappay's own swap fee (1% today), in `outputToken` units \u2014 paid by the payer, on top of `inputAmount`, separate from the merchant's own `feePercent`. Never subtracted from `outputAmount`."
909
+ ),
910
+ zeroExFee: z19.number().nullable().describe(
911
+ "0x's own protocol fee for this specific token pair, in `outputToken` units, or `null` when this pair isn't currently one 0x charges on. Also paid by the payer on top of `inputAmount`, also never subtracted from `outputAmount` \u2014 Klappay never sees this fee, it goes straight to 0x."
912
+ )
913
+ }).describe(
914
+ "Every fee the payer is charged for using swap-to-pay, broken out by who collects it \u2014 both already reflected in `inputAmount`, shown here separately for transparency. Neither ever reduces `outputAmount`."
915
+ ),
916
+ expiresAt: z19.string().datetime().describe(
917
+ "When this quote's price is no longer safely valid \u2014 a rough guide for the payer's UI countdown only. The actual price guarantee is enforced on-chain by the swap transaction itself (a signed Permit2 deadline, or a minimum-output check for a native-currency sell), not by this timestamp \u2014 submitting after it expires either reverts on-chain or simply gets re-quoted at the current price, never silently executes at a stale rate."
918
+ ),
919
+ transaction: z19.object({
920
+ to: z19.string().describe("Contract address the payer's wallet must send this transaction to."),
921
+ data: z19.string().describe("Calldata \u2014 opaque, must be sent unmodified."),
922
+ value: z19.string().describe(
923
+ "Native currency (ETH/BNB/MATIC/AVAX) to attach, in wei \u2014 `\"0\"` when `inputToken` isn't this network's native currency."
924
+ )
925
+ }).describe(
926
+ "Pass this directly to the payer's wallet (e.g. viem/ethers `sendTransaction`) \u2014 Klappay never touches the payer's private key or submits anything on their behalf. If `permit2` is present on this response, sign that first and append the signature to this `data` before sending; if `permit2` is absent, send `transaction` as-is with no extra step."
927
+ ),
928
+ permit2: z19.object({ eip712: z19.record(z19.unknown()) }).optional().describe(
929
+ "Present only when `inputToken` is an ERC-20 (today, only `BTC`) \u2014 the payer's wallet must sign this EIP-712 message and append the signature to `transaction.data` before sending, since an ERC-20 sell needs a Permit2 allowance signature that a native-currency sell doesn't. Absent when `inputToken` is a network's own native currency (ETH/BNB/MATIC/AVAX) \u2014 `transaction` is then ready to sign and send directly, no extra step."
930
+ )
931
+ });
797
932
  export {
933
+ ALT_TOKEN_ADDRESSES,
934
+ ALT_TOKEN_DECIMALS,
798
935
  API_KEY_SCOPES,
799
936
  AcceptedPaymentSchema,
937
+ AltTokenSchema,
800
938
  ApiKeyScopeSchema,
801
939
  CHARGE_ACCEPTED_PAYMENTS_MAX,
802
940
  CHARGE_AMOUNT_MAX,
@@ -804,6 +942,7 @@ export {
804
942
  CHARGE_EXPIRES_IN_MIN_SECONDS,
805
943
  CHARGE_SPLIT_RECIPIENTS_MAX,
806
944
  CHECKOUT_PRODUCTS_MAX,
945
+ CONFLICTING_SCOPE_PAIRS,
807
946
  CapabilitiesSchema,
808
947
  ChargeSchema,
809
948
  ChargeStatusSchema,
@@ -813,6 +952,8 @@ export {
813
952
  ChargesQueryFieldSchema,
814
953
  CheckoutProductSchema,
815
954
  CreateChargeSchema,
955
+ CreateRecipientSchema,
956
+ CreateSwapQuoteSchema,
816
957
  CreateWebhookSchema,
817
958
  DistributionsDateFieldSchema,
818
959
  DistributionsMetricFieldSchema,
@@ -855,10 +996,14 @@ export {
855
996
  PendingDistributionEventSchema,
856
997
  PendingDistributionRecipientSchema,
857
998
  PendingDistributionSchema,
999
+ RecipientSchema,
858
1000
  SandboxTriggerSchema,
1001
+ SetRecipientPayoutSchema,
859
1002
  SettlementStatusSchema,
860
1003
  SplitDistributionStatusSchema,
1004
+ SplitRecipientInputSchema,
861
1005
  SplitRecipientSchema,
1006
+ SwapQuoteSchema,
862
1007
  TOKEN_ADDRESSES,
863
1008
  TOKEN_DECIMALS,
864
1009
  TimelineEventSchema,
@@ -879,6 +1024,8 @@ export {
879
1024
  WebhookListItemSchema,
880
1025
  WebhookPayloadSchema,
881
1026
  WebhookSchema,
1027
+ findConflictingScopes,
1028
+ listAltTokensForNetworks,
882
1029
  paginatedSchema
883
1030
  };
884
1031
  //# sourceMappingURL=index.mjs.map