# Check a financier against APRA's list on a server

What you build: a server function that takes the ABN a business gives you for its financier and checks it against the list of registered financial corporations that APRA publishes. It answers "registered" (with the category), "exempted" (listed as exempted from registration, which is not registered), "not on the list" or "the list did not answer". A miss says only that the ABN is not on APRA's page; it is not proof of anything else.

## Before you start: a development app, then a production app

1. `npx comms-id init` makes a development key in `.env.local` and prints its public key. In the Companion App, register it as a development app, enable **APRA list of registered financial corporations** for it, and put the app id in `.env.local` as `COMMS_ID_CLIENT_ID`. This key reaches only the TEST service (`https://api-test.comms.id`): the same API, errors and authentication as LIVE, with synthetic replies.
2. When it works in TEST, `npx comms-id key --worker <your worker>` makes the production key straight into your server's `COMMS_ID_PRIVATE_JWK` secret and prints the public key; register it as a production app with the product enabled. Going live changes only the environment and the key: nothing in the code below.

A `.env.local` never holds a production key, and a browser never holds any key.

## The check

`pnpm add @comms-id/apra-list-registered-financial-corporations`

`check-financier.ts`:

```ts
// Check a financier's ABN against APRA's published list on your server. An exempted corporation
// is never reported as registered, a miss says only "not on the list", and an outage is never a miss.
import {
  type ApraListRegisteredFinancialCorporationsClient,
  outcomeOf,
} from "@comms-id/apra-list-registered-financial-corporations";

const ABN = /^\d{11}$/;
const SPACES = /\s+/g;

export type FinancierCheck =
  | {
      readonly listed: "registered" | "exempted";
      readonly name: string;
      readonly category: string;
      readonly sourceDate: string;
      readonly attribution: string;
    }
  | { readonly listed: false; readonly reason: "invalid" | "not-listed" | "unavailable"; readonly message: string };

export const checkFinancier = async (
  input: string,
  apra: ApraListRegisteredFinancialCorporationsClient
): Promise<FinancierCheck> => {
  const abn = input.replace(SPACES, "");
  if (!ABN.test(abn)) {
    return { listed: false, reason: "invalid", message: "An ABN has 11 digits." };
  }
  try {
    const reply = await apra.lookup({ abn });
    const fact = reply.facts[0];
    if (reply.outcome === "no-match" || fact === undefined) {
      // Not on APRA's published page. That says nothing else about the business.
      return { listed: false, reason: "not-listed", message: reply.meaning ?? "Not on APRA's published list." };
    }
    return {
      listed: fact.data.status,
      name: fact.data.name,
      category: fact.data.categoryLabel,
      sourceDate: fact.sourceDate,
      attribution: fact.attribution,
    };
  } catch (error) {
    if (outcomeOf(error) === "source-unavailable") {
      return { listed: false, reason: "unavailable", message: "The list did not answer; try again later." };
    }
    throw error;
  }
};
```

Call it with a server client:

```ts
import { createApraListRegisteredFinancialCorporationsClient } from "@comms-id/apra-list-registered-financial-corporations/server";

const apra = createApraListRegisteredFinancialCorporationsClient({
  clientId: process.env.COMMS_ID_CLIENT_ID ?? "",
  privateJwk: JSON.parse(process.env.COMMS_ID_PRIVATE_JWK ?? "{}"),
  environment: "test", // Going live: "live", with the production key.
});
const result = await checkFinancier("45 114 248 458", apra);
```

Keep `sourceDate` and `attribution` with the result you store: they say which edition of APRA's page answered, and the licence requires the attribution.

## TEST first

In TEST, `45114248458` is a registered corporation in category D, `10000000032` an exempted one, `10000000000` is not on the list, `10000000064` is an outage and `10000000096` the fair-use limit. All TEST replies are fictional and say so in their attribution.

## In a browser

The same lookup is a finished field: `@comms-id/apra-list-registered-financial-corporations-react` for React, or the `<comms-id-apra-list-registered-financial-corporations>` element from `cdn.comms.id`, both through your own relay route (see the [Charity guide](/docs/guides/charity) for the relay and its signer). The field shows an exempted corporation as exempted, never as registered.

## LIVE

Use `environment: "live"` and the production key. Reference: [APRA list API](/docs/reference/apra-list-registered-financial-corporations). The attribution in each LIVE reply (APRA, CC BY 4.0) is part of the licence: do not hide it.
