@klappay/types 2.0.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -28,6 +28,8 @@ __export(index_exports, {
28
28
  CHARGE_EXPIRES_IN_MAX_SECONDS: () => CHARGE_EXPIRES_IN_MAX_SECONDS,
29
29
  CHARGE_EXPIRES_IN_MIN_SECONDS: () => CHARGE_EXPIRES_IN_MIN_SECONDS,
30
30
  CHARGE_SPLIT_RECIPIENTS_MAX: () => CHARGE_SPLIT_RECIPIENTS_MAX,
31
+ CHECKOUT_PRODUCTS_MAX: () => CHECKOUT_PRODUCTS_MAX,
32
+ CONFLICTING_SCOPE_PAIRS: () => CONFLICTING_SCOPE_PAIRS,
31
33
  CapabilitiesSchema: () => CapabilitiesSchema,
32
34
  ChargeSchema: () => ChargeSchema,
33
35
  ChargeStatusSchema: () => ChargeStatusSchema,
@@ -35,7 +37,9 @@ __export(index_exports, {
35
37
  ChargesDateFieldSchema: () => ChargesDateFieldSchema,
36
38
  ChargesMetricFieldSchema: () => ChargesMetricFieldSchema,
37
39
  ChargesQueryFieldSchema: () => ChargesQueryFieldSchema,
40
+ CheckoutProductSchema: () => CheckoutProductSchema,
38
41
  CreateChargeSchema: () => CreateChargeSchema,
42
+ CreateRecipientSchema: () => CreateRecipientSchema,
39
43
  CreateWebhookSchema: () => CreateWebhookSchema,
40
44
  DistributionsDateFieldSchema: () => DistributionsDateFieldSchema,
41
45
  DistributionsMetricFieldSchema: () => DistributionsMetricFieldSchema,
@@ -46,6 +50,7 @@ __export(index_exports, {
46
50
  ErrorPayloadSchema: () => ErrorPayloadSchema,
47
51
  GetChargeQrCodeQuerySchema: () => GetChargeQrCodeQuerySchema,
48
52
  HealthSchema: () => HealthSchema,
53
+ KlappayCheckoutMetadataSchema: () => KlappayCheckoutMetadataSchema,
49
54
  ListChargesSchema: () => ListChargesSchema,
50
55
  ListWebhookDeliveriesSchema: () => ListWebhookDeliveriesSchema,
51
56
  ListenPendingDistributionsQuerySchema: () => ListenPendingDistributionsQuerySchema,
@@ -55,6 +60,7 @@ __export(index_exports, {
55
60
  METRICS_QUERY_MAX_GROUP_BY: () => METRICS_QUERY_MAX_GROUP_BY,
56
61
  METRICS_QUERY_MAX_METRICS: () => METRICS_QUERY_MAX_METRICS,
57
62
  METRICS_QUERY_MAX_ROW_LIMIT: () => METRICS_QUERY_MAX_ROW_LIMIT,
63
+ MetadataWithKlappaySchema: () => MetadataWithKlappaySchema,
58
64
  MetricsAggregationSchema: () => MetricsAggregationSchema,
59
65
  MetricsDateGranularitySchema: () => MetricsDateGranularitySchema,
60
66
  MetricsFilterOperatorSchema: () => MetricsFilterOperatorSchema,
@@ -76,9 +82,12 @@ __export(index_exports, {
76
82
  PendingDistributionEventSchema: () => PendingDistributionEventSchema,
77
83
  PendingDistributionRecipientSchema: () => PendingDistributionRecipientSchema,
78
84
  PendingDistributionSchema: () => PendingDistributionSchema,
85
+ RecipientSchema: () => RecipientSchema,
79
86
  SandboxTriggerSchema: () => SandboxTriggerSchema,
87
+ SetRecipientPayoutSchema: () => SetRecipientPayoutSchema,
80
88
  SettlementStatusSchema: () => SettlementStatusSchema,
81
89
  SplitDistributionStatusSchema: () => SplitDistributionStatusSchema,
90
+ SplitRecipientInputSchema: () => SplitRecipientInputSchema,
82
91
  SplitRecipientSchema: () => SplitRecipientSchema,
83
92
  TOKEN_ADDRESSES: () => TOKEN_ADDRESSES,
84
93
  TOKEN_DECIMALS: () => TOKEN_DECIMALS,
@@ -100,6 +109,7 @@ __export(index_exports, {
100
109
  WebhookListItemSchema: () => WebhookListItemSchema,
101
110
  WebhookPayloadSchema: () => WebhookPayloadSchema,
102
111
  WebhookSchema: () => WebhookSchema,
112
+ findConflictingScopes: () => findConflictingScopes,
103
113
  paginatedSchema: () => paginatedSchema
104
114
  });
105
115
  module.exports = __toCommonJS(index_exports);
@@ -136,11 +146,26 @@ var ApiKeyScopeSchema = import_zod3.z.enum([
136
146
  "metrics:charges:read",
137
147
  "metrics:transactions:read",
138
148
  "metrics:distributions:read",
139
- "sandbox:trigger"
149
+ "sandbox:trigger",
150
+ "charges:split_write",
151
+ "recipients:read",
152
+ "recipients:write",
153
+ "recipients:manage_payout"
140
154
  ]).describe(
141
- "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."
155
+ "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."
142
156
  );
143
157
  var API_KEY_SCOPES = ApiKeyScopeSchema.options;
158
+ var CONFLICTING_SCOPE_PAIRS = [
159
+ ["charges:split_write", "recipients:write"],
160
+ ["charges:split_write", "recipients:manage_payout"]
161
+ ];
162
+ function findConflictingScopes(scopes) {
163
+ const held = new Set(scopes);
164
+ for (const [a, b] of CONFLICTING_SCOPE_PAIRS) {
165
+ if (held.has(a) && held.has(b)) return [a, b];
166
+ }
167
+ return null;
168
+ }
144
169
 
145
170
  // src/networks.ts
146
171
  var import_zod4 = require("zod");
@@ -310,21 +335,31 @@ var SplitRecipientSchema = import_zod8.z.object({
310
335
  'Free-form label for your own bookkeeping (e.g. `"supplier"`, `"sales rep"`) \u2014 echoed back unchanged, never interpreted by Klappay.'
311
336
  )
312
337
  });
313
- var SplitRecipientsSchema = import_zod8.z.array(SplitRecipientSchema).max(CHARGE_SPLIT_RECIPIENTS_MAX).superRefine((recipients, ctx) => {
338
+ var SplitRecipientInputSchema = import_zod8.z.object({
339
+ recipientId: import_zod8.z.string().describe(
340
+ "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."
341
+ ),
342
+ percent: import_zod8.z.number().positive().max(100).describe(
343
+ "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."
344
+ ),
345
+ label: import_zod8.z.string().min(1).max(64).optional().describe(
346
+ '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.'
347
+ )
348
+ });
349
+ var SplitRecipientsInputSchema = import_zod8.z.array(SplitRecipientInputSchema).max(CHARGE_SPLIT_RECIPIENTS_MAX).superRefine((recipients, ctx) => {
314
350
  const seen = /* @__PURE__ */ new Set();
315
351
  recipients.forEach((recipient, index) => {
316
- const key = recipient.address.toLowerCase();
317
- if (seen.has(key)) {
352
+ if (seen.has(recipient.recipientId)) {
318
353
  ctx.addIssue({
319
354
  code: import_zod8.z.ZodIssueCode.custom,
320
- message: `Duplicate split recipient address: ${recipient.address}.`,
321
- path: [index, "address"]
355
+ message: `Duplicate split recipientId: ${recipient.recipientId}.`,
356
+ path: [index, "recipientId"]
322
357
  });
323
358
  }
324
- seen.add(key);
359
+ seen.add(recipient.recipientId);
325
360
  });
326
361
  }).describe(
327
- `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\`.`
362
+ `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\`.`
328
363
  );
329
364
  var CHARGE_AMOUNT_MAX = 999999999999;
330
365
  var CreateChargeSchema = import_zod8.z.object({
@@ -349,7 +384,7 @@ var CreateChargeSchema = import_zod8.z.object({
349
384
  redirectUrl: import_zod8.z.string().url().refine((value) => /^https?:\/\//.test(value), "must use http or https").optional().describe(
350
385
  "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."
351
386
  ),
352
- splitRecipients: SplitRecipientsSchema.optional()
387
+ splitRecipients: SplitRecipientsInputSchema.optional()
353
388
  });
354
389
  var ChargeSchema = import_zod8.z.object({
355
390
  id: import_zod8.z.string().describe("Klappay-generated id, e.g. `ch_...`. Use this to look up the charge later."),
@@ -822,12 +857,35 @@ var WebhookDeliverySchema = import_zod12.z.object({
822
857
  var ListWebhookDeliveriesSchema = PaginationQuerySchema;
823
858
  var PaginatedWebhookDeliveriesSchema = paginatedSchema(WebhookDeliverySchema);
824
859
 
825
- // src/timeline.ts
860
+ // src/recipients.ts
826
861
  var import_zod13 = require("zod");
827
- var TransactionSourceSchema = import_zod13.z.enum(["moralis_webhook", "reconciliation_job", "sandbox"]).describe(
862
+ var EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
863
+ var CreateRecipientSchema = import_zod13.z.object({
864
+ address: import_zod13.z.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."),
865
+ label: import_zod13.z.string().min(1).max(64).optional().describe('Free-form label for your own bookkeeping (e.g. `"supplier"`) \u2014 never interpreted.')
866
+ });
867
+ var RecipientSchema = import_zod13.z.object({
868
+ id: import_zod13.z.string().describe(
869
+ "Klappay-generated id, e.g. `rc_...` \u2014 this, not the raw address, is what a charge's `splitRecipients[].recipientId` references."
870
+ ),
871
+ environment: EnvironmentSchema,
872
+ address: import_zod13.z.string(),
873
+ label: import_zod13.z.string().nullable(),
874
+ payout: import_zod13.z.boolean().describe(
875
+ "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`."
876
+ ),
877
+ createdAt: import_zod13.z.string().datetime()
878
+ });
879
+ var SetRecipientPayoutSchema = import_zod13.z.object({
880
+ payout: import_zod13.z.boolean().describe("New payout-eligibility value for this recipient.")
881
+ });
882
+
883
+ // src/timeline.ts
884
+ var import_zod14 = require("zod");
885
+ var TransactionSourceSchema = import_zod14.z.enum(["moralis_webhook", "reconciliation_job", "sandbox"]).describe(
828
886
  "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)."
829
887
  );
830
- var TimelineEventTypeSchema = import_zod13.z.enum([
888
+ var TimelineEventTypeSchema = import_zod14.z.enum([
831
889
  "charge.created",
832
890
  "charge.expired",
833
891
  "transaction.detected",
@@ -838,11 +896,11 @@ var TimelineEventTypeSchema = import_zod13.z.enum([
838
896
  ]).describe(
839
897
  "`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)."
840
898
  );
841
- var TimelineEventSchema = import_zod13.z.object({
899
+ var TimelineEventSchema = import_zod14.z.object({
842
900
  type: TimelineEventTypeSchema,
843
- at: import_zod13.z.string().datetime(),
844
- txHash: import_zod13.z.string().optional().describe("Present for `transaction.detected` and `split.distributed` events only."),
845
- amount: import_zod13.z.number().optional().describe(
901
+ at: import_zod14.z.string().datetime(),
902
+ txHash: import_zod14.z.string().optional().describe("Present for `transaction.detected` and `split.distributed` events only."),
903
+ amount: import_zod14.z.number().optional().describe(
846
904
  "Present for `transaction.detected` events only \u2014 the amount that specific transfer carried."
847
905
  ),
848
906
  source: TransactionSourceSchema.optional().describe(
@@ -854,49 +912,49 @@ var TimelineEventSchema = import_zod13.z.object({
854
912
  network: NetworkSchema.optional().describe(
855
913
  "Present for `transaction.detected` and `split.distributed` events \u2014 which network this specific transfer, or settlement, used."
856
914
  ),
857
- causedTransition: import_zod13.z.boolean().optional().describe(
915
+ causedTransition: import_zod14.z.boolean().optional().describe(
858
916
  "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."
859
917
  ),
860
918
  event: WebhookEventTypeSchema.optional().describe(
861
919
  "Present for `webhook.*` events only \u2014 which event type this delivery was for."
862
920
  ),
863
- responseCode: import_zod13.z.number().nullable().optional().describe(
921
+ responseCode: import_zod14.z.number().nullable().optional().describe(
864
922
  "Present for `webhook.*` events only \u2014 HTTP status your endpoint returned, or `null` if the request never connected."
865
923
  ),
866
- attempts: import_zod13.z.number().optional().describe(
924
+ attempts: import_zod14.z.number().optional().describe(
867
925
  "Present for `webhook.*` events only \u2014 how many delivery attempts have been made so far."
868
926
  )
869
927
  });
870
928
 
871
929
  // src/health.ts
872
- var import_zod14 = require("zod");
873
- var HealthSchema = import_zod14.z.object({
874
- status: import_zod14.z.enum(["ok", "error"]).describe(
930
+ var import_zod15 = require("zod");
931
+ var HealthSchema = import_zod15.z.object({
932
+ status: import_zod15.z.enum(["ok", "error"]).describe(
875
933
  "`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."
876
934
  ),
877
- version: import_zod14.z.string(),
878
- timestamp: import_zod14.z.string().datetime(),
879
- db: import_zod14.z.enum(["ok", "error"]).describe("Result of a real database connectivity check, not just a process-alive check."),
880
- pendingWebhooks: import_zod14.z.number().describe("Count of webhook deliveries still awaiting a successful attempt."),
881
- oldestPendingChargeAgeSeconds: import_zod14.z.number().nullable().describe("Age of the oldest still-unpaid charge, in seconds. `null` if there are none."),
882
- lastMoralisEventAgeSeconds: import_zod14.z.number().nullable().describe(
935
+ version: import_zod15.z.string(),
936
+ timestamp: import_zod15.z.string().datetime(),
937
+ db: import_zod15.z.enum(["ok", "error"]).describe("Result of a real database connectivity check, not just a process-alive check."),
938
+ pendingWebhooks: import_zod15.z.number().describe("Count of webhook deliveries still awaiting a successful attempt."),
939
+ oldestPendingChargeAgeSeconds: import_zod15.z.number().nullable().describe("Age of the oldest still-unpaid charge, in seconds. `null` if there are none."),
940
+ lastMoralisEventAgeSeconds: import_zod15.z.number().nullable().describe(
883
941
  "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."
884
942
  )
885
943
  });
886
944
 
887
945
  // src/sandbox.ts
888
- var import_zod15 = require("zod");
889
- var SandboxTriggerSchema = import_zod15.z.object({
946
+ var import_zod16 = require("zod");
947
+ var SandboxTriggerSchema = import_zod16.z.object({
890
948
  event: TriggerableChargeEventSchema,
891
- amount: import_zod15.z.number().positive().max(CHARGE_AMOUNT_MAX).optional().describe(
949
+ amount: import_zod16.z.number().positive().max(CHARGE_AMOUNT_MAX).optional().describe(
892
950
  "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."
893
951
  )
894
952
  });
895
953
 
896
954
  // src/capabilities.ts
897
- var import_zod16 = require("zod");
898
- var CapabilitiesSchema = import_zod16.z.object({
899
- acceptedPayments: import_zod16.z.array(AcceptedPaymentSchema).describe(
955
+ var import_zod17 = require("zod");
956
+ var CapabilitiesSchema = import_zod17.z.object({
957
+ acceptedPayments: import_zod17.z.array(AcceptedPaymentSchema).describe(
900
958
  "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."
901
959
  )
902
960
  });
@@ -910,6 +968,8 @@ var CapabilitiesSchema = import_zod16.z.object({
910
968
  CHARGE_EXPIRES_IN_MAX_SECONDS,
911
969
  CHARGE_EXPIRES_IN_MIN_SECONDS,
912
970
  CHARGE_SPLIT_RECIPIENTS_MAX,
971
+ CHECKOUT_PRODUCTS_MAX,
972
+ CONFLICTING_SCOPE_PAIRS,
913
973
  CapabilitiesSchema,
914
974
  ChargeSchema,
915
975
  ChargeStatusSchema,
@@ -917,7 +977,9 @@ var CapabilitiesSchema = import_zod16.z.object({
917
977
  ChargesDateFieldSchema,
918
978
  ChargesMetricFieldSchema,
919
979
  ChargesQueryFieldSchema,
980
+ CheckoutProductSchema,
920
981
  CreateChargeSchema,
982
+ CreateRecipientSchema,
921
983
  CreateWebhookSchema,
922
984
  DistributionsDateFieldSchema,
923
985
  DistributionsMetricFieldSchema,
@@ -928,6 +990,7 @@ var CapabilitiesSchema = import_zod16.z.object({
928
990
  ErrorPayloadSchema,
929
991
  GetChargeQrCodeQuerySchema,
930
992
  HealthSchema,
993
+ KlappayCheckoutMetadataSchema,
931
994
  ListChargesSchema,
932
995
  ListWebhookDeliveriesSchema,
933
996
  ListenPendingDistributionsQuerySchema,
@@ -937,6 +1000,7 @@ var CapabilitiesSchema = import_zod16.z.object({
937
1000
  METRICS_QUERY_MAX_GROUP_BY,
938
1001
  METRICS_QUERY_MAX_METRICS,
939
1002
  METRICS_QUERY_MAX_ROW_LIMIT,
1003
+ MetadataWithKlappaySchema,
940
1004
  MetricsAggregationSchema,
941
1005
  MetricsDateGranularitySchema,
942
1006
  MetricsFilterOperatorSchema,
@@ -958,9 +1022,12 @@ var CapabilitiesSchema = import_zod16.z.object({
958
1022
  PendingDistributionEventSchema,
959
1023
  PendingDistributionRecipientSchema,
960
1024
  PendingDistributionSchema,
1025
+ RecipientSchema,
961
1026
  SandboxTriggerSchema,
1027
+ SetRecipientPayoutSchema,
962
1028
  SettlementStatusSchema,
963
1029
  SplitDistributionStatusSchema,
1030
+ SplitRecipientInputSchema,
964
1031
  SplitRecipientSchema,
965
1032
  TOKEN_ADDRESSES,
966
1033
  TOKEN_DECIMALS,
@@ -982,6 +1049,7 @@ var CapabilitiesSchema = import_zod16.z.object({
982
1049
  WebhookListItemSchema,
983
1050
  WebhookPayloadSchema,
984
1051
  WebhookSchema,
1052
+ findConflictingScopes,
985
1053
  paginatedSchema
986
1054
  });
987
1055
  //# sourceMappingURL=index.js.map