@fun-xyz/fiat-contract 0.10.0 → 0.11.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 +41 -1
- package/dist/{chunk-BAGIBS54.mjs → chunk-GKKBF7QB.mjs} +14 -8
- package/dist/index.js +168 -29
- package/dist/index.mjs +156 -23
- package/dist/schemas.d.ts +9 -2
- package/dist/table.js +13 -7
- package/dist/table.mjs +1 -1
- package/dist/types.d.ts +121 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,46 @@ this package is [ENG-5268](https://linear.app/funxyz/issue/ENG-5268).
|
|
|
64
64
|
infer capacity from provider metadata or floating point.
|
|
65
65
|
8. **Provider auth is capability-shaped.** SMS uses `SMS_OTP`; provider-owned auth uses
|
|
66
66
|
`PROVIDER_RENDERED` with an optional `AUTH_COMPONENT` surface rather than provider-specific channels.
|
|
67
|
+
9. **KYC SDK refresh is a transition.** `KYC{CAPTURE}` may offer `refresh_sdk_token` to
|
|
68
|
+
`POST /fiat/kyc/sdk-token`; a transition-following client does not invent an auxiliary endpoint call.
|
|
69
|
+
10. **SDK launch is not eligibility authority.** A `KYC_SDK_RESULT` report injects either
|
|
70
|
+
`{outcome: 'COMPLETED'}` or `{outcome: 'FAILED', errorType}`. `errorType` is opaque, and raw
|
|
71
|
+
messages/errors are excluded. The backend must still read the provider's authoritative state.
|
|
72
|
+
11. **Client-executed delivery is current-session-only.** A provider-owned surface may declare a
|
|
73
|
+
`CLIENT_EXECUTED` delivery with `source: 'CURRENT_SESSION_INPUTS'` and a value-free `allowedFieldIds`
|
|
74
|
+
allowlist. The delivery shape cannot request or contain backend-stored KYC values.
|
|
75
|
+
12. **Capability skew is explicit.** `BLOCKED{reason: 'CLIENT_UPGRADE_REQUIRED'}` means every
|
|
76
|
+
otherwise-viable route needs a client capability absent from the handshake. It is not a provider
|
|
77
|
+
decline and should render an upgrade path rather than generic retry copy.
|
|
78
|
+
13. **Providerless is terminal-routing-only.** `FiatStepResponse.provider` may be absent only for
|
|
79
|
+
`BLOCKED`, including `ALL_PROVIDERS_DECLINED` when routing selected no provider. Every other state
|
|
80
|
+
remains provider-bound and requires `provider`; provider-specific blocked responses may keep it.
|
|
81
|
+
|
|
82
|
+
## KYC SDK hints, results, and client delivery
|
|
83
|
+
|
|
84
|
+
`KYC_SDK` is a dedicated `Surface` union member. Its optional
|
|
85
|
+
`preferredDocuments.IDENTITY` contains the exact adapter input needed to bias document capture:
|
|
86
|
+
the identity-vendor `idDocType` plus its ISO alpha-3 `country`. The hint changes presentation only;
|
|
87
|
+
it does not establish that the document is accepted or available.
|
|
88
|
+
|
|
89
|
+
When `report.result` is `{type: 'KYC_SDK_RESULT', bodyKey: 'result'}`, the harness injects a
|
|
90
|
+
`KycSdkResultReport` under that body key. Completion means the SDK launch completed, not that KYC passed.
|
|
91
|
+
Failure exposes only the provider's opaque `errorType` (for example `Unauthorized`), never a raw
|
|
92
|
+
error object or message.
|
|
93
|
+
|
|
94
|
+
`surface.delivery` describes a client-side forwarding boundary on a provider-owned surface, not
|
|
95
|
+
transport mechanics. The selected provider adapter may forward only the declared `allowedFieldIds` that the user entered during the
|
|
96
|
+
current mounted session. The schema fixes the source to `CURRENT_SESSION_INPUTS`, requires a unique
|
|
97
|
+
non-empty field allowlist, and contains no values. Provider-specific submission mechanics remain
|
|
98
|
+
adapter-owned and are intentionally unresolved by this provider-neutral contract.
|
|
99
|
+
|
|
100
|
+
“Declarative KYC collection” means the backend sends a `FormDescriptor` listing canonical field
|
|
101
|
+
IDs, input kinds, labels, validation, and select options. The frontend renders that description,
|
|
102
|
+
collects values, and submits them through the transition's single `FORM_VALUES` input; it does not
|
|
103
|
+
hard-code a separate screen or endpoint for source of funds, occupation, purpose, nationality,
|
|
104
|
+
country of birth, employment status, or phone. Field IDs and type-specific properties are closed
|
|
105
|
+
unions, so misspelled IDs and impossible combinations such as `options` on a `TEXT` field fail
|
|
106
|
+
validation.
|
|
67
107
|
|
|
68
108
|
## Versioning — the package version IS the table version
|
|
69
109
|
|
|
@@ -450,7 +490,7 @@ The current documented shape is what ships; none of these are settled here.
|
|
|
450
490
|
| `orderId` placement | Both: optional on `PAYMENT{INSTRUCT}` (FE doc v0) **and** optional beside `state` (contract worked example). One fixture of each. |
|
|
451
491
|
| `PENDING_ORDER` removal | Kind ships, with the removal proposal flagged on the type, the table entry, and the fixture. Screen 12 stays frozen. |
|
|
452
492
|
| `[OQ7]` failure enumeration | The published `FailureReason` taxonomy only. Expired instructions, partial payment, and per-rail cancel eligibility remain unenumerated; KYC rejection is terminal only for an explicit non-retryable outcome. |
|
|
453
|
-
| Pre-order KYC sequencing | KYC states carry their quote; SDK submission reports to `POST /fiat/kyc/capture`; pending review polls the quote resource. There is no `GET /fiat/kyc`. |
|
|
493
|
+
| Pre-order KYC sequencing | KYC states carry their quote; SDK submission reports to `POST /fiat/kyc/capture`; expired SDK credentials refresh through the explicit `POST /fiat/kyc/sdk-token` transition; pending review polls the quote resource. There is no `GET /fiat/kyc`. |
|
|
454
494
|
| Card capture report target | Both topologies are legal in the table (`POST /fiat/orders` for capture-then-order; `POST /fiat/orders/:id/surface-result` otherwise) pending the Transak answer. |
|
|
455
495
|
| Cancel placement | `cancel` is legal on `PAYMENT{INSTRUCT}` and `ORDER{CREATED}`, marked conditional on the placement decision. |
|
|
456
496
|
| `FUN_AUTH` shape | `challenge: Record<string, JsonValue>`; the table entry is `docStatus: 'UNSPECIFIED'`, so `assertLegalEmission` reports it unjudgeable instead of guessing. |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// package.json
|
|
2
|
-
var version = "0.
|
|
2
|
+
var version = "0.11.0";
|
|
3
3
|
|
|
4
4
|
// src/table.ts
|
|
5
5
|
var TABLE_VERSION = version;
|
|
@@ -114,7 +114,8 @@ var TRANSITION_TABLE = {
|
|
|
114
114
|
terminal: false,
|
|
115
115
|
notes: [
|
|
116
116
|
"A bad code re-enters this same state with error {category: USER_ERROR, code: BAD_CODE} \u2014 banner, not a screen change.",
|
|
117
|
-
"No resend affordance is specified in either doc (known gap, FE doc Screen 2)."
|
|
117
|
+
"No resend affordance is specified in either doc (known gap, FE doc Screen 2).",
|
|
118
|
+
"A provider-owned surface may declare client-executed delivery from allowlisted CURRENT_SESSION_INPUTS only."
|
|
118
119
|
]
|
|
119
120
|
},
|
|
120
121
|
"KYC/CAPTURE": {
|
|
@@ -124,6 +125,12 @@ var TRANSITION_TABLE = {
|
|
|
124
125
|
screens: [8],
|
|
125
126
|
docStatus: "SPECIFIED",
|
|
126
127
|
allowedTransitions: [
|
|
128
|
+
{
|
|
129
|
+
id: "refresh_sdk_token",
|
|
130
|
+
mode: "SUBMIT",
|
|
131
|
+
endpoint: "POST /fiat/kyc/sdk-token",
|
|
132
|
+
note: "Refreshes an expired SDK credential and returns a new KYC/CAPTURE state."
|
|
133
|
+
},
|
|
127
134
|
{
|
|
128
135
|
id: "capture",
|
|
129
136
|
mode: "CLIENT_SURFACE",
|
|
@@ -143,7 +150,10 @@ var TRANSITION_TABLE = {
|
|
|
143
150
|
"ORDER/AWAITING_CONFIRMATION"
|
|
144
151
|
],
|
|
145
152
|
terminal: false,
|
|
146
|
-
notes: [
|
|
153
|
+
notes: [
|
|
154
|
+
"Carries the handshake-gated KYC_SDK surface; provider selects the identity-vendor adapter.",
|
|
155
|
+
"SDK completion or failure is only a launch report; eligibility is re-read authoritatively by the backend."
|
|
156
|
+
]
|
|
147
157
|
},
|
|
148
158
|
"KYC/INPUT_REQUIRED": {
|
|
149
159
|
key: "KYC/INPUT_REQUIRED",
|
|
@@ -399,10 +409,6 @@ var DOCUMENTED_ENDPOINTS = [
|
|
|
399
409
|
"POST /fiat/orders/:id/surface-result"
|
|
400
410
|
];
|
|
401
411
|
var UNOFFERED_ENDPOINTS = [
|
|
402
|
-
{
|
|
403
|
-
endpoint: "POST /fiat/kyc/sdk-token",
|
|
404
|
-
reason: "SDK token refresh is adapter-driven rather than a flow transition."
|
|
405
|
-
},
|
|
406
412
|
{
|
|
407
413
|
endpoint: "GET /fiat/payment-methods",
|
|
408
414
|
reason: "Discovery, called before any flow state exists \u2014 there is no state to hang it off."
|
|
@@ -500,4 +506,4 @@ export {
|
|
|
500
506
|
tableEntry,
|
|
501
507
|
isTerminal
|
|
502
508
|
};
|
|
503
|
-
//# sourceMappingURL=chunk-
|
|
509
|
+
//# sourceMappingURL=chunk-GKKBF7QB.mjs.map
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
BlockedStateSchema: () => BlockedStateSchema,
|
|
26
26
|
COUNTRY_NAMES: () => COUNTRY_NAMES,
|
|
27
27
|
COUNTRY_TO_CURRENCY: () => COUNTRY_TO_CURRENCY,
|
|
28
|
+
ClientExecutedDeliverySchema: () => ClientExecutedDeliverySchema,
|
|
28
29
|
ContractViolation: () => ContractViolation,
|
|
29
30
|
CountryCode: () => CountryCode,
|
|
30
31
|
CryptoAmountSchema: () => CryptoAmountSchema,
|
|
@@ -53,9 +54,11 @@ __export(index_exports, {
|
|
|
53
54
|
InstrumentSchema: () => InstrumentSchema,
|
|
54
55
|
JsonSchema: () => JsonSchema,
|
|
55
56
|
KYCProviderSchema: () => KYCProviderSchema,
|
|
57
|
+
KYC_ACKNOWLEDGEMENT_FIELD_IDS: () => KYC_ACKNOWLEDGEMENT_FIELD_IDS,
|
|
56
58
|
KycCaptureStateSchema: () => KycCaptureStateSchema,
|
|
57
59
|
KycInputRequiredStateSchema: () => KycInputRequiredStateSchema,
|
|
58
60
|
KycNoActionRequiredStateSchema: () => KycNoActionRequiredStateSchema,
|
|
61
|
+
KycSdkResultReportSchema: () => KycSdkResultReportSchema,
|
|
59
62
|
OrderAwaitingConfirmationStateSchema: () => OrderAwaitingConfirmationStateSchema,
|
|
60
63
|
OrderCreatedStateSchema: () => OrderCreatedStateSchema,
|
|
61
64
|
OrderRefSchema: () => OrderRefSchema,
|
|
@@ -66,6 +69,7 @@ __export(index_exports, {
|
|
|
66
69
|
PaymentInstructStateSchema: () => PaymentInstructStateSchema,
|
|
67
70
|
PendingOrderStateSchema: () => PendingOrderStateSchema,
|
|
68
71
|
PollSpecSchema: () => PollSpecSchema,
|
|
72
|
+
PreferredIdentityDocumentSchema: () => PreferredIdentityDocumentSchema,
|
|
69
73
|
QuoteLimitRangeSchema: () => QuoteLimitRangeSchema,
|
|
70
74
|
QuoteLimitsSchema: () => QuoteLimitsSchema,
|
|
71
75
|
QuoteSchema: () => QuoteSchema,
|
|
@@ -73,6 +77,7 @@ __export(index_exports, {
|
|
|
73
77
|
RecoverySchema: () => RecoverySchema,
|
|
74
78
|
RefundSchema: () => RefundSchema,
|
|
75
79
|
ReportSpecSchema: () => ReportSpecSchema,
|
|
80
|
+
STANDARD_KYC_FIELD_IDS: () => STANDARD_KYC_FIELD_IDS,
|
|
76
81
|
STATE_KEYS: () => STATE_KEYS,
|
|
77
82
|
SelectOptionSchema: () => SelectOptionSchema,
|
|
78
83
|
SessionAuthStateSchema: () => SessionAuthStateSchema,
|
|
@@ -83,6 +88,7 @@ __export(index_exports, {
|
|
|
83
88
|
TABLE_VERSION: () => TABLE_VERSION,
|
|
84
89
|
TERMINAL_ORDER_STATUSES: () => TERMINAL_ORDER_STATUSES,
|
|
85
90
|
TRANSITION_TABLE: () => TRANSITION_TABLE,
|
|
91
|
+
TermsAcceptanceSchema: () => TermsAcceptanceSchema,
|
|
86
92
|
TransitionInputsSchema: () => TransitionInputsSchema,
|
|
87
93
|
TransitionParamsSchema: () => TransitionParamsSchema,
|
|
88
94
|
TransitionSchema: () => TransitionSchema,
|
|
@@ -512,8 +518,7 @@ var $surfaceKind = import_zod.z.enum([
|
|
|
512
518
|
"AUTH_COMPONENT"
|
|
513
519
|
]);
|
|
514
520
|
var SurfaceKindSchema = $surfaceKind;
|
|
515
|
-
var $
|
|
516
|
-
kind: $surfaceKind,
|
|
521
|
+
var $surfaceBase = {
|
|
517
522
|
provider: $surfaceProvider,
|
|
518
523
|
/**
|
|
519
524
|
* The one deliberate non-strict leaf in the contract. Provider inputs are the adapter's
|
|
@@ -525,7 +530,84 @@ var $surface = obj({
|
|
|
525
530
|
*/
|
|
526
531
|
params: import_zod.z.record(import_zod.z.string(), JsonSchema),
|
|
527
532
|
expiresAt: import_zod.z.string().optional()
|
|
533
|
+
};
|
|
534
|
+
var $preferredIdentityDocument = obj({
|
|
535
|
+
idDocType: import_zod.z.enum(["PASSPORT", "ID_CARD", "DRIVERS", "RESIDENCE_PERMIT", "VISA"]),
|
|
536
|
+
country: import_zod.z.string().regex(/^[A-Z]{3}$/)
|
|
537
|
+
});
|
|
538
|
+
var PreferredIdentityDocumentSchema = $preferredIdentityDocument;
|
|
539
|
+
var STANDARD_KYC_FIELD_IDS = [
|
|
540
|
+
"LEGAL_FIRST_NAME",
|
|
541
|
+
"LEGAL_MIDDLE_NAME",
|
|
542
|
+
"LEGAL_LAST_NAME",
|
|
543
|
+
"DATE_OF_BIRTH",
|
|
544
|
+
"COUNTRY_OF_BIRTH",
|
|
545
|
+
"CITY_OF_BIRTH",
|
|
546
|
+
"NATIONALITY",
|
|
547
|
+
"EMAIL_ADDRESS",
|
|
548
|
+
"EMAIL_OWNERSHIP_VERIFICATION",
|
|
549
|
+
"PHONE_NUMBER",
|
|
550
|
+
"PHONE_OWNERSHIP_VERIFICATION",
|
|
551
|
+
"RESIDENTIAL_ADDRESS_LINE_1",
|
|
552
|
+
"RESIDENTIAL_ADDRESS_LINE_2",
|
|
553
|
+
"RESIDENTIAL_ADDRESS_CITY",
|
|
554
|
+
"RESIDENTIAL_ADDRESS_REGION",
|
|
555
|
+
"RESIDENTIAL_ADDRESS_POSTAL_CODE",
|
|
556
|
+
"RESIDENTIAL_ADDRESS_COUNTRY",
|
|
557
|
+
"TAX_IDENTIFIER",
|
|
558
|
+
"TAX_RESIDENCE_COUNTRY",
|
|
559
|
+
"EMPLOYMENT_STATUS",
|
|
560
|
+
"OCCUPATION_INDUSTRY",
|
|
561
|
+
"OCCUPATION_TITLE",
|
|
562
|
+
"SOURCE_OF_FUNDS_DECLARATION",
|
|
563
|
+
"SOURCE_OF_INCOME_DECLARATION",
|
|
564
|
+
"SOURCE_OF_WEALTH_DECLARATION",
|
|
565
|
+
"PURPOSE_OF_SERVICE",
|
|
566
|
+
"PURPOSE_OF_TRANSACTION",
|
|
567
|
+
"CUSTOMER_CATEGORY"
|
|
568
|
+
];
|
|
569
|
+
var KYC_ACKNOWLEDGEMENT_FIELD_IDS = [
|
|
570
|
+
"RISK_ACKNOWLEDGEMENT",
|
|
571
|
+
"COOLING_OFF_ACKNOWLEDGEMENT",
|
|
572
|
+
"TERMS_ACCEPTANCE"
|
|
573
|
+
];
|
|
574
|
+
var $questionnaireKycFieldId = import_zod.z.custom(
|
|
575
|
+
(value) => typeof value === "string" && /^QUESTIONNAIRE_RESPONSE:[^:]+:[^:]+$/.test(value),
|
|
576
|
+
"Questionnaire field ids must identify exactly one policy and question"
|
|
577
|
+
);
|
|
578
|
+
var $kycFieldId = import_zod.z.union([
|
|
579
|
+
import_zod.z.enum(STANDARD_KYC_FIELD_IDS),
|
|
580
|
+
import_zod.z.enum(KYC_ACKNOWLEDGEMENT_FIELD_IDS),
|
|
581
|
+
$questionnaireKycFieldId
|
|
582
|
+
]);
|
|
583
|
+
var $clientExecutedDelivery = obj({
|
|
584
|
+
channel: import_zod.z.literal("CLIENT_EXECUTED"),
|
|
585
|
+
source: import_zod.z.literal("CURRENT_SESSION_INPUTS"),
|
|
586
|
+
allowedFieldIds: import_zod.z.array($kycFieldId).min(1)
|
|
587
|
+
}).superRefine((delivery, context) => {
|
|
588
|
+
if (new Set(delivery.allowedFieldIds).size !== delivery.allowedFieldIds.length) {
|
|
589
|
+
context.addIssue({
|
|
590
|
+
code: "custom",
|
|
591
|
+
path: ["allowedFieldIds"],
|
|
592
|
+
message: "allowedFieldIds must be unique"
|
|
593
|
+
});
|
|
594
|
+
}
|
|
528
595
|
});
|
|
596
|
+
var ClientExecutedDeliverySchema = $clientExecutedDelivery;
|
|
597
|
+
var $surface = import_zod.z.union([
|
|
598
|
+
obj({
|
|
599
|
+
...$surfaceBase,
|
|
600
|
+
kind: import_zod.z.literal("KYC_SDK"),
|
|
601
|
+
provider: $kycProvider,
|
|
602
|
+
preferredDocuments: obj({ IDENTITY: $preferredIdentityDocument }).optional()
|
|
603
|
+
}),
|
|
604
|
+
obj({
|
|
605
|
+
...$surfaceBase,
|
|
606
|
+
kind: import_zod.z.enum(["URL_REDIRECT", "PCI_COMPONENT", "PAY_SHEET", "ACH_COMPONENT", "AUTH_COMPONENT"]),
|
|
607
|
+
provider: $provider,
|
|
608
|
+
delivery: $clientExecutedDelivery.optional()
|
|
609
|
+
})
|
|
610
|
+
]);
|
|
529
611
|
var SurfaceSchema = $surface;
|
|
530
612
|
var $instructionField = obj({
|
|
531
613
|
id: import_zod.z.string(),
|
|
@@ -548,15 +630,15 @@ var $selectOption = obj({ id: import_zod.z.string(), labelFallback: import_zod.z
|
|
|
548
630
|
var SelectOptionSchema = $selectOption;
|
|
549
631
|
var $formFieldType = import_zod.z.enum(["TEXT", "DATE", "SELECT", "COUNTRY", "FILE", "HOSTED_LINK"]);
|
|
550
632
|
var FormFieldTypeSchema = $formFieldType;
|
|
551
|
-
var $
|
|
552
|
-
|
|
553
|
-
type:
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
633
|
+
var $formFieldBase = { id: $kycFieldId, labelFallback: import_zod.z.string(), required: import_zod.z.boolean() };
|
|
634
|
+
var $formField = import_zod.z.union([
|
|
635
|
+
obj({ ...$formFieldBase, type: import_zod.z.literal("TEXT"), regex: import_zod.z.string().optional() }),
|
|
636
|
+
obj({ ...$formFieldBase, type: import_zod.z.literal("DATE") }),
|
|
637
|
+
obj({ ...$formFieldBase, type: import_zod.z.literal("SELECT"), options: import_zod.z.array($selectOption) }),
|
|
638
|
+
obj({ ...$formFieldBase, type: import_zod.z.literal("COUNTRY") }),
|
|
639
|
+
obj({ ...$formFieldBase, type: import_zod.z.literal("FILE") }),
|
|
640
|
+
obj({ ...$formFieldBase, type: import_zod.z.literal("HOSTED_LINK"), url: import_zod.z.string() })
|
|
641
|
+
]);
|
|
560
642
|
var FormFieldSchema = $formField;
|
|
561
643
|
var $formDescriptor = obj({ formId: import_zod.z.string(), fields: import_zod.z.array($formField) });
|
|
562
644
|
var FormDescriptorSchema = $formDescriptor;
|
|
@@ -567,7 +649,8 @@ var FIELD_SPEC_TYPES = [
|
|
|
567
649
|
"FILE",
|
|
568
650
|
"SELECT",
|
|
569
651
|
"HOSTED_LINK",
|
|
570
|
-
"FORM_VALUES"
|
|
652
|
+
"FORM_VALUES",
|
|
653
|
+
"TERMS_ACCEPTANCE"
|
|
571
654
|
];
|
|
572
655
|
var $fieldSpec = import_zod.z.union([
|
|
573
656
|
obj({
|
|
@@ -585,9 +668,19 @@ var $fieldSpec = import_zod.z.union([
|
|
|
585
668
|
labelFallback: import_zod.z.string().optional()
|
|
586
669
|
}),
|
|
587
670
|
obj({ type: import_zod.z.literal("HOSTED_LINK"), url: import_zod.z.string(), labelFallback: import_zod.z.string().optional() }),
|
|
588
|
-
obj({ type: import_zod.z.literal("FORM_VALUES") })
|
|
671
|
+
obj({ type: import_zod.z.literal("FORM_VALUES") }),
|
|
672
|
+
obj({
|
|
673
|
+
type: import_zod.z.literal("TERMS_ACCEPTANCE"),
|
|
674
|
+
documentUrl: import_zod.z.string(),
|
|
675
|
+
version: import_zod.z.string(),
|
|
676
|
+
labelFallback: import_zod.z.string().optional()
|
|
677
|
+
})
|
|
589
678
|
]);
|
|
590
679
|
var FieldSpecSchema = $fieldSpec;
|
|
680
|
+
var TermsAcceptanceSchema = obj({
|
|
681
|
+
version: import_zod.z.string(),
|
|
682
|
+
acceptedAt: import_zod.z.string()
|
|
683
|
+
});
|
|
591
684
|
var looksLikeFieldSpec = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && FIELD_SPEC_TYPES.includes(value.type);
|
|
592
685
|
var $params = import_zod.z.record(import_zod.z.string(), JsonSchema).superRefine((params, ctx) => {
|
|
593
686
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -634,9 +727,15 @@ var PollSpecSchema = $pollSpec;
|
|
|
634
727
|
var $reportSpec = obj({
|
|
635
728
|
endpoint: $endpoint,
|
|
636
729
|
params: $params.optional(),
|
|
637
|
-
expects: import_zod.z.array(import_zod.z.string()).optional()
|
|
730
|
+
expects: import_zod.z.array(import_zod.z.string()).optional(),
|
|
731
|
+
result: obj({ type: import_zod.z.literal("KYC_SDK_RESULT"), bodyKey: import_zod.z.string().trim().min(1) }).optional()
|
|
638
732
|
});
|
|
639
733
|
var ReportSpecSchema = $reportSpec;
|
|
734
|
+
var $kycSdkResult = import_zod.z.union([
|
|
735
|
+
obj({ outcome: import_zod.z.literal("COMPLETED") }),
|
|
736
|
+
obj({ outcome: import_zod.z.literal("FAILED"), errorType: import_zod.z.string().trim().min(1) })
|
|
737
|
+
]);
|
|
738
|
+
var KycSdkResultReportSchema = $kycSdkResult;
|
|
640
739
|
var $transition = import_zod.z.union([
|
|
641
740
|
obj({
|
|
642
741
|
id: import_zod.z.string(),
|
|
@@ -711,7 +810,8 @@ var $blockedReason = import_zod.z.enum([
|
|
|
711
810
|
"REGION_UNSUPPORTED",
|
|
712
811
|
"ASSET_UNSUPPORTED",
|
|
713
812
|
"ALL_PROVIDERS_DECLINED",
|
|
714
|
-
"AMOUNT_UNSERVABLE"
|
|
813
|
+
"AMOUNT_UNSERVABLE",
|
|
814
|
+
"CLIENT_UPGRADE_REQUIRED"
|
|
715
815
|
]);
|
|
716
816
|
var BlockedReasonSchema = $blockedReason;
|
|
717
817
|
var $blockedState = obj({
|
|
@@ -755,7 +855,7 @@ var $orderCreatedState = obj({
|
|
|
755
855
|
error: $failureReason.optional()
|
|
756
856
|
});
|
|
757
857
|
var OrderCreatedStateSchema = $orderCreatedState;
|
|
758
|
-
var $
|
|
858
|
+
var $providerBoundFlowState = import_zod.z.union([
|
|
759
859
|
$quoteState,
|
|
760
860
|
$funAuthState,
|
|
761
861
|
$sessionAuthState,
|
|
@@ -763,23 +863,30 @@ var $flowState = import_zod.z.union([
|
|
|
763
863
|
$kycInputRequiredState,
|
|
764
864
|
$kycNoActionRequiredState,
|
|
765
865
|
$pendingOrderState,
|
|
766
|
-
$blockedState,
|
|
767
866
|
$paymentCaptureState,
|
|
768
867
|
$paymentInstructState,
|
|
769
868
|
$orderAwaitingConfirmationState,
|
|
770
869
|
$orderCreatedState
|
|
771
870
|
]);
|
|
871
|
+
var $flowState = import_zod.z.union([$providerBoundFlowState, $blockedState]);
|
|
772
872
|
var FlowStateSchema = $flowState;
|
|
773
|
-
var $
|
|
774
|
-
state: $
|
|
873
|
+
var $providerBoundStepResponse = obj({
|
|
874
|
+
state: $providerBoundFlowState,
|
|
775
875
|
provider: $provider,
|
|
776
876
|
transitions: import_zod.z.array($transition),
|
|
777
877
|
orderId: import_zod.z.string().optional()
|
|
778
878
|
});
|
|
879
|
+
var $blockedStepResponse = obj({
|
|
880
|
+
state: $blockedState,
|
|
881
|
+
provider: $provider.optional(),
|
|
882
|
+
transitions: import_zod.z.array($transition),
|
|
883
|
+
orderId: import_zod.z.string().optional()
|
|
884
|
+
});
|
|
885
|
+
var $stepResponse = import_zod.z.union([$providerBoundStepResponse, $blockedStepResponse]);
|
|
779
886
|
var FiatStepResponseSchema = $stepResponse;
|
|
780
887
|
|
|
781
888
|
// package.json
|
|
782
|
-
var version = "0.
|
|
889
|
+
var version = "0.11.0";
|
|
783
890
|
|
|
784
891
|
// src/table.ts
|
|
785
892
|
var TABLE_VERSION = version;
|
|
@@ -894,7 +1001,8 @@ var TRANSITION_TABLE = {
|
|
|
894
1001
|
terminal: false,
|
|
895
1002
|
notes: [
|
|
896
1003
|
"A bad code re-enters this same state with error {category: USER_ERROR, code: BAD_CODE} \u2014 banner, not a screen change.",
|
|
897
|
-
"No resend affordance is specified in either doc (known gap, FE doc Screen 2)."
|
|
1004
|
+
"No resend affordance is specified in either doc (known gap, FE doc Screen 2).",
|
|
1005
|
+
"A provider-owned surface may declare client-executed delivery from allowlisted CURRENT_SESSION_INPUTS only."
|
|
898
1006
|
]
|
|
899
1007
|
},
|
|
900
1008
|
"KYC/CAPTURE": {
|
|
@@ -904,6 +1012,12 @@ var TRANSITION_TABLE = {
|
|
|
904
1012
|
screens: [8],
|
|
905
1013
|
docStatus: "SPECIFIED",
|
|
906
1014
|
allowedTransitions: [
|
|
1015
|
+
{
|
|
1016
|
+
id: "refresh_sdk_token",
|
|
1017
|
+
mode: "SUBMIT",
|
|
1018
|
+
endpoint: "POST /fiat/kyc/sdk-token",
|
|
1019
|
+
note: "Refreshes an expired SDK credential and returns a new KYC/CAPTURE state."
|
|
1020
|
+
},
|
|
907
1021
|
{
|
|
908
1022
|
id: "capture",
|
|
909
1023
|
mode: "CLIENT_SURFACE",
|
|
@@ -923,7 +1037,10 @@ var TRANSITION_TABLE = {
|
|
|
923
1037
|
"ORDER/AWAITING_CONFIRMATION"
|
|
924
1038
|
],
|
|
925
1039
|
terminal: false,
|
|
926
|
-
notes: [
|
|
1040
|
+
notes: [
|
|
1041
|
+
"Carries the handshake-gated KYC_SDK surface; provider selects the identity-vendor adapter.",
|
|
1042
|
+
"SDK completion or failure is only a launch report; eligibility is re-read authoritatively by the backend."
|
|
1043
|
+
]
|
|
927
1044
|
},
|
|
928
1045
|
"KYC/INPUT_REQUIRED": {
|
|
929
1046
|
key: "KYC/INPUT_REQUIRED",
|
|
@@ -1179,10 +1296,6 @@ var DOCUMENTED_ENDPOINTS = [
|
|
|
1179
1296
|
"POST /fiat/orders/:id/surface-result"
|
|
1180
1297
|
];
|
|
1181
1298
|
var UNOFFERED_ENDPOINTS = [
|
|
1182
|
-
{
|
|
1183
|
-
endpoint: "POST /fiat/kyc/sdk-token",
|
|
1184
|
-
reason: "SDK token refresh is adapter-driven rather than a flow transition."
|
|
1185
|
-
},
|
|
1186
1299
|
{
|
|
1187
1300
|
endpoint: "GET /fiat/payment-methods",
|
|
1188
1301
|
reason: "Discovery, called before any flow state exists \u2014 there is no state to hang it off."
|
|
@@ -1448,13 +1561,13 @@ var FIXTURE_DATA = {
|
|
|
1448
1561
|
"formId": "kyc_q_8f2_1",
|
|
1449
1562
|
"fields": [
|
|
1450
1563
|
{
|
|
1451
|
-
"id": "
|
|
1564
|
+
"id": "TAX_IDENTIFIER",
|
|
1452
1565
|
"type": "TEXT",
|
|
1453
1566
|
"labelFallback": "Tax ID",
|
|
1454
1567
|
"required": true
|
|
1455
1568
|
},
|
|
1456
1569
|
{
|
|
1457
|
-
"id": "
|
|
1570
|
+
"id": "RESIDENTIAL_ADDRESS_LINE_1",
|
|
1458
1571
|
"type": "TEXT",
|
|
1459
1572
|
"labelFallback": "Home address",
|
|
1460
1573
|
"required": true
|
|
@@ -1588,11 +1701,26 @@ var FIXTURE_DATA = {
|
|
|
1588
1701
|
"params": {
|
|
1589
1702
|
"accessToken": "eyJ\u2026"
|
|
1590
1703
|
},
|
|
1704
|
+
"preferredDocuments": {
|
|
1705
|
+
"IDENTITY": {
|
|
1706
|
+
"idDocType": "PASSPORT",
|
|
1707
|
+
"country": "GBR"
|
|
1708
|
+
}
|
|
1709
|
+
},
|
|
1591
1710
|
"expiresAt": "\u2026"
|
|
1592
1711
|
}
|
|
1593
1712
|
},
|
|
1594
1713
|
"provider": "TRANSAK",
|
|
1595
1714
|
"transitions": [
|
|
1715
|
+
{
|
|
1716
|
+
"id": "refresh_sdk_token",
|
|
1717
|
+
"mode": "SUBMIT",
|
|
1718
|
+
"endpoint": "POST /fiat/kyc/sdk-token",
|
|
1719
|
+
"params": {
|
|
1720
|
+
"quoteRef": "q_8f2"
|
|
1721
|
+
},
|
|
1722
|
+
"labelFallback": "Retry verification"
|
|
1723
|
+
},
|
|
1596
1724
|
{
|
|
1597
1725
|
"id": "capture",
|
|
1598
1726
|
"mode": "CLIENT_SURFACE",
|
|
@@ -1600,6 +1728,10 @@ var FIXTURE_DATA = {
|
|
|
1600
1728
|
"endpoint": "POST /fiat/kyc/capture",
|
|
1601
1729
|
"params": {
|
|
1602
1730
|
"quoteRef": "q_8f2"
|
|
1731
|
+
},
|
|
1732
|
+
"result": {
|
|
1733
|
+
"type": "KYC_SDK_RESULT",
|
|
1734
|
+
"bodyKey": "result"
|
|
1603
1735
|
}
|
|
1604
1736
|
}
|
|
1605
1737
|
}
|
|
@@ -1844,7 +1976,6 @@ var FIXTURE_DATA = {
|
|
|
1844
1976
|
"reason": "REGION_UNSUPPORTED",
|
|
1845
1977
|
"retryAfter": null
|
|
1846
1978
|
},
|
|
1847
|
-
"provider": "TRANSAK",
|
|
1848
1979
|
"transitions": []
|
|
1849
1980
|
},
|
|
1850
1981
|
"worked-example-01-quote": {
|
|
@@ -2222,6 +2353,9 @@ function bodyKeyIssues(transition) {
|
|
|
2222
2353
|
if (transition.mode === "CLIENT_SURFACE") {
|
|
2223
2354
|
const params = Object.keys(transition.report.params ?? {});
|
|
2224
2355
|
collide(params, transition.report.expects ?? [], "report.params", "report.expects");
|
|
2356
|
+
const resultKeys = transition.report.result ? [transition.report.result.bodyKey] : [];
|
|
2357
|
+
collide(params, resultKeys, "report.params", "report.result");
|
|
2358
|
+
collide(transition.report.expects ?? [], resultKeys, "report.expects", "report.result");
|
|
2225
2359
|
}
|
|
2226
2360
|
return issues;
|
|
2227
2361
|
}
|
|
@@ -2250,6 +2384,11 @@ function checkLegalEmission(state, transitions) {
|
|
|
2250
2384
|
`transition "${transition.id}": CLIENT_SURFACE requires the Surface in state (\u03942 single-carry) \u2014 ${entry.key} carries none`
|
|
2251
2385
|
);
|
|
2252
2386
|
}
|
|
2387
|
+
if (transition.mode === "CLIENT_SURFACE" && transition.report.result?.type === "KYC_SDK_RESULT" && (!("surface" in state) || state.surface?.kind !== "KYC_SDK")) {
|
|
2388
|
+
issues.push(
|
|
2389
|
+
`transition "${transition.id}": KYC_SDK_RESULT requires a KYC_SDK surface in state`
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2253
2392
|
if (transition.mode === "SUBMIT" && Object.values(transition.inputs ?? {}).some((spec) => spec.type === "FORM_VALUES") && !("form" in state)) {
|
|
2254
2393
|
issues.push(
|
|
2255
2394
|
`transition "${transition.id}": inputs {type: FORM_VALUES} collects state.form \u2014 ${entry.key} carries no form`
|
package/dist/index.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
tableEntry,
|
|
13
13
|
unofferedEndpoints,
|
|
14
14
|
unreachableStates
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-GKKBF7QB.mjs";
|
|
16
16
|
|
|
17
17
|
// src/codes.ts
|
|
18
18
|
var COUNTRY_CURRENCY_LIST = [
|
|
@@ -417,8 +417,7 @@ var $surfaceKind = z.enum([
|
|
|
417
417
|
"AUTH_COMPONENT"
|
|
418
418
|
]);
|
|
419
419
|
var SurfaceKindSchema = $surfaceKind;
|
|
420
|
-
var $
|
|
421
|
-
kind: $surfaceKind,
|
|
420
|
+
var $surfaceBase = {
|
|
422
421
|
provider: $surfaceProvider,
|
|
423
422
|
/**
|
|
424
423
|
* The one deliberate non-strict leaf in the contract. Provider inputs are the adapter's
|
|
@@ -430,7 +429,84 @@ var $surface = obj({
|
|
|
430
429
|
*/
|
|
431
430
|
params: z.record(z.string(), JsonSchema),
|
|
432
431
|
expiresAt: z.string().optional()
|
|
432
|
+
};
|
|
433
|
+
var $preferredIdentityDocument = obj({
|
|
434
|
+
idDocType: z.enum(["PASSPORT", "ID_CARD", "DRIVERS", "RESIDENCE_PERMIT", "VISA"]),
|
|
435
|
+
country: z.string().regex(/^[A-Z]{3}$/)
|
|
436
|
+
});
|
|
437
|
+
var PreferredIdentityDocumentSchema = $preferredIdentityDocument;
|
|
438
|
+
var STANDARD_KYC_FIELD_IDS = [
|
|
439
|
+
"LEGAL_FIRST_NAME",
|
|
440
|
+
"LEGAL_MIDDLE_NAME",
|
|
441
|
+
"LEGAL_LAST_NAME",
|
|
442
|
+
"DATE_OF_BIRTH",
|
|
443
|
+
"COUNTRY_OF_BIRTH",
|
|
444
|
+
"CITY_OF_BIRTH",
|
|
445
|
+
"NATIONALITY",
|
|
446
|
+
"EMAIL_ADDRESS",
|
|
447
|
+
"EMAIL_OWNERSHIP_VERIFICATION",
|
|
448
|
+
"PHONE_NUMBER",
|
|
449
|
+
"PHONE_OWNERSHIP_VERIFICATION",
|
|
450
|
+
"RESIDENTIAL_ADDRESS_LINE_1",
|
|
451
|
+
"RESIDENTIAL_ADDRESS_LINE_2",
|
|
452
|
+
"RESIDENTIAL_ADDRESS_CITY",
|
|
453
|
+
"RESIDENTIAL_ADDRESS_REGION",
|
|
454
|
+
"RESIDENTIAL_ADDRESS_POSTAL_CODE",
|
|
455
|
+
"RESIDENTIAL_ADDRESS_COUNTRY",
|
|
456
|
+
"TAX_IDENTIFIER",
|
|
457
|
+
"TAX_RESIDENCE_COUNTRY",
|
|
458
|
+
"EMPLOYMENT_STATUS",
|
|
459
|
+
"OCCUPATION_INDUSTRY",
|
|
460
|
+
"OCCUPATION_TITLE",
|
|
461
|
+
"SOURCE_OF_FUNDS_DECLARATION",
|
|
462
|
+
"SOURCE_OF_INCOME_DECLARATION",
|
|
463
|
+
"SOURCE_OF_WEALTH_DECLARATION",
|
|
464
|
+
"PURPOSE_OF_SERVICE",
|
|
465
|
+
"PURPOSE_OF_TRANSACTION",
|
|
466
|
+
"CUSTOMER_CATEGORY"
|
|
467
|
+
];
|
|
468
|
+
var KYC_ACKNOWLEDGEMENT_FIELD_IDS = [
|
|
469
|
+
"RISK_ACKNOWLEDGEMENT",
|
|
470
|
+
"COOLING_OFF_ACKNOWLEDGEMENT",
|
|
471
|
+
"TERMS_ACCEPTANCE"
|
|
472
|
+
];
|
|
473
|
+
var $questionnaireKycFieldId = z.custom(
|
|
474
|
+
(value) => typeof value === "string" && /^QUESTIONNAIRE_RESPONSE:[^:]+:[^:]+$/.test(value),
|
|
475
|
+
"Questionnaire field ids must identify exactly one policy and question"
|
|
476
|
+
);
|
|
477
|
+
var $kycFieldId = z.union([
|
|
478
|
+
z.enum(STANDARD_KYC_FIELD_IDS),
|
|
479
|
+
z.enum(KYC_ACKNOWLEDGEMENT_FIELD_IDS),
|
|
480
|
+
$questionnaireKycFieldId
|
|
481
|
+
]);
|
|
482
|
+
var $clientExecutedDelivery = obj({
|
|
483
|
+
channel: z.literal("CLIENT_EXECUTED"),
|
|
484
|
+
source: z.literal("CURRENT_SESSION_INPUTS"),
|
|
485
|
+
allowedFieldIds: z.array($kycFieldId).min(1)
|
|
486
|
+
}).superRefine((delivery, context) => {
|
|
487
|
+
if (new Set(delivery.allowedFieldIds).size !== delivery.allowedFieldIds.length) {
|
|
488
|
+
context.addIssue({
|
|
489
|
+
code: "custom",
|
|
490
|
+
path: ["allowedFieldIds"],
|
|
491
|
+
message: "allowedFieldIds must be unique"
|
|
492
|
+
});
|
|
493
|
+
}
|
|
433
494
|
});
|
|
495
|
+
var ClientExecutedDeliverySchema = $clientExecutedDelivery;
|
|
496
|
+
var $surface = z.union([
|
|
497
|
+
obj({
|
|
498
|
+
...$surfaceBase,
|
|
499
|
+
kind: z.literal("KYC_SDK"),
|
|
500
|
+
provider: $kycProvider,
|
|
501
|
+
preferredDocuments: obj({ IDENTITY: $preferredIdentityDocument }).optional()
|
|
502
|
+
}),
|
|
503
|
+
obj({
|
|
504
|
+
...$surfaceBase,
|
|
505
|
+
kind: z.enum(["URL_REDIRECT", "PCI_COMPONENT", "PAY_SHEET", "ACH_COMPONENT", "AUTH_COMPONENT"]),
|
|
506
|
+
provider: $provider,
|
|
507
|
+
delivery: $clientExecutedDelivery.optional()
|
|
508
|
+
})
|
|
509
|
+
]);
|
|
434
510
|
var SurfaceSchema = $surface;
|
|
435
511
|
var $instructionField = obj({
|
|
436
512
|
id: z.string(),
|
|
@@ -453,15 +529,15 @@ var $selectOption = obj({ id: z.string(), labelFallback: z.string() });
|
|
|
453
529
|
var SelectOptionSchema = $selectOption;
|
|
454
530
|
var $formFieldType = z.enum(["TEXT", "DATE", "SELECT", "COUNTRY", "FILE", "HOSTED_LINK"]);
|
|
455
531
|
var FormFieldTypeSchema = $formFieldType;
|
|
456
|
-
var $
|
|
457
|
-
|
|
458
|
-
type:
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
532
|
+
var $formFieldBase = { id: $kycFieldId, labelFallback: z.string(), required: z.boolean() };
|
|
533
|
+
var $formField = z.union([
|
|
534
|
+
obj({ ...$formFieldBase, type: z.literal("TEXT"), regex: z.string().optional() }),
|
|
535
|
+
obj({ ...$formFieldBase, type: z.literal("DATE") }),
|
|
536
|
+
obj({ ...$formFieldBase, type: z.literal("SELECT"), options: z.array($selectOption) }),
|
|
537
|
+
obj({ ...$formFieldBase, type: z.literal("COUNTRY") }),
|
|
538
|
+
obj({ ...$formFieldBase, type: z.literal("FILE") }),
|
|
539
|
+
obj({ ...$formFieldBase, type: z.literal("HOSTED_LINK"), url: z.string() })
|
|
540
|
+
]);
|
|
465
541
|
var FormFieldSchema = $formField;
|
|
466
542
|
var $formDescriptor = obj({ formId: z.string(), fields: z.array($formField) });
|
|
467
543
|
var FormDescriptorSchema = $formDescriptor;
|
|
@@ -472,7 +548,8 @@ var FIELD_SPEC_TYPES = [
|
|
|
472
548
|
"FILE",
|
|
473
549
|
"SELECT",
|
|
474
550
|
"HOSTED_LINK",
|
|
475
|
-
"FORM_VALUES"
|
|
551
|
+
"FORM_VALUES",
|
|
552
|
+
"TERMS_ACCEPTANCE"
|
|
476
553
|
];
|
|
477
554
|
var $fieldSpec = z.union([
|
|
478
555
|
obj({
|
|
@@ -490,9 +567,19 @@ var $fieldSpec = z.union([
|
|
|
490
567
|
labelFallback: z.string().optional()
|
|
491
568
|
}),
|
|
492
569
|
obj({ type: z.literal("HOSTED_LINK"), url: z.string(), labelFallback: z.string().optional() }),
|
|
493
|
-
obj({ type: z.literal("FORM_VALUES") })
|
|
570
|
+
obj({ type: z.literal("FORM_VALUES") }),
|
|
571
|
+
obj({
|
|
572
|
+
type: z.literal("TERMS_ACCEPTANCE"),
|
|
573
|
+
documentUrl: z.string(),
|
|
574
|
+
version: z.string(),
|
|
575
|
+
labelFallback: z.string().optional()
|
|
576
|
+
})
|
|
494
577
|
]);
|
|
495
578
|
var FieldSpecSchema = $fieldSpec;
|
|
579
|
+
var TermsAcceptanceSchema = obj({
|
|
580
|
+
version: z.string(),
|
|
581
|
+
acceptedAt: z.string()
|
|
582
|
+
});
|
|
496
583
|
var looksLikeFieldSpec = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && FIELD_SPEC_TYPES.includes(value.type);
|
|
497
584
|
var $params = z.record(z.string(), JsonSchema).superRefine((params, ctx) => {
|
|
498
585
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -539,9 +626,15 @@ var PollSpecSchema = $pollSpec;
|
|
|
539
626
|
var $reportSpec = obj({
|
|
540
627
|
endpoint: $endpoint,
|
|
541
628
|
params: $params.optional(),
|
|
542
|
-
expects: z.array(z.string()).optional()
|
|
629
|
+
expects: z.array(z.string()).optional(),
|
|
630
|
+
result: obj({ type: z.literal("KYC_SDK_RESULT"), bodyKey: z.string().trim().min(1) }).optional()
|
|
543
631
|
});
|
|
544
632
|
var ReportSpecSchema = $reportSpec;
|
|
633
|
+
var $kycSdkResult = z.union([
|
|
634
|
+
obj({ outcome: z.literal("COMPLETED") }),
|
|
635
|
+
obj({ outcome: z.literal("FAILED"), errorType: z.string().trim().min(1) })
|
|
636
|
+
]);
|
|
637
|
+
var KycSdkResultReportSchema = $kycSdkResult;
|
|
545
638
|
var $transition = z.union([
|
|
546
639
|
obj({
|
|
547
640
|
id: z.string(),
|
|
@@ -616,7 +709,8 @@ var $blockedReason = z.enum([
|
|
|
616
709
|
"REGION_UNSUPPORTED",
|
|
617
710
|
"ASSET_UNSUPPORTED",
|
|
618
711
|
"ALL_PROVIDERS_DECLINED",
|
|
619
|
-
"AMOUNT_UNSERVABLE"
|
|
712
|
+
"AMOUNT_UNSERVABLE",
|
|
713
|
+
"CLIENT_UPGRADE_REQUIRED"
|
|
620
714
|
]);
|
|
621
715
|
var BlockedReasonSchema = $blockedReason;
|
|
622
716
|
var $blockedState = obj({
|
|
@@ -660,7 +754,7 @@ var $orderCreatedState = obj({
|
|
|
660
754
|
error: $failureReason.optional()
|
|
661
755
|
});
|
|
662
756
|
var OrderCreatedStateSchema = $orderCreatedState;
|
|
663
|
-
var $
|
|
757
|
+
var $providerBoundFlowState = z.union([
|
|
664
758
|
$quoteState,
|
|
665
759
|
$funAuthState,
|
|
666
760
|
$sessionAuthState,
|
|
@@ -668,19 +762,26 @@ var $flowState = z.union([
|
|
|
668
762
|
$kycInputRequiredState,
|
|
669
763
|
$kycNoActionRequiredState,
|
|
670
764
|
$pendingOrderState,
|
|
671
|
-
$blockedState,
|
|
672
765
|
$paymentCaptureState,
|
|
673
766
|
$paymentInstructState,
|
|
674
767
|
$orderAwaitingConfirmationState,
|
|
675
768
|
$orderCreatedState
|
|
676
769
|
]);
|
|
770
|
+
var $flowState = z.union([$providerBoundFlowState, $blockedState]);
|
|
677
771
|
var FlowStateSchema = $flowState;
|
|
678
|
-
var $
|
|
679
|
-
state: $
|
|
772
|
+
var $providerBoundStepResponse = obj({
|
|
773
|
+
state: $providerBoundFlowState,
|
|
680
774
|
provider: $provider,
|
|
681
775
|
transitions: z.array($transition),
|
|
682
776
|
orderId: z.string().optional()
|
|
683
777
|
});
|
|
778
|
+
var $blockedStepResponse = obj({
|
|
779
|
+
state: $blockedState,
|
|
780
|
+
provider: $provider.optional(),
|
|
781
|
+
transitions: z.array($transition),
|
|
782
|
+
orderId: z.string().optional()
|
|
783
|
+
});
|
|
784
|
+
var $stepResponse = z.union([$providerBoundStepResponse, $blockedStepResponse]);
|
|
684
785
|
var FiatStepResponseSchema = $stepResponse;
|
|
685
786
|
|
|
686
787
|
// src/fixtures/data.ts
|
|
@@ -865,13 +966,13 @@ var FIXTURE_DATA = {
|
|
|
865
966
|
"formId": "kyc_q_8f2_1",
|
|
866
967
|
"fields": [
|
|
867
968
|
{
|
|
868
|
-
"id": "
|
|
969
|
+
"id": "TAX_IDENTIFIER",
|
|
869
970
|
"type": "TEXT",
|
|
870
971
|
"labelFallback": "Tax ID",
|
|
871
972
|
"required": true
|
|
872
973
|
},
|
|
873
974
|
{
|
|
874
|
-
"id": "
|
|
975
|
+
"id": "RESIDENTIAL_ADDRESS_LINE_1",
|
|
875
976
|
"type": "TEXT",
|
|
876
977
|
"labelFallback": "Home address",
|
|
877
978
|
"required": true
|
|
@@ -1005,11 +1106,26 @@ var FIXTURE_DATA = {
|
|
|
1005
1106
|
"params": {
|
|
1006
1107
|
"accessToken": "eyJ\u2026"
|
|
1007
1108
|
},
|
|
1109
|
+
"preferredDocuments": {
|
|
1110
|
+
"IDENTITY": {
|
|
1111
|
+
"idDocType": "PASSPORT",
|
|
1112
|
+
"country": "GBR"
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1008
1115
|
"expiresAt": "\u2026"
|
|
1009
1116
|
}
|
|
1010
1117
|
},
|
|
1011
1118
|
"provider": "TRANSAK",
|
|
1012
1119
|
"transitions": [
|
|
1120
|
+
{
|
|
1121
|
+
"id": "refresh_sdk_token",
|
|
1122
|
+
"mode": "SUBMIT",
|
|
1123
|
+
"endpoint": "POST /fiat/kyc/sdk-token",
|
|
1124
|
+
"params": {
|
|
1125
|
+
"quoteRef": "q_8f2"
|
|
1126
|
+
},
|
|
1127
|
+
"labelFallback": "Retry verification"
|
|
1128
|
+
},
|
|
1013
1129
|
{
|
|
1014
1130
|
"id": "capture",
|
|
1015
1131
|
"mode": "CLIENT_SURFACE",
|
|
@@ -1017,6 +1133,10 @@ var FIXTURE_DATA = {
|
|
|
1017
1133
|
"endpoint": "POST /fiat/kyc/capture",
|
|
1018
1134
|
"params": {
|
|
1019
1135
|
"quoteRef": "q_8f2"
|
|
1136
|
+
},
|
|
1137
|
+
"result": {
|
|
1138
|
+
"type": "KYC_SDK_RESULT",
|
|
1139
|
+
"bodyKey": "result"
|
|
1020
1140
|
}
|
|
1021
1141
|
}
|
|
1022
1142
|
}
|
|
@@ -1261,7 +1381,6 @@ var FIXTURE_DATA = {
|
|
|
1261
1381
|
"reason": "REGION_UNSUPPORTED",
|
|
1262
1382
|
"retryAfter": null
|
|
1263
1383
|
},
|
|
1264
|
-
"provider": "TRANSAK",
|
|
1265
1384
|
"transitions": []
|
|
1266
1385
|
},
|
|
1267
1386
|
"worked-example-01-quote": {
|
|
@@ -1639,6 +1758,9 @@ function bodyKeyIssues(transition) {
|
|
|
1639
1758
|
if (transition.mode === "CLIENT_SURFACE") {
|
|
1640
1759
|
const params = Object.keys(transition.report.params ?? {});
|
|
1641
1760
|
collide(params, transition.report.expects ?? [], "report.params", "report.expects");
|
|
1761
|
+
const resultKeys = transition.report.result ? [transition.report.result.bodyKey] : [];
|
|
1762
|
+
collide(params, resultKeys, "report.params", "report.result");
|
|
1763
|
+
collide(transition.report.expects ?? [], resultKeys, "report.expects", "report.result");
|
|
1642
1764
|
}
|
|
1643
1765
|
return issues;
|
|
1644
1766
|
}
|
|
@@ -1667,6 +1789,11 @@ function checkLegalEmission(state, transitions) {
|
|
|
1667
1789
|
`transition "${transition.id}": CLIENT_SURFACE requires the Surface in state (\u03942 single-carry) \u2014 ${entry.key} carries none`
|
|
1668
1790
|
);
|
|
1669
1791
|
}
|
|
1792
|
+
if (transition.mode === "CLIENT_SURFACE" && transition.report.result?.type === "KYC_SDK_RESULT" && (!("surface" in state) || state.surface?.kind !== "KYC_SDK")) {
|
|
1793
|
+
issues.push(
|
|
1794
|
+
`transition "${transition.id}": KYC_SDK_RESULT requires a KYC_SDK surface in state`
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1670
1797
|
if (transition.mode === "SUBMIT" && Object.values(transition.inputs ?? {}).some((spec) => spec.type === "FORM_VALUES") && !("form" in state)) {
|
|
1671
1798
|
issues.push(
|
|
1672
1799
|
`transition "${transition.id}": inputs {type: FORM_VALUES} collects state.form \u2014 ${entry.key} carries no form`
|
|
@@ -1723,6 +1850,7 @@ export {
|
|
|
1723
1850
|
BlockedStateSchema,
|
|
1724
1851
|
COUNTRY_NAMES,
|
|
1725
1852
|
COUNTRY_TO_CURRENCY,
|
|
1853
|
+
ClientExecutedDeliverySchema,
|
|
1726
1854
|
ContractViolation,
|
|
1727
1855
|
CountryCode,
|
|
1728
1856
|
CryptoAmountSchema,
|
|
@@ -1751,9 +1879,11 @@ export {
|
|
|
1751
1879
|
InstrumentSchema,
|
|
1752
1880
|
JsonSchema,
|
|
1753
1881
|
KYCProviderSchema,
|
|
1882
|
+
KYC_ACKNOWLEDGEMENT_FIELD_IDS,
|
|
1754
1883
|
KycCaptureStateSchema,
|
|
1755
1884
|
KycInputRequiredStateSchema,
|
|
1756
1885
|
KycNoActionRequiredStateSchema,
|
|
1886
|
+
KycSdkResultReportSchema,
|
|
1757
1887
|
OrderAwaitingConfirmationStateSchema,
|
|
1758
1888
|
OrderCreatedStateSchema,
|
|
1759
1889
|
OrderRefSchema,
|
|
@@ -1764,6 +1894,7 @@ export {
|
|
|
1764
1894
|
PaymentInstructStateSchema,
|
|
1765
1895
|
PendingOrderStateSchema,
|
|
1766
1896
|
PollSpecSchema,
|
|
1897
|
+
PreferredIdentityDocumentSchema,
|
|
1767
1898
|
QuoteLimitRangeSchema,
|
|
1768
1899
|
QuoteLimitsSchema,
|
|
1769
1900
|
QuoteSchema,
|
|
@@ -1771,6 +1902,7 @@ export {
|
|
|
1771
1902
|
RecoverySchema,
|
|
1772
1903
|
RefundSchema,
|
|
1773
1904
|
ReportSpecSchema,
|
|
1905
|
+
STANDARD_KYC_FIELD_IDS,
|
|
1774
1906
|
STATE_KEYS,
|
|
1775
1907
|
SelectOptionSchema,
|
|
1776
1908
|
SessionAuthStateSchema,
|
|
@@ -1781,6 +1913,7 @@ export {
|
|
|
1781
1913
|
TABLE_VERSION,
|
|
1782
1914
|
TERMINAL_ORDER_STATUSES,
|
|
1783
1915
|
TRANSITION_TABLE,
|
|
1916
|
+
TermsAcceptanceSchema,
|
|
1784
1917
|
TransitionInputsSchema,
|
|
1785
1918
|
TransitionParamsSchema,
|
|
1786
1919
|
TransitionSchema,
|
package/dist/schemas.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* They are module-private, so none of it reaches the published declarations.
|
|
28
28
|
*/
|
|
29
29
|
import { z } from 'zod';
|
|
30
|
-
import type { BlockedReason, CryptoAmount, FailureReason, FeeLine, FiatEndpoint, FieldSpec, FlowState, FormDescriptor, FormField, FormFieldType,
|
|
30
|
+
import type { BlockedReason, CryptoAmount, FailureReason, FeeLine, FiatAmount, FiatEndpoint, FiatProvider, FiatStepResponse, FieldSpec, FlowState, FormDescriptor, FormField, FormFieldType, InstructionField, Instructions, Instrument, JsonValue, KYCProvider, OrderRef, OrderStatus, OrderSummary, PollSpec, Quote, QuoteLimitRange, QuoteLimits, Recovery, Refund, ReportSpec, SelectOption, StatusHistoryEntry, Surface, PreferredIdentityDocument, ClientExecutedDelivery, KycSdkResultReport, SurfaceKind, SurfaceProvider, TermsAcceptance, Transition, TransitionInputs, TransitionParams, Tx } from './types';
|
|
31
31
|
export declare const JsonSchema: z.ZodType<JsonValue>;
|
|
32
32
|
export declare const HTTP_VERBS: readonly ["GET", "POST"];
|
|
33
33
|
/** `"POST /fiat/session/verify"`, `"GET /fiat/orders/o_31c"` — the verb rides the string. */
|
|
@@ -52,6 +52,10 @@ export declare const StatusHistoryEntrySchema: z.ZodType<StatusHistoryEntry>;
|
|
|
52
52
|
export declare const TxSchema: z.ZodType<Tx>;
|
|
53
53
|
export declare const RefundSchema: z.ZodType<Refund>;
|
|
54
54
|
export declare const SurfaceKindSchema: z.ZodType<SurfaceKind>;
|
|
55
|
+
export declare const PreferredIdentityDocumentSchema: z.ZodType<PreferredIdentityDocument>;
|
|
56
|
+
export declare const STANDARD_KYC_FIELD_IDS: readonly ["LEGAL_FIRST_NAME", "LEGAL_MIDDLE_NAME", "LEGAL_LAST_NAME", "DATE_OF_BIRTH", "COUNTRY_OF_BIRTH", "CITY_OF_BIRTH", "NATIONALITY", "EMAIL_ADDRESS", "EMAIL_OWNERSHIP_VERIFICATION", "PHONE_NUMBER", "PHONE_OWNERSHIP_VERIFICATION", "RESIDENTIAL_ADDRESS_LINE_1", "RESIDENTIAL_ADDRESS_LINE_2", "RESIDENTIAL_ADDRESS_CITY", "RESIDENTIAL_ADDRESS_REGION", "RESIDENTIAL_ADDRESS_POSTAL_CODE", "RESIDENTIAL_ADDRESS_COUNTRY", "TAX_IDENTIFIER", "TAX_RESIDENCE_COUNTRY", "EMPLOYMENT_STATUS", "OCCUPATION_INDUSTRY", "OCCUPATION_TITLE", "SOURCE_OF_FUNDS_DECLARATION", "SOURCE_OF_INCOME_DECLARATION", "SOURCE_OF_WEALTH_DECLARATION", "PURPOSE_OF_SERVICE", "PURPOSE_OF_TRANSACTION", "CUSTOMER_CATEGORY"];
|
|
57
|
+
export declare const KYC_ACKNOWLEDGEMENT_FIELD_IDS: readonly ["RISK_ACKNOWLEDGEMENT", "COOLING_OFF_ACKNOWLEDGEMENT", "TERMS_ACCEPTANCE"];
|
|
58
|
+
export declare const ClientExecutedDeliverySchema: z.ZodType<ClientExecutedDelivery>;
|
|
55
59
|
export declare const SurfaceSchema: z.ZodType<Surface>;
|
|
56
60
|
export declare const InstructionFieldSchema: z.ZodType<InstructionField>;
|
|
57
61
|
export declare const InstructionsSchema: z.ZodType<Instructions>;
|
|
@@ -59,8 +63,10 @@ export declare const SelectOptionSchema: z.ZodType<SelectOption>;
|
|
|
59
63
|
export declare const FormFieldTypeSchema: z.ZodType<FormFieldType>;
|
|
60
64
|
export declare const FormFieldSchema: z.ZodType<FormField>;
|
|
61
65
|
export declare const FormDescriptorSchema: z.ZodType<FormDescriptor>;
|
|
62
|
-
export declare const FIELD_SPEC_TYPES: readonly ["TEXT", "DATE", "COUNTRY", "FILE", "SELECT", "HOSTED_LINK", "FORM_VALUES"];
|
|
66
|
+
export declare const FIELD_SPEC_TYPES: readonly ["TEXT", "DATE", "COUNTRY", "FILE", "SELECT", "HOSTED_LINK", "FORM_VALUES", "TERMS_ACCEPTANCE"];
|
|
63
67
|
export declare const FieldSpecSchema: z.ZodType<FieldSpec>;
|
|
68
|
+
/** The value a `TERMS_ACCEPTANCE` input yields. */
|
|
69
|
+
export declare const TermsAcceptanceSchema: z.ZodType<TermsAcceptance>;
|
|
64
70
|
/**
|
|
65
71
|
* True when a `params` value is wearing a `FieldSpec`'s clothes — i.e. the exact defect the
|
|
66
72
|
* accepted split exists to kill (a spec object spread into a request body as if it were a value).
|
|
@@ -70,6 +76,7 @@ export declare const TransitionParamsSchema: z.ZodType<TransitionParams>;
|
|
|
70
76
|
export declare const TransitionInputsSchema: z.ZodType<TransitionInputs>;
|
|
71
77
|
export declare const PollSpecSchema: z.ZodType<PollSpec>;
|
|
72
78
|
export declare const ReportSpecSchema: z.ZodType<ReportSpec>;
|
|
79
|
+
export declare const KycSdkResultReportSchema: z.ZodType<KycSdkResultReport>;
|
|
73
80
|
export declare const TransitionSchema: z.ZodType<Transition>;
|
|
74
81
|
export declare const AuthChallengeSchema: z.ZodType<Record<string, JsonValue>>;
|
|
75
82
|
/** `Extract` keeps each per-state export pinned to the union member it validates. */
|
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.
|
|
39
|
+
var version = "0.11.0";
|
|
40
40
|
|
|
41
41
|
// src/table.ts
|
|
42
42
|
var TABLE_VERSION = version;
|
|
@@ -151,7 +151,8 @@ var TRANSITION_TABLE = {
|
|
|
151
151
|
terminal: false,
|
|
152
152
|
notes: [
|
|
153
153
|
"A bad code re-enters this same state with error {category: USER_ERROR, code: BAD_CODE} \u2014 banner, not a screen change.",
|
|
154
|
-
"No resend affordance is specified in either doc (known gap, FE doc Screen 2)."
|
|
154
|
+
"No resend affordance is specified in either doc (known gap, FE doc Screen 2).",
|
|
155
|
+
"A provider-owned surface may declare client-executed delivery from allowlisted CURRENT_SESSION_INPUTS only."
|
|
155
156
|
]
|
|
156
157
|
},
|
|
157
158
|
"KYC/CAPTURE": {
|
|
@@ -161,6 +162,12 @@ var TRANSITION_TABLE = {
|
|
|
161
162
|
screens: [8],
|
|
162
163
|
docStatus: "SPECIFIED",
|
|
163
164
|
allowedTransitions: [
|
|
165
|
+
{
|
|
166
|
+
id: "refresh_sdk_token",
|
|
167
|
+
mode: "SUBMIT",
|
|
168
|
+
endpoint: "POST /fiat/kyc/sdk-token",
|
|
169
|
+
note: "Refreshes an expired SDK credential and returns a new KYC/CAPTURE state."
|
|
170
|
+
},
|
|
164
171
|
{
|
|
165
172
|
id: "capture",
|
|
166
173
|
mode: "CLIENT_SURFACE",
|
|
@@ -180,7 +187,10 @@ var TRANSITION_TABLE = {
|
|
|
180
187
|
"ORDER/AWAITING_CONFIRMATION"
|
|
181
188
|
],
|
|
182
189
|
terminal: false,
|
|
183
|
-
notes: [
|
|
190
|
+
notes: [
|
|
191
|
+
"Carries the handshake-gated KYC_SDK surface; provider selects the identity-vendor adapter.",
|
|
192
|
+
"SDK completion or failure is only a launch report; eligibility is re-read authoritatively by the backend."
|
|
193
|
+
]
|
|
184
194
|
},
|
|
185
195
|
"KYC/INPUT_REQUIRED": {
|
|
186
196
|
key: "KYC/INPUT_REQUIRED",
|
|
@@ -436,10 +446,6 @@ var DOCUMENTED_ENDPOINTS = [
|
|
|
436
446
|
"POST /fiat/orders/:id/surface-result"
|
|
437
447
|
];
|
|
438
448
|
var UNOFFERED_ENDPOINTS = [
|
|
439
|
-
{
|
|
440
|
-
endpoint: "POST /fiat/kyc/sdk-token",
|
|
441
|
-
reason: "SDK token refresh is adapter-driven rather than a flow transition."
|
|
442
|
-
},
|
|
443
449
|
{
|
|
444
450
|
endpoint: "GET /fiat/payment-methods",
|
|
445
451
|
reason: "Discovery, called before any flow state exists \u2014 there is no state to hang it off."
|
package/dist/table.mjs
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -241,8 +241,7 @@ export type SurfaceProvider = FiatProvider | KYCProvider;
|
|
|
241
241
|
* `params` values are secrets by default. Client redaction defaults every key closed and opens
|
|
242
242
|
* only names that are safe under *any* provider's vocabulary.
|
|
243
243
|
*/
|
|
244
|
-
|
|
245
|
-
kind: SurfaceKind;
|
|
244
|
+
interface SurfaceBase {
|
|
246
245
|
provider: SurfaceProvider;
|
|
247
246
|
params: Record<string, JsonValue>;
|
|
248
247
|
/**
|
|
@@ -251,6 +250,49 @@ export interface Surface {
|
|
|
251
250
|
*/
|
|
252
251
|
expiresAt?: string;
|
|
253
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Identity-document kinds the Sumsub adapter may prefer for the `IDENTITY` step.
|
|
255
|
+
*
|
|
256
|
+
* These are the Sumsub identity spellings understood by the capture adapter. Adding a new kind is
|
|
257
|
+
* a contract change: the client adapter must know how to pass it to the identity SDK and every
|
|
258
|
+
* relying provider must either map or explicitly decline it.
|
|
259
|
+
*/
|
|
260
|
+
export type KycIdentityDocumentType = 'PASSPORT' | 'ID_CARD' | 'DRIVERS' | 'RESIDENCE_PERMIT' | 'VISA';
|
|
261
|
+
/** Provider-native document definition understood by the selected KYC adapter. */
|
|
262
|
+
export interface PreferredIdentityDocument {
|
|
263
|
+
idDocType: KycIdentityDocumentType;
|
|
264
|
+
/** ISO 3166-1 alpha-3 country code expected by the identity-vendor SDK. */
|
|
265
|
+
country: string;
|
|
266
|
+
}
|
|
267
|
+
/** Canonical KYC values a declarative form or current-session delivery may name. */
|
|
268
|
+
export type StandardKycFieldId = 'LEGAL_FIRST_NAME' | 'LEGAL_MIDDLE_NAME' | 'LEGAL_LAST_NAME' | 'DATE_OF_BIRTH' | 'COUNTRY_OF_BIRTH' | 'CITY_OF_BIRTH' | 'NATIONALITY' | 'EMAIL_ADDRESS' | 'EMAIL_OWNERSHIP_VERIFICATION' | 'PHONE_NUMBER' | 'PHONE_OWNERSHIP_VERIFICATION' | 'RESIDENTIAL_ADDRESS_LINE_1' | 'RESIDENTIAL_ADDRESS_LINE_2' | 'RESIDENTIAL_ADDRESS_CITY' | 'RESIDENTIAL_ADDRESS_REGION' | 'RESIDENTIAL_ADDRESS_POSTAL_CODE' | 'RESIDENTIAL_ADDRESS_COUNTRY' | 'TAX_IDENTIFIER' | 'TAX_RESIDENCE_COUNTRY' | 'EMPLOYMENT_STATUS' | 'OCCUPATION_INDUSTRY' | 'OCCUPATION_TITLE' | 'SOURCE_OF_FUNDS_DECLARATION' | 'SOURCE_OF_INCOME_DECLARATION' | 'SOURCE_OF_WEALTH_DECLARATION' | 'PURPOSE_OF_SERVICE' | 'PURPOSE_OF_TRANSACTION' | 'CUSTOMER_CATEGORY';
|
|
269
|
+
export type KycAcknowledgementFieldId = 'RISK_ACKNOWLEDGEMENT' | 'COOLING_OFF_ACKNOWLEDGEMENT' | 'TERMS_ACCEPTANCE';
|
|
270
|
+
export type QuestionnaireKycFieldId = `QUESTIONNAIRE_RESPONSE:${string}:${string}`;
|
|
271
|
+
export type KycFieldId = StandardKycFieldId | KycAcknowledgementFieldId | QuestionnaireKycFieldId;
|
|
272
|
+
export interface ClientExecutedDelivery {
|
|
273
|
+
channel: 'CLIENT_EXECUTED';
|
|
274
|
+
/**
|
|
275
|
+
* A hard security boundary: only values entered during this mounted session may be selected.
|
|
276
|
+
* Backend-stored KYC values are never returned to the client through this contract.
|
|
277
|
+
*/
|
|
278
|
+
source: 'CURRENT_SESSION_INPUTS';
|
|
279
|
+
/** Server-declared canonical field ids the adapter may select; this carries no field values. */
|
|
280
|
+
allowedFieldIds: KycFieldId[];
|
|
281
|
+
}
|
|
282
|
+
export type Surface = (SurfaceBase & {
|
|
283
|
+
kind: 'KYC_SDK';
|
|
284
|
+
provider: KYCProvider;
|
|
285
|
+
/** Credentials and adapter configuration only; never backend-stored KYC field values. */
|
|
286
|
+
params: Record<string, JsonValue>;
|
|
287
|
+
preferredDocuments?: {
|
|
288
|
+
IDENTITY: PreferredIdentityDocument;
|
|
289
|
+
};
|
|
290
|
+
}) | (SurfaceBase & {
|
|
291
|
+
kind: Exclude<SurfaceKind, 'KYC_SDK'>;
|
|
292
|
+
provider: FiatProvider;
|
|
293
|
+
/** Adapter-owned submission mechanics; the shared contract declares only the safe source. */
|
|
294
|
+
delivery?: ClientExecutedDelivery;
|
|
295
|
+
});
|
|
254
296
|
export interface InstructionField {
|
|
255
297
|
id: string;
|
|
256
298
|
labelFallback: string;
|
|
@@ -288,17 +330,28 @@ export interface SelectOption {
|
|
|
288
330
|
}
|
|
289
331
|
/** Field types inside a KYC form round. A new field TYPE is a client release (handshake-gated). */
|
|
290
332
|
export type FormFieldType = 'TEXT' | 'DATE' | 'SELECT' | 'COUNTRY' | 'FILE' | 'HOSTED_LINK';
|
|
291
|
-
|
|
292
|
-
id:
|
|
293
|
-
type: FormFieldType;
|
|
333
|
+
interface FormFieldBase {
|
|
334
|
+
id: KycFieldId;
|
|
294
335
|
labelFallback: string;
|
|
295
336
|
required: boolean;
|
|
296
|
-
regex?: string;
|
|
297
|
-
/** SELECT only. */
|
|
298
|
-
options?: SelectOption[];
|
|
299
|
-
/** HOSTED_LINK only. */
|
|
300
|
-
url?: string;
|
|
301
337
|
}
|
|
338
|
+
/** Invalid property combinations (for example `options` on TEXT) are unrepresentable. */
|
|
339
|
+
export type FormField = (FormFieldBase & {
|
|
340
|
+
type: 'TEXT';
|
|
341
|
+
regex?: string;
|
|
342
|
+
}) | (FormFieldBase & {
|
|
343
|
+
type: 'DATE';
|
|
344
|
+
}) | (FormFieldBase & {
|
|
345
|
+
type: 'SELECT';
|
|
346
|
+
options: SelectOption[];
|
|
347
|
+
}) | (FormFieldBase & {
|
|
348
|
+
type: 'COUNTRY';
|
|
349
|
+
}) | (FormFieldBase & {
|
|
350
|
+
type: 'FILE';
|
|
351
|
+
}) | (FormFieldBase & {
|
|
352
|
+
type: 'HOSTED_LINK';
|
|
353
|
+
url: string;
|
|
354
|
+
});
|
|
302
355
|
/** Payload of `KYC{INPUT_REQUIRED}`. One uniform renderer walks `fields`. */
|
|
303
356
|
export interface FormDescriptor {
|
|
304
357
|
formId: string;
|
|
@@ -310,7 +363,7 @@ export interface FormDescriptor {
|
|
|
310
363
|
* transition never re-carries the descriptor (the double-carry disease Δ2 killed).
|
|
311
364
|
*
|
|
312
365
|
* Note: the contract's illustrative list reads `'TEXT' | 'NUMBER' | …`; the conformance-package
|
|
313
|
-
* spec closes the set at the
|
|
366
|
+
* spec closes the set at the eight below, so `NUMBER` is not declared here.
|
|
314
367
|
*/
|
|
315
368
|
export type FieldSpec = {
|
|
316
369
|
type: 'TEXT';
|
|
@@ -336,8 +389,28 @@ export type FieldSpec = {
|
|
|
336
389
|
labelFallback?: string;
|
|
337
390
|
} | {
|
|
338
391
|
type: 'FORM_VALUES';
|
|
392
|
+
} | {
|
|
393
|
+
type: 'TERMS_ACCEPTANCE';
|
|
394
|
+
/** The document being accepted. The client shows it; the user cannot accept what they cannot read. */
|
|
395
|
+
documentUrl: string;
|
|
396
|
+
/**
|
|
397
|
+
* Which revision of that document. Opaque to the client and echoed back verbatim, so a later
|
|
398
|
+
* dispute names the text the user saw rather than whatever is at `documentUrl` today.
|
|
399
|
+
*/
|
|
400
|
+
version: string;
|
|
401
|
+
labelFallback?: string;
|
|
339
402
|
};
|
|
340
403
|
export type FieldSpecType = FieldSpec['type'];
|
|
404
|
+
/**
|
|
405
|
+
* What a `TERMS_ACCEPTANCE` input collects. A bare timestamp answers "when" and nothing else; the
|
|
406
|
+
* version is what ties the moment to the words, which is the only part a dispute turns on.
|
|
407
|
+
*/
|
|
408
|
+
export interface TermsAcceptance {
|
|
409
|
+
/** Echoed from the `FieldSpec`, unmodified. */
|
|
410
|
+
version: string;
|
|
411
|
+
/** ISO-8601, stamped by the client when the user accepted. Never defaulted server-side. */
|
|
412
|
+
acceptedAt: string;
|
|
413
|
+
}
|
|
341
414
|
/** Server literals — spread into the body verbatim, opaque to the client. */
|
|
342
415
|
export type TransitionParams = Record<string, JsonValue>;
|
|
343
416
|
/** Client-collected specs — the harness renders + locally validates, then merges values in. */
|
|
@@ -391,7 +464,22 @@ export interface ReportSpec {
|
|
|
391
464
|
* Declared so conformance can assert the surface actually yields them.
|
|
392
465
|
*/
|
|
393
466
|
expects?: string[];
|
|
467
|
+
/** Typed SDK result injected under `bodyKey`; mutually exclusive outcome shapes are below. */
|
|
468
|
+
result?: {
|
|
469
|
+
type: 'KYC_SDK_RESULT';
|
|
470
|
+
bodyKey: string;
|
|
471
|
+
};
|
|
394
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Client report for a KYC SDK launch. This reports SDK completion, not KYC eligibility: only the
|
|
475
|
+
* backend's authoritative provider read can advance eligibility.
|
|
476
|
+
*/
|
|
477
|
+
export type KycSdkResultReport = {
|
|
478
|
+
outcome: 'COMPLETED';
|
|
479
|
+
} | {
|
|
480
|
+
outcome: 'FAILED';
|
|
481
|
+
errorType: string;
|
|
482
|
+
};
|
|
395
483
|
export type TransitionMode = 'SUBMIT' | 'AWAIT' | 'CLIENT_SURFACE';
|
|
396
484
|
/**
|
|
397
485
|
* Body assembly, harness-owned, one rule:
|
|
@@ -544,16 +632,33 @@ export type FlowState =
|
|
|
544
632
|
error?: FailureReason;
|
|
545
633
|
};
|
|
546
634
|
export type FlowStateKind = FlowState['kind'];
|
|
547
|
-
export type BlockedReason = 'REGION_UNSUPPORTED' | 'ASSET_UNSUPPORTED' | 'ALL_PROVIDERS_DECLINED' | 'AMOUNT_UNSERVABLE'
|
|
635
|
+
export type BlockedReason = 'REGION_UNSUPPORTED' | 'ASSET_UNSUPPORTED' | 'ALL_PROVIDERS_DECLINED' | 'AMOUNT_UNSERVABLE'
|
|
636
|
+
/** The installed client did not declare a capability required by every otherwise-viable route. */
|
|
637
|
+
| 'CLIENT_UPGRADE_REQUIRED';
|
|
548
638
|
/**
|
|
549
639
|
* Every `/fiat/*` response. `transitions: []` = nothing the wizard can do; whether the *flow*
|
|
550
640
|
* is over is read from the table (`terminal`), never from array emptiness (ON_HOLD is the proof).
|
|
551
641
|
*/
|
|
552
|
-
|
|
553
|
-
state: FlowState;
|
|
554
|
-
provider: FiatProvider;
|
|
642
|
+
type FiatStepResponseBase = {
|
|
555
643
|
transitions: Transition[];
|
|
556
644
|
/** TODO(open-decision): `orderId` placement — see `PAYMENT{INSTRUCT}.orderId` above. */
|
|
557
645
|
orderId?: string;
|
|
558
|
-
}
|
|
646
|
+
};
|
|
647
|
+
/**
|
|
648
|
+
* Provider is absent only when routing ended before a provider could be selected. A BLOCKED response
|
|
649
|
+
* may still name a provider for compatibility with provider-specific terminal decisions; every other
|
|
650
|
+
* state is provider-bound and therefore requires one.
|
|
651
|
+
*/
|
|
652
|
+
export type FiatStepResponse = (FiatStepResponseBase & {
|
|
653
|
+
state: Extract<FlowState, {
|
|
654
|
+
kind: 'BLOCKED';
|
|
655
|
+
}>;
|
|
656
|
+
provider?: FiatProvider;
|
|
657
|
+
}) | (FiatStepResponseBase & {
|
|
658
|
+
state: Exclude<FlowState, {
|
|
659
|
+
kind: 'BLOCKED';
|
|
660
|
+
}>;
|
|
661
|
+
provider: FiatProvider;
|
|
662
|
+
});
|
|
663
|
+
export {};
|
|
559
664
|
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fun-xyz/fiat-contract",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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": {
|