# Bilingual Address Handler for Registration

**Date**: 2026-03-03
**Status**: Design

## Problem

Registration flows use `addressFormSchema` (simple English-only: address1, address2, city, state, postcode). The bilingual address infrastructure exists (`bilingualAddressSchema`, `prepareWhmcsAddressFields`, `prepareSalesforceContactAddressFields`, `JapanAddressForm`, Japan Post API integration) but isn't wired into signup workflows.

Salesforce receives English address data when it should receive Japanese. Address transformation logic is scattered inline across workflows.

## Requirements

| Outcome             | SF Write                        | WHMCS Write                     | Address Input                   |
| ------------------- | ------------------------------- | ------------------------------- | ------------------------------- |
| A (New Customer)    | Japanese (at signup)            | English (at signup)             | JapanAddressForm                |
| B (SF-Only)         | Japanese (at eligibility check) | English (at account completion) | JapanAddressForm at eligibility |
| C (WHMCS Migration) | None                            | None                            | None                            |
| D (Portal Exists)   | None                            | None                            | None                            |

## Key Decisions

1. **Japan Post API postal code lookup** auto-populates both JA and EN fields on the frontend.
2. **Always re-derive EN fields from postal code** via Japan Post API on the backend when writing to WHMCS. One code path regardless of whether data is in Redis (fresh) or Salesforce (days later). Avoids Redis TTL dependency.
3. **Centralized `AddressWriterService`** in BFF orchestrates all address writes.
4. **`bilingualAddressSchema` replaces `addressFormSchema`** in all signup request/session/handoff schemas.
5. **`JapanAddressForm` replaces simple address fields** in all signup frontend forms.

## Architecture

### AddressWriterService (BFF)

Location: `apps/bff/src/modules/address/address-writer.service.ts`

```
AddressWriterService
  Dependencies: JapanPostFacade, SalesforceAccountService

  writeToSalesforce(sfAccountId, bilingualAddress)
    → prepareSalesforceContactAddressFields(address) → SF Contact update
    → Writes: MailingStreet (JA town+street), MailingCity (JA), MailingState (JA), BuildingName__c, RoomNumber__c

  resolveAndPrepareWhmcsAddress(postalCode, townJa?, streetAddress, buildingInfo)
    → Japan Post API lookup by postal code
    → Match correct result using townJa (for multi-match postal codes)
    → prepareWhmcsAddressFields() with resolved EN fields
    → Returns: WhmcsAddressFields (address1, address2, city, state, postcode, country)
```

### Schema Changes

Replace `addressFormSchema` with `bilingualAddressSchema` in:

| Schema                               | File                           | Change                                                                                                     |
| ------------------------------------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `signupWithEligibilityRequestSchema` | `domain/get-started/schema.ts` | `address: addressFormSchema` → `address: bilingualAddressSchema`                                           |
| `completeAccountRequestSchema`       | `domain/get-started/schema.ts` | `address: addressFormSchema.optional()` → `address: bilingualAddressSchema.optional()`                     |
| `getStartedSessionSchema`            | `domain/get-started/schema.ts` | `address: addressFormSchema.partial().optional()` → `address: bilingualAddressSchema.partial().optional()` |
| `guestHandoffTokenSchema`            | `domain/get-started/schema.ts` | `address: addressFormSchema.partial().optional()` → `address: bilingualAddressSchema.partial().optional()` |
| `verifyCodeResponseSchema` prefill   | `domain/get-started/schema.ts` | `address: addressFormSchema.partial().optional()` → `address: bilingualAddressSchema.partial().optional()` |
| `guestEligibilityRequestSchema`      | `domain/get-started/schema.ts` | Remove `bilingualEligibilityAddressSchema`, use `bilingualAddressSchema`                                   |

### Workflow Changes

**NewCustomerSignupWorkflowService (Outcome A)**:

- Accept `bilingualAddressSchema` from request
- After SF account creation: `addressWriter.writeToSalesforce(sfAccountId, address)` → JA to SF
- For WHMCS client: `addressWriter.resolveAndPrepareWhmcsAddress(postcode, townJa, streetAddress, building)` → EN to WHMCS

**GuestEligibilityWorkflowService (Outcome B - eligibility check)**:

- Refactor inline SF address write (lines 103-113) to use `addressWriter.writeToSalesforce()`
- Store bilingual address in handoff token (for B1 immediate flow)

**SfCompletionWorkflowService (Outcome B - account completion)**:

- Resolve address from session (may have bilingual data if B1, or JA-only + postcode if B2)
- `addressWriter.resolveAndPrepareWhmcsAddress(postcode, townJa, streetAddress, building)` → EN to WHMCS

### Frontend Changes

Replace simple address fields with `JapanAddressForm` component in:

- Signup with eligibility form (Outcome A)
- Guest eligibility form (Outcome B)
- Forms submit `bilingualAddressSchema` shape

No address form needed for:

- Complete account (Outcome B) — address from session
- WHMCS migration (Outcome C) — no address needed
- Portal exists (Outcome D) — redirect to login

### Data Flow Diagrams

**Outcome A (New Customer)**:

```
User → JapanAddressForm → postal code lookup → auto-fill JA+EN
     → Submit (bilingualAddress) → BFF
     → addressWriter.writeToSalesforce(sfAccountId, address) → SF gets JA
     → addressWriter.resolveAndPrepareWhmcsAddress(postcode, townJa, ...) → Japan Post API → WHMCS gets EN
```

**Outcome B1 (SF-Only, immediate)**:

```
User → JapanAddressForm (guest eligibility) → Submit (bilingualAddress) → BFF
     → addressWriter.writeToSalesforce(sfAccountId, address) → SF gets JA
     → Store bilingual in handoff token → Redis (30 min TTL)
     → User verifies email → complete account
     → addressWriter.resolveAndPrepareWhmcsAddress(postcode, townJa, ...) → Japan Post API → WHMCS gets EN
```

**Outcome B2 (SF-Only, returns days later)**:

```
[Days ago] Guest eligibility → SF got JA address + postal code
[Now] User verifies email → system detects SF_UNMAPPED
     → Prefill from SF: postal code, JA address fields
     → addressWriter.resolveAndPrepareWhmcsAddress(postcode, townJa, ...) → Japan Post API → WHMCS gets EN
```

## Files to Modify

### Domain (packages/domain/)

- `get-started/schema.ts` — Replace addressFormSchema references with bilingualAddressSchema
- `address/schema.ts` — May need minor additions for the resolve flow

### BFF (apps/bff/)

- **New**: `modules/address/address-writer.service.ts` — Centralized address write service
- `modules/address/address.module.ts` — Export AddressWriterService
- `modules/auth/infra/workflows/new-customer-signup-workflow.service.ts` — Use AddressWriterService
- `modules/auth/infra/workflows/guest-eligibility-workflow.service.ts` — Use AddressWriterService
- `modules/auth/infra/workflows/sf-completion-workflow.service.ts` — Use AddressWriterService
- `modules/auth/infra/workflows/steps/create-whmcs-client.step.ts` — Accept WhmcsAddressFields
- `modules/auth/infra/workflows/verification-workflow.service.ts` — Prefill bilingual address from SF

### Portal (apps/portal/)

- Signup with eligibility form — Replace address fields with JapanAddressForm
- Guest eligibility form — Ensure JapanAddressForm is used, submits bilingualAddressSchema
- API layer — Update request types to match new schemas

## Risks

- **Japan Post API availability**: If API is down during WHMCS write, registration fails. Mitigation: retry with backoff (existing retry.util), graceful error message.
- **Multi-match postal codes**: Some postal codes return multiple town entries. Mitigation: match using `townJa` from the stored/submitted address.
- **Schema migration**: Changing session/handoff schemas could affect in-flight registrations. Mitigation: make new fields optional, handle old format gracefully during rollout.
