@payconnect.me/kyc-contract
v0.6.3
Published
Language-neutral source of truth for the PayConnect KYC wire contract: vocabularies, routes, error codes, validation rules, reference datasets and conformance vectors.
Readme
@payconnect.me/kyc-contract
The fixed values every PayConnect KYC client has to agree on: the enums, the API URL templates, the error-code map, the validation rules, and the country and Thai address datasets.
It is one package rather than a copy inside each SDK because a mistake in a shared value is a mistake in every language at once, and it fails quietly. There is no logic in here — only data and types.
Zero runtime dependencies, on purpose. Every SDK pulls this in, so anything it depended on would be pulled into all of them. CI checks the count is still zero.
Do you need this?
Probably not directly. It is installed automatically as a dependency of
@payconnect.me/kyc-core, which is itself installed by
@payconnect.me/kyc-react-native.
Install it on its own if you are:
- writing your own PayConnect KYC client, in TypeScript or otherwise, or
- building the Kotlin, Swift or Dart SDK and need the same values plus the conformance vectors.
Install
npm install @payconnect.me/kyc-contractNode 20.19 or newer. ESM-only.
What you get
import { ASSESSMENT_TYPES, KYC_API_PREFIX, VALIDATION, COUNTRIES } from '@payconnect.me/kyc-contract';
KYC_API_PREFIX; // '/v1/kyc'
VALIDATION.thaiNationalId.pattern; // '^\\d{13}$'| Export | What it holds |
| --- | --- |
| ASSESSMENT_TYPES | The 7 KYC modules, in order: Identity, AdditionalDocuments, AmloCheck, Suitability, RiskEvaluationStandard, RiskEvaluationEnhanced, Completed. |
| ASSESSMENT_FORM_TYPES | The question form types a module can ask for. |
| DOCUMENT_TYPES | The identity document types. |
| KYC_API_PREFIX | /v1/kyc — the prefix every route sits under. |
| KYC_ROUTE_TEMPLATES | 18 URL templates for the Partners API. |
| ERROR_CODE_KINDS | 53 API error codes mapped to the category a client should react to. |
| DEFAULT_ERROR_KIND | What to use for a code that is not in the map. |
| IDENTITY_ERROR_KINDS | The categories themselves. |
| VALIDATION | 7 rule groups: minimum age, Thai national ID, laser ID, passport number, Thai postcode, required Thai address fields, and gender values. |
| COUNTRIES | 249 countries — ISO 2 and 3 letter codes, and names in English, Thai and Russian. |
| SUGGESTED_COUNTRIES | The 6 pinned to the top of a nationality picker. |
| PROVINCE_ROWS, AMPHURE_ROWS, TAMBON_ROWS | The Thai address hierarchy: 77 provinces, 930 districts, 7,477 sub-districts. |
| IDENTITY_STEPS, ADDITIONAL_DOCS_STEPS | The steps inside each module. |
| LIVENESS_MODES | smile and passive — which liveness check the licence supports. |
| IDENTITY_PROGRESS_STEPS, ADDITIONAL_DOCS_PROGRESS_STEPS, KYC_MODULE_MAX_STEPS | The numbers behind the progress bar. |
| NFC_AVAILABILITYS, NFC_VENDOR_FAILURES, VENDOR_FAILURE_CODES, CHIP_FAILURE_ACTIONS | Chip-read states, the vendor failures that can occur, and what to do about each. |
Conformance vectors
Only relevant if you are porting the KYC logic to another language.
The decisions — which error category a code maps to, what to do when a chip read fails, how the progress bar advances — are not in this package. They cannot be: a TypeScript function is no use to Kotlin, so every SDK writes its own. The conformance vectors are how you prove yours behaves the same as everyone else's.
There are 358 cases across seven files. Each is a set of inputs and the output your implementation must produce.
import vectors from '@payconnect.me/kyc-contract/conformance/classify-identity-error.json' with { type: 'json' };
// Every file has a `cases` array. Some also name the `function` and its `signature`.
for (const { input, expected } of vectors.cases) {
assert.equal(myClassifyIdentityError(input), expected);
}The with { type: 'json' } attribute is required by Node. A TypeScript build with resolveJsonModule, or a
bundler, does not need it.
| File | Cases | Covers |
| --- | --- | --- |
| chip-failure-action.json | 206 | What happens after a failed chip read. |
| classify-identity-error.json | 57 | Error code to category. |
| progress.json | 50 | Progress bar arithmetic. |
| safe-return-to.json | 17 | Which return URLs are accepted. |
| nfc.json | 15 | Chip availability states. |
| can-route-without-navigator.json | 9 | Whether a module can be routed to without asking the navigator. |
| append-kyc-status.json | 4 | Adding session and status to a return URL. |
Non-JavaScript SDKs read these straight out of the published package or the repository — the JSON is the contract, not the TypeScript around it.
Working on this package
Only for maintainers of this repository.
From the repository root, because this package is a workspace member and neither command exists inside it:
npm ci
npm run verify # regenerate, fail on drift, then typecheck, lint, format and testsrc/generated/ is emitted from spec/ and data/ and is committed. Do not hand-edit it — change the
source file and re-run the generator.
The four below are this package's own scripts. Run them from packages/kyc-contract/, or from the root with
--workspace @payconnect.me/kyc-contract.
| Command | Does |
| --- | --- |
| npm run generate | Emit src/generated/ from spec/ and data/. |
| npm run build:data | Rebuild data/ from data/upstream/ and data/overlay/countries.json. No arguments, no sibling reads. |
| npm run sync:upstream | Pull a fresh snapshot from S3. Needs AWS credentials. |
| npm run build:conformance | Regenerate the vectors from the reference implementation. |
Only generate runs in CI, and it must reproduce the committed output byte for byte. The other three
need credentials or a sibling checkout, so they are deliberate acts you run by hand and review as a diff.
Two files worth knowing about:
spec/known-data-defects.json— mistakes in the upstream master data that this package corrects on the way in. Each entry stays open until the fix lands upstream, because other systems read the same source directly and are still wrong.spec/pending-language-review.json— user-facing strings that no native speaker has confirmed. Nothing here blocks a release; the fallback is always a real string.
Versioning and release are covered in docs/engineering/09-versioning-and-release.md.
