by Pablo Ruiz
How to add support for gift cards on your Ethereum-based dapps
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 cardrule_Rechargeable
defines whether or not funds can be added to the cardrule_Transfereable
defines whether or not the card can be given to someone else once issuedrule_MinValue
andrule_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.