@iips/quasar-sdk
v1.1.1
Published
Official iiips Quasar Node.js SDK
Downloads
63
Readme
IIPS Quasar SDK
Official Node.js SDK for Quasar APIs.
Install
npm install @iips/quasar-sdkQuick Start
import { QuasarClient } from "@iips/quasar-sdk";
const client = new QuasarClient({
apiKey: process.env.QUASAR_API_KEY!,
baseUrl: process.env.QUASAR_BASE_URL // optional
});- Default
baseUrl:https://api-quasar.iips.app/api/v1 - Auth header used by SDK:
Authorization: Bearer <apiKey>
Core Modules
client.payments- payment intents and Paystack card / hosted checkout flowsclient.endUsers- dedicated virtual accounts (bank transfer) for any tenant using parent/child wallet owners (school, retail, service, etc.)client.school- deprecated alias ofendUsers(studentId/schoolIdnaming)client.transfers- payout / sweep transfers to external bank accountsclient.wallets- wallet balances and transactionsclient.webhooks- webhook endpoint registration and signature verification
Choosing a flow: use client.payments when the payer pays by card / Paystack redirect. Use client.endUsers.createVirtualAccount when you issue a static account number for bank transfers (NUBAN-style) and track inflows against a child wallet owner.
Admin API — Invify device fleet (v1.1.0+)
Use QuasarAdminClient with an admin JWT (from Quasar admin login), not sk_* API keys.
import { QuasarAdminClient } from "@iips/quasar-sdk";
const admin = new QuasarAdminClient({
adminAccessToken: process.env.QUASAR_ADMIN_JWT!,
baseUrl: process.env.QUASAR_BASE_URL, // optional; same default as QuasarClient
vertical: "invify_retail" // optional; sets X-IIPS-Vertical on every request
});
const { data, meta } = await admin.invify.listDevices({
page: 1,
pageSize: 100,
vertical: "invify_retail",
tenantId: undefined
});
for (const row of data) {
console.log(row.id, row.agent?.business_name, row.tenant?.slug, row.status);
}See also: iips-pay/docs/INVIFY_DEVICE_FLEET_API.md in this monorepo for full field reference.
Payments Example
const intent = await client.payments.createIntent({
amount: "50000.00",
currency: "NGN",
customerId: "cust_123",
metadata: { orderId: "INV-1001" }
});Virtual accounts (dedicated bank transfer) — integration
Use this when end users pay by bank transfer into a dedicated virtual account tied to a child wallet owner (student, customer, member). Use client.endUsers with generic childId / parentId — the HTTP route is unchanged (/school/students/{id}/...) for backward compatibility.
API key scopes
- Create / provision:
virtual_accounts:write - Read VA, wallet, inflow list:
virtual_accounts:read
Tenant is determined by the API key. Do not send a separate tenant id in the body for this route.
Identity fields (required on every create)
Quasar forwards email, firstName, and lastName to the PSP when creating the customer behind the dedicated account. Send the real account-holder identity for all modes (school, retail, service); omitting or placeholder values can break name validation and reconciliation.
| Field | Required | Notes |
|--------|----------|--------|
| currency | Yes | ISO 4217, three letters (e.g. NGN). |
| email | Yes | Account-holder email. |
| firstName | Yes | Given name, 1–64 characters. |
| lastName | Yes | Family name, 1–64 characters. |
| parentId (SDK) | Yes | Parent wallet-owner UUID — school, merchant, or platform that may receive the optional split share. Sent as merchantWalletOwnerId on the wire. |
| parentShareBps (SDK) | No | Basis points 0–10000 on the parent share. Default 0 (100% to the child). Maps to counterpartyShareBps. |
| preferredBankCode / paystackPreferredBank | No | Preferred issuing bank code for the active VA provider. When the provider is Paystack, this is a Paystack bank slug (e.g. wema-bank). Ignored for mock or other providers until wired. Prefer preferredBankCode; paystackPreferredBank is a deprecated alias. |
| metadata | No | Strongly recommended: your own ids (customerId, schoolId, orderId, etc.) for support and tracing. |
Parent / child (SDK)
| SDK field | Meaning |
|-----------|---------|
| childId | Child wallet-owner UUID — end-user who owns the VA (student, customer, member). Same as {id} in POST /api/v1/school/students/{id}/virtual-account. |
| parentId | Parent wallet-owner UUID — counterparty that may receive a split (school, merchant, platform). |
Legacy client.school still accepts studentId + schoolId / merchantWalletOwnerId and delegates to endUsers.
Idempotency
Provisioning is idempotent per (tenant, end-user wallet owner, currency): a second call returns the same VA. For network retries, pass an Idempotency-Key:
await client.endUsers.createVirtualAccount(
{ childId, parentId, currency, email, firstName, lastName },
{ idempotencyKey: `va:${childId}:${currency}` }
);Raw HTTP (reference)
POST {baseUrl}/school/students/{endUserWalletOwnerId}/virtual-account with Authorization: Bearer <sk_*> and a JSON body matching the table above. Default baseUrl includes /api/v1.
Response shape
createVirtualAccount returns both accountNumber (camelCase) and account_number (snake_case) for compatibility, plus bankName, provider, reference, currency. Persist at least reference, account_number, bankName, and currency in your database.
Retail / service example
const va = await client.endUsers.createVirtualAccount({
childId: "660e8400-e29b-41d4-a716-446655440001",
parentId: "6ba7b813-9dad-11d1-80b4-00c04fd430c8",
currency: "NGN",
email: "[email protected]",
firstName: "Ada",
lastName: "Buyer",
parentShareBps: 0,
preferredBankCode: "wema-bank",
metadata: {
customerId: "660e8400-e29b-41d4-a716-446655440001",
merchantId: "6ba7b813-9dad-11d1-80b4-00c04fd430c8"
}
});
console.log(va.accountNumber, va.bankName, va.reference);School onboarding example (same contract)
When a student record exists in your system, provision the VA with the same required identity fields:
const va = await client.endUsers.createVirtualAccount({
childId: student.id,
parentId: student.schoolId,
currency: "NGN",
email: student.email,
firstName: student.firstName,
lastName: student.lastName,
metadata: {
studentId: student.id,
schoolId: student.schoolId
}
});
await db.studentVirtualAccounts.upsert({
studentId: student.id,
schoolId: student.schoolId,
quasarAccountId: va.reference,
accountNumber: va.account_number,
bankName: va.bankName
});Related client.endUsers calls
getVirtualAccount(childId, { currency })— fetch existing VAlistPayments(childId, { page, pageSize })— VA inflow rows (data+meta)getWallet/getBalance— wallet snapshot for that child wallet owner
More context for operators: sdk/CLIENT_INTEGRATION_GUIDE.md (checklist, errors, webhooks).
Webhook Signature Verification
const ok = client.webhooks.verifySignature(
rawPayload,
headers["x-quasar-signature"] as string,
process.env.QUASAR_WEBHOOK_SECRET!
);Transfers (Payout / Sweep)
const transfer = await client.transfers.create({
amount: "50000.0000",
currency: "NGN",
reference: "trf_ref_20260428_001",
destination: {
account_number: "0123456789",
bank_code: "058",
account_name: "IIPS SCHOOL ACCOUNT"
},
metadata: {
tenantId: process.env.QUASAR_TENANT_ID!,
schoolId: "550e8400-e29b-41d4-a716-446655440000"
}
});
const latest = await client.transfers.get(transfer.reference);
console.log(latest.status); // PENDING | PROCESSING | SUCCESS | FAILEDErrors and Retries
- SDK retries transient failures internally (network errors and
408/429/5xx). - Write operations should include an idempotency key when possible.
await client.payments.createIntent(
{ amount: "50000.00", currency: "NGN" },
{ idempotencyKey: "intent:order:INV-1001" }
);Scripts (SDK Workspace)
npm run generate:sdk- regenerate OpenAPI-generated client from../swagger.public.jsonnpm run build- build ESM + CJS bundles with type declarationsnpm run test- run SDK tests
Additional Docs
- Integration guide:
sdk/CLIENT_INTEGRATION_GUIDE.md
Publishing Notes
For GitHub Actions publishing:
- Create npm automation token.
- Add repo secret
NPM_TOKEN. - Ensure npm access for package
@iips/quasar-sdk.
Manual publish from the sdk/ directory (runs codegen, build, and tests via prepublishOnly):
cd sdk
npm ci
npm publish --access publicRequires npm login (or NPM_TOKEN in ~/.npmrc) for the @iips scope.
