# GMT registration with MiCash

Use `GMT_MiCash360Service.cs` in GMT's backend to replace the legacy MiCash registration/import/balance calls. This client targets the maintained MiCash endpoints. It is not a replacement for GMT's controllers, local authentication or EF user model; wire those callers to the steps below before deploying GMT.

## Configure once

The same names apply to payment and wallet integrations: `urlHost`, `companyUuid`, `apiKey`. The environment names below map to those three values. API permissions come from the issued configuration; do not substitute another organizationâ€™s key.

```env
MICASH_HOST=https://YOUR_MICASH_SERVER
MICASH_COMPANY_UUID=<company UUID copied from Super Admin>
MICASH_API_KEY=<GMT application key copied from Super Admin>
```

Use the **GMT application credential** issued through Organizations â†’ Onboarding access. Existing general merchant keys do not automatically become GMT application keys. MiCash and GMT must use the corresponding configurations in each environment. Local/dev remain on sandbox and production remains on its live configuration.

GMT V2 bank URLs and the separate GMT server-key configuration are retired. Use the maintained MiCash service shown here; it calls the shared provider flow. GMT migration is the next phase after publishing MiCash.

No business ID, account UUID, provider JWT, package name or ASP.NET onboarding session is required in GMT's client configuration. `packageName` is omitted so MiCash resolves its configured channel. Never send the API key to GMT's browser or mobile app.

Copy the C# source into GMT's project. It uses GMT's existing Newtonsoft.Json dependency and namespace `Indisty.Helper`. Remove or rename the old MiCash360Service class so there is only one definition. Register it in GMT's ASP.NET Core startup:

```csharp
builder.Services.AddSingleton(new MiCash360Options {
    UrlHost = builder.Configuration["MICASH_HOST"],
    CompanyUuid = builder.Configuration["MICASH_COMPANY_UUID"],
    ApiKey = builder.Configuration["MICASH_API_KEY"]
});
builder.Services.AddSingleton<MiCash360Service>(sp =>
    new MiCash360Service(sp.GetRequiredService<MiCash360Options>()));
```

The service reuses HttpClient, disables cookies and redirects by default, requires HTTPS and does not disable certificate verification. Constructor errors deliberately do not echo configured values. The optional transport argument is for controlled tests/custom backend setup.

## New customer

Persist a registration draft in GMT before starting. Give it a stable reference such as `GMT-<draft GUID>`, retain the returned onboarding ID, and reuse both after refresh/recovery. Do not generate a new reference each time the user clicks Continue. Controllers must authorize the signed-in GMT user/draft owner before using a saved onboarding ID.

```csharp
var started = await micash.StartRegistration(
    null, draft.MobileNumber, draft.Email,
    termsAccepted: true); // only after actual customer acceptance
draft.MiCashOnboardingId = started.Onboarding.Id;
draft.ExternalReference = started.Onboarding.ExternalReference; // generated by MiCash
// Persist this draft now, before the next screen.

await micash.VerifyRegisterOtp(draft.MiCashOnboardingId, registrationOtp);
await micash.ValidateProfile(draft.MiCashOnboardingId, profile);
await micash.Register(draft.MiCashOnboardingId, profile);
await micash.RequestWalletPinOtp(draft.MiCashOnboardingId);
// Show the separate PIN OTP step; do not reuse registrationOtp.
await micash.SetWalletPin(draft.MiCashOnboardingId, pinOtp, customerPin);
var completed = await micash.CompleteRegistration(
    draft.MiCashOnboardingId, customerPin, termsAccepted: true);
// Save the returned canonical identity and GMT wallet mapping locally.
```

`profile` is a JObject using the maintained MiCash schema, for example for an RSA identity:

```csharp
var profile = JObject.FromObject(new {
    displayName = user.DisplayName,
    email = user.Email,
    photo = user.Selfie, // actual JPEG/PNG data URL
    identity = new {
        identityNumber = user.IdNumber,
        firstNames = user.Name,
        surname = user.Surname,
        maidenName = user.MaidenName,
        genderType = user.Gender, // use the values supported by MiCash
        countryOfBirth = user.CountryOfBirth,
        dateOfBirth = user.BateOfBirth?.ToString("yyyy-MM-dd"),
        photo = user.IdPassportImage // actual JPEG/PNG data URL
    }
});
```

For passports, use `passport` instead of `identity`, with `passportNumber`, `firstNames`, `surname`, `genderType`, `countryOfIssue`, `countryOfCitizenship`, `dateOfIssue`, `dateOfExpiry`, `dateOfBirth`, and `photo`. Never send both identity and passport. Verify the source country/gender values; do not guess or hardcode a person's details. MiCash validates the request and stores encrypted documents and their paths.

## Existing customer / import

If StartRegistration throws `MiCashApiException` with `EXISTING_PROFILE_REQUIRES_LINKING`, save `exception.Onboarding.Id`. Show the customer's existing wallet PIN step and call LinkExistingCustomer then CompleteRegistration. Do not recreate their provider profile.

Alternatively, use the explicit existing-user helper:

```csharp
var imported = await micash.ImportData(
    null, draft.MobileNumber, draft.Email,
    customerPin, termsAccepted: true);
```

This starts/retrieves the same reference, verifies ownership through profile linking and completes the association. It does not create a new person. An unregistered customer returns `EXISTING_CUSTOMER_REQUIRED`; continue the saved draft through the new-customer steps. First-time import without customer PIN proof is not provided.

## Save GMT's mapping

After successful completion, save:

| GMT value | MiCash result |
|---|---|
| Existing `User.EId` | `completed.Customer.EId` |
| Shared account UUID | `completed.Customer.AccountUuid` |
| MiCash onboarding ID | `completed.Onboarding.Id` |
| Associated wallet ID | `completed.Wallet.Wallet.Id` |
| GMT wallet balance | `completed.Wallet.AvailableBalance` |

Find an existing GMT user by returned EId and verify the local draft/owner before updating. Do not generate another EId, copy MiCash's overall account balance, overwrite an existing GMT password, replace local authentication tokens or reactivate a blocked GMT account as part of import. MiCash doesn't return provider JWTs or password hashes.

GMT needs durable storage for the onboarding/account/wallet mapping. Add properties/columns through **GMT's own EF migration**, matching its actual User table; do not run this migration in the MiCash database. Example properties for a partial GMT User model:

```csharp
public Guid? MiCashOnboardingId { get; set; }
public Guid? MiCashAccountUuid { get; set; }
public int? MiCashWalletId { get; set; }
```

Keep externalReference and onboardingId in the registration draft as well, so a failed GMT database save can resume without creating another MiCash/provider account. These columns and the controller wiring are not installed automatically by this downloadable service.

## Balance and wallet operations

```csharp
var wallet = await micash.GetBalance(savedOnboardingId);
var gmtBalance = wallet.AvailableBalance;
```

Resolve savedOnboardingId from the authenticated GMT user's local mapping. This GET sends no request body, cookie, PIN or wallet session. The service checks the response belongs to GMT and rejects a broader MiCash wallet view. Reconcile an old cached WalletAmount against this endpoint rather than treating a locally copied global balance as authoritative.

ListWallets, FindByReference and UnlinkBeneficiary use the maintained integrator routes. Listing verifies its returned rows against the application-scoped status endpoint; this adds a read call per row. Unlink removes integration access and preserves the customer account and financial records.

QuoteBankCashout first verifies the GMT association, obtains a backend wallet session using the customer's PIN, then requests a quote. It does not pay out. Store the returned quote on GMT's backend against the customer/draft, with its WalletSession protected. WalletSession is excluded from JSON serialization; if storing a quote as JSON, preserve the token separately in a protected backend store. Show the bank destination, exact bank amount and fees for customer confirmation. SubmitBankCashout requires that saved quote, a stable withdrawal reference and explicit confirmation. CheckBankCashout checks the existing reference after an uncertain result. Do not accept a quote/session object supplied by the browser as the saved backend quote.

## Error and retry handling

- `MiCashApiException.Error` contains the controlled MiCash error; HttpStatus and an owned onboarding record support recovery.
- HTTP success alone does not mean business success; the service checks `success` and handles the expected 409 existing-profile response.
- No automatic write retries are performed. Timeout/network cancellation after submission can be ambiguous. Use CheckRegistration, ResumeRegistration or CheckBankCashout and reuse the same saved reference. Never recreate or pay again merely because a response was lost.
- GMT controller actions should return deliberate HTTP status codes (e.g. `StatusCode(exception.HttpStatus, ...)`). Your old `new JsonResult(new JsonResponce(500,...))` embeds a code but does not itself set the HTTP response status.
- Never log raw request bodies, PINs, OTPs, document images, API keys, backend wallet sessions or full upstream responses.

## What is not silently carried over

Legacy FundWallet, Pay, Transactions, password reset and no-PIN full-profile import are not reimplemented through invented wallet endpoints. The supplied service covers maintained onboarding, associated balance, listing, reference recovery, unlink and bank cashout. Keep other consumers under explicit review until their approved MiCash contracts are migrated separately.

The client compiles independently and its flow/security checks use fake HTTP responses. GMT's actual controllers, EF migrations, runtime configuration and live provider flow still require integration and verification before handover.


## Unlink and relink with a project key

Configure only host, sponsor company UUID and the project’s own API key. No application header is required. A project key owns its registrations and wallet allocations; using the same sponsor UUID does not make Leo, Kindred and GMT records interchangeable.

For an existing association owned by that project, run Postman 18 Unlink, then 12 Link existing profile using the same onboarding ID, registered mobile number and entered PIN. The returned status progresses UNLINKED → PROFILE_CREATED. Run 07 Open wallet session to obtain a fresh session, then 08 Read balance and 19 List merchant wallets. Old sessions and quotes must be discarded. Preserve cashout references for status/reconciliation; unlink does not cancel submitted withdrawals. The canonical customer E_ID/account UUID stays unchanged.

If the existing record was made using the shared Leo key, a new Kindred key cannot unlink, recover or claim it. Unlink the old association with its owning key only if that is intended. Under Kindred’s new key, clear client IDs/sessions, run 01 Create once for the same customer, save its new project-scoped ID/reference, then run 12 to link the existing customer profile. No new provider profile is created, and old funds or allocations are not transferred automatically. In Kindred, the new-project helper starts this flow; after unlink the page displays Reconnect my Kindred wallet.
