Node.js SDK

Official Node.js SDK for Scan & Pay — accept PayTo PayID payments via QR code from any Node.js environment (Express, NestJS, Next.js, etc.). Includes React components for the frontend.

Install

npm install @scanandpay/node

Requires Node.js 18.0.0 or higher.

Quickstart (Backend)

import { ScanAndPay } from '@scanandpay/node';

const client = new ScanAndPay(
  process.env.SCANANDPAY_MERCHANT_ID!,
  process.env.SCANANDPAY_API_SECRET!,
  process.env.SCANANDPAY_WEBHOOK_SECRET, // optional
);

// 1. Create a session at checkout. Amount is float dollars.
const session = await client.createSession({
  amount: 19.90,                             // $19.90
  platformOrderId: 'order_456',
  payId: 'merchant@example.com.au',
  merchantName: 'Acme Coffee',
});

// 2. Verify and consume webhooks (Express example).
import { WebhookSignatureError } from '@scanandpay/node';

app.post('/webhooks/scanandpay', async (req, res) => {
  try {
    const signature = req.headers['x-scanpay-signature'] as string;
    const event = client.webhooks.verify(signature, req.body);

    if (event.status === 'confirmed') {
      // Mark order paid using event.order_id, event.tx_id, ...
    }

    res.json({ received: true });
  } catch (err) {
    if (err instanceof WebhookSignatureError) {
      return res.status(401).send('Webhook verification failed');
    }
    throw err;
  }
});

Quickstart (React)

The Node.js SDK includes a React component for rendering the QR checkout widget on your frontend.

import { ScanAndPayCheckout } from '@scanandpay/node/react';

function CheckoutPage({ session }) {
  return (
    <ScanAndPayCheckout
      session={session}
      pollUrl="/api/scanandpay/status"
      onSuccess={(sessionId) => { window.location.href = '/thank-you'; }}
      theme="light"
    />
  );
}

Amount format

amount is always float dollars (e.g. 19.90 for $19.90). This matches the Scan & Pay API directly — no multiplication or division needed.

await client.createSession({ amount: 19.90, ... });     // ✓ $19.90
await client.createSession({ amount: 0.50, ... });     // ✓ $0.50
await client.createSession({ amount: 1000.00, ... });  // ✓ $1,000.00
await client.createSession({ amount: -1, ... });       // ✗ ValidationError
await client.createSession({ amount: 0, ... });        // ✗ ValidationError

For display, use session.amount directly:

const display = session.amount.toFixed(2);  // "19.90"

Error handling

All thrown errors extend ScanAndPayError.

ClassWhen
ValidationErrorBad input rejected before any HTTP call.
AuthenticationErrorAPI rejected API Secret (rotate your keys).
ApiErrorNon-2xx response from the API.
NetworkErrorTransport failure after exhausting retries.
WebhookSignatureErrorWebhook signature or timestamp check failed.

Resources