Skip to content
GitLab
Projects Groups Topics Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Register
  • Sign in
  • A Ahad Mobile
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributor statistics
    • Graph
    • Compare revisions
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Terraform modules
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • holatech
  • Ahad Mobile
  • Merge requests
  • !114

feat(cardholder): separate card form with english-only names and editable retries

  • Review changes

  • Download
  • Patches
  • Plain diff
Merged Khanh Pham requested to merge feat/cardholder-form-retry into develop Oct 09, 2026
  • Overview 0
  • Commits 3
  • Pipelines 1
  • Changes 48

Why

The card application failed on staging for a new user (2026-10-09). KeySecure /cardholder/apply answered 2012 · KYC submission name is illegal.

  • KeySecure reads the cardholder name from the Sumsub applicant fixedInfo. KycService.buildFixedInfo had prefilled that name from the signup name, which contained a Vietnamese diacritic.
  • A hyphen also triggers 2012. A name of letters and single spaces is accepted. This was confirmed by patching the applicant by hand on staging: the apply then succeeded and the row went to IN_REVIEW.
  • Our code mapped every 2012 to PROOF_OF_ADDRESS_REQUIRED and dropped the provider message. The app showed "Application on hold" with no button, and the user could do nothing.

What changes

  • Name rule ^[A-Za-z]+( [A-Za-z]+)*$ applies in signup, profile edit and the card form, in the app and in the server DTOs (IsLatinName). The forms check on change. Sumsub gets the signup name only when it obeys the rule.
  • The card form is separate from KYC. It always asks for every card detail: first and last name, gender, address with a required postcode, and phone. Nothing is prefilled from KYC. Before each apply the server writes these values to the Sumsub fixedInfo.
  • The last submitted values are stored in the new keysecure_cardholders.profile column (jsonb, nullable; migration 1790500000002). GET returns them, so after a refusal the form opens prefilled. Resend posts the stored profile.
  • A 2012 refusal is classified from its message into NAME_INVALID, VERIFICATION_FAILED, ADDRESS_INVALID, PHONE_INVALID or DETAILS_INVALID. The message itself is never stored or logged; only the code goes into the log.
  • Fail kinds in the app: every fixable reason, including the legacy PROOF_OF_ADDRESS_REQUIRED, shows "Update details". Only VERIFICATION_FAILED and UNIQUE_NO_MISMATCH stay on hold.

Deploy order (important)

  1. Deploy the BE first.
  2. Ship the FE (OTA staging) right after.

Old app builds POST {} or a partial body. The new BE answers that with 400, so on an old build the user cannot apply or retry until the app updates. The new FE tolerates an old BE: profile defaults to null.

Release note: users whose stored name has accents (from signup, or from KYC sync of the ID) must retype it in plain letters before they can save the profile form.

Verification

  • be: the keysecure-cardholder, sumsub, kyc, users, auth and common suites pass (458 tests). yarn build is clean. Every new test was mutation-checked.
  • fe: the cardholder, create-account and update-account suites pass. pnpm type-check and pnpm lint are clean. New tests were mutation-checked.
  • Opus gate: PASS after 2 rounds, with 0 blocking findings. The round-1 should-fixes were fixed: profile nullable default, mode: 'onChange', and word boundaries in classify2012. The Sumsub fixedInfo PATCH on a GREEN applicant was checked by hand on staging (HTTP 200, still GREEN, apply then succeeded).
  • After merge: run one staging E2E through the new code with an accented signup name, then submit "Nguyen Van A" and expect IN_REVIEW.

🤖 Generated with Claude Code

Assignee
Assign to
Reviewers
Request review from
Time tracking
Source branch: feat/cardholder-form-retry