Obsługa płatności talerzykowych po stronie serwera
Obsługę płatności talerzykowych należy rozpocząć od zainstalowania frameworku Sails.js (instrukcja), utworzenia aplikacji Sails.js i zainstalowaniu w niej modułu Node.js – billon: sudo npm install billon --save
Po zainstalowaniu modułu obsługującego płatność w wygenerowanej przez „Sails.js” strukturze plików należy utworzyć w katalogu „/api/services/” plik (usługę) o nazwie np. „plates.js”:
var billon = require("billon"); var myShop = new billon.Payment(); var alpha = 10; // cena za najtańszy artykuł, np. 1 gr (liczba dziesiątych części grosza) var beta = 100; // cena za drugi artykuł, np. 10 gr var gamma = 200; // cena za trzeci artykuł, np. 20 gr var delta = 500; // cena za czwarty artykuł, np. 50 gr var epsilon = 1000; // cena za najdroższy artykuł, np. 1 zł myShop.user(function (requestData, cb) { cb(requestData.user); }).products(function (requestData, cb) { cb(requestData.itemsId); }).product(function (requestData, itemId, cb) { switch (itemId) { // lista artykułów - produktów, ustawianie artykułu według typu talerzyka jest niezalecane w przypadku taryf składających się z mniejszej liczby talerzyków 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) { // wywołanie metody niewymagane, jeśli dla wszystkich produktów określono parametr "quantity" cb(item); }).paymentTitle(function (requestData, items, delivery, cb) { return cb("Tytuł płatności"); }).plates(function(requestData, cb) { // ustawienie cen, brak argumentu lub liczba 0 usuwa talerzyk z taryfy 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", // nazwa użytkownika węzła Corporate Node address: "http://213.189.38.34:20891" // adres i port węzła Corporate Node });*/ // komunikacja bezpośrednio z węzłem Corporate Node }).currency(function(requestData, cb) { // kod ISO 4217 waluty, metoda niewymagana, domyślnie "PLN" cb('PLN'); }).maxTotalAmount(function(requestData, cb) { // maksymalna suma płatności w serii wyrażona w dziesiątych częściach grosza, metoda niewymagana cb(10000); }).duration(function(requestData, cb) { // czas trwania serii płatności w sekundach, metoda wymagana, aby seria miała nie zerową długość czasu trwania cb(3600); }).maxPaymentsInSeries(function(requestData, cb) { // maksymalna liczba płatności w serii, metoda niewymagana cb(10); }).platePayment(function(requestData, cb) { // ustawienie identyfikatora serii i rodzaju talerzyka cb(requestData.seriesId, requestData.type); }).merchantBrandName(function(requestData, cb) { // ustawienie brandu, metoda niewymagana, domyślnie domena strony, na której wykonywane są płatności cb('Nazwa brandu'); }).onReceivedPlatePaymentSeriesStatus(function (response) { // zdarzenie odpowiedzi węzła Corporate Node w tracie operacji akceptacji serii, metoda niewymagana console.log(response); if (response.status == 'SERIES_ACCEPTED' || response.status == 'TRANSFERRING' || response.status == 'TRANSFERING') { console.log('Seria płatności zakceptowana'); } }).onAcceptedPlatePaymentSeries(function (paymentData) { // zdarzenie zaakceptowanej serii płatności, metoda niewymagana console.log(paymentData); }).onFinishedPayment(function (items, delivery_option, document, requestData) { // zdarzenie ukończonej płatności, metoda niewymagana console.log(document); console.log(requestData); if (requestData.itemsId.constructor === Array) { for (var i = 0; i < requestData.itemsId; ++i) { // tutaj można wykonać operację po pomyślnie dokonanym zakupie artykułu - produktu console.log(requestData.itemsId[i]); } } }).onRequestSend(function (requestName, parameters, requestResponse, error) { // zdarzenie wysłania operacji SOAP, metoda niewymagana console.log('Wiadomość SOAP: ', requestName); console.log('Parametry wiadomości: ', parameters); console.log('Odpowiedź węzła Corporate Node: ', requestResponse); console.log('Błąd przy wysyłaniu: ', error); }); module.exports = myShop;
Kiedy już posiadamy skonfigurowany serwer i usługę Sails.js, trzeba utworzyć kontroler, aby mógł się on skomunikować z użytkownikiem strony WWW:
sails generate controller Plates
W katalogu „api/controllers” powstanie plik „PlatesController.js”, w którym należy obsłużyć komunikację do danego adresu i powiązać ją z usługą „plates.js”, którą wcześniej utworzyliśmy. Przykład pliku „PlatesController.js”:
module.exports = { list: function(req, res) { // metoda wysyłającą ustawioną listę cen, walutę, // limit czasu trwania serii, maksymalną sumę płatności w serii i nazwę brandu plates.tariff(res); }, initiate: function(req, res) { // metoda inicjująca start serii płatności plates.platePaymentSeriesStart(req, res); }, pay: function(req, res) { // metoda inicjująca płatność plates.paymentInitiation(req, res); }, status: function(req, res) { // metoda nasłuchująca plates.paymentStatus(req.body.taskId || req.query.taskId, function(response) { res.json(response); }); } };
Podpięcie płatności talerzykowych na stronach internetowych
W celu podpięcia płatności do strony internetowej należy skorzystać ze skryptów JavaScript Billon:
<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"), // adres wskazujący na kontroler Sails.js obsługujący płatność (w tym przypadku PlatesController) lang: 'pl', // język "pl" lub "en", domyślnie "en" plateButtonAddress: "https://showroom.billongroup.com/lib/screen/plate-button/index.html", // w przypadku problemów z niepokazywaniem przycisków, w przypadku braku problemów pominąć plateDialogAddress: "https://showroom.billongroup.com/lib/screen/plate-dialog/index.html", // w przypadku problemów z niepokazywaniem okna dialogowego, w przypadku braku problemów pominąć skipAnotherUser: false, // opcjonalne - pominięcie kroku wyboru innego użytkownika niż wykryty, domyślnie krok nie jest pomijany disableButtonsTitle: false, // opcjonalne - domyślne niedodawanie tytułu (etykiety) z ceną do przycisków, domyślnie tytuły są ustawiane tariff: { address: 'list', // nazwa parametru kontrolera, gdzie wywoływana jest metoda "tariff" displayedCurrency: ' zł', // wyświetlana waluta, // domyślnie kwoty są sformatowana według kodu ISO 4217 waluty ustawionej za pomocą serwerowego modułu Node.js 'billon' i ustawień regionalnych użytkownika, // jeśli nie ustawiono parametru "displayedCurrencyBefore" i gdy są wyższe lub równe 1 lub nie ustawiono parametru, // przy ustawionym parametrze "displayCurrencyBefore" jako symbol waluty używany jest kod ISO 4217 waluty ustawionej za pomocą modułu Node.js 'billon' displayCurrencyBefore: false, // czy wyświetlać walutę przed liczbą, // domyślnie symbol z parametru "displayedCurrency" doklejany jest na końcu liczby centSign: ' gr', // symbol waluty dla setnej części podstawowej jednostki pieniężnej wyświetlany dla cen o wartości poniżej tej jednostki, // domyślnie stosowany jest symbol, jak dla wyższych cen displayCentSignBefore: false // czy wyświetlać symbol waluty ustawiony w parametrze "centSign" przed liczbą, domyślnie ten symbol doklejany jest na końcu liczby }, series: { request: { address: 'initiate', // nazwa parametru kontrolera, gdzie wywoływana jest metoda "platePaymentSeriesStart" data: function (user) { return { user: user }; } }, listenResponse: function (response, user) { if (response.status == "SERIES_ACCEPTED" || response.status == "TRANSFERRING" || response.status == "TRANSFERING") { // zapis identyfikatora serii i nazwy użytkownika Billon, w tym przypadku w Local Storage localStorage.setItem("seriesId", response.taskId); localStorage.setItem("user", JSON.stringify(user)); } } }, paymentRequest: { address: 'pay', // nazwa parametru kontrolera, gdzie wywoływana jest metoda "paymentInitiation" data: function() { // odczyt zapisanego wcześniej identyfikatora serii i nazwy użytkownika Billon, w tym przypadku z Local Storage try { return { seriesId: localStorage.getItem("seriesId"), user: JSON.parse(localStorage.getItem("user")) }; } catch (e) { return {}; } } }, listen: { address: "status", // nazwa parametru kontrolera, gdzie wywoływana jest metoda "paymentStatus" data: function (requestResponse) { return { taskId: requestResponse.taskId }; } }, buttons: [ // tablica przycisków { place: document.getElementById('button-container-1'), // element HTML, w którym ma być umieszczony przycisk type: 'alpha', // można podać 0 zamiast 'alpha' - typ przycisku disableTitle: false, // opcjonalne - nieustawianie tytułu przycisku z ceną, // domyślnie brana pod uwagę jest globalna opcja "DisableButtonsTitle" requestData: function(type) { return { type: type, itemsId: [1] // tablica identyfikatorów produktów } }, listenResponse: function(response) { // metoda opcjonalna switch (response.status) { case "FINISHED_OK": // zdarzenie pomyślnej zapłaty przyciskiem break; case "FINISHED_ERR_USER_REJECTED": // brak możliwości płatności w ramach zapamiętanej serii 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": // zdarzenie pomyślnej zapłaty przyciskiem break; case "FINISHED_ERR_USER_REJECTED": // brak możliwości płatności w ramach zapamiętanej serii 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": // zdarzenie pomyślnej zapłaty przyciskiem break; case "FINISHED_ERR_USER_REJECTED": // brak możliwości płatności w ramach zapamiętanej serii 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": // zdarzenie pomyślnej zapłaty przyciskiem break; case "FINISHED_ERR_USER_REJECTED": // brak możliwości płatności w ramach zapamiętanej serii 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": // zdarzenie pomyślnej zapłaty przyciskiem break; case "FINISHED_ERR_USER_REJECTED": // brak możliwości płatności w ramach zapamiętanej serii localStorage.setItem("seriesId", null); localStorage.setItem("user", JSON.stringify(null)); } } }] }); </script>