Server-side plate payment processing
To begin support plate payments, install the framework Sails.js (guide), create a Sails.js application and install the Node.js module ‘billon’: sudo npm install billon --save
After installing the module that supports payments a file (service) named eg. ‘plates.js’ must be created in file structure generated by ‘Sails.js’ in catalog ‘/api/services/’:
var billon = require("billon"); var myShop = new billon.Payment(); var alpha = 10; // the cheapest article, number of thousandths of a basic monetary unit, eg. 1p var beta = 100; // a price of the second article, eg. 10p var gamma = 200; // a price of the third article, eg. 20p var delta = 500; // a price of the fourth article, eg. 50p var epsilon = 1000; // a price of the most expensive article, eg. 1 pound myShop.user(function (requestData, cb) { cb(requestData.user); }).products(function (requestData, cb) { cb(requestData.itemsId); }).product(function (requestData, itemId, cb) { switch (itemId) { // a list of articles - products, article setting according to the type of the plate is not recommended in case of tariffs consisting of a smaller number of plates case 1: cb(alpha); break; case 2: cb(beta); break; case 3: cb(gamma); break; case 4: cb(delta); break; case 5: cb(epsilon); break; } }).productBillon(function (requestData, item, cb) { // call of the method not required if parameter 'quantity' is defined for all products cb(item); }).paymentTitle(function (requestData, items, delivery, cb) { return cb("Payment title"); }).plates(function(requestData, cb) { // price setting, the lack of an argument or number 0 removes a plate from the tariff cb(alpha, beta, gamma, delta, epsilon); }).cNode(function (requestData, items, delivery, cb) { cb("eyJhkGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3VyIjp7ImlkIjoyNSwic2FsdCI6IjEyZWVkMzQwLTM2NGUtMThNjgyN1hNDM5zA1MiOjEh0OTCJpYXQiDk0NjF9Q1M.zeWXG7Nyr3PFVhb4HRgLi5rh8fLZx41VHySHWUStIzc"); // API key użytkownika serwisu "billon.me" /*cb({ name: "billonpayments4", // CNode username address: "http://213.189.38.34:20891" // address and port of CNode });*/ // direct communication with CNode }).currency(function(requestData, cb) { // ISO 4217 currency code, not required method, 'PLN' by default cb('GBP'); }).maxTotalAmount(function(requestData, cb) { // the maximum sum payment in the series expressed in thousandths of a basic monetary unit, not required method cb(10000); }).duration(function(requestData, cb) { // the duration of a series of payments in seconds, the required method in order that the series has a non-zero length of duration cb(3600); }).maxPaymentsInSeries(function(requestData, cb) { // the maximum number of payments in the series, not required method cb(10); }).platePayment(function(requestData, cb) { // setting of series identifier and kind of plate cb(requestData.seriesId, requestData.type); }).merchantBrandName(function(requestData, cb) { // setting of brand, not required method, domain of the page on which payment is executed by default cb('Nazwa brandu'); }).onReceivedPlatePaymentSeriesStatus(function (response) { // event of CNode response during the proces of the acceptance of the series, not reqiured method console.log(response); if (response.status == 'SERIES_ACCEPTED' || response.status == 'TRANSFERRING' || response.status == 'TRANSFERING') { console.log('The series of payment accepted'); } }).onAcceptedPlatePaymentSeries(function (paymentData) { // event of acceptance of the series, not required method console.log(paymentData); }).onFinishedPayment(function (items, delivery_option, document, requestData) { // event of finished payment, not required method console.log(document); console.log(requestData); if (requestData.itemsId.constructor === Array) { for (var i = 0; i < requestData.itemsId; ++i) { // a operation can be performed here after successfully finished purchase of article - product console.log(requestData.itemsId[i]); } } }).onRequestSend(function (requestName, parameters, requestResponse, error) { // the event of sending SOAP request, not required method console.log('SOAP request: ', requestName); console.log('Parameters of request: ', parameters); console.log('Response of the app: ', requestResponse); console.log('Error while sending: ', error); }); module.exports = myShop;
Once you have configured the server and Sails.js service, you need to create a controller to be able to communicate with the user on a webpage
sails generate controller Plates
A file ‘PlatesController.js’, in which the communication to given address must be handled and associated with previously created service ‘plates.js’, will be created in catalog ‘api/controllers’. Example of file ‘PlatesController.js’:
module.exports = { list: function(req, res) { // method that outputs the list of prices, the currency, the limit of the duration of the series, // the maximum amount of the payments in the series and merchant brand name plates.tariff(res); }, initiate: function(req, res) { // method that initiate start of series of payment plates.platePaymentSeriesStart(req, res); }, pay: function(req, res) { // method that initiates payment plates.paymentInitiation(req, res); }, status: function(req, res) { // listening method plates.paymentStatus(req.body.taskId || req.query.taskId, function(response) { res.json(response); }); } };
Taking plate payments on webpages
Use JavaScript Billon scripts for taking the plate payments on a webpage:
<div id="button-container-1"></div> <script src="//showroom.billongroup.com/lib/js/billon.js"></script> <script src="//showroom.billongroup.com/lib/js/billon-agent.js"></script> <script src="//showroom.billongroup.com/lib/js/billon-object.js"></script> <script src="//showroom.billongroup.com/lib/js/billon-user.js"></script> <script src="//showroom.billongroup.com/lib/js/billon-plates.js"></script> <script> billon.plates({ connection: new billon.agent("http://billon.info:1401/plates"), // address pointing to Sails.js controller that supports payment (in this case PlatesController) lang: 'en', // language 'en' or 'pl', 'en' by default plateButtonAddress: "https://showroom.billongroup.com/lib/screen/plate-button/index.html", // in the case of not showing buttons, skip in the case of the lack of problems plateDialogAddress: "https://showroom.billongroup.com/lib/screen/plate-dialog/index.html", // in the case of not showing dialog, skip in the case of the lack of problems skipAnotherUser: false, // optional - omission of the step of of choosing another user than detected, the step is not skipped by default disableButtonsTitle: false, // optional - default not adding title (tooltip) with the price to the buttons, the titles are being set by default tariff: { address: 'list', // name of controller parameter where method 'tariff' is being called displayedCurrency: '£', // displayed currency, ISO 4217 currency code set with Node.js module 'billon' by default // if a parameter 'displayedCurrencyBefore' is not set and the amounts greater than or equal to 1 or a parameter 'centSign' is not set, // they are formatted by default according to ISO 4217 currency code set with Node.js module 'billon' and user's locale, // ISO 4217 currency code set with server payment module is used as sign of currency by default, when 'displayCurrencyBefore' is set displayCurrencyBefore: true, // displaying the sign of currency before the number, // the sign from the parameter 'displayedCurrency' is appended to the number at the end by default centSign: 'p', // sign of currency for the hundredth part of the basic monetary unit displayed for the price lower than this unit, // the sign is used as for higher prices by default displayCentSignBefore: false // displaying the sign of currency set in parameter 'centSign' before number, this sign is appended to the number at the end by default }, series: { request: { address: 'initiate', // name of controller parameter where method 'platePaymentSeriesStart' is being called data: function (user) { return { user: user }; } }, listenResponse: function (response, user) { if (response.status == "SERIES_ACCEPTED" || response.status == "TRANSFERRING" || response.status == "TRANSFERING") { // save of identifier of series and Billon username, in this case in Local Storage localStorage.setItem("seriesId", response.taskId); localStorage.setItem("user", JSON.stringify(user)); } } }, paymentRequest: { address: 'pay', // name of controller parameter where method 'paymentInitiation' is being called data: function() { // reading previously saved identifier of series and Billon username, in this case from Local Storage try { return { seriesId: localStorage.getItem("seriesId"), user: JSON.parse(localStorage.getItem("user")) }; } catch (e) { return {}; } } }, listen: { address: "status", // name of controller parameter where method 'paymentStatus' is being called data: function (requestResponse) { return { taskId: requestResponse.taskId }; } }, buttons: [ // array of buttons { place: document.getElementById('button-container-1'), // HTML element in which the button is to be placed type: 'alpha', // 0 can by given instead of 'aplha' - type of the button disableTitle: false, // optional - not setting the title of the button with the price, // the global option 'DisableButtonsTitle' is acted on by default requestData: function(type) { return { type: type, itemsId: [1] // array of identifiers of products } }, listenResponse: function(response) { // optional method switch (response.status) { case "FINISHED_OK": // event of successfully purchase with the button break; case "FINISHED_ERR_USER_REJECTED": // a lack of possibility to pay within the stored series localStorage.setItem("seriesId", null); localStorage.setItem("user", JSON.stringify(null)); } } }, { place: document.getElementById('button-container-2'), type: 'beta', requestData: function(type) { return { type: type, itemsId: [2] } }, listenResponse: function(response) { switch (response.status) { case "FINISHED_OK": // event of successfully purchase with the button break; case "FINISHED_ERR_USER_REJECTED": // a lack of possibility to pay within the stored series localStorage.setItem("seriesId", null); localStorage.setItem("user", JSON.stringify(null)); } } }, { place: document.getElementById('button-container-3'), type: 'gamma', requestData: function(type) { return { type: type, itemsId: [3] } }, listenResponse: function(response) { switch (response.status) { case "FINISHED_OK": // event of successfully purchase with the button break; case "FINISHED_ERR_USER_REJECTED": // a lack of possibility to pay within the stored series localStorage.setItem("seriesId", null); localStorage.setItem("user", JSON.stringify(null)); } } }, { place: document.getElementById('button-container-4'), type: 'delta', requestData: function(type) { return { type: type, itemsId: [4] } }, listenResponse: function(response) { switch (response.status) { case "FINISHED_OK": // event of successfully purchase with the button break; case "FINISHED_ERR_USER_REJECTED": // a lack of possibility to pay within the stored series localStorage.setItem("seriesId", null); localStorage.setItem("user", JSON.stringify(null)); } } }, { place: document.getElementById('button-container-5'), type: 'epsilon', requestData: function(type) { return { type: type, itemsId: [5] } }, listenResponse: function(response) { switch (response.status) { case "FINISHED_OK": // event of successfully purchase with the button break; case "FINISHED_ERR_USER_REJECTED": // a lack of possibility to pay within the stored series localStorage.setItem("seriesId", null); localStorage.setItem("user", JSON.stringify(null)); } } }] }); </script>