Getting Started

This document provides instructions for embedding PC WEB SDK in web application. In order to understand better how to work with PC we recommend you to read the following documents as well:

  • Architecture and functionality presentation
  • Architecture and functionality

The PC WEB SDK allow to implement all functionality of PC right in your web application.

Project integration

The PC WEB SDK library can be imported into your project from the SafeTech npm repository.

npm i @safetech/pcsdk

After installing the library, PCSDK can be imported into the project.

import PCSDK from @safetech/pcsdk

Your web application must use one instance of PCSDK class during operating.

SDK Initialization

To initialize PC SDK library you need to call async method PCSDK.init(callback) passing the callback function. The function is called when a token is connected or disconnected. An object with properties {connected, disconnected} is passed as arguments, which contains arrays with identifiers of connected and disconnected tokens. Your are expected to initialize PCSDK before calling any other methods from PCSDK.

import PCSDK from @safetech/pcsdk
try {
    await PCSDK.init(callback);
} catch (error) {
    // handle the error
}

Registering users

Process of user registration includes followings steps:

  1. Getting the personalization data from the PC Server (via deeplink, deeplink + activation code) see Architecture and functionality document for more details on this.
  2. Registering a PC User on PC Server. During this process PCSDK generates needed key sets
  3. Storing user's keys in token storage for further usage

Step 1. Import PCUser from an appropriate source

First of all, you need to get a list of connected tokens and information about them using the method PCSDK.getTokensInfo()

Second, you should construct a PCUser object (also referenced as key) from String value which contains either JSON extracted from deeplink or JSON delivered to your app with usage of PC Server API and the deviceId where the user will be stored.

import PCSDK, {PCUsersManager} from @safetech/pcsdk’;

try {
    const devices = await PCSDK.getTokensInfo(); 
    //returns array of objects with properties { label, serial, leftSpace, deviceId}

    const user = await PCUsersManager.importUser(source, deviceId);
} catch(error) {
    // handle the error
}

// If no errors, PCUser imported successfully - continue registration

Step 2. Check if PCUser requires activation

When delivering personalization data to your clients your infrastructure may use different methods (via deeplink, deeplink + activation code, JSON-value) to deliver the key data to the web app. The following snippet demonstrates how to import key data to a valid PCUser object:

import PCSDK, {PCUsersManager} from @safetech/pcsdk’;

try {
    const devices = await PCSDK.getTokensInfo()
    if (devices.length) {
        const {deviceId} = devices[0];
        const user = await PCUsersManager.importUser(source, deviceId);
        if (!user.isActivated()) {
            // PCUser requires activation, get activation code according to your rules
            const activationCode = getActivationCode();
            await PCUsersManager.activate(user, activationCode);
        } else {
             // No activation is required
        }
    } else {
        console.log(No connected tokens)
    }
} catch(error) {
    // handle the error
}

// If no errors, PCUser imported successfully - continue registration

Step 3. Registering the key on the PC Server

Now, you can register the PCUser on the PC Server. The key pair is generated and the public key is sent to the server while the private key is encrypted with password and encryption layer. You use PCUsersManager.register() to registered the key on the server side.

import {PCUsersManager} from @safetech/pcsdk’;

try {
    // Prompt the tokens password
    const password = promptPassword();
    await PCUsersManager.register(user, password)
} catch(error) {
    // handle the error
}

// If no errors, PCUser register successfully - continue registration

Step 4: Save the PCUser object to the token store.

The final step is, you must save the PCUser into storage. Each PCUser object is stored in token and referenced by a unique name which is supposed to be entered by a client of your app.

Besides, the keys which are used to confirm transactions (HMAC key which is used to calculate a confirmation code and private key used to generate ECDSA-signature) are always additionally protected with a tokens password and encryption layer.

So, the process of saving a key will look in this way:

// Prompt a unique name for the key from your client or generate it in your web app
const keyName = promptKeyName();

// Prompt the tokens password
const password = promptPassword();

// Save the key to storage
try {
    await PCUsersManager.store(user, keyName, password);
} catch(error) {
    // handle the error
}

// If no errors, PCUser save successfully

Overall key registration flow

The overall process of registration may have the following structure:

import PCSDK, {PCUsersManager} from @safetech/pcsdk’;

// 1. Import PCUser
let user = null;

try {
    const devices = await PCSDK.getTokensInfo()
    if (devices.length) {
        const {deviceId} = devices[0];
        user = await PCUsersManager.importUser(source, deviceId);
    } else {
        console.log(No connected tokens)
    }
} catch(error) {
    // Import failed - handle a error
}

// 2. Check if PCUser requires to be activated
try {
    if (!user.isActivated()) {
        const activationCode = getActivationCode();
        await PCUsersManager.activate(user, activationCode);
    } else {
         // No activation is required
    }
} catch(error) {
    // Activation failed - handle a error
}

// 3. Register PCUser
try {
    const password = promptPassword();
    await PCUsersManager.register(user, password)
} catch(error) {
    // Registration failed - handle a error
}

// 4. Store the key
const keyName = promptKeyName();
const password = promptPassword();

try {
    await PCUsersManager.store(user, keyName, password);
} catch(error) {
    // Store failed - handle a error
}

Process transactions

The overall scenario of processing a transaction includes the following stages:

  1. Call PCUsersManager.listStorage() to obtain a list of users from the storage.
  2. Filter the list of users for which transactions will be loaded.
  3. Call PCTransactionsManager.getTransactionList() for each desired PCUser to find out if there are any transactions to be processed.
  4. Call PCTransactionsManager.getTransaction() to get transaction by its identifier. Consider also calling PCTransactionsManager.getTransactionBinaryData() for transactions which contain attachments.
  5. Display loaded transactions to your client in a desired way with buttons for confirmation and declination.
  6. Handle events from clicking the confirmation/declination buttons:
    • Prompt the password
    • Call either PCTransactionsManager.sign() or PCTransactionsManager.decline() to sign or decline the transaction.

Getting transaction data

This code snippet demonstrates how to download available transactions and get ready to display them.

import {PCUsersManager, PCTransactionsManager} from @safetech/pcsdk’;

try {
    const users = await PCUsersManager.listStorage();
    // Filter list of PUsers in a desired way
    for (let user of users) {
        if (user.userId === TARGET_USER_ID || user.keyName === TARGET_KEY_NAME) {
            // Load list of transactions for the user
            const transactionsIdArr = await PCTransactionsManager.getTransactionList(user)
            if (transactionsId.length) {
                for (let transactionId of transactionsIdArr) {
                    const transaction = await PCTransactionsManager.getTransaction(user,transactionId);
                    if (transaction.hasBinaryData()) {
                        await PCTransactionsManager.getTransactionBinaryData(user, transaction);
                    } else {
                        // No attachment for this transaction - display it immediately
                    }
                }
            }
        }
    }
} catch (error) {
    // handle the error
}

Displaying transaction data

Before transaction can be confirmed or declined it should be shown to a client. To show the transaction in more user-friendly way consider the following methods that you can use when working with a particular PCTransaction:

  • PCTransaction.getTransactionText() - returns the text of transaction. Might be null if the transaction contains an attachment only;
  • PCTransaction.getSnippet() - returns short description of the transaction which comes in handy when displaying a transaction in the list. Might be null.
  • PCTransaction.getStoredBinaryData() - returns Uint8Array pointing to binary attachment (which is PDF document for example). Might be null if the transaction does not contain any attachment.
  • PCTransaction.getTextRenderType() - returns type of transaction text. It makes sense to call this method if PCTransaction.text non-null and non-empty value to render the text in a proper way. This method returns either null or 'raw' for plain-text transactions and 'markdown' if the text of the transaction follows markdown syntax.
  • PCTransaction.getSnippetRenderType() - the similar for the PCTransaction.textRenderType.

Confirm or decline the transaction

As soon as you have downloaded and displayed transaction data, it can be confirmed through sign method or declined with decline method of PCTransactionsManager class. The sample confirmation flow looks like the following:

import {PCTransactionsManager} from @safetech/pcsdk’;

const password = promptPassword();

if (confirmationButtonPressed) {
    PCTransactionsManager.sign(user, transaction, password)
        .then(/* Notify the client that the transaction has been signed successfully*/)
        .catch((error) => {/* Handle the error if the transaction was not signed*/})
} else {
    PCTransactionsManager.decline(user, transaction, password)
        .then(/* Notify the client that the transaction has been declined successfully*/)
        .catch((error) => {/* Handle the error if the transaction was not declined*/})
}