{"_id":"@danidoble/webserial-pinpad","name":"@danidoble/webserial-pinpad","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@danidoble/webserial-pinpad","type":"module","version":"1.0.0","description":"A strongly-typed, event-driven USB pinpad driver for the Web Serial API, built on top of webserial-core.","author":{"name":"Danidoble","email":"ddanidoble@gmail.com"},"license":"GPL-3.0-only","homepage":"https://github.com/danidoble/webserial-pinpad#readme","repository":{"type":"git","url":"git+https://github.com/danidoble/webserial-pinpad.git"},"bugs":{"url":"https://github.com/danidoble/webserial-pinpad/issues"},"exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./package.json":"./package.json"},"dependencies":{"jsencrypt":"^3.5.4"},"devDependencies":{"@eslint/js":"^10.0.1","@typescript/native-preview":"7.0.0-dev.20260328.1","bumpp":"^11.0.1","eslint":"^10.2.1","globals":"^17.5.0","prettier":"3.8.3","tsdown":"^0.21.10","typescript":"^6.0.3","typescript-eslint":"^8.59.0","vitest":"^4.1.5","webserial-core":"^2.1.0"},"peerDependencies":{"webserial-core":"^2.1.0"},"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.cts","scripts":{"build":"tsdown","dev":"prettier --write ./src/**/*.ts && tsdown --watch","test":"vitest","typecheck":"tsc --noEmit","release":"bumpp","lint":"eslint ./src/**/*.ts ./tests/**/*.ts","format":"prettier --write ./src/ ./tests/ ./README.md ./package.json"},"_id":"@danidoble/webserial-pinpad@1.0.0","_integrity":"sha512-Eqe5CsgRPFncIIyMqwCnt3EQnrbatbCycYODbiL5jTC2LwDR58Eos7Y1YYXzNFcU3VjJDE/8OkpAW+yeMfwRPw==","_resolved":"/tmp/631a9a36bb8b77de7754af79066ac069/danidoble-webserial-pinpad-1.0.0.tgz","_from":"file:danidoble-webserial-pinpad-1.0.0.tgz","_nodeVersion":"24.12.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-Eqe5CsgRPFncIIyMqwCnt3EQnrbatbCycYODbiL5jTC2LwDR58Eos7Y1YYXzNFcU3VjJDE/8OkpAW+yeMfwRPw==","shasum":"5eb33b18b84286b34309161d1220ea3bf3b5d6e6","tarball":"https://registry.npmjs.org/@danidoble/webserial-pinpad/-/webserial-pinpad-1.0.0.tgz","fileCount":7,"unpackedSize":147366,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCLgLdjKGSfcaDsnq3aqKuJeuazlvN//KbvHAr1E4/6ywIgKPF/Y97tu33ZbefIRQ3NYexAFQ4+ZthH/dLRoOB5TxE="}]},"_npmUser":{"name":"danidoble","email":"ddanidoble@gmail.com"},"directories":{},"maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webserial-pinpad_1.0.0_1777223878706_0.46723143566902103"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-26T17:17:58.597Z","1.0.0":"2026-04-26T17:17:58.847Z","modified":"2026-04-26T17:17:59.063Z"},"maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"description":"A strongly-typed, event-driven USB pinpad driver for the Web Serial API, built on top of webserial-core.","homepage":"https://github.com/danidoble/webserial-pinpad#readme","repository":{"type":"git","url":"git+https://github.com/danidoble/webserial-pinpad.git"},"author":{"name":"Danidoble","email":"ddanidoble@gmail.com"},"bugs":{"url":"https://github.com/danidoble/webserial-pinpad/issues"},"license":"GPL-3.0-only","readme":"# @danidoble/webserial-pinpad\n\nA strongly-typed, event-driven pinpad driver built on top of [`webserial-core`](https://github.com/danidoble/webserial-core).\n\nHandles 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.\n\nCompatible 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.\n\n[![npm version](https://img.shields.io/npm/v/@danidoble/webserial-pinpad)](https://www.npmjs.com/package/@danidoble/webserial-pinpad)\n[![license](https://img.shields.io/npm/l/@danidoble/webserial-pinpad)](./LICENSE.md)\n\n---\n\n## Requirements\n\n- [`webserial-core`](https://www.npmjs.com/package/webserial-core) `^2.1.0` (peer dependency)\n- A browser that exposes one of the following transport APIs:\n  - **Web Serial API** — Chrome / Edge 89+ (default, no extra setup)\n  - **WebUSB** — Chrome / Edge (via `WebUsbProvider`)\n  - **Web Bluetooth** — Chrome / Edge (via `createBluetoothProvider`, Nordic UART Service)\n  - **WebSocket** — any environment (via `createWebSocketProvider` + a bridge server)\n  - **Custom** — any platform via your own `SerialProvider` implementation\n- A MIT payment gateway account (`username` + `password`) for gateway operations\n\n---\n\n## Installation\n\n```bash\n# npm\nnpm install @danidoble/webserial-pinpad webserial-core\n\n# pnpm\npnpm add @danidoble/webserial-pinpad webserial-core\n\n# yarn\nyarn add @danidoble/webserial-pinpad webserial-core\n\n# bun\nbun add @danidoble/webserial-pinpad webserial-core\n```\n\n> `webserial-core` is a **peer dependency** — it must be installed alongside this package.\n\n---\n\n## Quick start\n\n```ts\nimport { PinPad } from '@danidoble/webserial-pinpad';\n\nconst pinpad = new PinPad({\n  username: 'YOUR_MIT_USER',\n  password: 'YOUR_MIT_PASSWORD',\n  environment: 'production',\n  filters: [{ usbVendorId: 0x0801 }], // optional USB filter\n});\n\n// Serial lifecycle events\npinpad.on('serial:connecting',   () => console.log('Opening port…'));\npinpad.on('serial:connected',    () => console.log('Port open'));\npinpad.on('serial:disconnected', () => console.log('Disconnected'));\n\n// PinPad events\npinpad.on('pinpad:connected',        () => console.log('PinPad ready'));\npinpad.on('pinpad:processing-card',  ({ waiting }) => console.log('Processing…', waiting));\npinpad.on('pinpad:read-card',        (data) => console.log('Card read:', data.maskPan));\npinpad.on('pinpad:error',            (err)  => console.error(`[${err.error}] ${err.message}`));\npinpad.on('pinpad:print',            (evt)  => console.log('Print event:', evt.type));\npinpad.on('pinpad:dukpt',            (evt)  => console.log('DUKPT:', evt.status));\n\n// Opens a port picker dialog (requires a user gesture)\nawait pinpad.connect();\n\n// ── Sale ────────────────────────────────────────────────────────\nconst result = await pinpad.sendSale({ amount: 150.00, reference: 'ORDER001' });\nif (result.approved) {\n  console.log('Approved:', result.object);\n} else {\n  console.error('Declined:', result.message);\n}\n\n// ── Cancel / void ───────────────────────────────────────────────\nawait pinpad.cancelPurchase({\n  amount: 150.00,\n  authorization: 'ABC123',  // 6-char alphanumeric\n  folio: '123456789',       // 9-digit operation number\n});\n\n// ── Re-print voucher ────────────────────────────────────────────\nawait pinpad.rePrint({ folio: '123456789' });\n\n// ── Consult transaction ─────────────────────────────────────────\nconst tx = await pinpad.consult({ reference: 'ORDER001' });\nconsole.log(tx);\n```\n\n---\n\n## Serial settings\n\nThe constructor pre-configures the following defaults — no extra setup needed:\n\n| Setting             | Value                     |\n| ------------------- | ------------------------- |\n| Baud rate           | 19 200                    |\n| Data bits           | 8                         |\n| Stop bits           | 1                         |\n| Parity              | none                      |\n| Flow control        | none                      |\n| Buffer size         | 32 768 B                  |\n| Parser              | interByteTimeout (50 ms)  |\n| Command timeout     | 30 000 ms                 |\n| Auto-reconnect      | enabled (1 500 ms)        |\n| Handshake timeout   | 5 000 ms                  |\n\n---\n\n## Providers\n\nBy 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.\n\n### Web Serial API (default)\n\nNo setup required — works out of the box in Chrome / Edge 89+.\n\n```ts\nimport { PinPad } from '@danidoble/webserial-pinpad';\n\nconst pinpad = new PinPad({ username: 'USER', password: 'PASS' });\nawait pinpad.connect();\n```\n\n### WebUSB (`WebUsbProvider`)\n\n```ts\nimport { PinPad, WebUsbProvider } from '@danidoble/webserial-pinpad';\n\nconst pinpad = new PinPad({\n  username: 'USER',\n  password: 'PASS',\n  provider: new WebUsbProvider(),\n});\nawait pinpad.connect();\n```\n\n### Web Bluetooth (`createBluetoothProvider`)\n\n```ts\nimport { PinPad, createBluetoothProvider } from '@danidoble/webserial-pinpad';\n\nconst pinpad = new PinPad({\n  username: 'USER',\n  password: 'PASS',\n  provider: createBluetoothProvider(),\n});\nawait pinpad.connect();\n```\n\n### WebSocket (`createWebSocketProvider`)\n\n```ts\nimport { PinPad, createWebSocketProvider } from '@danidoble/webserial-pinpad';\n\nconst pinpad = new PinPad({\n  username: 'USER',\n  password: 'PASS',\n  provider: createWebSocketProvider('ws://localhost:8080'),\n});\nawait pinpad.connect();\n```\n\n---\n\n## API\n\n### `new PinPad(options?)`\n\n| Option            | Type                  | Default        | Description                                                  |\n| ----------------- | --------------------- | -------------- | ------------------------------------------------------------ |\n| `username`        | `string \\| null`      | `null`         | MIT gateway username.                                        |\n| `password`        | `string \\| null`      | `null`         | MIT gateway password (stored as uppercase).                  |\n| `environment`     | `PinPadEnvironment`   | `'production'` | Target environment for API calls.                            |\n| `filters`         | `SerialPortFilter[]`  | `[]`           | USB vendor/product filters for port matching.                |\n| `provider`        | `SerialProvider`      | —              | Per-instance transport provider.                             |\n| `polyfillOptions` | `SerialDeviceOptions` | —              | Extra options forwarded to the underlying `AbstractSerialDevice`. |\n\n### `pinpad.connect()`\n\nOpens the serial port and performs the device handshake (`about` command). Shows a browser port-picker dialog on first use.\n\n```ts\nawait pinpad.connect();\n```\n\n### `pinpad.disconnect()`\n\nGracefully closes the port and stops auto-reconnect.\n\n```ts\nawait pinpad.disconnect();\n```\n\n### `pinpad.login({ force? })`\n\nAuthenticates against the MIT gateway. Caches credentials in `localStorage` for 24 hours. Pass `force: true` to bypass the cache.\n\n```ts\nconst info = await pinpad.login();         // cached\nconst info = await pinpad.login({ force: true }); // force refresh\n```\n\n### `pinpad.sendSale({ amount, reference })`\n\nFull sale flow: login → device init → read card → process payment. Returns a `SaleResult`.\n\n```ts\nconst result = await pinpad.sendSale({ amount: 250.00, reference: 'ORDER123' });\n// result: { error: boolean, message: string | null, approved: boolean, object: Record<string, unknown> }\n```\n\n| Option      | Type               | Description                             |\n| ----------- | ------------------ | --------------------------------------- |\n| `amount`    | `number`           | Amount in currency units (e.g. `150.00`). Must be > 0. |\n| `reference` | `string \\| null`   | Alphanumeric transaction reference.     |\n\n### `pinpad.cancelPurchase({ amount, authorization, folio })`\n\nVoids a previously approved transaction. Returns the raw gateway JSON string.\n\n```ts\nawait pinpad.cancelPurchase({\n  amount: 250.00,\n  authorization: 'ABC123',  // exactly 6 alphanumeric characters\n  folio: '123456789',       // exactly 9-digit operation number\n});\n```\n\n### `pinpad.rePrint({ folio? })`\n\nRe-prints the last voucher or a specific one by folio. Stores the decoded commerce and client vouchers internally.\n\n```ts\nawait pinpad.rePrint({ folio: '123456789' });\n// then print:\nawait pinpad.sendPrint('client');\nawait pinpad.sendPrint('commerce');\n```\n\n### `pinpad.consult({ reference? })`\n\nQueries the status of a transaction by reference.\n\n```ts\nconst tx = await pinpad.consult({ reference: 'ORDER123' });\n```\n\n### `pinpad.sendAbout()`\n\nQueries the terminal for device information (model, serial, capabilities). Called automatically during handshake.\n\n```ts\nawait pinpad.sendAbout();\n```\n\n### `pinpad.sendReadCard()`\n\nInitiates the card-reading process. Requires `amount` to be set beforehand. Resolves when a card is read or rejects on timeout / error.\n\n```ts\npinpad.amount = 150.00;\nawait pinpad.sendReadCard();\n```\n\n### `pinpad.sendCancelReadCard()`\n\nCancels an in-progress card read.\n\n```ts\nawait pinpad.sendCancelReadCard();\n```\n\n### `pinpad.sendPrint(voucherType?)`\n\nPrints the stored voucher. Pass `'client'` (default) or `'commerce'`.\n\n```ts\nawait pinpad.sendPrint('client');\nawait pinpad.sendPrint('commerce');\n```\n\n### `pinpad.sendFinishEMV()`\n\nSends the finish-EMV command after a successful sale. Called automatically inside `sendSale`.\n\n```ts\nawait pinpad.sendFinishEMV();\n```\n\n### `pinpad.sendCustomCode(code)`\n\nSends a raw string command directly to the device. Useful for debugging or unsupported commands.\n\n```ts\nawait pinpad.sendCustomCode('\\x02012VXVCANCEL\\x03l');\n```\n\n### `pinpad.getPosition()`\n\nRequests the device geolocation via `navigator.geolocation`. Caches the result until `clearSession()` is called.\n\n```ts\nconst { latitude, longitude } = await pinpad.getPosition();\n```\n\n### `pinpad.checkPositionPermission()`\n\nReturns `true` if the geolocation permission is already granted.\n\n```ts\nconst granted = await pinpad.checkPositionPermission();\n```\n\n### `pinpad.getClientVoucher()` / `pinpad.getCommerceVoucher()`\n\nReturns the last decoded client or commerce voucher string (empty string if unavailable).\n\n```ts\nconst clientVoucher   = pinpad.getClientVoucher();\nconst commerceVoucher = pinpad.getCommerceVoucher();\n```\n\n### `pinpad.clearSession()`\n\nRemoves cached login response, RSA key, and public IP from `localStorage`.\n\n```ts\npinpad.clearSession();\n```\n\n### `pinpad.isConnected()`\n\nReturns `true` when the port is open and the handshake has succeeded.\n\n---\n\n## Getters and setters\n\n| Property        | Type                | Description                                                                     |\n| --------------- | ------------------- | ------------------------------------------------------------------------------- |\n| `username`      | `string \\| null`    | MIT gateway username.                                                           |\n| `password`      | `string \\| null`    | MIT gateway password (read as uppercase).                                       |\n| `amount`        | `number`            | Transaction amount. Must be > 0.                                                |\n| `reference`     | `string`            | Transaction reference. Alphanumeric, no special characters.                     |\n| `environment`   | `PinPadEnvironment` | Active environment. One of `development`, `qa`, `production`, `productionAlternative`. |\n| `timeoutPinPad` | `number`            | Card-read timeout in seconds (11–299). Default `100`.                           |\n| `url`           | `string` (readonly) | Base URL for the current environment.                                           |\n| `version`       | `object` (readonly) | `{ name, version, environment }` — library name, version, and current env.     |\n\n---\n\n## Events\n\n### Core events (from `webserial-core`)\n\n| Event                    | Payload                           | Description                                       |\n| ------------------------ | --------------------------------- | ------------------------------------------------- |\n| `serial:connecting`      | `instance`                        | Port is being opened.                             |\n| `serial:connected`       | `instance`                        | Port opened successfully.                         |\n| `serial:disconnected`    | `instance`                        | Port closed or device unplugged.                  |\n| `serial:reconnecting`    | `instance`                        | Auto-reconnect attempt in progress.               |\n| `serial:data`            | `data: Uint8Array`, `instance`    | Raw binary frame received from the device.        |\n| `serial:sent`            | `data: Uint8Array`, `instance`    | Raw bytes written to the port.                    |\n| `serial:error`           | `error: Error`, `instance`        | An error occurred during communication.           |\n| `serial:need-permission` | `instance`                        | No authorised port found; user must grant access. |\n| `serial:timeout`         | `command: Uint8Array`, `instance` | A queued command timed out.                       |\n\n### PinPad events\n\n| Event                      | Payload                          | Description                                                        |\n| -------------------------- | -------------------------------- | ------------------------------------------------------------------ |\n| `pinpad:connected`         | —                                | Handshake succeeded; terminal is ready for commands.               |\n| `pinpad:processing-card`   | `{ waiting: boolean }`           | Terminal is processing the card (e.g. chip being read).            |\n| `pinpad:read-card`         | `PinPadReadCardEvent`            | Card read successfully. Contains masked PAN, name, expiry.         |\n| `pinpad:error`             | `PinPadErrorEvent`               | A terminal or gateway error occurred.                              |\n| `pinpad:print`             | `PinPadPrintEvent`               | Print operation result (`success`, `warning`, or `error`).         |\n| `pinpad:dukpt`             | `PinPadDukptEvent`               | DUKPT key status (`unsupported` or `charged`).                     |\n| `pinpad:finish-emv`        | `Record<string, unknown>`        | EMV finish response from the gateway.                              |\n| `pinpad:response`          | `{ raw: string }`                | Raw string response from the terminal (emitted for every frame).   |\n\n#### `PinPadReadCardEvent`\n\n```ts\ninterface PinPadReadCardEvent {\n  ERROR: string;\n  maskPan: string; // masked card number, e.g. '411111******1111'\n  name: string;    // cardholder name\n  month: string;   // expiry month (MM)\n  year: string;    // expiry year (YY)\n}\n```\n\n#### `PinPadErrorEvent`\n\n```ts\ninterface PinPadErrorEvent {\n  error: string;   // error code, e.g. 'A10'\n  message: string; // human-readable message\n}\n```\n\n#### `PinPadPrintEvent`\n\n```ts\ninterface PinPadPrintEvent {\n  type?: string;    // 'success' | 'warning' | 'error'\n  error?: boolean;\n  code?: string;\n  message?: string;\n}\n```\n\n---\n\n## Environments\n\n| Value                    | URL                               |\n| ------------------------ | --------------------------------- |\n| `development`            | `https://fcdev.mitec.com.mx`      |\n| `qa`                     | `https://fcqa.mitec.com.mx`       |\n| `production`             | `https://m.mit.com.mx`            |\n| `productionAlternative`  | `https://m2.mit.com.mx`           |\n\nThe gateway automatically falls back to `productionAlternative` on the second retry of a sale.\n\n---\n\n## TypeScript\n\nAll events and method signatures are fully typed. The package ships with `.d.mts` / `.d.cts` declaration files — no extra `@types` package required.\n\nCommonly used types and all built-in providers are re-exported so you do not need to import directly from `webserial-core`:\n\n```ts\nimport {\n  PinPad,\n  WebUsbProvider,\n  createBluetoothProvider,\n  createWebSocketProvider,\n} from '@danidoble/webserial-pinpad';\n\nimport type {\n  PinPadOptions,\n  PinPadAbout,\n  PinPadAboutPP,\n  PinPadConfig,\n  PinPadReadConfig,\n  PinPadOperation,\n  PinPadEnvironment,\n  PinPadReadCardEvent,\n  PinPadErrorEvent,\n  PinPadPrintEvent,\n  PinPadDukptEvent,\n  SaleResult,\n  WaitingStatus,\n  PinPadDukptStatus,\n  SerialPortFilter,\n  SerialDeviceOptions,\n  SerialEventMap,\n  SerialProvider,\n  SerialPolyfillOptions,\n} from '@danidoble/webserial-pinpad';\n```\n\n---\n\n## License\n\n[GPL-3.0-only](./LICENSE.md) © [Danidoble](https://github.com/danidoble)\n\n","readmeFilename":"README.md","_rev":"1-acfc1dadb0e87f442ed3133accb7ec47"}