Skip to main content

JavaScript SDK — Setup & Integration

The Handpoint JavaScript SDK (@handpoint/cloud-js-sdk) wraps the Handpoint Cloud REST API and Pusher WebSocket layer into a single npm package. Your server or browser application calls methods like hp.sale() and hp.refund(), and the SDK handles authentication, command routing, and result delivery transparently.

Supported environments: Node.js 16+ and any modern browser (via webpack bundle).
Hardware: PAX SmartPOS terminals (A920, A920 Pro, and equivalents) registered in the Handpoint TMS.
PCI scope: Out of scope — card data is captured directly by the terminal, never by your application.


Prerequisites​

RequirementDetail
Node.js16.0 or higher
Cloud API KeyObtain from the Handpoint TMS or your Handpoint integration engineer
PAX SmartPOS terminalRegistered and connected to Wi-Fi, running the Handpoint Payments App

Step 1 — Install​

npm install @handpoint/cloud-js-sdk

Note: If the package is not yet on the public npm registry, install from the internal Nexus feed — contact your Handpoint engineer for credentials.


Step 2 — Import​

Node.js​

const hp = require('@handpoint/cloud-js-sdk');

Or with ES modules:

import hp from '@handpoint/cloud-js-sdk';

Browser (webpack bundle)​

Build the bundle with the npm script provided in the package, then include it:

<script src="dist/handpoint-bundle.js"></script>

The Handpoint class is a singleton — multiple imports return the same instance.


Step 3 — Initialise​

Call hp.init() once at application start. It authenticates the SDK, selects the correct Cloud endpoints, and returns a Promise that resolves with the list of terminals registered to your API key.

const API_KEY = 'your-cloud-api-key';

const devices = await hp.init(API_KEY);
console.log('Registered terminals:', devices);
// devices[n].device_name === '<serialNumber>-<terminalType>'

Production vs development​

// Production (default)
await hp.init(API_KEY);

// Development / staging
await hp.init(API_KEY, true); // second argument true = dev endpoints

With a pending-EOT callback​

If a transaction result may arrive after a network interruption, register a callback so late results are never lost:

function onPendingEot(result) {
// A result arrived for a transaction whose Promise already timed out
console.log('Late result:', result.finStatus, result.eFTTransactionID);
// Persist this to your database — do not treat it as a new transaction
}

await hp.init(API_KEY, false, onPendingEot);

Step 4 — Connect to a terminal​

Use the device_name from the list returned by init(). The format is <serialNumber>-<terminalType> (e.g., 1851075595-PAXA920PRO).

const deviceName = devices[0].device_name;
await hp.connect(deviceName);
console.log('Connected to', deviceName);

One connection at a time. Call connect() for the terminal the current operator session is using. Call disconnect() before switching to a different terminal.


Step 5 — Run a sale​

Amounts are always in the smallest currency unit — cents for USD, pence for GBP, etc. Never pass a float.

const amount = 1250; // $12.50 USD
const currency = 'USD';

const { transactionReference, transactionResult } = hp.sale(amount, currency);

// Persist transactionReference immediately — before awaiting the result
await db.savePendingTransaction(transactionReference);

const result = await transactionResult;

switch (result.finStatus) {
case 'AUTHORISED':
console.log('Approved — receipt:', result.customerReceipt);
break;
case 'DECLINED':
console.log('Declined');
break;
case 'CANCELLED':
case 'FAILED':
case 'NO_CONNECTION':
console.log('Not charged:', result.errorMessage);
break;
}

Why save transactionReference before awaiting?​

transactionReference is generated client-side before the card is presented. If your process crashes or the network drops after the card is tapped but before the result arrives, you can recover the final status by polling getTransactionStatus(transactionReference). See Transaction Recovery — JavaScript SDK.


Step 6 — Optional: status callback​

Pass a fourth argument to receive intermediate status updates (card presented, processing, etc.) during the transaction:

function onStatus(statusInfo) {
// statusInfo.status: 'WaitingForCard', 'CardTapped', 'Processing', ...
console.log('Status:', statusInfo.status);
}

const { transactionReference, transactionResult } = hp.sale(
1250, 'USD', {}, onStatus
);

The status callback is not the final result. Always await transactionResult for the authorisation outcome.


All financial operations​

Every financial method follows the same return shape: { transactionReference, transactionResult }.

// Refund — standalone (no card on file)
hp.refund(500, 'USD');

// Linked refund — tied to a previous sale
hp.refund(500, 'USD', 'original-eft-txn-id');

// Void a sale (reversal)
hp.saleReversal(1250, 'USD', 'original-eft-txn-id');

// Void a refund
hp.refundReversal(500, 'USD', 'original-eft-txn-id');

// Pre-authorisation (hold)
const { transactionResult: preAuthResult } = hp.preAuthorization(5000, 'USD');
const preAuth = await preAuthResult;
const preAuthTxnId = preAuth.eFTTransactionID;

// Capture a pre-auth
hp.preAuthorizationCapture(5000, 'USD', preAuthTxnId);

// Increase a pre-auth hold before capture — amount is the delta to add, not the new total
hp.preAuthorizationIncrease(1000, 'USD', preAuthTxnId);

// Void a pre-auth
hp.preAuthorizationReversal(preAuthTxnId);

// Mail-order / telephone-order sale (card data entered on terminal)
hp.moToSale(1250, 'USD');

// Tokenise card without charging
hp.tokenizeCard();

// Sale + tokenise in one terminal interaction
hp.saleAndTokenization(1250, 'USD');

Pre-auth adjustments are cumulative deltas and always reference the original pre-auth ID — see the Pre-Authorization Guide.

Tip adjustment​

tipAdjustment() returns a Promise directly (no transactionReference), since the tip is adjusted post-authorisation via the REST back-office API:

// Adjust settled amount — tipAmount in smallest currency unit
const status = await hp.tipAdjustment(200, 'eft-txn-id');
console.log('Tip status:', status.finStatus);

Transaction status query​

Query the Cloud for the current status of any transaction by reference. Use this for recovery after a network interruption:

const status = await hp.getTransactionStatus('transactionReference-uuid');
console.log(status.finStatus); // 'AUTHORISED', 'DECLINED', 'IN_PROGRESS', ...

See Transaction Recovery — JavaScript SDK for the full recovery algorithm.


Batch operations​

Available as standalone back-office REST calls — no terminal connection required:

// Batch summary for a device
const summary = await hp.batchSummary(batchNumber, deviceType, serialNumber);

// Batch detail (individual transaction list)
const detail = await hp.batchDetail(batchNumber, deviceType, serialNumber);

// Close a batch
const closed = await hp.closeBatch(batchNumber, deviceType, serialNumber);

Terminal utility operations​

// List registered terminals (re-fetches from TMS)
const terminals = await hp.listDevices();

// Ping the connected terminal to verify it is online
await hp.pingDevice();

// Cancel a transaction in progress
await hp.stopCurrentTransaction();

// Disconnect from the terminal
await hp.disconnect(deviceName);

// Trigger firmware update on the terminal
hp.update();

// Print a receipt on the terminal printer
hp.printReceipt(receiptHtmlString);

Complete minimal example​

const hp = require('@handpoint/cloud-js-sdk');

async function runPayment() {
// 1. Initialise
const devices = await hp.init('YOUR_API_KEY');
if (!devices.length) throw new Error('No terminals registered');

// 2. Connect
const deviceName = devices[0].device_name;
await hp.connect(deviceName);

// 3. Sale
const { transactionReference, transactionResult } = hp.sale(1000, 'USD');
console.log('Reference:', transactionReference); // save this before awaiting

const result = await transactionResult;
console.log('Final status:', result.finStatus);

// 4. Disconnect
await hp.disconnect(deviceName);
}

runPayment().catch(console.error);

Troubleshooting​

SymptomLikely cause
init() rejects with 403Invalid or expired API key
connect() rejects with 403Pusher auth failed — check API key and terminal registration in TMS
transactionResult never resolvesTerminal offline or Pusher channel subscription failed
listDevices() returns emptyNo terminals registered for this API key
Status callback fires but transactionResult never resolvesNetwork dropped after card tap — use getTransactionStatus() to recover

Next steps​