@nokinc-flur/sdk
v3.0.7
Published
Flur Wallet SDK
Downloads
3,849
Readme
@nokinc-flur/sdk
Typed Flur Wallet client aligned to the backend OpenAPI contract.
Install (local dev)
pnpm install --frozen-lockfile
pnpm test
pnpm buildGenerate types (optional)
This repo includes a minimal OpenAPI file at openapi/flur.openapi.json.
pnpm genE2E test against a running backend (optional)
- Start backend locally (see backend README)
- Run (E2E executes when RUN_E2E=1 or FLUR_BASE_URL is set):
FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1 pnpm testContract-focused E2E only:
FLUR_BASE_URL=https://your-oci-gateway.example.com RUN_E2E=1 pnpm test:contractOptional envs for full send-money E2E coverage:
FLUR_E2E_SESSION_TOKEN(required for account/transactions/recipient)FLUR_E2E_RECIPIENT_IDENTIFIER(required for recipient resolve / transfer)FLUR_E2E_SEND_AUTH_TOKEN(required for transfer create)FLUR_E2E_DEVICE_ID(required for transfer create)FLUR_E2E_AMOUNT_MINOR(optional, default100)FLUR_E2E_CURRENCY(optional, defaultNGN)
Generate real E2E auth inputs against deployed backend:
# Step 1: start onboarding and get requestId
pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx
# Step 2: rerun with OTP/silent-auth code (and optional recipient)
pnpm e2e:bootstrap -- --base-url https://your-oci-gateway --phone +23480xxxxxxx --code 123456 --recipient +23481xxxxxxx
# Script prints PowerShell env exports, then run:
pnpm test:contractInternal-admin intake kit
For the MVP controlled-operations workflow, external organisations and merchants should hand Flur structured source data, not production write credentials. The SDK repo includes a local intake helper for generating valid handoff templates and checking obvious schema issues before an in-house operator uses the admin APIs.
pnpm admin:intake -- template --kind bill-organisation
pnpm admin:intake -- template --kind bill-intake
pnpm admin:intake -- template --kind explore-card
pnpm admin:intake -- check --kind bill-intake --payload ./bill-intake.json
pnpm admin:intake -- template --kind explore-card | pnpm admin:intake -- check --kind explore-card --payload -Supported kinds are bill-organisation, bill-intake, and explore-card.
check performs local validation only; backend validation and internal-admin
review remain authoritative. A successful check prints the parsed payload and
the next matching admin command, such as pnpm admin:bills -- create-intake or
pnpm admin:explore -- create-card.
Internal-admin Bills operations CLI
For the MVP controlled-operations workflow, the SDK repo includes a small scriptable admin tool around the Bills organisation, intake, commit, and audit APIs, including bill-level event history. It is intended for in-house operators and automation only; do not hand these credentials to external organisations or merchants.
Required config can be supplied as flags or environment variables:
--base-urlorFLUR_BASE_URL--admin-tokenorFLUR_ADMIN_TOKEN/INTERNAL_ADMIN_TOKEN--admin-user-idorFLUR_ADMIN_USER_ID/INTERNAL_ADMIN_USER_ID; if only backendINTERNAL_ADMIN_USER_IDSis set, the first id is used- optional
--admin-mfa-tokenorFLUR_ADMIN_MFA_TOKEN/ADMIN_MFA_TOKEN - optional
--forwarded-fororFLUR_ADMIN_FORWARDED_FOR
pnpm admin:bills -- list-orgs --status active --env-file ../flur-backend/.env
pnpm admin:bills -- org-events --organisation-id <uuid> --env-file ../flur-backend/.env
pnpm admin:bills -- bill-events --bill-id <uuid> --env-file ../flur-backend/.env
pnpm admin:bills -- list-intakes --status pending --env-file ../flur-backend/.env
pnpm admin:bills -- create-intake --payload ./bill-intake.json --env-file ../flur-backend/.env
pnpm admin:bills -- review-intake --intake-id <uuid> --decision approve --note "source verified" --env-file ../flur-backend/.env
pnpm admin:bills -- commit-intake --intake-id <uuid> --env-file ../flur-backend/.env
pnpm admin:bills -- intake-events --intake-id <uuid> --env-file ../flur-backend/.envcreate-intake expects the same body as the backend API:
{
"payload": {
"userId": "<uuid>",
"organisationId": "<uuid>",
"collectionIntentId": "<uuid>",
"externalReference": "MWB-2026-0001",
"title": "June water bill",
"description": "Monthly utility charge",
"amountKobo": 125000,
"currency": "NGN",
"status": "open",
"dueAtMs": 1782777600000,
"issuedAtMs": 1780272000000,
"metadata": { "batch": "june-2026" }
},
"metadata": { "sourceReference": "secure-upload-2026-06" }
}Internal-admin Explore operations CLI
The SDK repo also includes a scriptable operations tool for the Explore publisher/review workflow. Publisher/account commands use an authenticated account-admin session token. Review and audit commands use internal-admin credentials.
Required config can be supplied as flags or environment variables:
--base-urlorFLUR_BASE_URL- publisher commands:
--session-tokenorFLUR_SESSION_TOKEN/FLUR_ACCESS_TOKEN - review/audit commands:
--admin-tokenorFLUR_ADMIN_TOKEN/INTERNAL_ADMIN_TOKEN - review/audit commands:
--admin-user-idorFLUR_ADMIN_USER_ID/INTERNAL_ADMIN_USER_ID; if only backendINTERNAL_ADMIN_USER_IDSis set, the first id is used - optional
--admin-mfa-tokenorFLUR_ADMIN_MFA_TOKEN/ADMIN_MFA_TOKEN - optional
--forwarded-fororFLUR_ADMIN_FORWARDED_FOR
pnpm admin:explore -- list-account-cards --account-id <uuid> --session-token <token> --base-url http://127.0.0.1:8080
pnpm admin:explore -- create-card --account-id <uuid> --payload ./explore-card.json --session-token <token> --base-url http://127.0.0.1:8080
pnpm admin:explore -- submit-card --account-id <uuid> --card-id <uuid> --session-token <token> --base-url http://127.0.0.1:8080
pnpm admin:explore -- list-review-cards --status pending_review --base-url http://127.0.0.1:8080 --env-file ../flur-backend/.env
pnpm admin:explore -- review-card --card-id <uuid> --decision approve --priority 25 --base-url http://127.0.0.1:8080 --env-file ../flur-backend/.env
pnpm admin:explore -- card-events --card-id <uuid> --base-url http://127.0.0.1:8080 --env-file ../flur-backend/.envcreate-card expects the same account-publisher payload as the backend API:
{
"type": "deal",
"title": "Keke Ride Pass",
"merchantName": "My Keke",
"description": "Save on short city rides with a reusable ride-pass offer.",
"reference": "KEKE30",
"valueLabel": "30% off",
"accentColor": "#0F766E",
"toneColor": "#ECFDF5",
"audience": "all",
"startsAtMs": 1780272000000,
"metadata": { "channel": "merchant_portal" }
}Internal-admin audit export CLI
For review handoff, support, and compliance sampling, the SDK repo includes a read-only audit export tool that bundles selected Bills and Explore event ledgers into one JSON document. It uses the same internal-admin credentials as the Bills and Explore review commands and does not mutate backend state.
Required config can be supplied as flags or environment variables:
--base-urlorFLUR_BASE_URL--admin-tokenorFLUR_ADMIN_TOKEN/INTERNAL_ADMIN_TOKEN--admin-user-idorFLUR_ADMIN_USER_ID/INTERNAL_ADMIN_USER_ID; if only backendINTERNAL_ADMIN_USER_IDSis set, the first id is used- optional
--admin-mfa-tokenorFLUR_ADMIN_MFA_TOKEN/ADMIN_MFA_TOKEN - optional
--forwarded-fororFLUR_ADMIN_FORWARDED_FOR
pnpm admin:audit -- export --bill-id <uuid> --base-url http://127.0.0.1:8080 --env-file ../flur-backend/.env
pnpm admin:audit -- export --organisation-id <uuid> --intake-id <uuid> --case-id bill-review-2026-06 --base-url http://127.0.0.1:8080 --env-file ../flur-backend/.env
pnpm admin:audit -- export --explore-card-id <uuid> --subject "Explore review" --base-url http://127.0.0.1:8080 --env-file ../flur-backend/.envThe export accepts comma-separated ids through --bill-ids,
--organisation-ids, --intake-ids, and --explore-card-ids. The output
contains request metadata, per-ledger event arrays, and event-count summaries;
redirect stdout when a durable review file is needed.
Onboarding flow methods
import { FlurClient } from '@nokinc-flur/sdk';
const client = new FlurClient({ baseUrl: 'https://api.example.com' });
const started = await client.onboardingStart({
phoneE164: '+14155550123',
appInstanceId: 'app-instance-1',
platform: 'ios',
});
// started: { requestId, checkUrl?, expiresInSec, fallback }
const completed = await client.onboardingComplete({
requestId: started.requestId,
code: '123456',
appInstanceId: 'app-instance-1',
});
// completed: { accessToken, refreshToken, userId, restricted, risk_reasons }Auth + device security methods
const registered = await client.registerDevice(
{
userId: completed.userId,
appInstanceId: 'app-instance-1',
platform: 'ios',
networkSignals: {},
},
{ accessToken: completed.accessToken },
);
// { deviceId, fingerprintHash, driftScore, trustState, stepUpRequired }
const refreshed = await client.authRefresh({
userId: completed.userId,
refreshToken: completed.refreshToken,
appInstanceId: 'app-instance-1',
fingerprintHash: registered.fingerprintHash,
});
// { accessToken, refreshToken, stepUpRequired }
await client.pinSet(
{ userId: completed.userId, pin: '123456' },
{ accessToken: completed.accessToken },
);
await client.pinVerify(
{ userId: completed.userId, pin: '123456' },
{ accessToken: completed.accessToken },
);
await client.authLogout(
{ userId: completed.userId, refreshToken: refreshed.refreshToken },
{ accessToken: refreshed.accessToken },
);Send-money methods
// Register per-device signing key for send authorization
await client.registerSendDeviceKey(
{
userId: completed.userId,
deviceId: registered.deviceId,
publicKey: '-----BEGIN PUBLIC KEY-----...',
},
{ accessToken: completed.accessToken },
);
// Obtain challenge and verify signature to get short-lived send token
const challenge = await client.createSendChallenge(
{
userId: completed.userId,
deviceId: registered.deviceId,
},
{ accessToken: completed.accessToken },
);
const send = await client.verifySendChallenge(
{
userId: completed.userId,
deviceId: registered.deviceId,
challengeId: challenge.challengeId,
signature: 'base64-signature',
},
{ accessToken: completed.accessToken },
);
// Resolve recipient and create transfer
const recipient = await client.resolveRecipient(
{ identifier: '+14155550123' },
{ accessToken: completed.accessToken },
);
await client.createTransfer(
{
recipientIdentifier: recipient.normalizedIdentifier,
amountMinor: 5000,
currency: 'NGN',
sendAuthToken: send.sendAuthToken,
},
{
accessToken: completed.accessToken,
deviceId: registered.deviceId,
idempotencyKey: crypto.randomUUID(),
},
);
// Account + transaction endpoints
await client.accountSummary({ accessToken: completed.accessToken });
await client.listTransactions({
accessToken: completed.accessToken,
limit: 20,
});Error code mapping
SDK maps backend error payload code into typed FlurError.code when available:
TOKEN_REPLAYEDSESSION_MISMATCHPIN_INVALIDPIN_LOCKEDPIN_NOT_SETSTEP_UP_REQUIRED
Fallback codes remain:
HTTP_ERROR,NETWORK_ERROR,TIMEOUT,UNKNOWN
Lean prod release workflow (one-person team)
PowerShell script (Windows):
# patch release (recommended for this onboarding addition)
./scripts/release.ps1 -Bump patch
# exact version release (example: 1.0.1)
./scripts/release.ps1 -Version 1.0.1
# include E2E run (uses default OCI API Gateway URL from release script)
./scripts/release.ps1 -Version 1.0.1 -RunE2E
# include E2E run with override URL
./scripts/release.ps1 -Version 1.0.1 -RunE2E -E2EBaseUrl https://your-api.example.com
# publish to npm after all checks pass
./scripts/release.ps1 -Bump patch -Publish
./scripts/release.ps1 -Version 1.0.1 -PublishWhat it does:
- verifies git working tree is clean
- runs
pnpm install --frozen-lockfile - runs
pnpm lint,pnpm typecheck,pnpm test,pnpm build - creates tarball via
npm packand smoke-tests install in a temp project - bumps version (
patch/minor/major) and creates git tag - pushes commit and tag (
git push,git push --tags) - optionally publishes to npm (
-Publish)
Release script aliases:
pnpm release:patch
pnpm release:minor
pnpm release:major