npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@danidoble/webserial-pinpad

v1.0.0

Published

A strongly-typed, event-driven USB pinpad driver for the Web Serial API, built on top of webserial-core.

Readme

@danidoble/webserial-pinpad

A strongly-typed, event-driven pinpad driver built on top of webserial-core.

Handles the serial connection, binary handshake, auto-reconnect, DUKPT key injection, EMV card reading, and communication with the MIT payment gateway — so you only deal with clean, typed events and simple async methods.

Compatible with Verifone and Ingenico terminals. Not tied to a single transport: swap in the WebUSB, Web Bluetooth, or WebSocket provider from webserial-core, or implement your own SerialProvider for any platform.

npm version license


Requirements

  • webserial-core ^2.1.0 (peer dependency)
  • A browser that exposes one of the following transport APIs:
    • Web Serial API — Chrome / Edge 89+ (default, no extra setup)
    • WebUSB — Chrome / Edge (via WebUsbProvider)
    • Web Bluetooth — Chrome / Edge (via createBluetoothProvider, Nordic UART Service)
    • WebSocket — any environment (via createWebSocketProvider + a bridge server)
    • Custom — any platform via your own SerialProvider implementation
  • A MIT payment gateway account (username + password) for gateway operations

Installation

# npm
npm install @danidoble/webserial-pinpad webserial-core

# pnpm
pnpm add @danidoble/webserial-pinpad webserial-core

# yarn
yarn add @danidoble/webserial-pinpad webserial-core

# bun
bun add @danidoble/webserial-pinpad webserial-core

webserial-core is a peer dependency — it must be installed alongside this package.


Quick start

import { PinPad } from '@danidoble/webserial-pinpad';

const pinpad = new PinPad({
  username: 'YOUR_MIT_USER',
  password: 'YOUR_MIT_PASSWORD',
  environment: 'production',
  filters: [{ usbVendorId: 0x0801 }], // optional USB filter
});

// Serial lifecycle events
pinpad.on('serial:connecting',   () => console.log('Opening port…'));
pinpad.on('serial:connected',    () => console.log('Port open'));
pinpad.on('serial:disconnected', () => console.log('Disconnected'));

// PinPad events
pinpad.on('pinpad:connected',        () => console.log('PinPad ready'));
pinpad.on('pinpad:processing-card',  ({ waiting }) => console.log('Processing…', waiting));
pinpad.on('pinpad:read-card',        (data) => console.log('Card read:', data.maskPan));
pinpad.on('pinpad:error',            (err)  => console.error(`[${err.error}] ${err.message}`));
pinpad.on('pinpad:print',            (evt)  => console.log('Print event:', evt.type));
pinpad.on('pinpad:dukpt',            (evt)  => console.log('DUKPT:', evt.status));

// Opens a port picker dialog (requires a user gesture)
await pinpad.connect();

// ── Sale ────────────────────────────────────────────────────────
const result = await pinpad.sendSale({ amount: 150.00, reference: 'ORDER001' });
if (result.approved) {
  console.log('Approved:', result.object);
} else {
  console.error('Declined:', result.message);
}

// ── Cancel / void ───────────────────────────────────────────────
await pinpad.cancelPurchase({
  amount: 150.00,
  authorization: 'ABC123',  // 6-char alphanumeric
  folio: '123456789',       // 9-digit operation number
});

// ── Re-print voucher ────────────────────────────────────────────
await pinpad.rePrint({ folio: '123456789' });

// ── Consult transaction ─────────────────────────────────────────
const tx = await pinpad.consult({ reference: 'ORDER001' });
console.log(tx);

Serial settings

The constructor pre-configures the following defaults — no extra setup needed:

| Setting | Value | | ------------------- | ------------------------- | | Baud rate | 19 200 | | Data bits | 8 | | Stop bits | 1 | | Parity | none | | Flow control | none | | Buffer size | 32 768 B | | Parser | interByteTimeout (50 ms) | | Command timeout | 30 000 ms | | Auto-reconnect | enabled (1 500 ms) | | Handshake timeout | 5 000 ms |


Providers

By default the library uses the browser's native Web Serial API (navigator.serial). You can replace this with any of the built-in providers from webserial-core, or write your own.

Web Serial API (default)

No setup required — works out of the box in Chrome / Edge 89+.

import { PinPad } from '@danidoble/webserial-pinpad';

const pinpad = new PinPad({ username: 'USER', password: 'PASS' });
await pinpad.connect();

WebUSB (WebUsbProvider)

import { PinPad, WebUsbProvider } from '@danidoble/webserial-pinpad';

const pinpad = new PinPad({
  username: 'USER',
  password: 'PASS',
  provider: new WebUsbProvider(),
});
await pinpad.connect();

Web Bluetooth (createBluetoothProvider)

import { PinPad, createBluetoothProvider } from '@danidoble/webserial-pinpad';

const pinpad = new PinPad({
  username: 'USER',
  password: 'PASS',
  provider: createBluetoothProvider(),
});
await pinpad.connect();

WebSocket (createWebSocketProvider)

import { PinPad, createWebSocketProvider } from '@danidoble/webserial-pinpad';

const pinpad = new PinPad({
  username: 'USER',
  password: 'PASS',
  provider: createWebSocketProvider('ws://localhost:8080'),
});
await pinpad.connect();

API

new PinPad(options?)

| Option | Type | Default | Description | | ----------------- | --------------------- | -------------- | ------------------------------------------------------------ | | username | string \| null | null | MIT gateway username. | | password | string \| null | null | MIT gateway password (stored as uppercase). | | environment | PinPadEnvironment | 'production' | Target environment for API calls. | | filters | SerialPortFilter[] | [] | USB vendor/product filters for port matching. | | provider | SerialProvider | — | Per-instance transport provider. | | polyfillOptions | SerialDeviceOptions | — | Extra options forwarded to the underlying AbstractSerialDevice. |

pinpad.connect()

Opens the serial port and performs the device handshake (about command). Shows a browser port-picker dialog on first use.

await pinpad.connect();

pinpad.disconnect()

Gracefully closes the port and stops auto-reconnect.

await pinpad.disconnect();

pinpad.login({ force? })

Authenticates against the MIT gateway. Caches credentials in localStorage for 24 hours. Pass force: true to bypass the cache.

const info = await pinpad.login();         // cached
const info = await pinpad.login({ force: true }); // force refresh

pinpad.sendSale({ amount, reference })

Full sale flow: login → device init → read card → process payment. Returns a SaleResult.

const result = await pinpad.sendSale({ amount: 250.00, reference: 'ORDER123' });
// result: { error: boolean, message: string | null, approved: boolean, object: Record<string, unknown> }

| Option | Type | Description | | ----------- | ------------------ | --------------------------------------- | | amount | number | Amount in currency units (e.g. 150.00). Must be > 0. | | reference | string \| null | Alphanumeric transaction reference. |

pinpad.cancelPurchase({ amount, authorization, folio })

Voids a previously approved transaction. Returns the raw gateway JSON string.

await pinpad.cancelPurchase({
  amount: 250.00,
  authorization: 'ABC123',  // exactly 6 alphanumeric characters
  folio: '123456789',       // exactly 9-digit operation number
});

pinpad.rePrint({ folio? })

Re-prints the last voucher or a specific one by folio. Stores the decoded commerce and client vouchers internally.

await pinpad.rePrint({ folio: '123456789' });
// then print:
await pinpad.sendPrint('client');
await pinpad.sendPrint('commerce');

pinpad.consult({ reference? })

Queries the status of a transaction by reference.

const tx = await pinpad.consult({ reference: 'ORDER123' });

pinpad.sendAbout()

Queries the terminal for device information (model, serial, capabilities). Called automatically during handshake.

await pinpad.sendAbout();

pinpad.sendReadCard()

Initiates the card-reading process. Requires amount to be set beforehand. Resolves when a card is read or rejects on timeout / error.

pinpad.amount = 150.00;
await pinpad.sendReadCard();

pinpad.sendCancelReadCard()

Cancels an in-progress card read.

await pinpad.sendCancelReadCard();

pinpad.sendPrint(voucherType?)

Prints the stored voucher. Pass 'client' (default) or 'commerce'.

await pinpad.sendPrint('client');
await pinpad.sendPrint('commerce');

pinpad.sendFinishEMV()

Sends the finish-EMV command after a successful sale. Called automatically inside sendSale.

await pinpad.sendFinishEMV();

pinpad.sendCustomCode(code)

Sends a raw string command directly to the device. Useful for debugging or unsupported commands.

await pinpad.sendCustomCode('\x02012VXVCANCEL\x03l');

pinpad.getPosition()

Requests the device geolocation via navigator.geolocation. Caches the result until clearSession() is called.

const { latitude, longitude } = await pinpad.getPosition();

pinpad.checkPositionPermission()

Returns true if the geolocation permission is already granted.

const granted = await pinpad.checkPositionPermission();

pinpad.getClientVoucher() / pinpad.getCommerceVoucher()

Returns the last decoded client or commerce voucher string (empty string if unavailable).

const clientVoucher   = pinpad.getClientVoucher();
const commerceVoucher = pinpad.getCommerceVoucher();

pinpad.clearSession()

Removes cached login response, RSA key, and public IP from localStorage.

pinpad.clearSession();

pinpad.isConnected()

Returns true when the port is open and the handshake has succeeded.


Getters and setters

| Property | Type | Description | | --------------- | ------------------- | ------------------------------------------------------------------------------- | | username | string \| null | MIT gateway username. | | password | string \| null | MIT gateway password (read as uppercase). | | amount | number | Transaction amount. Must be > 0. | | reference | string | Transaction reference. Alphanumeric, no special characters. | | environment | PinPadEnvironment | Active environment. One of development, qa, production, productionAlternative. | | timeoutPinPad | number | Card-read timeout in seconds (11–299). Default 100. | | url | string (readonly) | Base URL for the current environment. | | version | object (readonly) | { name, version, environment } — library name, version, and current env. |


Events

Core events (from webserial-core)

| Event | Payload | Description | | ------------------------ | --------------------------------- | ------------------------------------------------- | | serial:connecting | instance | Port is being opened. | | serial:connected | instance | Port opened successfully. | | serial:disconnected | instance | Port closed or device unplugged. | | serial:reconnecting | instance | Auto-reconnect attempt in progress. | | serial:data | data: Uint8Array, instance | Raw binary frame received from the device. | | serial:sent | data: Uint8Array, instance | Raw bytes written to the port. | | serial:error | error: Error, instance | An error occurred during communication. | | serial:need-permission | instance | No authorised port found; user must grant access. | | serial:timeout | command: Uint8Array, instance | A queued command timed out. |

PinPad events

| Event | Payload | Description | | -------------------------- | -------------------------------- | ------------------------------------------------------------------ | | pinpad:connected | — | Handshake succeeded; terminal is ready for commands. | | pinpad:processing-card | { waiting: boolean } | Terminal is processing the card (e.g. chip being read). | | pinpad:read-card | PinPadReadCardEvent | Card read successfully. Contains masked PAN, name, expiry. | | pinpad:error | PinPadErrorEvent | A terminal or gateway error occurred. | | pinpad:print | PinPadPrintEvent | Print operation result (success, warning, or error). | | pinpad:dukpt | PinPadDukptEvent | DUKPT key status (unsupported or charged). | | pinpad:finish-emv | Record<string, unknown> | EMV finish response from the gateway. | | pinpad:response | { raw: string } | Raw string response from the terminal (emitted for every frame). |

PinPadReadCardEvent

interface PinPadReadCardEvent {
  ERROR: string;
  maskPan: string; // masked card number, e.g. '411111******1111'
  name: string;    // cardholder name
  month: string;   // expiry month (MM)
  year: string;    // expiry year (YY)
}

PinPadErrorEvent

interface PinPadErrorEvent {
  error: string;   // error code, e.g. 'A10'
  message: string; // human-readable message
}

PinPadPrintEvent

interface PinPadPrintEvent {
  type?: string;    // 'success' | 'warning' | 'error'
  error?: boolean;
  code?: string;
  message?: string;
}

Environments

| Value | URL | | ------------------------ | --------------------------------- | | development | https://fcdev.mitec.com.mx | | qa | https://fcqa.mitec.com.mx | | production | https://m.mit.com.mx | | productionAlternative | https://m2.mit.com.mx |

The gateway automatically falls back to productionAlternative on the second retry of a sale.


TypeScript

All events and method signatures are fully typed. The package ships with .d.mts / .d.cts declaration files — no extra @types package required.

Commonly used types and all built-in providers are re-exported so you do not need to import directly from webserial-core:

import {
  PinPad,
  WebUsbProvider,
  createBluetoothProvider,
  createWebSocketProvider,
} from '@danidoble/webserial-pinpad';

import type {
  PinPadOptions,
  PinPadAbout,
  PinPadAboutPP,
  PinPadConfig,
  PinPadReadConfig,
  PinPadOperation,
  PinPadEnvironment,
  PinPadReadCardEvent,
  PinPadErrorEvent,
  PinPadPrintEvent,
  PinPadDukptEvent,
  SaleResult,
  WaitingStatus,
  PinPadDukptStatus,
  SerialPortFilter,
  SerialDeviceOptions,
  SerialEventMap,
  SerialProvider,
  SerialPolyfillOptions,
} from '@danidoble/webserial-pinpad';

License

GPL-3.0-only © Danidoble