11 KiB
Sign-up Facts intake
Task #46, branch
identity-facts-46. Primary module: identity-facts; secondary modules: auth,
customer creation, runtime, shared database source and Flutter error feedback.
Behavior and boundaries
With api.sendico configured, the product BFF creates the local pending customer
and activation token atomically, then registers an independent individual through
Sendico's public HTTP API and submits the sign-up assertions. It persists the
intake receipt before sending activation email or returning sign-up success.
Activation resend runs the same handoff; activation checks the receipt in its
local transaction, without external I/O. A failed receipt check rolls back token
consumption and leaves the account pending. Existing active customers are not
backfilled or blocked at login.
The adapter consumes these existing Sendico contracts:
POST /individuals/{organization_ref}, empty body, HTTP 200 registration receipt containingsubject_refandregistered_at.POST /identity_facts/{organization_ref}, HTTP 200 immutable intake receipt containingsubmission_ref,subject_refandaccepted_at.
The configured API prefix precedes both paths. See Sendico
individual subjects
and Facts intake.
The only assertions sent are full_name, normalized email and
external_customer_ref (the local customer ID). Passwords, password hashes,
activation tokens, session credentials and device data never enter the adapter.
The input name is retained as a full name, without guessing name parts. No facts
are marked verified; acceptance does not establish identity, KYC completeness,
verification, limits or payout eligibility. The existing KYC preview is unchanged.
Only the BFF owns Sendico credentials and organization scope. Local customer
models have no organization, membership or platform permissions. HTTP DTOs and
mappers stay in providers/sendico; the handoff operates on domain facts and
receipts in identity. Flutter continues through its DTO, mapper, service,
model and provider layers. It never calls Sendico or supplies subject references.
Configuration and rollout
Add this block under api in the product service configuration, retaining
api.notifications:
sendico:
api_url: https://your-sendico-host/api/v1
organization_ref: <provisioned-SXCHNGE-organization-object-id>
source_account_ref: <provisioned-integration-account-object-id>
token_file: /run/sxchnge/sendico-token
Resolve the real IDs from the target Sendico deployment. The integration account
must be a member of the SXCHNGE organization with the existing organizations
update permission; reconciliation GET requests also require read. The token
must be a full, unexpired Sendico access JWT whose id claim is that account.
The adapter inspects claims only to protect the replay namespace; Sendico checks
the signature and authorizes every call. No login, refresh or privileged account
provisioning is performed by this adapter.
Keep the token in Vault at services/bff/sendico, field access_token. Render it
with the deployment's Vault Agent into the configured private runtime file; give
only the BFF policy read access to that KV path. Endpoint, organization and source
IDs remain in service YAML. For the local stack, configure
infra/local/bff/runtime.yml, render to the BFF's existing /run/sxchnge tmpfs,
and extend its Vault Agent policy/template for this provisioned secret. These
deployment values and secret provisioning are deliberately not guessed or
enabled by the example. Do not place tokens in Git, browser builds or shell
history. Refresh the file atomically before expiry using the same Sendico account;
the adapter rereads it on every request. Startup rejects missing, expired or
mismatched credentials and rejects Sendico configuration without notifications.
HTTPS is required except for loopback HTTP test/development endpoints. Redirects are rejected and each HTTP call has a four-second timeout. Non-200 responses, malformed/truncated receipts, invalid identifiers, wrong subjects and unsuccessful envelopes never count as acceptance. Upstream error bodies are not surfaced.
Omitting api.sendico keeps existing local-only behavior. Enabling it also gates
activation for previously pending customers; use activation resend to submit
their saved name/email before confirming their old link. There is no automatic
bulk migration. Changing endpoint, organization or source account changes the
adapter fingerprint and blocks existing bindings for explicit reconciliation.
Token rotation for the same account retains that fingerprint. Plan such changes
as migrations; never reset a binding just to bypass the check.
Persistence, retries and recovery
The local signup_facts collection uses the customer ID as _id, so its unique
primary key coordinates concurrent requests and separate BFF instances. It stores
an immutable sign-up facts snapshot, adapter fingerprint, random registration
request correlation, registration claim, subject and confirmed receipt. It uses
the shared database repository and connection lifecycle with majority writes and
primary majority reads, overriding weaker connection defaults. No external call
runs in a database transaction. Snapshots and bindings contain personal data and
must follow the customer data retention policy; no TTL silently removes replay
state, and customer deletion does not delete the immutable Sendico submission.
Once a subject is stored, intake retries use
sxchnge:signup:<local-customer-id> as source_submission_ref. Sendico's
organization/authenticated-account/source-reference uniqueness returns the
original receipt for that subject. Retries send the saved snapshot even if a
later request changes name or email. Losing the HTTP receipt or a local receipt
write is recoverable by replay. A saved receipt skips subsequent external calls.
Retrying sign-up still requires the original password; active, archived or
otherwise conflicting accounts cannot be resumed as a new customer.
Individual registration is not idempotent. The BFF commits a claim before dispatch and never expires or automatically retries that registration. It cannot prove that a timed-out request, malformed response or failed local binding write did not create a subject. Another request sees an in-progress claim as temporarily unavailable for 30 seconds; an older unresolved claim requires support. The registration caller immediately reports assistance on an uncertain failure. An accepted registration is saved with a short independent context even if the browser disconnects. A crash before dispatch can also require reconciliation. This trades automatic recovery for avoiding duplicate subjects under the current upstream contract; there is no distributed exactly-once guarantee.
For an unresolved registration, operators must stop requests for the affected
customer and inspect the saved registrationRequest plus Sendico identity logs
(X-Request-ID, operation subject_register) under the configured account and
organization. A successful registration's server-issued record_ref identifies
the candidate subject. Confirm it using the organization-scoped individual GET
API before atomically setting the matching local binding's subjectRef and
clearing registering; retain its snapshot, fingerprint and request reference.
If a local binding write failed after receipt, BFF diagnostics also contain the
subject reference. If authoritative records prove no subject was created, an
operator may clear the claim after fixing the dependency. If neither result can
be established, leave it blocked; never register a speculative replacement.
Resume sign-up with the original credentials or activation resend after this
repair. These are controlled operational repairs, not customer API endpoints.
HTTP 503 signup_attention_required maps to typed shared auth and verification
state and localized EN/RU support guidance, including activation resend recovery.
Other intake/receipt failures remain temporary service
failures. Form input is retained, duplicate submissions stay disabled during
loading, and neither failure navigates to an email-sent screen or creates a
session. The existing support channel must be available to handle reconciliation.
Measurement and UX review
The existing auth journey keeps its attempt, retry,
abandonment and 30-minute settled inactivity rules. auth_signup_succeeded now
requires server confirmation of configured intake plus activation queue acceptance;
it remains an intermediate step and email confirmation remains the terminal
onboarding outcome. Sign-up dependency failures and assistance outcomes use the
existing allowlisted unavailable analytics category; verification assistance
uses it too. Retries retain the journey
and operation semantics. No names, emails or provider response bodies are added.
sxchnge_signup_facts_total{outcome} records accepted, replayed,
unavailable and attention_required handoff attempts;
sxchnge_signup_facts_duration_seconds measures their latency. Counts are
attempts, not distinct users. Use unavailable/attention counts divided by total
handoff attempts for operational failure rates; use the existing correlated auth
journey for conversion, duration and abandonment. Structured handoff logs include
local customer and random registration request references for support correlation,
without asserted values, credentials or raw errors. IDs are never metric labels.
The product team uses the distinction to separate retryable intake outages from
registration reconciliation. The default client analytics sink remains in memory;
production retention and cross-device conversion correlation remain deployment
gaps, as documented by the existing journey contract.
Review covers loading suppression, input preservation, success only on confirmed handoff, temporary retry and assistance states, existing navigation, mobile layout, text scaling and live-region error feedback. Focused tests cover these states, the mapped backend error, non-inflated client success, immutable retry payloads, credential rotation, timeout/cancellation/redirect behavior, receipt validation, privacy-safe errors, concurrent durable claims and activation rollback. The tests use disposable local MongoDB and a simulated Sendico HTTP server; they do not create live subjects or send email. Live integration and support operations must be verified against provisioned deployment credentials before enabling the block. The EN/RU error state is exercised at a 390px viewport with 1.5x text scaling; existing keyboard regression tests and a code review of the live-region semantics cover the unchanged controls. No manual browser or screen-reader walkthrough against a live Sendico deployment was performed.