@fun-xyz/fiat-contract 0.20.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -99,29 +99,35 @@ this package is [ENG-5268](https://linear.app/funxyz/issue/ENG-5268).
99
99
 
100
100
  A provider can run its own flow instead of the shared step machine. `POST /fiat/quote` still routes
101
101
  and prices. When the request's `handoffs` lists the winning provider, the response carries
102
- `handoff: { provider, terms }` and no transitions, and the client calls that provider's
103
- `POST /fiat/providers/{provider}/intent`. `terms` lists the `CHECKOUT` documents this login has not
104
- accepted at the current version; it is empty for a returning buyer. The schema refuses a handoff on
105
- any state but `QUOTE`, a handoff to a provider other than the response's own, a handoff that still
106
- offers transitions, and a `terms` entry that is not a unique `CHECKOUT` document.
102
+ `handoff: { provider }` and no transitions. The client shows that provider's `CHECKOUT` documents
103
+ from `PROVIDER_CHECKOUT_DOCUMENTS`, records them with `POST /fiat/terms` on Continue, and then calls
104
+ `POST /fiat/providers/{provider}/intent`. The schema refuses a handoff on any state but `QUOTE`, a
105
+ handoff to a provider other than the response's own, and a handoff that still offers transitions.
107
106
 
108
107
  **Terms (`./terms`).** Every provider records acceptances through the platform `POST /fiat/terms`
109
- (`FIAT_TERMS_ENDPOINT`), with an `Idempotency-Key`. A `TermsDocument` has an `id`, a `kind`
110
- (`FUN_TERMS`, `PROVIDER_TERMS`, `FUN_KYC_COLLECTION`, `PROVIDER_KYC_SHARING`), a `stage` (`CHECKOUT` or
108
+ (`FIAT_TERMS_ENDPOINT`). A `TermsDocument` has an `id`, a `kind`
109
+ (`PROVIDER_TERMS`, `FUN_KYC_COLLECTION`, `PROVIDER_KYC_SHARING`), a `stage` (`CHECKOUT` or
111
110
  `KYC`), an opaque `version` and `contentDigest`, and at least one `https` link. `FiatTermsRequest` is
112
111
  `{ provider, acceptances: [{ documentId, version, contentDigest, acceptedAt }] }`, each document at
113
112
  most once; `FiatTermsResponse` is `{ recorded }`, the recorded document ids. A fault is a
114
113
  `FiatTermsErrorBody`: the shared `errorCode`, `errorMsg` and `reqId`, plus `reason` (`STALE_TERMS`,
115
- `IN_PROGRESS`, `IDEMPOTENCY_KEY_REUSED`, `INVALID_REQUEST`, `RATE_LIMITED`, `PROVIDER_UNAVAILABLE`) and `recovery`.
114
+ `INVALID_REQUEST`, `RATE_LIMITED`, `PROVIDER_UNAVAILABLE`) and `recovery`.
115
+
116
+ The package publishes every document as a constant, so the client and the backend read the same
117
+ text. `PROVIDER_CHECKOUT_DOCUMENTS` maps each provider to its current `CHECKOUT` documents.
118
+ `FUN_KYC_CONSENT_DOCUMENTS` is Fun's KYC consent, one digest per `FunKycConsentLocale`.
119
+ `PUBLISHED_TERMS_DOCUMENTS` is every version and locale ever published. Every published version
120
+ stays valid: the backend refuses only an unknown version, a wrong digest, or an `acceptedAt` older
121
+ than 5 minutes. A repeat of a version the login already holds writes nothing and returns the ids.
116
122
 
117
123
  **MoonPay (`./moonpay`).** One route, `MOONPAY_ENDPOINTS.intent`. A `MoonPayIntentRequest` is one of
118
124
  three kinds:
119
125
 
120
- | `kind` | Carries | `Idempotency-Key` |
126
+ | `kind` | Carries | A repeat |
121
127
  | --- | --- | --- |
122
- | `observe` | `checkout`, and `credentials` (`customerId`, `accessToken`) once the SDK has them | ignored |
123
- | `kyc` | `checkout`, `credentials`, `values` keyed by `KycFieldId` (no form id) | required |
124
- | `order` | `orderId`, `transactionId` | required |
128
+ | `observe` | `checkout`, and `credentials` (`customerId`, `accessToken`) once the SDK has them | writes nothing new |
129
+ | `kyc` | `checkout`, `credentials`, `values` keyed by `KycFieldId` (no form id) | drops values MoonPay no longer asks for |
130
+ | `order` | `orderId`, `transactionId?`, `quotedAmount` | returns the recorded order |
125
131
 
126
132
  `observe` and `kyc` answer a `MoonPayStepResponse`, which says what the backend needs next:
127
133
 
@@ -134,20 +140,24 @@ three kinds:
134
140
  | `READY` | `orderId`, `quoteInput` | mounts Apple Pay, then sends `order` after the charge |
135
141
  | `REFUSED` | `reason` (`NOT_ELIGIBLE`, `DOCUMENTS_REQUIRED`, `FINAL_REJECTION`, `CUSTOMER_MISMATCH`), `recovery: REQUOTE \| NONE` | exits the provider flow |
136
142
 
137
- `order` runs after the charge and answers a `MoonPayOrderResponse`:
143
+ `order` runs after every charge attempt, a failed one too, and answers a `MoonPayOrderResponse`.
144
+ `transactionId` is optional because a failed charge may have none; without it the backend records the
145
+ failed charges MoonPay lists for `orderId`. `quotedAmount` is the fiat amount the quote showed. A
146
+ failed charge answers `DONE` with the order in `FAILED`, and the client requotes for a new `orderId`.
147
+ After a failed charge with no `transactionId`, the client caps its `PENDING` polls.
138
148
 
139
149
  | `status` | Carries | The client then |
140
150
  | --- | --- | --- |
141
151
  | `DONE` | `order`, the `GET /fiat/orders/:orderId` response | shows the shared order status |
142
- | `PENDING` | `retryAfterMs` (at most 60 s) | sends the same `order`, with the same key, after the delay |
152
+ | `PENDING` | `retryAfterMs` (at most 60 s) | sends the same `order` after the delay |
143
153
  | `REFUSED` | `reason` (`CUSTOMER_MISMATCH`, `WALLET_MISMATCH`, `UNRECORDABLE`), `recovery: NONE` | exits the provider flow |
144
154
 
145
155
  `MoonPayIntentResponseFor` maps each `kind` to its union, and `MoonPayIntentResponse` is both.
146
156
 
147
157
  A fault is a 4xx or 5xx with a `MoonPayErrorBody`: the shared `errorCode`, `errorMsg` and `reqId`,
148
- plus `reason` (`SESSION_BUDGET_SPENT`, `RATE_LIMITED`, `PROVIDER_UNAVAILABLE`, `IN_PROGRESS`,
149
- `IDEMPOTENCY_KEY_REUSED`, `INVALID_REQUEST`) and `recovery`:
150
- `RETRY` (send the same request, with the same key), `REQUOTE`, or `NONE`. A field error is a `KYC`
158
+ plus `reason` (`SESSION_BUDGET_SPENT`, `RATE_LIMITED`, `PROVIDER_UNAVAILABLE`, `INVALID_REQUEST`)
159
+ and `recovery`:
160
+ `RETRY` (send the same request again), `REQUOTE`, or `NONE`. A field error is a `KYC`
151
161
  response, not a fault. The zod schemas (`MoonPay*Schema`, `TermsDocumentSchema`, `FiatTerms*Schema`)
152
162
  are exported from the root only.
153
163
 
@@ -0,0 +1,44 @@
1
+ // src/terms.ts
2
+ var FIAT_TERMS_ENDPOINT = "POST /fiat/terms";
3
+ var MOONPAY_TERMS = {
4
+ id: "MOONPAY_TERMS",
5
+ kind: "PROVIDER_TERMS",
6
+ stage: "CHECKOUT",
7
+ version: "2026-09-l2-kyc-sharing-v2",
8
+ contentDigest: "44e096af329262134fe6680659c9bd42014cbf5e96f064d67869173a4f7d822b",
9
+ links: [
10
+ { label: "MoonPay Terms of Use", url: "https://www.moonpay.com/legal/terms" },
11
+ { label: "MoonPay Privacy Policy", url: "https://www.moonpay.com/legal/privacy_policy" },
12
+ { label: "Persona Privacy Policy", url: "https://withpersona.com/legal/privacy-policy" }
13
+ ]
14
+ };
15
+ var PROVIDER_CHECKOUT_DOCUMENTS = {
16
+ MOONPAY: [MOONPAY_TERMS]
17
+ };
18
+ var funKycConsent = (contentDigest) => ({
19
+ id: "FUN_KYC_CONSENT",
20
+ kind: "FUN_KYC_COLLECTION",
21
+ stage: "KYC",
22
+ version: "fun-kyc-consent-2026-09-11",
23
+ contentDigest,
24
+ links: [{ label: "Fun.xyz Privacy Policy", url: "https://fun.xyz/privacy" }]
25
+ });
26
+ var FUN_KYC_CONSENT_DOCUMENTS = {
27
+ en: funKycConsent("da31f2467deb996476b131116508ea2a00220765e658a79abc6d2bd44337504a"),
28
+ es: funKycConsent("73753b70ad8d1b48dfdcda35e6028c3a7ada66c311a67a1d8f6fe13c76a32f3b"),
29
+ fr: funKycConsent("5ee82675984591dbf66337f31246179103c5624a189850943d400f9b85a84965"),
30
+ ja: funKycConsent("3f272da313f66601a20c3ca43c967aff4bb7f73488177f0a4ce94cbc78bafce2"),
31
+ ko: funKycConsent("aab7839bfba3f34aa47ca6b10647c2c8965c37d845494ef8f3067ca3e3821200"),
32
+ ru: funKycConsent("17168083ebcc4fd11ea80b08c3f230bdec03a1e7b89173a38819ae5533b0fc96"),
33
+ tr: funKycConsent("463040c3b58c922ecae2ae8262a78eb732bbcc5606c394723626378b3db2bbc3"),
34
+ zh: funKycConsent("7f76d850adaf69783c5d9365e51abb5446f4e5778c63eabbdb4784d72464f855")
35
+ };
36
+ var PUBLISHED_TERMS_DOCUMENTS = [MOONPAY_TERMS, ...Object.values(FUN_KYC_CONSENT_DOCUMENTS)];
37
+
38
+ export {
39
+ FIAT_TERMS_ENDPOINT,
40
+ PROVIDER_CHECKOUT_DOCUMENTS,
41
+ FUN_KYC_CONSENT_DOCUMENTS,
42
+ PUBLISHED_TERMS_DOCUMENTS
43
+ };
44
+ //# sourceMappingURL=chunk-5GRPYE2U.mjs.map
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.20.2";
2
+ var version = "0.22.0";
3
3
 
4
4
  // src/table.ts
5
5
  var TABLE_VERSION = version;
@@ -514,4 +514,4 @@ export {
514
514
  tableEntry,
515
515
  isTerminal
516
516
  };
517
- //# sourceMappingURL=chunk-CYT2NARF.mjs.map
517
+ //# sourceMappingURL=chunk-7OUDI3JX.mjs.map
@@ -6,4 +6,4 @@ var MOONPAY_ENDPOINTS = {
6
6
  export {
7
7
  MOONPAY_ENDPOINTS
8
8
  };
9
- //# sourceMappingURL=chunk-WW26WEZO.mjs.map
9
+ //# sourceMappingURL=chunk-SJ7V3YOM.mjs.map
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  FIELD_SPEC_TYPES: () => FIELD_SPEC_TYPES,
37
37
  FIXTURES: () => FIXTURES,
38
38
  FIXTURE_COVERAGE_GAPS: () => FIXTURE_COVERAGE_GAPS,
39
+ FUN_KYC_CONSENT_DOCUMENTS: () => FUN_KYC_CONSENT_DOCUMENTS,
39
40
  FailureReasonSchema: () => FailureReasonSchema,
40
41
  FeeLineSchema: () => FeeLineSchema,
41
42
  FiatAmountSchema: () => FiatAmountSchema,
@@ -80,6 +81,8 @@ __export(index_exports, {
80
81
  OrderStatusSchema: () => OrderStatusSchema,
81
82
  OrderSummarySchema: () => OrderSummarySchema,
82
83
  PAYMENT_METHOD_CATEGORIES: () => PAYMENT_METHOD_CATEGORIES,
84
+ PROVIDER_CHECKOUT_DOCUMENTS: () => PROVIDER_CHECKOUT_DOCUMENTS,
85
+ PUBLISHED_TERMS_DOCUMENTS: () => PUBLISHED_TERMS_DOCUMENTS,
83
86
  PaymentCaptureStateSchema: () => PaymentCaptureStateSchema,
84
87
  PaymentInstructStateSchema: () => PaymentInstructStateSchema,
85
88
  PaymentSurfaceSchema: () => PaymentSurfaceSchema,
@@ -975,7 +978,7 @@ var $nonBlank = import_zod.z.string().refine((value) => value.trim().length > 0,
975
978
  var $termsStage = import_zod.z.enum(["CHECKOUT", "KYC"]);
976
979
  var $termsDocument = obj({
977
980
  id: $nonBlank,
978
- kind: import_zod.z.enum(["FUN_TERMS", "PROVIDER_TERMS", "FUN_KYC_COLLECTION", "PROVIDER_KYC_SHARING"]),
981
+ kind: import_zod.z.enum(["PROVIDER_TERMS", "FUN_KYC_COLLECTION", "PROVIDER_KYC_SHARING"]),
979
982
  stage: $termsStage,
980
983
  version: $nonBlank,
981
984
  contentDigest: $nonBlank,
@@ -983,10 +986,6 @@ var $termsDocument = obj({
983
986
  });
984
987
  var TermsDocumentSchema = $termsDocument;
985
988
  var hasUniqueIds = (ids) => new Set(ids).size === ids.length;
986
- var $termsDocuments = (stage) => import_zod.z.array($termsDocument).refine((documents) => hasUniqueIds(documents.map((document) => document.id)), "document ids must be unique").refine(
987
- (documents) => stage === void 0 || documents.every((document) => document.stage === stage),
988
- `every document must be at stage ${stage}`
989
- );
990
989
  var $termsAcceptance = obj({
991
990
  documentId: $nonBlank,
992
991
  version: $nonBlank,
@@ -1005,7 +1004,7 @@ var $termsErrorBody = obj({
1005
1004
  errorCode: $nonBlank,
1006
1005
  errorMsg: import_zod.z.string(),
1007
1006
  reqId: import_zod.z.string().optional(),
1008
- reason: import_zod.z.enum(["STALE_TERMS", "IN_PROGRESS", "IDEMPOTENCY_KEY_REUSED", "INVALID_REQUEST", "RATE_LIMITED", "PROVIDER_UNAVAILABLE"]),
1007
+ reason: import_zod.z.enum(["STALE_TERMS", "INVALID_REQUEST", "RATE_LIMITED", "PROVIDER_UNAVAILABLE"]),
1009
1008
  recovery: import_zod.z.enum(["RETRY", "REQUOTE", "NONE"])
1010
1009
  });
1011
1010
  var FiatTermsErrorBodySchema = $termsErrorBody;
@@ -1014,7 +1013,7 @@ var $providerBoundStepResponse = obj({
1014
1013
  provider: $provider,
1015
1014
  transitions: import_zod.z.array($transition),
1016
1015
  preparedPayment: $preparedPayment.optional(),
1017
- handoff: obj({ provider: $provider, terms: $termsDocuments("CHECKOUT") }).optional(),
1016
+ handoff: obj({ provider: $provider }).optional(),
1018
1017
  routingSessionRef: import_zod.z.string().optional(),
1019
1018
  providerAuthToken: obj({
1020
1019
  clientSecret: import_zod.z.string(),
@@ -1068,7 +1067,7 @@ var $stepResponse = import_zod.z.union([$providerBoundStepResponse, $blockedStep
1068
1067
  var FiatStepResponseSchema = $stepResponse;
1069
1068
 
1070
1069
  // package.json
1071
- var version = "0.20.2";
1070
+ var version = "0.22.0";
1072
1071
 
1073
1072
  // src/table.ts
1074
1073
  var TABLE_VERSION = version;
@@ -2636,6 +2635,40 @@ function assertAllFixtures() {
2636
2635
 
2637
2636
  // src/terms.ts
2638
2637
  var FIAT_TERMS_ENDPOINT = "POST /fiat/terms";
2638
+ var MOONPAY_TERMS = {
2639
+ id: "MOONPAY_TERMS",
2640
+ kind: "PROVIDER_TERMS",
2641
+ stage: "CHECKOUT",
2642
+ version: "2026-09-l2-kyc-sharing-v2",
2643
+ contentDigest: "44e096af329262134fe6680659c9bd42014cbf5e96f064d67869173a4f7d822b",
2644
+ links: [
2645
+ { label: "MoonPay Terms of Use", url: "https://www.moonpay.com/legal/terms" },
2646
+ { label: "MoonPay Privacy Policy", url: "https://www.moonpay.com/legal/privacy_policy" },
2647
+ { label: "Persona Privacy Policy", url: "https://withpersona.com/legal/privacy-policy" }
2648
+ ]
2649
+ };
2650
+ var PROVIDER_CHECKOUT_DOCUMENTS = {
2651
+ MOONPAY: [MOONPAY_TERMS]
2652
+ };
2653
+ var funKycConsent = (contentDigest) => ({
2654
+ id: "FUN_KYC_CONSENT",
2655
+ kind: "FUN_KYC_COLLECTION",
2656
+ stage: "KYC",
2657
+ version: "fun-kyc-consent-2026-09-11",
2658
+ contentDigest,
2659
+ links: [{ label: "Fun.xyz Privacy Policy", url: "https://fun.xyz/privacy" }]
2660
+ });
2661
+ var FUN_KYC_CONSENT_DOCUMENTS = {
2662
+ en: funKycConsent("da31f2467deb996476b131116508ea2a00220765e658a79abc6d2bd44337504a"),
2663
+ es: funKycConsent("73753b70ad8d1b48dfdcda35e6028c3a7ada66c311a67a1d8f6fe13c76a32f3b"),
2664
+ fr: funKycConsent("5ee82675984591dbf66337f31246179103c5624a189850943d400f9b85a84965"),
2665
+ ja: funKycConsent("3f272da313f66601a20c3ca43c967aff4bb7f73488177f0a4ce94cbc78bafce2"),
2666
+ ko: funKycConsent("aab7839bfba3f34aa47ca6b10647c2c8965c37d845494ef8f3067ca3e3821200"),
2667
+ ru: funKycConsent("17168083ebcc4fd11ea80b08c3f230bdec03a1e7b89173a38819ae5533b0fc96"),
2668
+ tr: funKycConsent("463040c3b58c922ecae2ae8262a78eb732bbcc5606c394723626378b3db2bbc3"),
2669
+ zh: funKycConsent("7f76d850adaf69783c5d9365e51abb5446f4e5778c63eabbdb4784d72464f855")
2670
+ };
2671
+ var PUBLISHED_TERMS_DOCUMENTS = [MOONPAY_TERMS, ...Object.values(FUN_KYC_CONSENT_DOCUMENTS)];
2639
2672
 
2640
2673
  // src/providers/moonpay.ts
2641
2674
  var MOONPAY_ENDPOINTS = {
@@ -2665,7 +2698,7 @@ var $intentRequest = import_zod2.z.discriminatedUnion("kind", [
2665
2698
  credentials: $credentials,
2666
2699
  values: import_zod2.z.record(KycFieldIdSchema, import_zod2.z.string()).refine((values) => Object.keys(values).length > 0, "values must not be empty")
2667
2700
  }),
2668
- obj2({ kind: import_zod2.z.literal("order"), orderId: $nonEmpty, transactionId: $nonEmpty })
2701
+ obj2({ kind: import_zod2.z.literal("order"), orderId: $nonEmpty, transactionId: $nonEmpty.optional(), quotedAmount: FiatAmountSchema })
2669
2702
  ]);
2670
2703
  var MoonPayIntentRequestSchema = $intentRequest;
2671
2704
  var $quoteInput = obj2({
@@ -2717,14 +2750,7 @@ var MoonPayOrderResponseSchema = $orderResponse;
2717
2750
  var $intentResponse = import_zod2.z.union([$stepResponse2, $orderResponse]);
2718
2751
  var MoonPayIntentResponseSchema = $intentResponse;
2719
2752
  var $faultFields = {
2720
- reason: import_zod2.z.enum([
2721
- "SESSION_BUDGET_SPENT",
2722
- "RATE_LIMITED",
2723
- "PROVIDER_UNAVAILABLE",
2724
- "IN_PROGRESS",
2725
- "IDEMPOTENCY_KEY_REUSED",
2726
- "INVALID_REQUEST"
2727
- ]),
2753
+ reason: import_zod2.z.enum(["SESSION_BUDGET_SPENT", "RATE_LIMITED", "PROVIDER_UNAVAILABLE", "INVALID_REQUEST"]),
2728
2754
  recovery: import_zod2.z.enum(["RETRY", "REQUOTE", "NONE"])
2729
2755
  };
2730
2756
  var $fault = obj2($faultFields);
package/dist/index.mjs CHANGED
@@ -12,13 +12,16 @@ import {
12
12
  tableEntry,
13
13
  unofferedEndpoints,
14
14
  unreachableStates
15
- } from "./chunk-CYT2NARF.mjs";
15
+ } from "./chunk-7OUDI3JX.mjs";
16
16
  import {
17
17
  MOONPAY_ENDPOINTS
18
- } from "./chunk-WW26WEZO.mjs";
18
+ } from "./chunk-SJ7V3YOM.mjs";
19
19
  import {
20
- FIAT_TERMS_ENDPOINT
21
- } from "./chunk-WRRCWAY2.mjs";
20
+ FIAT_TERMS_ENDPOINT,
21
+ FUN_KYC_CONSENT_DOCUMENTS,
22
+ PROVIDER_CHECKOUT_DOCUMENTS,
23
+ PUBLISHED_TERMS_DOCUMENTS
24
+ } from "./chunk-5GRPYE2U.mjs";
22
25
 
23
26
  // src/codes.ts
24
27
  var US_STATE_CODES = [
@@ -859,7 +862,7 @@ var $nonBlank = z.string().refine((value) => value.trim().length > 0, "must not
859
862
  var $termsStage = z.enum(["CHECKOUT", "KYC"]);
860
863
  var $termsDocument = obj({
861
864
  id: $nonBlank,
862
- kind: z.enum(["FUN_TERMS", "PROVIDER_TERMS", "FUN_KYC_COLLECTION", "PROVIDER_KYC_SHARING"]),
865
+ kind: z.enum(["PROVIDER_TERMS", "FUN_KYC_COLLECTION", "PROVIDER_KYC_SHARING"]),
863
866
  stage: $termsStage,
864
867
  version: $nonBlank,
865
868
  contentDigest: $nonBlank,
@@ -867,10 +870,6 @@ var $termsDocument = obj({
867
870
  });
868
871
  var TermsDocumentSchema = $termsDocument;
869
872
  var hasUniqueIds = (ids) => new Set(ids).size === ids.length;
870
- var $termsDocuments = (stage) => z.array($termsDocument).refine((documents) => hasUniqueIds(documents.map((document) => document.id)), "document ids must be unique").refine(
871
- (documents) => stage === void 0 || documents.every((document) => document.stage === stage),
872
- `every document must be at stage ${stage}`
873
- );
874
873
  var $termsAcceptance = obj({
875
874
  documentId: $nonBlank,
876
875
  version: $nonBlank,
@@ -889,7 +888,7 @@ var $termsErrorBody = obj({
889
888
  errorCode: $nonBlank,
890
889
  errorMsg: z.string(),
891
890
  reqId: z.string().optional(),
892
- reason: z.enum(["STALE_TERMS", "IN_PROGRESS", "IDEMPOTENCY_KEY_REUSED", "INVALID_REQUEST", "RATE_LIMITED", "PROVIDER_UNAVAILABLE"]),
891
+ reason: z.enum(["STALE_TERMS", "INVALID_REQUEST", "RATE_LIMITED", "PROVIDER_UNAVAILABLE"]),
893
892
  recovery: z.enum(["RETRY", "REQUOTE", "NONE"])
894
893
  });
895
894
  var FiatTermsErrorBodySchema = $termsErrorBody;
@@ -898,7 +897,7 @@ var $providerBoundStepResponse = obj({
898
897
  provider: $provider,
899
898
  transitions: z.array($transition),
900
899
  preparedPayment: $preparedPayment.optional(),
901
- handoff: obj({ provider: $provider, terms: $termsDocuments("CHECKOUT") }).optional(),
900
+ handoff: obj({ provider: $provider }).optional(),
902
901
  routingSessionRef: z.string().optional(),
903
902
  providerAuthToken: obj({
904
903
  clientSecret: z.string(),
@@ -2039,7 +2038,7 @@ var $intentRequest = z2.discriminatedUnion("kind", [
2039
2038
  credentials: $credentials,
2040
2039
  values: z2.record(KycFieldIdSchema, z2.string()).refine((values) => Object.keys(values).length > 0, "values must not be empty")
2041
2040
  }),
2042
- obj2({ kind: z2.literal("order"), orderId: $nonEmpty, transactionId: $nonEmpty })
2041
+ obj2({ kind: z2.literal("order"), orderId: $nonEmpty, transactionId: $nonEmpty.optional(), quotedAmount: FiatAmountSchema })
2043
2042
  ]);
2044
2043
  var MoonPayIntentRequestSchema = $intentRequest;
2045
2044
  var $quoteInput = obj2({
@@ -2091,14 +2090,7 @@ var MoonPayOrderResponseSchema = $orderResponse;
2091
2090
  var $intentResponse = z2.union([$stepResponse2, $orderResponse]);
2092
2091
  var MoonPayIntentResponseSchema = $intentResponse;
2093
2092
  var $faultFields = {
2094
- reason: z2.enum([
2095
- "SESSION_BUDGET_SPENT",
2096
- "RATE_LIMITED",
2097
- "PROVIDER_UNAVAILABLE",
2098
- "IN_PROGRESS",
2099
- "IDEMPOTENCY_KEY_REUSED",
2100
- "INVALID_REQUEST"
2101
- ]),
2093
+ reason: z2.enum(["SESSION_BUDGET_SPENT", "RATE_LIMITED", "PROVIDER_UNAVAILABLE", "INVALID_REQUEST"]),
2102
2094
  recovery: z2.enum(["RETRY", "REQUOTE", "NONE"])
2103
2095
  };
2104
2096
  var $fault = obj2($faultFields);
@@ -2122,6 +2114,7 @@ export {
2122
2114
  FIELD_SPEC_TYPES,
2123
2115
  FIXTURES,
2124
2116
  FIXTURE_COVERAGE_GAPS,
2117
+ FUN_KYC_CONSENT_DOCUMENTS,
2125
2118
  FailureReasonSchema,
2126
2119
  FeeLineSchema,
2127
2120
  FiatAmountSchema,
@@ -2166,6 +2159,8 @@ export {
2166
2159
  OrderStatusSchema,
2167
2160
  OrderSummarySchema,
2168
2161
  PAYMENT_METHOD_CATEGORIES,
2162
+ PROVIDER_CHECKOUT_DOCUMENTS,
2163
+ PUBLISHED_TERMS_DOCUMENTS,
2169
2164
  PaymentCaptureStateSchema,
2170
2165
  PaymentInstructStateSchema,
2171
2166
  PaymentSurfaceSchema,
@@ -7,8 +7,8 @@
7
7
  * the platform `POST /fiat/terms` (`./terms`). Identity and the deposit wallet always come from the
8
8
  * verified partner assertion, never from a request body.
9
9
  *
10
- * `kyc` and `order` require an `Idempotency-Key` header; `observe` ignores it. A repeated key skips
11
- * the effect and still returns the current response.
10
+ * A retry sends the same request. Each write is idempotent on data the request carries, and every
11
+ * call, a retry too, returns the current response.
12
12
  *
13
13
  * A fault is a 4xx/5xx whose body is `MoonPayErrorBody`. Anything the user can act on is a 200
14
14
  * `MoonPayIntentResponse`.
@@ -57,11 +57,16 @@ export type MoonPayIntentRequest =
57
57
  credentials: MoonPaySdkCredentials;
58
58
  values: Partial<Record<KycFieldId, string>>;
59
59
  }
60
- /** After the Apple Pay sheet. `transactionId` is from the Apple Pay completion event. */
60
+ /**
61
+ * After every charge attempt, a failed one too. `transactionId` is from the Apple Pay event when it
62
+ * has one; without it the backend records the failed charges MoonPay lists for `orderId`.
63
+ * `quotedAmount` is the fiat amount the buyer saw on the quote.
64
+ */
61
65
  | {
62
66
  kind: 'order';
63
67
  orderId: string;
64
- transactionId: string;
68
+ transactionId?: string;
69
+ quotedAmount: FiatAmount;
65
70
  };
66
71
  export type MoonPayIntentKind = MoonPayIntentRequest['kind'];
67
72
  /**
@@ -138,14 +143,20 @@ export type MoonPayStepResponse =
138
143
  reason: MoonPayStepRefusal;
139
144
  recovery: 'REQUOTE' | 'NONE';
140
145
  };
141
- /** The answer to `order`, which runs after the charge. */
146
+ /**
147
+ * The answer to `order`, which runs after each charge attempt. A failed charge is recorded too: it
148
+ * answers `DONE` with the order in `FAILED`, and the client requotes for a new `orderId`.
149
+ */
142
150
  export type MoonPayOrderResponse =
143
151
  /** The `GET /fiat/orders/:orderId` response; the shared order screen renders it. */
144
152
  {
145
153
  status: 'DONE';
146
154
  order: FiatStepResponse;
147
155
  }
148
- /** MoonPay does not show the transaction yet. Send the same `order`, with the same key, after the delay. */
156
+ /**
157
+ * MoonPay does not list the transaction yet. Send the same `order` after the delay. After a failed
158
+ * charge with no `transactionId`, the client caps these polls.
159
+ */
149
160
  | {
150
161
  status: 'PENDING';
151
162
  retryAfterMs: number;
@@ -163,15 +174,14 @@ export type MoonPayIntentResponseFor = {
163
174
  };
164
175
  export type MoonPayIntentResponse = MoonPayStepResponse | MoonPayOrderResponse;
165
176
  export type MoonPayIntentStatus = MoonPayIntentResponse['status'];
166
- /** `RETRY` sends the same request again, with the same key. `REQUOTE` goes back to `POST /fiat/quote`. `NONE` ends the flow. */
177
+ /** `RETRY` sends the same request again. `REQUOTE` goes back to `POST /fiat/quote`. `NONE` ends the flow. */
167
178
  export type MoonPayRecovery = 'RETRY' | 'REQUOTE' | 'NONE';
168
179
  /**
169
180
  * `SESSION_BUDGET_SPENT`: the per-login SDK session mint budget (429). `RATE_LIMITED`: the kind's
170
- * request budget (429). `PROVIDER_UNAVAILABLE`: MoonPay or Sumsub is down. `IN_PROGRESS`: the same
171
- * key is still running (409). `IDEMPOTENCY_KEY_REUSED`: the same key with a different body (422).
172
- * `INVALID_REQUEST`: a bad body or a missing key (400).
181
+ * request budget (429). `PROVIDER_UNAVAILABLE`: MoonPay or Sumsub is down. `INVALID_REQUEST`: a bad
182
+ * body (400).
173
183
  */
174
- export type MoonPayFaultReason = 'SESSION_BUDGET_SPENT' | 'RATE_LIMITED' | 'PROVIDER_UNAVAILABLE' | 'IN_PROGRESS' | 'IDEMPOTENCY_KEY_REUSED' | 'INVALID_REQUEST';
184
+ export type MoonPayFaultReason = 'SESSION_BUDGET_SPENT' | 'RATE_LIMITED' | 'PROVIDER_UNAVAILABLE' | 'INVALID_REQUEST';
175
185
  /** The fields a MoonPay fault adds to the shared error body. */
176
186
  export interface MoonPayFault {
177
187
  reason: MoonPayFaultReason;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MOONPAY_ENDPOINTS
3
- } from "../chunk-WW26WEZO.mjs";
3
+ } from "../chunk-SJ7V3YOM.mjs";
4
4
  export {
5
5
  MOONPAY_ENDPOINTS
6
6
  };
package/dist/table.js CHANGED
@@ -36,7 +36,7 @@ __export(table_exports, {
36
36
  module.exports = __toCommonJS(table_exports);
37
37
 
38
38
  // package.json
39
- var version = "0.20.2";
39
+ var version = "0.22.0";
40
40
 
41
41
  // src/table.ts
42
42
  var TABLE_VERSION = version;
package/dist/table.mjs CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  tableEntry,
12
12
  unofferedEndpoints,
13
13
  unreachableStates
14
- } from "./chunk-CYT2NARF.mjs";
14
+ } from "./chunk-7OUDI3JX.mjs";
15
15
  export {
16
16
  DOCUMENTED_ENDPOINTS,
17
17
  STATE_KEYS,
package/dist/terms.d.ts CHANGED
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * The platform terms route: `POST /fiat/terms`, shared by every provider.
3
3
  *
4
- * A checkout shows the user documents to accept. `CHECKOUT` documents arrive in `handoff.terms` on
5
- * a `QUOTE`; `KYC` documents arrive in a provider flow's `TERMS` response. The user's Continue tap
6
- * is the acceptance, and the client records it here, with an `Idempotency-Key`, before the next
7
- * provider call.
4
+ * A checkout shows the user documents to accept. `CHECKOUT` documents are the constants below;
5
+ * `KYC` documents arrive in a provider flow's `TERMS` response. The user's Continue tap is the
6
+ * acceptance, and the client records it here before the next provider call. A repeat writes nothing.
8
7
  *
9
8
  * Zero runtime dependencies, so production React Native code can import it. The zod schemas are
10
9
  * exported from the package root.
11
10
  */
12
11
  import type { FiatProvider } from './types';
13
12
  export declare const FIAT_TERMS_ENDPOINT: "POST /fiat/terms";
14
- export type TermsDocumentKind = 'FUN_TERMS' | 'PROVIDER_TERMS' | 'FUN_KYC_COLLECTION' | 'PROVIDER_KYC_SHARING';
13
+ export type TermsDocumentKind = 'PROVIDER_TERMS' | 'FUN_KYC_COLLECTION' | 'PROVIDER_KYC_SHARING';
15
14
  /** `CHECKOUT` documents gate the provider session; `KYC` documents gate the first KYC write. */
16
15
  export type TermsStage = 'CHECKOUT' | 'KYC';
17
16
  export interface TermsDocument {
@@ -45,14 +44,13 @@ export interface FiatTermsResponse {
45
44
  recorded: string[];
46
45
  }
47
46
  /**
48
- * `STALE_TERMS`: a version, digest, or `acceptedAt` the backend no longer accepts; the client
49
- * fetches the documents again. `IN_PROGRESS`: the same key is still running (409).
50
- * `IDEMPOTENCY_KEY_REUSED`: the same key with a different body (422). `INVALID_REQUEST`: a bad body
51
- * or a missing key (400). `RATE_LIMITED`: the per-login terms budget is spent (429).
52
- * `PROVIDER_UNAVAILABLE`: the KYC service or a store is down (503); nothing is recorded, so retry.
47
+ * `STALE_TERMS`: an unpublished version, a wrong digest, or an `acceptedAt` older than 5 minutes
48
+ * (400). `INVALID_REQUEST`: a bad body (400).
49
+ * `RATE_LIMITED`: the per-login terms budget is spent (429). `PROVIDER_UNAVAILABLE`: the KYC service
50
+ * or a store is down (503); nothing is recorded, so retry.
53
51
  */
54
- export type FiatTermsFaultReason = 'STALE_TERMS' | 'IN_PROGRESS' | 'IDEMPOTENCY_KEY_REUSED' | 'INVALID_REQUEST' | 'RATE_LIMITED' | 'PROVIDER_UNAVAILABLE';
55
- /** `RETRY` sends the same request, with the same key. `REQUOTE` goes back to `POST /fiat/quote`. `NONE` ends the flow. */
52
+ export type FiatTermsFaultReason = 'STALE_TERMS' | 'INVALID_REQUEST' | 'RATE_LIMITED' | 'PROVIDER_UNAVAILABLE';
53
+ /** `RETRY` sends the same request again. `REQUOTE` goes back to `POST /fiat/quote`. `NONE` ends the flow. */
56
54
  export type FiatTermsRecovery = 'RETRY' | 'REQUOTE' | 'NONE';
57
55
  /** The 4xx/5xx body of `POST /fiat/terms`: the shared error body plus `reason` and `recovery`. */
58
56
  export interface FiatTermsErrorBody {
@@ -62,4 +60,14 @@ export interface FiatTermsErrorBody {
62
60
  reason: FiatTermsFaultReason;
63
61
  recovery: FiatTermsRecovery;
64
62
  }
63
+ /** Each provider's current `CHECKOUT` documents, shown next to Continue on the amount screen. */
64
+ export declare const PROVIDER_CHECKOUT_DOCUMENTS: Readonly<Partial<Record<FiatProvider, readonly TermsDocument[]>>>;
65
+ export type FunKycConsentLocale = 'en' | 'es' | 'fr' | 'ja' | 'ko' | 'ru' | 'tr' | 'zh';
66
+ /** Fun's KYC consent, one digest per locale the KYC intro screen shows. It counts under any provider. */
67
+ export declare const FUN_KYC_CONSENT_DOCUMENTS: Readonly<Record<FunKycConsentLocale, TermsDocument>>;
68
+ /**
69
+ * Every version and locale ever published. Append a new version here and keep the old one: the
70
+ * backend accepts any entry, so a client that ships an older version can still check out.
71
+ */
72
+ export declare const PUBLISHED_TERMS_DOCUMENTS: readonly TermsDocument[];
65
73
  //# sourceMappingURL=terms.d.ts.map
package/dist/terms.js CHANGED
@@ -20,8 +20,45 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/terms.ts
21
21
  var terms_exports = {};
22
22
  __export(terms_exports, {
23
- FIAT_TERMS_ENDPOINT: () => FIAT_TERMS_ENDPOINT
23
+ FIAT_TERMS_ENDPOINT: () => FIAT_TERMS_ENDPOINT,
24
+ FUN_KYC_CONSENT_DOCUMENTS: () => FUN_KYC_CONSENT_DOCUMENTS,
25
+ PROVIDER_CHECKOUT_DOCUMENTS: () => PROVIDER_CHECKOUT_DOCUMENTS,
26
+ PUBLISHED_TERMS_DOCUMENTS: () => PUBLISHED_TERMS_DOCUMENTS
24
27
  });
25
28
  module.exports = __toCommonJS(terms_exports);
26
29
  var FIAT_TERMS_ENDPOINT = "POST /fiat/terms";
30
+ var MOONPAY_TERMS = {
31
+ id: "MOONPAY_TERMS",
32
+ kind: "PROVIDER_TERMS",
33
+ stage: "CHECKOUT",
34
+ version: "2026-09-l2-kyc-sharing-v2",
35
+ contentDigest: "44e096af329262134fe6680659c9bd42014cbf5e96f064d67869173a4f7d822b",
36
+ links: [
37
+ { label: "MoonPay Terms of Use", url: "https://www.moonpay.com/legal/terms" },
38
+ { label: "MoonPay Privacy Policy", url: "https://www.moonpay.com/legal/privacy_policy" },
39
+ { label: "Persona Privacy Policy", url: "https://withpersona.com/legal/privacy-policy" }
40
+ ]
41
+ };
42
+ var PROVIDER_CHECKOUT_DOCUMENTS = {
43
+ MOONPAY: [MOONPAY_TERMS]
44
+ };
45
+ var funKycConsent = (contentDigest) => ({
46
+ id: "FUN_KYC_CONSENT",
47
+ kind: "FUN_KYC_COLLECTION",
48
+ stage: "KYC",
49
+ version: "fun-kyc-consent-2026-09-11",
50
+ contentDigest,
51
+ links: [{ label: "Fun.xyz Privacy Policy", url: "https://fun.xyz/privacy" }]
52
+ });
53
+ var FUN_KYC_CONSENT_DOCUMENTS = {
54
+ en: funKycConsent("da31f2467deb996476b131116508ea2a00220765e658a79abc6d2bd44337504a"),
55
+ es: funKycConsent("73753b70ad8d1b48dfdcda35e6028c3a7ada66c311a67a1d8f6fe13c76a32f3b"),
56
+ fr: funKycConsent("5ee82675984591dbf66337f31246179103c5624a189850943d400f9b85a84965"),
57
+ ja: funKycConsent("3f272da313f66601a20c3ca43c967aff4bb7f73488177f0a4ce94cbc78bafce2"),
58
+ ko: funKycConsent("aab7839bfba3f34aa47ca6b10647c2c8965c37d845494ef8f3067ca3e3821200"),
59
+ ru: funKycConsent("17168083ebcc4fd11ea80b08c3f230bdec03a1e7b89173a38819ae5533b0fc96"),
60
+ tr: funKycConsent("463040c3b58c922ecae2ae8262a78eb732bbcc5606c394723626378b3db2bbc3"),
61
+ zh: funKycConsent("7f76d850adaf69783c5d9365e51abb5446f4e5778c63eabbdb4784d72464f855")
62
+ };
63
+ var PUBLISHED_TERMS_DOCUMENTS = [MOONPAY_TERMS, ...Object.values(FUN_KYC_CONSENT_DOCUMENTS)];
27
64
  //# sourceMappingURL=terms.js.map
package/dist/terms.mjs CHANGED
@@ -1,7 +1,13 @@
1
1
  import {
2
- FIAT_TERMS_ENDPOINT
3
- } from "./chunk-WRRCWAY2.mjs";
2
+ FIAT_TERMS_ENDPOINT,
3
+ FUN_KYC_CONSENT_DOCUMENTS,
4
+ PROVIDER_CHECKOUT_DOCUMENTS,
5
+ PUBLISHED_TERMS_DOCUMENTS
6
+ } from "./chunk-5GRPYE2U.mjs";
4
7
  export {
5
- FIAT_TERMS_ENDPOINT
8
+ FIAT_TERMS_ENDPOINT,
9
+ FUN_KYC_CONSENT_DOCUMENTS,
10
+ PROVIDER_CHECKOUT_DOCUMENTS,
11
+ PUBLISHED_TERMS_DOCUMENTS
6
12
  };
7
13
  //# sourceMappingURL=terms.mjs.map
package/dist/types.d.ts CHANGED
@@ -19,7 +19,6 @@
19
19
  * - terminality comes from the table (`src/table.ts`), never from `transitions.length`
20
20
  */
21
21
  import type { FiatCurrencyCode, PaymentMethodCategory } from './codes';
22
- import type { TermsDocument } from './terms';
23
22
  /**
24
23
  * JSON value. `params` carry these — opaque literals the client spreads verbatim.
25
24
  * Named `JsonValue` rather than `Json` because this package's surface is re-exported wholesale;
@@ -722,11 +721,6 @@ export type FiatStepResponse = (FiatStepResponseBase & {
722
721
  */
723
722
  handoff?: {
724
723
  provider: FiatProvider;
725
- /**
726
- * The `CHECKOUT` documents this login has not accepted at the current version, shown next to
727
- * Continue and recorded with `POST /fiat/terms` on the tap. Empty for a returning buyer.
728
- */
729
- terms: TermsDocument[];
730
724
  };
731
725
  });
732
726
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fun-xyz/fiat-contract",
3
- "version": "0.20.2",
3
+ "version": "0.22.0",
4
4
  "description": "Published conformance contract for Fun's headless fiat onramp: FlowState/Transition types, zod schemas, the transition table as data, recorded fixtures, assertion helpers.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,7 +0,0 @@
1
- // src/terms.ts
2
- var FIAT_TERMS_ENDPOINT = "POST /fiat/terms";
3
-
4
- export {
5
- FIAT_TERMS_ENDPOINT
6
- };
7
- //# sourceMappingURL=chunk-WRRCWAY2.mjs.map