magensa-bluetooth
Interface between MagTek® Bluetooth devices, and Chromium based browsers.
Browser Support
| | | |
---|
Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ |
This library utilizes WebBluetooth API.
WebBluetooth compatibility information can be found at the implementation status README and caniuse.com.
Additionally, we have put together detailed compatibility information, as well as initial pair instructions, in our Playground.
Installation
NPM:
npm install magensa-bluetooth
Yarn:
yarn add magensa-bluetooth
Grab your MagTek® device and Get Started
In order to utilize this library, you must have a MagTek® device with Bluetooth capabilities. Currently the compatible devies are
If you have a device, and would like to see this library in action, please head over to our Playground.
If you would like to purchase a device, please head over to the MagTek Store.
Usage
The implementation below will prompt a pair window displaying all MagTek devices in range. Once the end user selects the appropriate device from the pair window, the response to this function call will be a device object.
The device pair window is part of the WebBluetooth API, and is currently mandatory (no bypass exists as of this time).
import { scanForDevices } from 'magensa-bluetooth';
function exampleCallback(dataObj) {
console.log("data send from device", dataObj);
};
scanForDevices(exampleCallback).then( connectedDevice =>
window.MagTekDevice = connectedDevice;
).catch(err => console.error(err));
const examplePairing = async() => {
try {
const connectedDevice = await scanForDevices(exampleCallback);
window.MagTekDevice = connectedDevice;
}
catch(error) {
console.error(error);
}
}
The callback function(s) provided are the only way the paired device can send data to the host.
- All data returned to the provided callbacks will be of type
object
. Please see Return Objects section for more information. - Please see the Callbacks section below for more information about user provided callback functions.
- For single page applications, please ensure that the callback function(s) provided is always "mounted" to receive data.
Device Interface API
All methods are asynchronous (isDeviceOpen
being the only synchronous exception). Be sure to catch all exceptions - as any error occured upon invocation of these functions will throw an Error.
Function | Input Parameters | Output | Notes |
---|
scanForDevices | callbacks [,deviceName[, deviceType]] | Device Object | Please refer to callback examples below. Device name is optional. Device type is optional and supports special cases. Available types listed here |
startTransaction | emvOptions | Success | Initiates EMV transaction. emvOptions is optional - any property supplied will override the default |
cancelTransaction | none | Success | Cancel any transaction that is in progress. |
openDevice | none | Success | Opens paired device to receive commands |
closeDevice | none | Success | Clears session (when applicable) and closes device safely |
clearSession | Number (optional) | Success | Removes previous card data from device's volatile memory. Only PinPad devices have session. Optional input is "Bitmap slot number" for displaying custom display templates |
deviceInfo | none | Device Information | Be aware this call will clear device session prior to returning device information |
requestCardSwipe | swipeOptions | Success | swipeOptions is optional. Any property supplied will override the default |
isDeviceOpen | none | Boolean | synchronous function that returns device's open status |
sendCommand | Hex String or Array<Number> | object | send raw command to device. Output will be an object (if the response has a parser) or array (if returning raw device response) |
forceDisconnect | none | void | Sever device connection, in the case that the device becomes unresponsive |
requestPinEntry | pinOptions | Success | PinPad devices only |
setDisplayMessage | displayOptions | Success | PinPad devices only |
sendUserSelection | Number | Success | SCRA devices only. This command is only used to respond to device's userSelectionRequest |
sendArpcResponse | Hex String or Array<Numbers> | Success | For more information about building ARPC, please see the MagTek® documentation |
setDeviceDateTime | JavaScript Date object | Success | SCRA devices only |
requestTipOrCashback | tipCashbackOptions | Success | DynaPro Go Only |
EMV Options Object
Emv Options object is the input object for the startTransaction
function. All property values have default values. Any property supplied will override the default value, while any not supplied will use default values.
There are some differences between devices - so this section will be broken down into four parts:
- Properties that are shared by both device types
- Properties for SCRA devices only
- PinPad properties only
- DynaPro Go properties only
Shared Properties:
Property Name | Description | Type | Acceptable Values | Default Value |
---|
timeout | time, in seconds, before transaction timeout | Number | 1 - 255 | 60 |
cardType | types of cards to accept for transaction | String | 'msr', 'chip', 'chipMsr', 'contactless', 'contactlessChip', 'all' | chipMsr |
cashBack | amount to process as cashback transaction. For transactionType 'refund' , this value must be 0 | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number Number type has limitations, please plan accordingly | 0 |
currencyCode | type of currency. 'default' uses device's application terminal setting (usually USA dollar) | String | 'dollar', 'euro', 'pound', 'default' | 'dollar' |
authorizedAmount | amount to authorize for transaction. For transactionType 'refund' , this value must be 0 | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number Number type has limitations, please plan accordingly | 100 |
SCRA properties only:
Property Name | Description | Type | Acceptable Values | Default Value |
---|
emvOptions | online EMV options | String | 'normal', 'byPassPin', 'forceOnline', 'quickChip', 'pinByPassQuickChip', 'forceOnlineQuickChip' | 'quickChip' |
transactionType | type of transaction to process | String | 'purchase', 'cashback', 'refund', 'contactlessCashback' | 'purchase' |
reportVerbosity | EMV Transaction Status/Progress messaging level | String | 'minimum', 'medium', 'verbose' | 'minimum' |
PinPad properties only:
Property Name | Description | Type | Acceptable Values | Default Value |
---|
emvOptions | online EMV options | String | 'normal', 'byPassPin', 'forceOnline', 'acquirerNotAvailable' | 'normal' |
transactionType | type of transaction to process | String | 'purchase', 'cashAdvance', 'cashback', 'purchaseGoods', 'purchaseServices', 'cashManual', 'refund', 'chipOnlyPayment' | 'purchase' |
pinTimeout | wait time in seconds for cardholder to enter PIN | Number | 1 -255 | 20 |
toneChoice | Select device beep behavior | String | 'noSound', 'oneBeep', 'twoBeeps' | 'oneBeep' |
isQuickChip | arm in QuickChip mode, or not | Boolean | true or false | true |
DynaPro Go properties only:
Property Name | Description | Type | Acceptable Values | Default Value | Notes |
---|
taxAmount | Total tax amount | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number | 0 | No decimal points. Number type has limitations, please plan accordingly |
taxPercent | Tax percentage rate | Number or Array<Numbers> | Number or 4 'byte' (n6 format x 100) representation of number | 0 - 99 | This number is for display purposes only - device does not perform tax calculations |
tipAmount | Total tip amount | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number | 0 | No decimal points. Number type has limitations, please plan accordingly |
balanceBeforeGenAC | (Contactless Only) Balance Read Before Gen AC (EMV Tag DF8104) | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number | 0 | No decimal points. Number type has limitations, please plan accordingly |
balanceAfterGenAC | (Contactless Only) Balance Read After Gen AC (EMV Tag DF8105) | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number | 0 | No decimal points. Number type has limitations, please plan accordingly |
trxCategoryCode | (PayPass/MCL Tag 9F53) | Number | N/A | 0 | Optional value |
Swipe Options Object
Swipe options are only valid for PinPad devices. Swipe options passed to a SCRA device will be ignored.
Property Name | Description | Type | Acceptable Values | Default Value |
---|
timeout | time, in seconds, before transaction timeout | Number | 1 - 255 (0 for infinite wait) | 60 |
displayType | choose which display customer will see on device display | | 'swipeIdleAlternate', 'swipeCard', 'pleaseSwipe', 'pleaseSwipeAgain', 'chipErrorUseSwipe' | 'pleaseSwipe' |
toneChoice | Select device beep behavior | String | 'noSound', 'oneBeep', 'twoBeeps' | 'oneBeep' |
isFallback | execute swipe with fallback flag. Be aware this will set displayType to 'chipErrorUseSwipe' | Boolean | true or false | false |
PIN Options Object
Property Name | Description | Type | Acceptable Values | Default Value | Notes |
---|
languageSelection | Define language prompt behavior | String | 'disabled', 'englishFrench', 'allSpecified' | 'disabled' | 'allSpecified' uses tag DFDF2D to define available languages |
displayType | Define prompt template for PIN entry | String | 'enterPin', 'enterPinAmount', 'reEnterPin', 'reEnterPinAmount', 'verifyPin' | 'enterPin' | |
timeout | time, in seconds, before requestPin timeout | Number | 0 - 255 (0 for 256 seconds) | 30 | |
toneChoice | Select device beep behavior | String | 'noSound', 'oneBeep', 'twoBeeps' | 'oneBeep' | |
pinBlockFormat | Define Pin Block Format | String | 'iso0', 'iso3' | 'iso0' | This value is only respected if device is sent a PAN. If device has no PAN, device creates EPB using ISO Format 1 |
verifyPin | Should device prompt for PIN verification | Boolean | true, false | true | |
waitMessage | Display wait message | Boolean | true, false | true | |
maxPinLength | Specify maximum PIN length | Number | <=12 && >= minPinLength | 12 | Value must be <=12 and >= minPinLength |
minPinLength | Specify minimum PIN length | Number | >=4 && <= maxPinLength | 4 | Value must be >=4 and <= maxPinLength |
Tip Cashback Options Object
Property Name | Description | Type | Acceptable Values | Default Value | Notes |
---|
timeout | time (in seconds) before operation timeout | Number | 1 - 60 | 30 | |
commandType | Tip or Cashback Mode | String | 'tip', 'cashback' | N/A | commandType is required |
toneChoice | Select device beep behavior | String | 'noSound', 'oneBeep', 'twoBeeps' | 'oneBeep' | |
transactionAmount | Subtotal amount for transaction | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number | N/A | No decimal points. Number type has limitations, please plan accordingly |
calculatedTaxAmount | Total tax amount | Number or Array<Numbers> | Number or 6 'byte' n12 format representation of number | N/A | No decimal points. Number type has limitations, please plan accordingly |
taxRate | Tax percentage rate | Number or Array<Numbers> | Number or 4 'byte' (n6 format x 100) representation of number | 0 - 99 | This number is for display purposes only - device does not perform tax calculations. taxRate is required |
tipSelectionMode | Preset tip amount type | String | 'percent', 'amount' | N/A | This value is only mandatory in Tip mode. N/A for Cashback Mode |
leftAmount | Fixed Percent or Amount for left display | Number | 0-99 | 0 | N/A for Cashback Mode |
middleAmount | Fixed Percent or Amount for middle display | Number | 0-99 | 0 | N/A for Cashback Mode |
rightAmount | Fixed Percent or Amount for right display | Number | 0-99 | 0 | N/A for Cashback Mode |
Display Options Object
Property Name | Description | Type | Acceptable Values | Default Value | Notes |
---|
messageId | Id for message to display | Number | Acceptable IDs are listed here | N/A | No default value - ID is required |
displayTime | How long the display message will display on screen | Number | 0 (infinite) - 255 | 15 seconds | |
Callbacks
User defined callback functions can be as granular as desired. For this purpose - there is only one callback that is mandatory to provide to the scanForDevices
method. The remaining callbacks are subscription based. In reference to the return objects for the below callbacks - please see the Return Objects section for object structures.
errorCallback
is the only special case. Please see description for behavior. For all other callbacks - if one is not provided - data will be sent to the main callback (transactionCallback
). This can sometimes clutter the callback data, and become problematic, so please plan callback structure according to your specific needs.
Callback | Return object | Notes |
---|
transactionCallback | Transaction Result Object | Transaction data. Object structure will depend on which type of transaction was requested. This is the only mandatory callback, as it serves as the main/default callback. |
errorCallback | Error Object | If provided, all internal errors that cannot be thrown to a caller will be piped to this callback. If not provided, internal errors will log to JavaScript console. All errors pertaining to functions invoked via the deviceInterface will always be thrown back to the caller |
displayCallback | Display Message Object | Message to display directly to the end user. This callback is only used by SCRA devices. PinPad devices will display messages directly on the device |
transactionStatusCallback | Transaction Status Object | Status, Progress, Messages, and Codes will all be piped to this callback. You can throttle reportVerbosity on SCRA devices |
disconnectHandler | Disconnect Event | Disconnect Event inherits from Event. Disconnect events are emitted every time a device disconnects (closes) |
userSelectionCallback | userSelectionRequest | SCRA devices that have multiple applications, and process a card with multiple applications will send this report. User must select item from the menu items listed in the report and respond using the sendUserSelection function with the menu selection number |
Callback Examples
This is the most basic example - using Promises.
let exampleCallback = responseObj => {
if ('swipeData' in responseObj) {
}
}
scanForDevices(exampleCallback)
.then( device => {
window.MagTekDevice = device
})
.catch( errObj => {
console.log(errObj)
});
This is the most basic example - using async/await.
let exampleCallback = responseObj => {
const { swipeData } = responseObj;
if (swipeData) {
}
}
const connectDevice = async() => {
try {
let deviceResp = await scanForDevices(exampleCallback)
window.MagTekDevice = devicedeviceResp
}
catch(error) {
console.log(error)
}
}
These are two examples of the most granular way to interact with this library:
Structure callbacks using function
property assignment:
const callbacks = (function() {
const allCallbacks = responseObj => {
if ('swipeData' in responseObj) {
}
}
allCallbacks.errorCallback = errObj => {
}
allCallbacks.transactionStatusCallback = statusObj => {
}
allCallbacks.displayCallback = ({ displayMessage }) => {
document.getElementById('display-to-user').innerText = displayMessage;
}
allCallbacks.userSelectionCallback = ({ userSelectionRequest }) => {
}
allCallbacks.disconnectHandler = event => {
let message = `Device: ${event.target.name} has disconnected`;
}
return allCallbacks;
})();
scanForDevices(callbacks)
.then( device => {
window.MagTekDevice = device
})
.catch( errObj => {
console.error("Caught Error: ", errObj);
});
const pairDevice = async() => {
try {
const deviceResp = await scanForDevices(callbacks);
window.MagTekDevice = deviceResp;
}
catch(error) {
console.error("Caught Error: ", errObj);
}
}
Structure callbacks using an Object
:
const exampleErrorHandler = errObj => {
}
const exampleTransactionHandler = dataObj => {
}
const exampleTransactionStatusHandler = statusObj => {
}
const exampleDisplayMessageHandler = ({ displayMessage }) => {
document.getElementById('display-to-user').innerText = displayMessage;
}
const exampleDisconnectHandler = event => {
let message = `Device: ${event.target.name} has disconnected`;
}
const exampleUserSelectionCallback = ({ userSelectionRequest }) => {
}
let callBackObject = {
transactionCallback: exampleTransactionHandler,
errorCallback: exampleErrorHandler,
displayCallback: exampleDisplayMessageHandler,
transactionStatusCallback: exampleTransactionStatusHandler,
disconnectHandler: exampleDisconnectHandler,
userSelectionCallback: exampleUserSelectionCallback
}
scanForDevices(callBackObject)
.then( device => {
window.MagTekDevice = device
})
.catch( errObj => {
console.error("Caught Error: ", errObj);
});
const connectDevice = async() => {
try {
const deviceResp = await scanForDevices(callBackObject);
window.MagTekDevice = deviceResp;
}
catch(error) {
console.error("Caught Error: ", errObj);
}
}
Return Objects
1. Device Object
{
id: String,
name: String,
deviceType: String,
deviceInterface: {
openDevice: Function
startTransaction: Function
cancelTransaction: Function
sendCommand: Function
clearSession: Function
closeDevice: Function
deviceInfo: Function
requestCardSwipe: Function
isDeviceOpen: Function
forceDisconnect: Function
requestPinEntry: Function
setDisplayMessage: Function
sendUserSelection: Function
sendArpcResponse: Function
setDeviceDateTime: Function
requestTipOrCashback: Function
}
}
2. Transaction Result Object:
arqcData
and batchData
are hex strings that are unparsed, and contain the same data as the parsed objects.
{
arqcData: String,
arqcDataParsed: [
{ tag: String, length: Number, value: String },
{ tag: String, length: Number, value: String }
],
batchData: String,
batchDataParsed: [
{ tag: String, length: Number, value: String },
{ tag: String, length: Number, value: String }
],
swipeData: {
ksn: String,
Last4: String,
encSessionId: String,
expirationDate: String,
magnePrint: String,
magnePrintStatus: String,
maskedPAN: String,
serialNumber: String,
track1: String,
track1Masked: String,
track1DecodeStatus: Number,
track2: String,
track2DecodeStatus: Number,
track2Masked: String,
track3: String,
track3DecodeStatus: Number,
track3Masked: String
},
signatureRequired: Boolean
}
3. Display Message Object:
{
displayMessage: String
}
4. Transaction Status Object:
{
transactionStatus: {
statusCode: Number,
statusMsg: String,
progressCode: Number,
progressMsg: String
}
}
5. Error Object
There are many error objects, depending on what layer threw the error.
All errors extend JavaScript's Error. All have the following properties.
{
Error: {
code: Number,
name: String,
message: String
}
}
6. Success Object
{
code: 0,
message: String
}
7. Device Information
{
deviceName: String,
deviceType: String,
isConnected: Boolean,
serialNumber: String,
batteryLevel: Number
}
8. Tip Cashback Report
{
tipCashbackReport: {
operationStatus: String,
reportMode: String,
amount: Array<Number>,
tax: Array<Number>,
taxRate: Array<Number>,
tipOrCashbackAmount: Array<Number>
}
}
9. Pin Data Report
{
pinData: {
operationStatus: String,
pinKsn: String,
encryptedPinBlock: String
}
}
10. User Selection Request
{
userSelectionRequest: {
selectionType: String,
timeRemaining: Number,
menuItems: Array<Number>
}
}
Playground and Additional Information
Please visit our Playground for an interactive demo.
- The Playground also offers detailed compatibility information, as well as first time pairing instructions for all compatible browsers and operating systems.
- The Playground source code is also available as an example implementation.
Debug Event
For added visibility during development, this library has a debug event emitter (deviceLog
) that will log verbose details for all device interactions.
This can be especially useful when a bad command is sent - or to see the behavior when a device begins to refuse commands.
If you wish to subscribe to the event, you may do so:
const debugLogger = logInfo => console.log(logInfo.detail);
window.addEventListener('deviceLog', debugLogger, { passive: true});
window.removeEventListener('deviceLog', debugLogger, { passive: true});
WebBluetooth Info
There are some WebBluetooth issues that users of this library should be made aware of:
Transaction Amount Limitations
Please be aware that there are limitations on maximum amounts for transactions:
The maximum length of Transaction Amount, Calculated Tax Amount, Tip dollar amount, and Cash Back
dollar amount is 10 digits. If the Tip calculated by percentage equals or exceeds $42,949,672.95, the
device shows 0.
Device Types
Under very rare circumstances, it is possible this library will fail to identify a valid device type.
In this case, there is a third parameter for scanForDevices
function that accepts a deviceType
. The available types are:
tDynamo
eDynamo
dynaProGo
DynaPro Mini
MagTek® is a registered trademark of MagTek, Inc.
Magensa™ is a trademark of MagTek, Inc.