How to Charge with PumaPay on Your Site

The alpha version of the PumaPay SDK is now available for developers and businesses to use free of charge. Built around the features of the ERC20 token, this initial version of the PumaPay solution is an open-source protocol that flexibly integrates with any kind of merchant platform. This allows developers to either clone the entire project or use only parts of it directly into their platform or edit it as it may suit the merchant's needs. While this only facilitates a crypto-typical push action, the full-scale Pull protocol will come with a future version of this software.

This first version of our SDK can be found on our GitHub account and consists of a user interface (UI) through which people can buy photos using PumaPay Tokens (PMA) and the back-end server, which consists of a set of APIs that can be used to allow for the customers to make purchases using either the PumaPay wallet mobile application or Metamask. A PostgreSQL database is attached to the back-end server to deal with authentication, purchases and basic e-commerce business logic.


The main programming language that we’ve used for the development of the different components is Typescript. Specifically, we’ve used NodeJS written in TS for our backend and Angular 5 for our frontend.

Before diving into more technical details, let us first explore the application, what it does and how someone can use it to purchase photos (or any other kind of product). Feel free to check our live demo.

Our PumaPay demo boutique allows us to demonstrate the concept of purchasing photos using PumaPay Tokens without the need to register (unregistered purchase). We will refer to the photos as items from hereafter since our solution is generic.

It illustrates the concept of purchasing credit packages that can be used for buying the items (credit purchase).

For additional details please refer to our previous article.

PumaPay payment flow

The payment flow using PumaPay Tokens is very simple and straightforward, and it supports both registered and unregistered user’s purchases. The customers can select an item (unregistered purchase) or a credit package (credit purchase) that they would like to purchase using PumaPay. On the item/credit package selection a session is being initiated in our backend and stored in our database for future reference of the specific purchase. The session ID is also stored in the local storage of the client. The customer is then presented with the option to purchase using either our PumaPay Mobile Wallet or Metamask.

We will stick to the unregistered purchase scenario in the rest of the article, even though there are no differences between registered and unregistered use case when it comes to the payment flow. The only difference is that in the latter case, the customer purchases item, whereas in the former one, credits that the customer can use for purchases.

PumaPay cryptocurrency wallet payment

When the user decides to pay using the PumaPay Cryptocurrency Wallet, a QR is being generated using the session ID and the item ID to construct a URL linked to one of our APIs. That API constructs the transaction details and presents it to the mobile wallet for the customer to approve the payment and submit the transaction to the blockchain. At that point, a web socket is established for that specific session, which communicates the transaction statuses (scanned, open, approved, declined, cancelled) to the client. Once the transaction is approved by the blockchain network, the customer has completed the purchase.


Metamask payment

We have integrated our transactions via the Metamask chrome plugin, this allows us to verify and carry out transactions through the plugin. The integration with Metamask is very similar with the wallet payment flow, with the only difference being that instead of calling the API to retrieve the transaction details data from the wallet, an API is being called from the client to retrieve the same data, and initiates the transaction through the Metamask plugin.

Technical deep dive | Code samples

In this section the most important parts of our SDK will be explained in more detail with code samples. You can find the full list of our APIs here.

Backend | Node.js – Typescript

Session creation

Once the customer selects an item to purchase, a session is created in our backend, which is stored in our database and sent back to the client to be used as a reference for the purchase session that has been initiated.

interface ISession {
    sessionID: string;
    txHash: string;
    status: TxStatus;
    fromPumaWallet: boolean;

enum TxStatus { initiated = -1, scanned, open, approved, declined, cancelled }

Transaction data creation

When the customer selects the payment option (PumaPay wallet or Metamask) a request is being sent in our backend, which constructs the transaction data for that specific purchase. The session ID is being sent along with the item ID. The item details (_name, _description, _value) are retrieved from our DB with the owner ETH address (_to) to build the transaction data through our Transaction Builder.

class Transaction {
    private _hash: string;
    private _callback: string;
    private _description: string;
    private _name: string;
    private _networkID: number;
    private _to: string;
    private _value: number;
    private _signature: string;

    public constructor(transactionBuilder: TransactionBuilder) {
        this._callback = transactionBuilder.callbackUrl;
        this._description = transactionBuilder.description;
        this._name =;
        this._networkID = 3;
        this._to =;
        this._value = unit.convert(transactionBuilder.value, 'eth', 'wei');
        this._signature = new SignatureCalculator(this).calculate();

Transaction data signing

The transaction data includes the signature of the transaction itself, which serves as a verification mechanism from the mobile wallet to specify whether a transaction comes from a secure and verified merchant. The hash is being calculated using solidity Keccak256 function with a prefixed message, which is eventually being signed with a private key. On the wallet side, the signature is being decrypted and the public key is checked against a list of verified sources.

public calculate(): string {
        // convert the transaction data into a hex hash using keccak256
        const hash = Buffer.from(ethers.utils.solidityKeccak256(
            ['bytes', 'bytes', 'bytes', 'uint256', 'address', 'uint256'],
                //the variables of type bytes need to be converted in utf8
                Buffer.from(this.transaction.callback, 'utf8'),
                Buffer.from(this.transaction.description, 'utf8'),
                Buffer.from(, 'utf8'),
            ]).substr(2), 'hex');

        // store in a buffer the message as a prefix variable
        const prefix = new Buffer('\x19Ethereum Signed Message:\n');
        // store in a buffer the hash and then convert the hash with the prefix into a new hex hash using keccak256
        const prefixedMsg = Buffer.from(
                [prefix, new Buffer(String(hash.length)), hash]
            )).substr(2), 'hex');
        const privateKey = Buffer.from(PRIVATE_KEY_OF_MERCHANT, 'hex');
        // convert the prefix message and the private key using ecsign to create the digital signature
        const sig = utils.ecsign(prefixedMsg, privateKey);
        // convert the signature parameters r,v,s into the format of eth signature RPC method using toRpcSig
        return utils.toRpcSig(sig.v, sig.r, sig.s);

Callback URL

The callback URL is used for updating the transaction status from the blockchain network. Query parameters for the transaction hash, the status and a boolean are attached to it to update the session in the DB. It is called from the mobile wallet or the client when the transaction is being scanned, submitted, approved, declined or cancelled.

transactionBuilder.callbackUrl =

Web socket

When the callback URL is being called either from the wallet app or the client, a web socket is created for that specific session, which transmits the updated session details (see above) to the client.

webSocket.emit(`txStatus/${sessionID}`, sessionDetails);

Frontend | Angular 5 – Typescript

Session handling

A new session is initiated from the client every time that the customer selects an item to purchase (photo/credit package). Once the session ID is retrieved from the backend, it is stored in the local storage of the browser from where it is retrieved later to generate the QR code, establish the web socket connect and retrieve the transaction status for the specific session. When the customer decides to select another product for purchase, the local storage of the browser is cleared, and the new session ID is stored. This is a very simplistic way of dealing with sessions, and it is suggested that the merchant should use their existing session handling.

QR code generator

The QR generator lives within our client, which essentially constructs the URL to one of our APIs to retrieve the transaction data for the initiated session and the selected item for purchase.

public getQrDataForItem(): string {
    const sessionID = localStorage.getItem('sessionID');
    const itemID = localStorage.getItem('itemID');
    return JSON.stringify(
url: `${HOST}${PREFIX}transaction/item/tx/plain/${sessionID}/${itemID}`});

Web socket

The connection with web socket initiated in the backend is established from the client once the customer selects to pay with either our PumaPay wallet or the Metamask plugin. The session ID from the local storage is being used to connect to the correct web socket connection. Once there is a new status coming from the backend, the status on the UI is also being updated to let the customer know the status of their transaction, at any point.


We have designed and implemented a unique push payment protocol and demo boutique that both demonstrates and gives implementers the ability to make purchases in a quick and efficient manner.

PumaPay GitHub Repo
PoC Repo