Od nástupu elektronického obchodování se kreditní karty staly rozšířeným způsobem plateb za zboží a služby. Po jejich masivním přijetí se staly zranitelnými vůči únikům dat a hackům. V důsledku toho musí online tržiště chránit platební údaje svých uživatelů. Jedním ze způsobů, jak je to možné, je tokenizace – kdy je číslo kreditní karty zákazníka nahrazeno řadou náhodně generovaných čísel nazývaných „token“. Tento token pak může být předán do různých bezdrátových sítí potřebných ke zpracování platby, aniž by byly odhaleny skutečné údaje o kartě, protože jsou uloženy v zabezpečeném trezoru tokenů. V tomto článku si ukážeme následující:
- Vytvořte falešnou aplikaci pro výběr daní pomocí prostého JavaScriptu a Rave – online platební brány.
- Použijte kreditní kartu k zaplacení naší daně a nechte aplikaci zobrazit token použitý k nahrazení údajů o naší kartě.
- Ověřte, že token skutečně představuje podrobnosti o naší kreditní kartě tím, že jej použijete k provedení další platby v aplikaci.
Pojďme kopat!
Začínáme
Začneme instalací knihoven, které naše aplikace bude potřebovat. Předtím se předpokládá, že na vašem počítači již máte Node a npm. Zde jsou knihovny, které nainstalujeme:
live-server:HTTP vývojový server s možností živého načítání. Nainstalujte to globálně.
body-parser:middleware pro analýzu těla uzlu, který analyzuje těla příchozích požadavků v middlewaru před vašimi handlery a zpřístupňuje je v req.body
vlastnictví.
dotenv:Modul s nulovou závislostí, který načítá proměnné prostředí z .env
soubor do process.env
.
express:Minimální a flexibilní rámec webových aplikací Node, který poskytuje robustní sadu funkcí pro vývoj webových a mobilních aplikací.
morgan:Middleware pro protokolování požadavků HTTP pro Node.
ravepay:Raveova knihovna pro Node.
heroku-logger:Logger pro aplikace Heroku.
Nejprve vytvoříme složku pro daňovou aplikaci, nainstalujeme do ní tyto knihovny spuštěním následujících příkazů na našem terminálu:
mkdir tax-app
cd tax-app
npm install #library name#
Poté si vytvoříme účet Rave, abychom mohli získat naše veřejné a tajné klíče API. Zde si můžete zaregistrovat účet Rave.
Když dokončíme vytváření celého projektu, jeho složka by měla vypadat takto:
Definování uživatelského rozhraní naší aplikace
Prvním krokem je vytvoření HTML stránky pro naši aplikaci. Ve složce aplikace vytvořte podsložku a nazvěte ji frontend
. Zde vytvoříte index.html
soubor:
// frontend/index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Tax Application</title>
<link rel="stylesheet" href="./style.css">
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
</head>
<body>
<div class="parent">
<div id="wrapper" class="wrapper">
<section class="card-api">
<h4>Tax Payment App</h4>
<h5>Pay via credit/debit card here:</h5>
<form id="card-form" method="post">
<div class="cardno">
<label for="cardno">Card Number: </label>
<input type="text" name="cardno" id="cardno">
</div>
<div class="cvv">
<label for="cvv">CVV: </label>
<input type="text" name="cvv" id="cvv">
</div>
<div class="expiry">
<label for="expiry">Expiry Date: </label>
<input type="text" name="expdate" id="expdate">
</div>
<div class="pin">
<label for="pin">PIN: </label>
<input type="text" name="pin" id="pin">
</div>
<button class="pay" id="card-pay" type="submit">Pay Here</button>
</form>
</section>
<section class="tokencharge">
<h4>Pay with Token here:</h4>
<form id="token-form" method="post">
<div class="token">
<label for="token">Card Token: </label>
<input type="text" name="token" id="token">
</div>
<button class="pay" id="token-pay" type="submit">Pay Here</button>
</form>
</section>
</div>
</div>
<script src="./js/index.js"></script>
</body>
</html>
Poté nakonfigurujeme styl pro náš soubor HTML:
// frontend/styles.css
input {
border-radius: 5px;
width: 50%;
}
.wrapper {
display: block;
justify-content:center;
align-self: stretch;
padding: 20px;
background-color: #75a3a3;
border-radius: 5px;
font-family: 'Montserrat', sans-serif;
font-size: 20px;
width: 30%;
}
.error {
margin-top: 15px;
background: #a5391c;
color: #fafafa;
padding: 15px;
border-radius: 6px;
margin-left: 10px;
}
.success {
margin-top: 15px;
background: #138052;
color: #fafafa;
padding: 15px;
width: auto;
border-radius: 6px;
max-width: 100%;
margin-left: 10px;
}
button.pay {
padding: 10px;
border: 1px solid #1d255b;
background: none;
cursor: pointer;
margin-top: 20px;
border-radius: 5px;
width: 100%;
font-family: 'Montserrat', sans-serif;
font-size: 15px;
}
button.pay:hover{
background: #1d255b;
color: #fafafa;
cursor: pointer;
}
.cardno {
display: flex;
justify-content:space-between;
margin: 10px
}
.cvv {
display: flex;
justify-content:space-between;
margin: 10px
}
.expiry {
display: flex;
justify-content:space-between;
margin: 10px
}
.pin {
display: flex;
justify-content:space-between;
margin: 10px
}
.token {
display: flex;
justify-content:space-between;
margin: 10px
}
section {
justify-content:center;
margin: 50px;
}
Až budete hotovi, uložte oba soubory a přejděte na frontend
složku ve vašem terminálu pro opětovné spuštění aplikace:
cd frontend && live-server --port=3000
Ve vašem prohlížeči byste měli mít něco podobného:
Vytváření platebních cest a funkcí
V této části vytvoříme trasy pro naši aplikaci. Uděláme to tak, že nejprve vytvoříme instanci Express Router()
a pomocí jeho post
směrovací API k vytvoření initiatecharge
a completecharge
koncové body, které budou účtovat jakékoli poplatky za kreditní karty. Vytvoříme také chargetokenizedcard
koncový bod, který tokenizuje všechny informace o kreditní kartě a vrátí token, který lze použít pro následné transakce. Je důležité poznamenat, že toto vše se děje prostřednictvím knihovny Node JS společnosti Rave, která slouží jako základní infrastruktura:
const router = require('express').Router();
var Ravepay = require("ravepay"); // require rave nodejs sdk
const logger = require('heroku-logger');
var rave = new Ravepay(process.env.RAVE_PUBLIC_KEY, process.env.RAVE_SECRET_KEY, false); // get public and secret keys from environment variables stored in the .env file.
const util = require('util');
Pojďme definovat cardPaymentObject
který zpracovává údaje o kreditní kartě jakéhokoli daňového poplatníka:
// rave/index.js
var cardPaymentObject = {
"currency": "NGN",
"country": "NG",
"amount": "10",
"suggested_auth": "pin",
"email": "[email protected]",
"phonenumber": "08147658720",
"firstname": "Raphael",
"lastname": "Ugwu",
"IP": "355426087298442",
"txRef": "MC-" + Date.now(),// your unique merchant reference
"meta": [{metaname: "flightID", metavalue: "123949494DC"}],
"redirect_url": "https://your-redirect-url.com/redirect",
"device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c",
}
Poté definujeme naše směrovací API:
// rave/index.js
router.get('/', (req, res) => {
console.log("Here's the rave route");
res.json({message: "Here's the rave route"});
});
router.post('/initiatecharge', (req, res) => {
var { cardno, expdate, cvv, pin } = req.body;
// update payload
cardPaymentObject.cardno = cardno;
cardPaymentObject.cvv = cvv;
cardPaymentObject.pin = pin;
cardPaymentObject.expirymonth = expdate.split('/')[0];
cardPaymentObject.expiryyear = expdate.split('/')[1];
logger.info(JSON.stringify(cardPaymentObject));
rave.Card.charge(cardPaymentObject)
.then((response) => {
logger.info(JSON.stringify(response));
res.json(response)
})
.catch((error) => {
logger.error(error)
res.json(error)
})
});
router.post('/chargetokenizedcard', (req, res) => {
var { token } = req.body;
cardPaymentObject.token = token;
logger.info(cardPaymentObject);
rave.TokenCharge.card(cardPaymentObject)
.then((response) => {
// console.log(response)
res.json(response)
}).catch(error => {
// console.log(error)
res.json(error)
})
});
router.post('/completecharge', (req,res) => {
var { transaction_reference, transactionreference, otp } = req.body;
// perform card charge validation
rave.Card.validate({
transaction_reference,
otp
}).then((response) => {
console.log(response)
res.json(response)
}).catch(error => {
console.log(error)
res.json(error)
})
})
module.exports = router;
Vytvoření serveru Node
Naším dalším krokem je vytvoření serveru Node, který bude reagovat na požadavky, které zadáme na frontendu naší aplikace. V kořenové složce aplikace vytvořte soubor s názvem app.js
a do něj vložte ukázku kódu níže:
const app = require('express')();
const fs = require('fs')
const bodyParser = require('body-parser');
const morgan = require('morgan');
var port = process.env.PORT || 80 // 2. Using process.env.PORT
// app.use(cors(corsOptions));
app.use(function (req, res, next) {
// 'https://hidden-earth-62758.herokuapp.com'
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type,Authorization,Accept');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
const rave = require('./rave');
app.use(bodyParser.urlencoded({extended:false, limit: '10mb'}));
app.use(bodyParser.json());
app.use(morgan('dev'));
app.get('/', (req, res) => {
res.send({message: 'Split Payment Sample'});
})
app.use('/rave', rave);
app.set('port', port);
app.listen(port, '', () => {
console.info('App listening on port %s', port);
})
Ve výše uvedené ukázce kódu zajišťujeme, že náš server zpracovává jakýkoli příchozí požadavek prostřednictvím middlewaru Express. To zahrnuje protokolování všech požadavků HTTP pomocí morgan
, analyzující tělo příchozích požadavků pomocí bodyParser
a vyžadující platební cesty a funkce, které jsme definovali dříve.
Zpracování platební logiky naší aplikace
Pojďme vytvořit některé funkce pro lepší interakci s našimi aplikacemi na frontendu. V frontend
vytvořte soubor a pojmenujte jej index.js
. Nejprve definujeme všechny proměnné, které potřebujeme pro manipulaci s daty:
var cardPay = document.getElementById('card-pay');
var tokenPay = document.getElementById('token-pay');
var cardForm = document.getElementById("card-form");
var tokenForm = document.getElementById("token-form");
var wrapper = document.getElementById("wrapper");
var server_url = 'http://localhost:80/'; // the nodejs server url
Poté definujeme funkci, kterou potřebujeme pro zpracování jakéhokoli požadavku, který vytvoříme na koncových bodech, které jsme vytvořili dříve:
function makeRequest(endpoint, data, external=false) {
var url = external ? endpoint : server_url + endpoint;
var options = {
method: "POST",
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json; charset=utf-8",
},
redirect: "follow",
referrer: "no-referrer",
body: JSON.stringify(data)
}
return new Promise(function _(resolve, reject) {
fetch(url, options).then(function _(response) {
console.log(response)
return response.json()
}).then(function _ (data) {
console.log(data)
if(data.body == undefined) resolve(data)
resolve(data.body)
}).catch(function _ (error) {
reject(error)
}).catch(function _ (error) {
reject(error)
})
});
}
Abychom dali našim uživatelům vědět, pokud by mohlo dojít k chybě, vytvoříme dvě funkce – jednu pro zobrazení zprávy o úspěchu a druhou pro zobrazení chybové zprávy v případě, že požadavek POST neprojde:
//frontent/index.js
function showSuccessMessage(message, element) {
var div = document.createElement("div");
div.setAttribute('class','success');
div.setAttribute('id','message');
div.innerHTML = '<i class="fas fa-check-circle"></i> ' +message
div.appendAfter(element)
}
function showErrorMessage(message, element) {
var div = document.createElement("div");
div.setAttribute('class','error')
div.setAttribute('id','message')
div.innerHTML = '<i class="fas fa-times-circle"></i> ' +message
div.appendAfter(element)
}
Dále propojme tlačítko s makeRequest()
funkce a initiatecharge
koncový bod:
cardPay.addEventListener('click', function(e) {
e.preventDefault();
// call api
var formData = extractFormValues(cardForm);
makeRequest('rave/initiatecharge', formData)
.then(response => {
if (response.status == "error") {
showErrorMessage(response.message, cardPay);
}
if (response.data.chargeResponseCode == 02) { // a chargeResponseCode of 02 depicts that the transaction needs OTP validation to continue
otp = prompt(response.data.chargeResponseMessage);
transaction_reference = response.data.flwRef;
makeRequest('rave/completecharge', {
otp,
transaction_reference
})
.then(function _(response) {
if (response.data.data.responsecode == 00) {
// the card token is accessed here: response.data.tx.chargeToken.embed_token
showSuccessMessage(response.data.data.responsemessage + "<br/>Here is your token, you may use this for subsequent payments<br/>" + response.data.tx.chargeToken.embed_token, cardPay);
} else if (response.data.data.responsecode == 'RR') { // the charge failed for the reason contained in // response.data.data.responsemessage
showErrorMessage(response.data.data.responsemessage, cardPay)
} else { // the charge failed for the reason contained in // response.message
showErrorMessage(response.message, cardPay)
}
}).catch(function _(error) {
showErrorMessage(error, cardPay)
})
}
}).catch(function _(error) {
showErrorMessage(error, cardPay)
})
});
Zadávání proměnných prostředí
Ve všech trasách, které jsme vytvořili, jsme zmínili naše veřejné a tajné klíče, ale ještě je musíme definovat. Udělejme to tak, že je specifikujeme jako proměnné prostředí v .env
soubor, který vytvoříme v kořenové složce naší aplikace:
// .env
RAVE_PUBLIC_KEY= *YOUR RAVE PUBLIC KEY HERE*
RAVE_SECRET_KEY=*YOUR RAVE SECRET KEY HERE*
Ověření platby
Dokončili jsme vytváření celé aplikace. Uložte veškerou svou práci a na svém terminálu spusťte server Node:
nodemon app.js
Zatímco server běží, otevřete další kartu v terminálu a spusťte vývojový server pro aplikaci:
cd client && live-server --port=3000
Tím se aplikace otevře na adrese http://127.0.0.1:3000 ve vašem prohlížeči. V tomto okamžiku byste měli aplikaci vidět. Zkuste provést platbu pomocí jedné z testovacích karet uvedených zde:
Abychom potvrdili, že platba proběhla úspěšně, zašleme nám potvrzení:
Když jsme poprvé použili k platbě kartu, dostali jsme token. Proveďte platbu pomocí tokenu:
A také za to dostaneme účtenku:
Závěr
Tokenizace karty je skvělý způsob, jak chránit údaje o kartách uživatelů vaší aplikace před zachycením a zneužitím ke škodlivým účelům. V tomto článku jsme vytvořili aplikaci, která ukazuje, jak Rave tokenizuje kartu a používá tento token pro budoucí transakci. Zdrojový kód této aplikace si můžete prohlédnout na GitHubu.