0.3.1 • Published 2 years ago

olaii-nfc v0.3.1

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

Olaii NFC Documentation

The library allows for completely offline operation of the Olaii Cashless system.

Current version supports only NTAG213 cards in conjunction with ACR1222L reader. For more details about the implementation please refer to the internal documentation of the Offline Card Operation stored in the Company Document Storage.

1. Library Overview

Library currently supports the following functionality:

  • ACR1222L reader manipulation (LCD, readNDEF, ...)
  • Initial card configuration (done automatically)
  • Perform Deposit
  • Perform Bill
  • Perform Balance Check
  • Perform Withdraw for specific amount

For reader specific commands please look at the node-acr1222l library documentation located at: https://github.com/Olaii/node-acr1222l

1.1 Library Initialization

To initialize the library you will need to perform the following:

  • Set the Master Password (Buffer 4 Bytes)
  • Set the current configuration year (Integer smaller than 128)
  • Declare the PCSC Error Callback function (function)
  • Declare the Report Callback Function (should report errors to the PAY server.) (function)
  • Activation fee (Integer - amount to deduct upon activation)

Code sample:

const master_pwd = Buffer([0xff, 0xfe, 0xff, 0xfe]);
const conf_year = 0x02;
const act_fee = 100; // 1 eur
  
await olaiiNFC.initialize(report_error_cb, pcsc_error_cb, master_password=master_pwd, current_conf_year=conf_year, activation_fee=act_fee, debug=true);

1.1.1 Report Callback Function

Report callback function is invoked when a balance store procedure fails or succeeds after a retry. The function will receive at input an object, that will contain the following properties:

  • serial_number - card serial number e.g: OLA123456789
  • type - type of the message
  • message - message text
  • hash - hex hash number of the transaction 1 Byte long
  • balance_one - card balance one storage - multiplied by 100 to remove decimal places
  • balance_two - card balance two storage - multiplied by 100 to remove decimal places

These properties should be logged to the application journal and possibly the master server, to enable the administrators a look at the error rate and if certain errors were not fixed.

Possible types of the message are:

  • COMPLETED_RETRY_TRANSACTION - Once the retry transaction is successfully completed.
  • BALANCE_STORE_FAILED - If the balance storage fails
  • PREVIOUS_TRANSACTION_SUCCCEEDED - Happens on retry transaction, where the previous transaction has succeeded in the first try.
  • BALANCE_DIFFERENCE - when on the regular transaction balances one and two do not match. This is very important!

1.1.2 Master Password And Configuration Year

Master password is set by the master server for each and every campaign. Master password is 32 bits long (4B), and should be passed upon the library initialization.

Configuration year is also set by the master server and is a single integer smaller than 128 (1B). If the card configuration year does not match the passed parameter, the card will be reformatted with balance 0 upon first deposit. Bill and withdraw functions will raise an error CARD_NOT_YET_ACTIVATED.

2. Application Flow

Here we will briefly describe the application flow that you should follow. Since the NTAG213 does not support tearing protection, the retry policy should be implemented on the application level and thus you must follow theese instructions carefully!

2.1. Deposit procedure

  1. Cashier selects the amount on the register and clicks "Deposit" button.
  2. The application writes to the reader LCD screen and requests readNDEF to receive serial number and card uuid.
  3. Once the customer taps the card, the application calls performDeposit function provided by this library with serial number, uuid, amount to deposit, transaction hash (number between 1-128) and is_retry set to false.
  4. If the balance update succeeds everything is ok. Should it fail, the application should perform number 5.
  5. On store failure, the application should reissue readNDEF, validate that it is the same card as before and call the performDeposit function with the same hash as before and is_retry set to true.
  6. Only on successful completion the application GUI should display the success message.

2.2. Bill procedure

  1. Cashier selects the items on the register and clicks "Bill" button.
  2. The application writes to the reader LCD screen and requests readNDEF to receive serial number and card uuid.
  3. Once the customer taps the card, the application calls performBill function provided by this library with serial number, uuid, amount to bill, transaction hash (number between 1-128) and is_retry set to false.
  4. If the balance update succeeds everything is ok. Should it fail, the application should perform number 5.
  5. On store failure, the application should reissue readNDEF, validate that it is the same card as before and call the performBill function with the same hash as before and is_retry set to true.
  6. Only on successful completion the application GUI should display the success message.

2.3. Withdraw procedure

Offline operation withdraw is slightly different than the one of online model.

  1. Cashier selects the withdraw option.
  2. Application writes to LCD and waits for the readNDEF to complete
  3. Once we have the card serial number and uuid, we call the getBalance to get current balance on the card.
  4. The current balance is displayed on the screen.
  5. Cashier confirms the withdraw - the performWithdraw is called with parameters: uuid, serial number, amount to withdraw, hash (0-128), is retry is set to false.
  6. If it succeeds we display the success message.
  7. If it fails, we must store the current balance and retry the procedure readNDEF again, check it's the same, and call the performWithdraw with same hash and is retry set to true.

3. Library Specific Errors

List of library errors and description of error object.

The library can throw several errors that must be handled correctly. First, to check if the error is library specific or a regular excpetion you must check if the error contains the status property.

 ...
 } catch(err) {
     if(err.status && err.status==='BALANCE_STORE_FAILED') {
 ...

List of application level errors is the following:

  • CARD_NOT_CONFIGURED
  • WRONG_PASSWORD
  • CARD_REMOVED
  • CARD_TAMPER
  • CONFIGURATION_FAILED
  • BALANCE_STORE_FAILED
  • CARD_NOT_YET_ACTIVATED
  • INSUFFICIENT_BALANCE
  • CARD_LOCKED
  • WRITE_FAILED
  • CARD_NOT_PRESENT
  • READ_FAILED
  • FAST_READ_FAILED
  • DEPOSIT_TO_SMALL
  • MASTER_PWD_WRONG_LENGTH
  • CANNOT_DEPOSIT_TO_PROMO: Cannot deposit to promotional card!
  • CANNOT_DEPOSIT_TO_REGULAR: Cannot deposit to regular card!
  • CANNOT_WITHDRAW_WITH_PROMO: Cannot withdraw with promotional card!
  • UNDERAGE_PURCHASE_PROHIBITED: Visitor is underage. Cannot make a purchase of selected items!
  • CARD_ALREADY_CONFIGURED: Cannot configure already activated card

Some errors can happen only if you issue calls to the underlying reader library, but for clarity we should handle all of them in the application.

4. Deposit

Short description of the deposit operation and possible errors and how they should be handled.

Parameter description:

  • card_uuid - Buffer - card UUID
  • serial_number - string, card serial number: OLA123456789
  • amount - integer amount to deposit. Eg. 1.10EUR must be passed as 110 (multiplied by 100 to remove decimal places)
  • hash - integer between 1-128, randomly generated for every transaction
  • is_retry - boolean, must be set to false on the first try.

Function name: performDeposit

If we are depositing to a fresh card, the library will detect that and try to configure the card for current year. If the configuration fails, card is removed or any other error it will throw CONFIGURATION_FAILED error.

After configuration check the library will perform read card operation which may result in READ_FAILED error.

The function will perform validation of the current configured year. If it doesn't match it will try to store balance 0 and current configuration year to the card. At this point it can raise errors:

  • CARD_LOCKED
  • WRITE_FAILED

Next the library will try to parse the card balance and perform the deposit.

Should the function call throw error BALANCE_STORE_FAILED the application must switch to retry mode. Meaning it should reissue the call with is_retry set to true.

Simple example of the retry logic (does not handle all the errors...):

async function deposit(amount, hash, is_retry) {
    await olaiiNFC.writeToLCD('Tap card please', 'Deposit ' + amount);
    
    const ndef_obj = await olaiiNFC.readNDEF();
    
    await olaiiNFC.performDeposit(ndef_obj.uuid_bytes, ndef_obj.ndef, amount, hash, is_retry);
    
    await olaiiNFC.clearLCD();
    
    
const hash = Math.floor(Math.random()*128);
   try {
       await deposit(1000, hash, false);
   } catch(err) {
       if(err.status && err.status==='BALANCE_STORE_FAILED') {
           console.log('Failed to store balance. Please retry...');
           await deposit(1000, hash, true);
       } else {
           throw err;
       }
   }
   

Should the first deposit be smaller than activation fee, the library will throw DEPOSIT_TO_SMALL error.

The function will return object with properties:

  • new_balance - new balance on the card (integer)
  • freshly_activated - was the card just activated (boolean)

If we are issuing refund of the invoice, the library will basically call performDeposit with the refund amount.

5. Bill

Parameter description:

  • card_uuid - Buffer - card UUID
  • serial_number - string, card serial number: OLA123456789
  • amount - integer amount to bill. Eg. 1.10EUR must be passed as 110 (multiplied by 100 to remove decimal places)
  • hash - integer between 1-128, randomly generated for every transaction
  • is_retry - boolean, must be set to false on the first try.

Function name: performBill

Bill is very similar to the deposit in terms of the flow. Therefore this segment will only outline the differences, that you should be aware of.

  1. Bill will not configure a fresh card, it will rather throw: CARD_NOT_CONFIGURED
  2. Bill will not configure card from previous year, it will rather throw: CARD_NOT_YET_ACTIVATED
  3. Should the balance after bill be less than 0, it will throw: INSUFFICIENT_BALANCE

Also please note that, once again all amounts must be positive integers. So for example:

  • 0,01 EUR = 1
  • 0,10 EUR = 10
  • 1,00 EUR = 100
  • 10,00 EUR = 1000
  • etc..

Other possible errors bill might throw are:

  • CARD_TAMPER
  • CARD_REMOVED

Upon BALANCE_STORE_FAILED, the application must switch to retry mode.

6. Withdraw

Withdraw is a wrapper around performBill. It's functionality and parameters are the same.

7. Balance check

To check the card balance all you have to do is obtain the card uuid and serial number through the readNDEF function.

Then you should call the getBalance like the example shows below:

const ndef_obj = await olaiiNFC.readNDEF();

const balance = await olaiiNFC.getBalance(ndef_obj.uuid_bytes, ndef_obj.ndef);

console.log('Current balance:', balance);
0.3.1

2 years ago