by Pablo Ruiz

How to add support for gift cards on your Ethereum-based dapps

y2pSCXiQn90NVJpJLky3GCTNMA7HePjtrHkh
Photo by Ben White on Unsplash

Ahhh, Christmas… That magical time of the year where you have to buy presents for all your loved ones and you don’t know what to get them…

Gift Cards are the perfect option for those who don’t know what to buy for someone for the Holidays, their Birthday, or some other special occasion.

Having your store accept gift cards provides a really good way to boost sales and actually allows on-chain purchases “on-behalf” of someone else.

Don’t know which Cryptokitties to buy for your loved ones? Why not get them a gift card and let them buy the ones they really want? (Of course, this wouldn’t work right off the bat as the Cryptokitties smart contracts would have to be modified to accept the gift cards. ?)

In this article, I’m going to go over how to issue on-chain gift cards and how to accept them in your own smart contracts/dapps. For that, we are going to build a GiftCardIssuer smart contract that your own smart contracts can inherit from to work with gift cards.

How the Gift Cards Work

The idea behind this smart contract is to provide the necessary logic for your own contracts that receive payments so they can accept a gift card with a pre-paid balance instead of “cash.”

Anyone would be able to issue a new gift card that is only valid for purchases on your smart contract, and only valid to the person the issuing account chooses as a beneficiary.

Issuing a gift card on a supporting store would be very easy and straightforward. The account wanting to give a gift card as a present just has to call the corresponding function on your smart contract, provide an ID for the card, select the beneficiary account, and pay for it.

Our GiftCardIssuer smart contract will then generate the gift card according to the parameters and business rules we pre-defined. For example, we can have the gift cards generated by our smart contracts only accept a minimum amount of funding to be provided, or we could make them rechargeable.

Developing the GiftCardIssuer Smart Contract

You can check the complete, fully commented code and example implementation on my Github repository.

In the following paragraphs I’ll go over the most important aspects of the GiftCardIssuer smart contract.

Structure of a Gift Card

This is how a gift card issued by our smart contracts would look:

struct Card {        uint value;        uint issueDate;        uint validThru;        address beneficiary;        address generatedBy;        bool rechargeable;        bool transfereable;    }

The struct above defines the basic properties of the gift cards we will be issuing, and helps us set and enforce the business rules we define for them later on.

Setting the Business Rules for the Gift Cards

The gift cards that we create will have some business rules coded into them. These are the rules I’ve defined, but more could be added depending on the store’s needs:

// Card business rules variablesuint public rule_Duration = 365 days;bool public rule_Rechargeable = false;uint public rule_MinValue = 1 wei;uint public rule_MaxValue = 100 ether;bool public rule_Transfereable = true;
  • rule_Duration defines the expiration date of the gift card
  • rule_Rechargeable defines whether or not funds can be added to the card
  • rule_Transfereable defines whether or not the card can be given to someone else once issued
  • rule_MinValue and rule_MaxValue defines the minimum and maximum funding the issuer can add to the card

These business rules can be modified on the fly by the store owner using the function setGiftCardRules(), but have in mind that changes only apply for newly issued gift cards. Cards already issued will retain the rules they were issued with.

function setGiftCardRules(        bool _rechargeable,        bool _transfereable,        uint _duration,        uint _minValue,        uint _maxValue        ) public {        require(msg.sender == owner);        require(duration >= 1 days);        require(_minValue > 0);        require(_maxValue >= _minValue);                rule_Rechargeable = _rechargeable;        rule_Transfereable = _transfereable;        rule_Duration = _duration;        rule_MinValue = _minValue;        rule_MaxValue = _maxValue;    }

Issuing a New Gift Card

Users can issue new gift cards by calling the payable function issueGiftCard() which takes 2 parameters:

  • _cardId: Which is a unique 32 bytes string of the issuer’s choice (They could create a card whose id is “HAPPY BIRTHDAY, JOHN!”)
  • _beneficiary: Which is the account that will be able to use the issued gift card.

When the function is executed, a new gift card will be issued (with the business rules previously set) to the name of the beneficiary and with the funds sent alongside the function call.

function issueGiftCard(bytes32 _cardId, address _beneficiary) public payable {        require(msg.value > 0);        require(cards[_cardId].issueDate == 0);        require(msg.value >= rule_MinValue);        require(msg.value <= rule_MaxValue);                cards[_cardId].value = msg.value;        cards[_cardId].beneficiary = _beneficiary;        cards[_cardId].generatedBy = msg.sender;        cards[_cardId].issueDate = now;        cards[_cardId].validThru = now + rule_Duration;        cards[_cardId].rechargeable = rule_Rechargeable;        cards[_cardId].transfereable = rule_Transfereable;                // add value to merchant balance        balance += msg.value;                E_GiftCardIssued(_cardId, now, msg.sender, _beneficiary,msg.value);    }

Processing the Gift Cards

In order to accept a gift card payment, the store smart contract would have to implement a function that calls the GiftCardIssuer’s useGiftCard() function:

function useGiftCard(bytes32 _cardId, uint _prodPrice) public returns (bool){                // Gift card can only be used by the account it was issued to        require(msg.sender == cards[_cardId].beneficiary);                // card must exist        require(cards[_cardId].issueDate > 0);                // Card must not have expired        require(now <= cards[_cardId].validThru);                // Card should have enough funds to cover the purchase        require(cards[_cardId].value >= _prodPrice);                // remove value from card balance        cards[_cardId].value -= _prodPrice;                E_GiftCardUsed(_cardId, now, cards[_cardId].beneficiary, _prodPrice);            return (true);    }

The function above would be called from the store smart contract (which inherits from GiftCardIssuer) like this:

function buyWithGiftCard(bytes32 _cardId) public {        // Try to buy the product with the gift card provided        require(useGiftCard(_cardId, itemPrice));                itemsBought[msg.sender]++;    }

buyWithGiftCard() accepts a _cardId and calls its useGiftCard function by passing the _cardId and itemPrice . This is the price of the product that will be purchased (and represents how much ether we will subtract from the gift card’s balance).

useGiftCard() proceeds to making sure the gift card is valid and if it is, it subtracts the balance and accepts the payment. If the gift card is not valid, or it doesn’t have enough funds, the function would throw and the whole transaction would fail.

Accepting Payments with Gift Cards

Here is an example implementation of an extremely simple store smart contract that accepts both ether or gift cards:

contract Store is GiftCardIssuer {        uint itemPrice = 1 ether;        mapping(address => uint) public itemsBought;        function buyWithGiftCard(bytes32 _cardId) public {        // Try to buy the product with the gift card provided        require(useGiftCard(_cardId, itemPrice));                itemsBought[msg.sender]++;    }        function buyWithEther() public payable {        require(msg.value == itemPrice);                itemsBought[msg.sender]++;    }}

A smart contract wanting to issue gift cards and accepting them instead of ether would just need to inherit from the GiftCardIssuer base contract and correctly call require(useGiftCard(_cardId, itemPrice));.

Recharging and Transferring the Gift Cards

As defined in the gift card’s business rules section, we can set the gift cards to be “rechargeable” and/or “transferable.”

A transferable gift card can be given to another account by the account currently set as beneficiary. All they have to do is call the following function (as long as the gift card’s rules allow it):

function transferGiftCardTo(bytes32 _cardId, address _newBeneficiary) public {        require(msg.sender == cards[_cardId].beneficiary);        require(cards[_cardId].transfereable);        require(cards[_cardId].issueDate > 0);        require(_newBeneficiary != address(0));                cards[_cardId].beneficiary = _newBeneficiary;    }

A rechargeable gift card can have funds added to it. Anyone can call the following function to add funds to an existing gift card(as long as the gift card’s rules allow it):

function addFundsToGiftCard(bytes32 _cardId) public payable {        require(cards[_cardId].rechargeable);        require(msg.value > 0);        require(cards[_cardId].issueDate > 0);        require(msg.value >= rule_MinValue);        require(msg.value <= rule_MaxValue);                cards[_cardId].value += msg.value;        cards[_cardId].validThru = now + rule_Duration; //Extend duration                // add value to merchant balance        balance += msg.value;    }

Happy Gift Giving!

There’s a lot of new rules and properties that could be added to the gift cards. There is also the possibility of making a universal Gift Card Issuer that generates gift cards not only valid on one store, but on any store part of the issuer network.

I hope you enjoyed reading this article as much as I enjoyed writing it. I’m currently taking consultancy jobs related to smart contracts development. If you are planning on raising funds through an ICO or building a Blockchain-based product, feel free to get in touch with me.