Add Bitcoin payment method (#121)
* fix settins form submit not working * prepare routes/config structure to support new blockonomics payment method * setup blockonomics view, get address and calculate btc * blockonomics subscribe to web socket to listen for real time order updates on checkout page * create order with pending status while waiting for blockonomics payment and pass orderid to frontend * check received amount is enough * blockonomics btc amount check, backend order confirmation, show order info into backend and frontend * cleanup and empty cart when order is payed * decline order if insufficient amount * handle email and lunr indexing for blockonomis * blockonomics new confirmation page, auto redirect at confirmation zero, 10 minutes timeout * Update README with Blockonomics * add countdown timer to blockonomics payment page * updated README and checkout page * update README with blockonomics return url * build * reply to blockonomics http callback * dont check amount on frontend * restore default settings * switch from unirest to axios, remove unirest dep * fix lint problems * restore gitignore * restore baseSchema * build Co-authored-by: GECKO <4787777-geckojs@users.noreply.gitlab.com>master
parent
f3a7231016
commit
7c7af39f4b
|
@ -5,4 +5,3 @@ public/uploads
|
||||||
.vscode
|
.vscode
|
||||||
**.DS_Store
|
**.DS_Store
|
||||||
env.yaml
|
env.yaml
|
||||||
ecosystem.config.js
|
|
19
README.md
19
README.md
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
![expressCart](https://raw.githubusercontent.com/mrvautin/expressCart/master/public/images/logo.png)
|
![expressCart](https://raw.githubusercontent.com/mrvautin/expressCart/master/public/images/logo.png)
|
||||||
|
|
||||||
`expressCart` is a fully functional shopping cart built in Node.js (Express, MongoDB) with Stripe, PayPal, Authorize.net, Adyen and Instore payments.
|
`expressCart` is a fully functional shopping cart built in Node.js (Express, MongoDB) with Stripe, PayPal, Blockonomics, Authorize.net, Adyen and Instore payments.
|
||||||
|
|
||||||
[![Github stars](https://img.shields.io/github/stars/mrvautin/expressCart.svg?style=social&label=Star)](https://github.com/mrvautin/expressCart)
|
[![Github stars](https://img.shields.io/github/stars/mrvautin/expressCart.svg?style=social&label=Star)](https://github.com/mrvautin/expressCart)
|
||||||
[![Build Status](https://travis-ci.org/mrvautin/expressCart.svg?branch=master)](https://travis-ci.org/mrvautin/expressCart)
|
[![Build Status](https://travis-ci.org/mrvautin/expressCart.svg?branch=master)](https://travis-ci.org/mrvautin/expressCart)
|
||||||
|
@ -275,6 +275,23 @@ The Stripe config file is located: `/config/stripe.json`. A example Stripe setti
|
||||||
|
|
||||||
Note: The `secretKey`, `publicKey` and `stripeWebhookSecret` is obtained from your Stripe account dashboard.
|
Note: The `secretKey`, `publicKey` and `stripeWebhookSecret` is obtained from your Stripe account dashboard.
|
||||||
|
|
||||||
|
##### Blockonomics (Bitcoin Payments)
|
||||||
|
|
||||||
|
You have to configure the `HTTP Callback URL` parameter into Blockonomics -> Merchants -> Settings:
|
||||||
|
http://CartURL/blockonomics/checkout_return where [**CartURL**](#cart-url) is the address of your server
|
||||||
|
|
||||||
|
The Blockonomics config file is located: `/config/blockonomics.json`. A example Blockonomics settings file is provided:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"apiKey": "this_is_not_real",
|
||||||
|
"hostUrl": "https://www.blockonomics.co", // You usually don't need to change this
|
||||||
|
"newAddressApi": "/api/new_address", // You usually don't need to change this
|
||||||
|
"priceApi": "/api/price?currency=" // You usually don't need to change this
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note: The `apiKey` is obtained from your Blockonomics account.
|
||||||
|
|
||||||
##### Authorize.net (Payments)
|
##### Authorize.net (Payments)
|
||||||
|
|
||||||
The Authorize.net config file is located: `/config/authorizenet.json`. A example Authorize.net settings file is provided:
|
The Authorize.net config file is located: `/config/authorizenet.json`. A example Authorize.net settings file is provided:
|
||||||
|
|
2
app.js
2
app.js
|
@ -83,6 +83,7 @@ const order = require('./routes/order');
|
||||||
const user = require('./routes/user');
|
const user = require('./routes/user');
|
||||||
const paypal = require('./routes/payments/paypal');
|
const paypal = require('./routes/payments/paypal');
|
||||||
const stripe = require('./routes/payments/stripe');
|
const stripe = require('./routes/payments/stripe');
|
||||||
|
const blockonomics = require('./routes/payments/blockonomics');
|
||||||
const authorizenet = require('./routes/payments/authorizenet');
|
const authorizenet = require('./routes/payments/authorizenet');
|
||||||
const adyen = require('./routes/payments/adyen');
|
const adyen = require('./routes/payments/adyen');
|
||||||
const instore = require('./routes/payments/instore');
|
const instore = require('./routes/payments/instore');
|
||||||
|
@ -418,6 +419,7 @@ app.use('/', user);
|
||||||
app.use('/', admin);
|
app.use('/', admin);
|
||||||
app.use('/paypal', paypal);
|
app.use('/paypal', paypal);
|
||||||
app.use('/stripe', stripe);
|
app.use('/stripe', stripe);
|
||||||
|
app.use('/blockonomics', blockonomics);
|
||||||
app.use('/authorizenet', authorizenet);
|
app.use('/authorizenet', authorizenet);
|
||||||
app.use('/adyen', adyen);
|
app.use('/adyen', adyen);
|
||||||
app.use('/instore', instore);
|
app.use('/instore', instore);
|
||||||
|
|
|
@ -73,11 +73,12 @@
|
||||||
},
|
},
|
||||||
"currencyISO": {
|
"currencyISO": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"enum": ["USD", "EUR", "GBP"],
|
||||||
"default": "USD"
|
"default": "USD"
|
||||||
},
|
},
|
||||||
"paymentGateway": {
|
"paymentGateway": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["paypal", "stripe", "authorizenet", "adyen", "instore"]
|
"enum": ["paypal", "blockonomics", "stripe", "authorizenet", "adyen", "instore"]
|
||||||
},
|
},
|
||||||
"databaseConnectionString": {
|
"databaseConnectionString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"apiKey": "WQh8bEZkDbWZQSDUnKEUr24W02p7NxAEJsvmDhS6ymU",
|
||||||
|
"hostUrl": "https://www.blockonomics.co",
|
||||||
|
"newAddressApi": "/api/new_address",
|
||||||
|
"priceApi": "/api/price?currency="
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"apiKey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hostUrl": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"newAddressApi": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"priceApi": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"api_key", "hostUrl", "newAddressApi", "priceApi"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
|
@ -23,7 +23,10 @@
|
||||||
"theme": "Cloth",
|
"theme": "Cloth",
|
||||||
"trackStock": false,
|
"trackStock": false,
|
||||||
"orderHook": "",
|
"orderHook": "",
|
||||||
"availableLanguages": ["en", "it"],
|
"availableLanguages": [
|
||||||
|
"en",
|
||||||
|
"it"
|
||||||
|
],
|
||||||
"defaultLocale": "en",
|
"defaultLocale": "en",
|
||||||
"maxQuantity": 25,
|
"maxQuantity": 25,
|
||||||
"twitterHandle": "",
|
"twitterHandle": "",
|
||||||
|
@ -32,6 +35,10 @@
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"shipping": "shipping-basic",
|
"shipping": "shipping-basic",
|
||||||
"discount": "discount-voucher"
|
"discount": "discount-voucher"
|
||||||
|
},
|
||||||
|
"loaded": {
|
||||||
|
"shipping": {},
|
||||||
|
"discount": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -196,6 +196,12 @@
|
||||||
"Users": "Users",
|
"Users": "Users",
|
||||||
"Create Order": "Create Order",
|
"Create Order": "Create Order",
|
||||||
"User edit": "User edit",
|
"User edit": "User edit",
|
||||||
|
"Blockonomics payment details": "Blockonomics payment details",
|
||||||
|
"Order Expected BTC": "Order Expected BTC",
|
||||||
|
"Order Received BTC": "Order Received BTC",
|
||||||
|
"Order Blockonomics Txid": "Order Blockonomics Txid",
|
||||||
|
"Currency ISO": "Currency ISO",
|
||||||
|
"Currency used for Blockonomics conversion": "Currency used for Blockonomics conversion",
|
||||||
"Logout": "Logout",
|
"Logout": "Logout",
|
||||||
"Company": "Company"
|
"Company": "Company"
|
||||||
}
|
}
|
|
@ -160,5 +160,29 @@
|
||||||
"Shipping": "Spedizione:",
|
"Shipping": "Spedizione:",
|
||||||
"Empty cart": "Svuota carrello",
|
"Empty cart": "Svuota carrello",
|
||||||
"Payment ID": "Payment ID",
|
"Payment ID": "Payment ID",
|
||||||
"Search shop": "Search shop"
|
"Search shop": "Search shop",
|
||||||
|
"Dashboard": "Dashboard",
|
||||||
|
"Menu": "Menu",
|
||||||
|
"Discount codes": "Discount codes",
|
||||||
|
"Shipping options": "Shipping options",
|
||||||
|
"Return to information": "Return to information",
|
||||||
|
"Proceed to payment": "Proceed to payment",
|
||||||
|
"Discount code": "Discount code",
|
||||||
|
"Apply": "Apply",
|
||||||
|
"Currency ISO": "Currency ISO",
|
||||||
|
"Currency used for Blockonomics conversion": "Currency used for Blockonomics conversion",
|
||||||
|
"Blockonomics payment details": "Blockonomics payment details",
|
||||||
|
"Payment Message": "Payment Message",
|
||||||
|
"Order net amount": "Order net amount",
|
||||||
|
"Order shipping amount": "Order shipping amount",
|
||||||
|
"Order type": "Order type",
|
||||||
|
"Order Expected BTC": "Order Expected BTC",
|
||||||
|
"Order Received BTC": "Order Received BTC",
|
||||||
|
"Order Blockonomics Txid": "Order Blockonomics Txid",
|
||||||
|
"Users": "Users",
|
||||||
|
"New user": "New user",
|
||||||
|
"Customers can be filtered by: email, name or phone number": "Customers can be filtered by: email, name or phone number",
|
||||||
|
"Password": "Password",
|
||||||
|
"Create Order": "Create Order",
|
||||||
|
"Create order": "Create order"
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 224 KiB |
File diff suppressed because one or more lines are too long
|
@ -432,6 +432,52 @@ $(document).ready(function (){
|
||||||
// alert
|
// alert
|
||||||
showNotification(messageVal, messageTypeVal || 'danger', false);
|
showNotification(messageVal, messageTypeVal || 'danger', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkout-blockonomics page (blockonomics_payment route) handling START ***
|
||||||
|
if($('#blockonomics_div').length > 0){
|
||||||
|
var orderid = $('#blockonomics_div').data('orderid') || '';
|
||||||
|
var timestamp = $('#blockonomics_div').data('timestamp') || -1;
|
||||||
|
var address = $('#blockonomics_div').data('address') || '';
|
||||||
|
var blSocket = new WebSocket('wss://www.blockonomics.co/payment/' + address + '?timestamp=' + timestamp);
|
||||||
|
blSocket.onopen = function (msg){
|
||||||
|
};
|
||||||
|
var timeOutMinutes = 10;
|
||||||
|
setTimeout(function(){
|
||||||
|
$('#blockonomics_waiting').html('<b>Payment expired</b><br><br><b><a href=\'/checkout/payment\'>Click here</a></b> to try again.<br><br>If you already paid, your order will be processed automatically.');
|
||||||
|
showNotification('Payment expired', 'danger');
|
||||||
|
blSocket.close();
|
||||||
|
}, 1000 * 60 * timeOutMinutes);
|
||||||
|
|
||||||
|
var countdownel = $('#blockonomics_timeout');
|
||||||
|
var endDatebl = new Date((new Date()).getTime() + 1000 * 60 * timeOutMinutes);
|
||||||
|
var blcountdown = setInterval(function(){
|
||||||
|
var now = new Date().getTime();
|
||||||
|
var distance = endDatebl - now;
|
||||||
|
if(distance < 0){
|
||||||
|
clearInterval(blcountdown);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
||||||
|
countdownel.html(minutes + 'm ' + seconds + 's');
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
blSocket.onmessage = function (msg){
|
||||||
|
var data = JSON.parse(msg.data);
|
||||||
|
if((data.status === 0) || (data.status === 1) || (data.status === 2)){
|
||||||
|
// redirect to order confirmation page
|
||||||
|
var orderMessage = '<br>View <b><a href="/payment/' + orderid + '">Order</a></b>';
|
||||||
|
$('#blockonomics_waiting').html('Payment detected (<b>' + data.value / 1e8 + ' BTC</b>).' + orderMessage);
|
||||||
|
showNotification('Payment detected', 'success');
|
||||||
|
$('#cart-count').html('0');
|
||||||
|
blSocket.close();
|
||||||
|
$.ajax({ method: 'POST', url: '/product/emptycart' }).done(function(){
|
||||||
|
window.location.replace('/payment/' + orderid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// checkout-blockonomics page (blockonomics_payment route) handling *** END
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkMaxQuantity(e, element){
|
function checkMaxQuantity(e, element){
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -58,8 +58,9 @@ router.get('/payment/:orderId', async (req, res, next) => {
|
||||||
if(config.orderHook){
|
if(config.orderHook){
|
||||||
await hooker(order);
|
await hooker(order);
|
||||||
};
|
};
|
||||||
|
let paymentView = `${config.themeViews}payment-complete`;
|
||||||
res.render(`${config.themeViews}payment-complete`, {
|
if(order.orderPaymentGateway === 'Blockonomics') paymentView = `${config.themeViews}payment-complete-blockonomics`;
|
||||||
|
res.render(paymentView, {
|
||||||
title: 'Payment complete',
|
title: 'Payment complete',
|
||||||
config: req.app.config,
|
config: req.app.config,
|
||||||
session: req.session,
|
session: req.session,
|
||||||
|
@ -214,6 +215,32 @@ router.get('/checkout/payment', async (req, res) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/blockonomics_payment', (req, res, next) => {
|
||||||
|
const config = req.app.config;
|
||||||
|
let paymentType = '';
|
||||||
|
if(req.session.cartSubscription){
|
||||||
|
paymentType = '_subscription';
|
||||||
|
}
|
||||||
|
// show bitcoin address and wait for payment, subscribing to wss
|
||||||
|
|
||||||
|
res.render(`${config.themeViews}checkout-blockonomics`, {
|
||||||
|
title: 'Checkout - Payment',
|
||||||
|
config: req.app.config,
|
||||||
|
paymentConfig: getPaymentConfig(),
|
||||||
|
session: req.session,
|
||||||
|
paymentPage: true,
|
||||||
|
paymentType,
|
||||||
|
cartClose: true,
|
||||||
|
cartReadOnly: true,
|
||||||
|
page: 'checkout-information',
|
||||||
|
countryList,
|
||||||
|
message: clearSessionValue(req.session, 'message'),
|
||||||
|
messageType: clearSessionValue(req.session, 'messageType'),
|
||||||
|
helpers: req.handlebars.helpers,
|
||||||
|
showFooter: 'showFooter'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
router.post('/checkout/adddiscountcode', async (req, res) => {
|
router.post('/checkout/adddiscountcode', async (req, res) => {
|
||||||
const config = req.app.config;
|
const config = req.app.config;
|
||||||
const db = req.app.db;
|
const db = req.app.db;
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
const express = require('express');
|
||||||
|
const common = require('../../lib/common');
|
||||||
|
const { indexOrders } = require('../../lib/indexing');
|
||||||
|
const router = express.Router();
|
||||||
|
const axios = require('axios').default;
|
||||||
|
|
||||||
|
router.get('/checkout_cancel', (req, res, next) => {
|
||||||
|
// return to checkout for adjustment or repayment
|
||||||
|
res.redirect('/checkout');
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/checkout_return', async (req, res, next) => {
|
||||||
|
const db = req.app.db;
|
||||||
|
const config = req.app.config;
|
||||||
|
|
||||||
|
const status = req.query.status || -1;
|
||||||
|
const address = req.query.addr || 'na';
|
||||||
|
const amount = (req.query.value || 0) / 1e8;
|
||||||
|
const txid = req.query.txid || 'na';
|
||||||
|
|
||||||
|
if(Number(status) === 2){
|
||||||
|
// we are interested only in final confirmations
|
||||||
|
const order = await db.orders.findOne({ orderPaymentId: address });
|
||||||
|
if(order){
|
||||||
|
if(amount >= order.orderExpectedBtc){
|
||||||
|
try{
|
||||||
|
await db.orders.updateOne({
|
||||||
|
_id: order._id },
|
||||||
|
{ $set: { orderStatus: 'Paid', orderReceivedBtc: amount, orderBlockonomicsTxid: txid }
|
||||||
|
}, { multi: false });
|
||||||
|
// if approved, send email etc
|
||||||
|
// set payment results for email
|
||||||
|
const paymentResults = {
|
||||||
|
message: 'Your payment was successfully completed',
|
||||||
|
messageType: 'success',
|
||||||
|
paymentEmailAddr: order.orderEmail,
|
||||||
|
paymentApproved: true,
|
||||||
|
paymentDetails: '<p><strong>Order ID: </strong>' + order._id + '</p><p><strong>Transaction ID: </strong>' + order.orderPaymentId + '</p>'
|
||||||
|
};
|
||||||
|
|
||||||
|
// send the email with the response
|
||||||
|
// TODO: Should fix this to properly handle result
|
||||||
|
common.sendEmail(req.session.paymentEmailAddr, 'Your payment with ' + config.cartTitle, common.getEmailTemplate(paymentResults));
|
||||||
|
res.status(200).json({ err: '' });
|
||||||
|
}catch(ex){
|
||||||
|
console.info('Error updating status success blockonomics', ex);
|
||||||
|
res.status(200).json({ err: 'Error updating status' });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.info('Amount not sufficient blockonomics', address);
|
||||||
|
try{
|
||||||
|
await db.orders.updateOne({
|
||||||
|
_id: order._id },
|
||||||
|
{ $set: { orderStatus: 'Declined', orderReceivedBtc: amount }
|
||||||
|
}, { multi: false });
|
||||||
|
}catch(ex){
|
||||||
|
console.info('Error updating status insufficient blockonomics', ex);
|
||||||
|
}
|
||||||
|
res.status(200).json({ err: 'Amount not sufficient' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).json({ err: 'Order not found' });
|
||||||
|
console.info('Order not found blockonomics', address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).json({ err: 'Payment not final' });
|
||||||
|
console.info('Payment not final blockonomics', address);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/checkout_action', (req, res, next) => {
|
||||||
|
const blockonomicsConfig = common.getPaymentConfig();
|
||||||
|
const config = req.app.config;
|
||||||
|
const db = req.app.db;
|
||||||
|
const blockonomicsParams = {};
|
||||||
|
// get current rate
|
||||||
|
axios
|
||||||
|
.get(blockonomicsConfig.hostUrl + blockonomicsConfig.priceApi + config.currencyISO)
|
||||||
|
.then((response) => {
|
||||||
|
blockonomicsParams.expectedBtc = Math.round(req.session.totalCartAmount / response.data.price * Math.pow(10, 8)) / Math.pow(10, 8);
|
||||||
|
// get new address
|
||||||
|
axios
|
||||||
|
.post(blockonomicsConfig.hostUrl + blockonomicsConfig.newAddressApi, {}, { headers: { 'Content-Type': 'application/json', 'User-Agent': 'blockonomics', Accept: 'application/json', Authorization: 'Bearer ' + blockonomicsConfig.apiKey } })
|
||||||
|
.then((response) => {
|
||||||
|
blockonomicsParams.address = response.data.address;
|
||||||
|
blockonomicsParams.timestamp = Math.floor(new Date() / 1000);
|
||||||
|
// create order with status Pending and save ref
|
||||||
|
|
||||||
|
const orderDoc = {
|
||||||
|
orderPaymentId: blockonomicsParams.address,
|
||||||
|
orderPaymentGateway: 'Blockonomics',
|
||||||
|
orderExpectedBtc: blockonomicsParams.expectedBtc,
|
||||||
|
orderTotal: req.session.totalCartAmount,
|
||||||
|
orderShipping: req.session.totalCartShipping,
|
||||||
|
orderItemCount: req.session.totalCartItems,
|
||||||
|
orderProductCount: req.session.totalCartProducts,
|
||||||
|
orderEmail: req.session.customerEmail,
|
||||||
|
orderFirstname: req.session.customerFirstname,
|
||||||
|
orderLastname: req.session.customerLastname,
|
||||||
|
orderAddr1: req.session.customerAddress1,
|
||||||
|
orderAddr2: req.session.customerAddress2,
|
||||||
|
orderCountry: req.session.customerCountry,
|
||||||
|
orderState: req.session.customerState,
|
||||||
|
orderPostcode: req.session.customerPostcode,
|
||||||
|
orderPhoneNumber: req.session.customerPhone,
|
||||||
|
orderComment: req.session.orderComment,
|
||||||
|
orderStatus: 'Pending',
|
||||||
|
orderDate: new Date(),
|
||||||
|
orderProducts: req.session.cart,
|
||||||
|
orderType: 'Single'
|
||||||
|
};
|
||||||
|
db.orders.insertOne(orderDoc, (err, newDoc) => {
|
||||||
|
if(err){
|
||||||
|
console.info(err.stack);
|
||||||
|
}
|
||||||
|
// get the new ID
|
||||||
|
const newId = newDoc.insertedId;
|
||||||
|
// add to lunr index
|
||||||
|
indexOrders(req.app)
|
||||||
|
.then(() => {
|
||||||
|
// set the order ID in the session, to link to it from blockonomics payment page
|
||||||
|
blockonomicsParams.pendingOrderId = newId;
|
||||||
|
req.session.blockonomicsParams = blockonomicsParams;
|
||||||
|
res.redirect('/blockonomics_payment');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -134,9 +134,10 @@ router.post('/checkout_action', (req, res, next) => {
|
||||||
// create payment
|
// create payment
|
||||||
paypal.payment.create(payment, (error, payment) => {
|
paypal.payment.create(payment, (error, payment) => {
|
||||||
if(error){
|
if(error){
|
||||||
req.session.message = 'There was an error processing your payment. You have not been changed and can try again.';
|
req.session.message = 'There was an error processing your payment. You have not been charged and can try again.';
|
||||||
req.session.messageType = 'danger';
|
req.session.messageType = 'danger';
|
||||||
res.redirect('/checkout/payment');
|
res.redirect('/checkout/payment');
|
||||||
|
console.log(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(payment.payer.payment_method === 'paypal'){
|
if(payment.payer.payment_method === 'paypal'){
|
||||||
|
|
|
@ -28,6 +28,15 @@
|
||||||
<li class="list-group-item"><strong> {{ @root.__ "Order ID" }}: </strong><span class="float-right">{{result._id}}</span></li>
|
<li class="list-group-item"><strong> {{ @root.__ "Order ID" }}: </strong><span class="float-right">{{result._id}}</span></li>
|
||||||
<li class="list-group-item"><strong> {{ @root.__ "Payment Gateway ref" }}: </strong><span class="float-right">{{result.orderPaymentId}}</span></li>
|
<li class="list-group-item"><strong> {{ @root.__ "Payment Gateway ref" }}: </strong><span class="float-right">{{result.orderPaymentId}}</span></li>
|
||||||
<li class="list-group-item"><strong> {{ @root.__ "Payment Gateway" }}: </strong><span class="float-right">{{result.orderPaymentGateway}}</span></li>
|
<li class="list-group-item"><strong> {{ @root.__ "Payment Gateway" }}: </strong><span class="float-right">{{result.orderPaymentGateway}}</span></li>
|
||||||
|
{{#if result.orderExpectedBtc }}
|
||||||
|
<li class="list-group-item"><strong> {{ @root.__ "Order Expected BTC" }}: </strong><span class="float-right">{{result.orderExpectedBtc}}</span></li>
|
||||||
|
{{/if}}
|
||||||
|
{{#if result.orderReceivedBtc }}
|
||||||
|
<li class="list-group-item"><strong> {{ @root.__ "Order Received BTC" }}: </strong><span class="float-right">{{result.orderReceivedBtc}}</span></li>
|
||||||
|
{{/if}}
|
||||||
|
{{#if result.orderBlockonomicsTxid }}
|
||||||
|
<li class="list-group-item"><strong> {{ @root.__ "Order Blockonomics Txid" }}: </strong><span class="float-right">{{result.orderBlockonomicsTxid}}</span></li>
|
||||||
|
{{/if}}
|
||||||
{{#if result.orderPaymentMessage}}
|
{{#if result.orderPaymentMessage}}
|
||||||
<li class="list-group-item"><strong> {{ @root.__ "Payment Message" }}: </strong><span class="float-right">{{result.orderPaymentMessage}}</span></li>
|
<li class="list-group-item"><strong> {{ @root.__ "Payment Message" }}: </strong><span class="float-right">{{result.orderPaymentMessage}}</span></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="col-sm-12 text-center">
|
||||||
|
<button id="checkout_blockonomics" class="btn btn-outline-success waves-effect waves-light blue darken-3" type="submit">Pay with Bitcoin</button>
|
||||||
|
</div>
|
|
@ -1,10 +1,10 @@
|
||||||
{{> partials/menu}}
|
{{> partials/menu}}
|
||||||
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
|
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
|
||||||
|
<form id="settingsForm" data-toggle="validator">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h2>{{ @root.__ "General settings" }} <span class="float-right"><button type="submit" id="btnSettingsUpdate" class="btn btn-outline-success">{{ @root.__ "Update" }}</button></span></h2>
|
<h2>{{ @root.__ "General settings" }} <span class="float-right"><button type="submit" id="btnSettingsUpdate" class="btn btn-outline-success">{{ @root.__ "Update" }}</button></span></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<form id="settingsForm" data-toggle="validator">
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ @root.__ "Cart name" }} *</label>
|
<label>{{ @root.__ "Cart name" }} *</label>
|
||||||
<input type="text" class="form-control" name="cartTitle" value="{{config.cartTitle}}" required>
|
<input type="text" class="form-control" name="cartTitle" value="{{config.cartTitle}}" required>
|
||||||
|
@ -36,6 +36,7 @@
|
||||||
<select class="form-control" name="paymentGateway">
|
<select class="form-control" name="paymentGateway">
|
||||||
<option {{selectState 'paypal' config.paymentGateway}} value="paypal">Paypal</option>
|
<option {{selectState 'paypal' config.paymentGateway}} value="paypal">Paypal</option>
|
||||||
<option {{selectState 'stripe' config.paymentGateway}} value="stripe">Stripe</option>
|
<option {{selectState 'stripe' config.paymentGateway}} value="stripe">Stripe</option>
|
||||||
|
<option {{selectState 'blockonomics' config.paymentGateway}} value="blockonomics">Blockonomics</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="help-block">{{ @root.__ "Payment_Gateway_Info" }}</p>
|
<p class="help-block">{{ @root.__ "Payment_Gateway_Info" }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,6 +45,15 @@
|
||||||
<input type="text" class="form-control" name="currencySymbol" value="{{currencySymbol config.currencySymbol}}">
|
<input type="text" class="form-control" name="currencySymbol" value="{{currencySymbol config.currencySymbol}}">
|
||||||
<p class="help-block">{{ @root.__ "Set this to your currency symbol. Eg: $, £, €" }}</p>
|
<p class="help-block">{{ @root.__ "Set this to your currency symbol. Eg: $, £, €" }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ @root.__ "Currency ISO" }}</label>
|
||||||
|
<select class="form-control" name="currencyISO">
|
||||||
|
<option {{selectState 'USD' config.currencyISO}} value="USD">USD</option>
|
||||||
|
<option {{selectState 'EUR' config.currencyISO}} value="EUR">EUR</option>
|
||||||
|
<option {{selectState 'GBP' config.currencyISO}} value="GBP">GBP</option>
|
||||||
|
</select>
|
||||||
|
<p class="help-block">{{ @root.__ "Currency used for Blockonomics conversion" }}</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ @root.__ "Theme" }}</label>
|
<label>{{ @root.__ "Theme" }}</label>
|
||||||
<select class="form-control" name="theme">
|
<select class="form-control" name="theme">
|
||||||
|
@ -128,6 +138,6 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button id="sendTestEmail" class="btn btn-outline-success">{{ @root.__ "Send test email" }}</button>
|
<button id="sendTestEmail" class="btn btn-outline-success">{{ @root.__ "Send test email" }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</main>
|
</main>
|
|
@ -0,0 +1,83 @@
|
||||||
|
<div class="col-md-10 offset-md-1 col-sm-12">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/">Home</a></li>
|
||||||
|
<li class="breadcrumb-item" aria-current="page"><a href="/checkout/information">Information</a></li>
|
||||||
|
<li class="breadcrumb-item" aria-current="page"><a href="/checkout/shipping">Shipping</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page"><a href="/checkout/payment">Payment</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">BTC Address</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
<div class="row">
|
||||||
|
{{#if paymentMessage}}
|
||||||
|
<p class="text-danger text-center">{{paymentMessage}}</p>
|
||||||
|
{{/if}}
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="card top-marg-15">
|
||||||
|
<div class="card-body" id="blockonomics_div" data-address="{{@root.session.blockonomicsParams.address}}" data-amount="{{@root.session.blockonomicsParams.expectedBtc}}" data-timestamp="{{@root.session.blockonomicsParams.timestamp}}" data-orderid="{{@root.session.blockonomicsParams.pendingOrderId}}">
|
||||||
|
<h5 class="card-title">{{ @root.__ "Blockonomics payment details" }}</h5>
|
||||||
|
<ul class="list-group bottom-pad-15">
|
||||||
|
<li class="list-group-item">
|
||||||
|
{{@root.session.customerFirstname}} {{@root.session.customerLastname}} -
|
||||||
|
{{@root.session.customerEmail}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="list-group bottom-pad-15">
|
||||||
|
{{#ifCond @root.session.totalCartShipping '>' 0}}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
{{@root.session.shippingMessage}}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<span><strong>{{currencySymbol @root.config.currencySymbol}}{{formatAmount @root.session.totalCartShipping}}</strong></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{else}}
|
||||||
|
{{/ifCond}}
|
||||||
|
</ul>
|
||||||
|
<ul class="list-group bottom-pad-15">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
Send BTC amount
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<span><strong>{{@root.session.blockonomicsParams.expectedBtc}}</strong></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<ul class="list-group bottom-pad-15">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12" }>
|
||||||
|
Address:<br>
|
||||||
|
<span style="font-size:14px;"><strong>{{@root.session.blockonomicsParams.address}}</strong></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<ul class="list-group bottom-pad-15">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12" id="blockonomics_waiting" style="text-align:center;">
|
||||||
|
Waiting for payment<br>
|
||||||
|
<strong><span id="blockonomics_timeout">10m 0s</span></strong> left<br>
|
||||||
|
<img src="/images/spinner.gif">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="cart" class="col-md-7">
|
||||||
|
{{> (getTheme 'cart')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -55,6 +55,9 @@
|
||||||
{{#ifCond config.paymentGateway '==' 'paypal'}}
|
{{#ifCond config.paymentGateway '==' 'paypal'}}
|
||||||
{{> partials/payments/paypal}}
|
{{> partials/payments/paypal}}
|
||||||
{{/ifCond}}
|
{{/ifCond}}
|
||||||
|
{{#ifCond config.paymentGateway '==' 'blockonomics'}}
|
||||||
|
{{> partials/payments/blockonomics}}
|
||||||
|
{{/ifCond}}
|
||||||
{{#ifCond config.paymentGateway '==' 'stripe'}}
|
{{#ifCond config.paymentGateway '==' 'stripe'}}
|
||||||
{{> partials/payments/stripe}}
|
{{> partials/payments/stripe}}
|
||||||
{{/ifCond}}
|
{{/ifCond}}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="col-md-10 offset-md-1 col-sm-12 top-pad-50">
|
||||||
|
<div class="row">
|
||||||
|
<div class="text-center col-md-10 offset-md-1">
|
||||||
|
<h2 class="text-success">Thank you. Order have been received.</h2>
|
||||||
|
<div>
|
||||||
|
<p><h5>Order will be be processed upon confirmation by the bitcoin network. Please keep below order details for reference.</h5></p>
|
||||||
|
<p><strong>{{ @root.__ "Order ID" }}:</strong> {{result._id}}</p>
|
||||||
|
<p><strong>{{ @root.__ "Payment ID" }}:</strong> {{result.orderPaymentId}}</p>
|
||||||
|
</div>
|
||||||
|
<a href="/" class="btn btn-outline-warning">Home</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Reference in New Issue