This is the code that runs https://bandolier.learnjsthehardway.com/ for you to review. It uses the https://git.learnjsthehardway.com/learn-javascript-the-hard-way/bandolier-template to create the documentation for the project.
https://bandolier.learnjsthehardway.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.1 KiB
140 lines
4.1 KiB
import { Payment } from '../../lib/models.js';
|
|
import logging from '../../lib/logging.js';
|
|
import { API, defer } from "../../lib/api.js";
|
|
import fetch from 'node-fetch';
|
|
import { btcpay_private } from '../../lib/config.js';
|
|
import { product_id, fake_payments, register_enabled } from '../../client/config.js';
|
|
import { Product } from "../../lib/models.js";
|
|
import assert from "assert";
|
|
|
|
const product = await Product.first({id: product_id});
|
|
|
|
const rules = {
|
|
amount: "required|numeric|min:0|max:100",
|
|
}
|
|
|
|
const log = logging.create("api/payments/btcpay.js");
|
|
|
|
export const get = async (req, res) => {
|
|
const api = new API(req, res);
|
|
|
|
if(!register_enabled) {
|
|
return api.error(401, {errors: { main: "Registration is disabled."}});
|
|
}
|
|
|
|
try {
|
|
assert(!fake_payments, "You have fake_payments set so use /api/user/payment APIs.");
|
|
assert(!btcpay_private.disabled, "BTC Payments are disabled in secrets/config.json.");
|
|
|
|
const form = api.validate(rules);
|
|
assert(form.amount === product.price, `Form amount ${form.amount} doesn't match course price ${product.price}`);
|
|
|
|
if(form._valid) {
|
|
let internal_id = Payment.gen_internal_id();
|
|
|
|
const options = {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': btcpay_private.auth
|
|
},
|
|
body: JSON.stringify({
|
|
"amount": form.amount,
|
|
"currency": product.currency,
|
|
"checkout": {
|
|
"speedPolicy": "HighSpeed"
|
|
},
|
|
"metadata": {
|
|
"orderId": internal_id,
|
|
"itemDesc": product.description,
|
|
}
|
|
}),
|
|
}
|
|
|
|
const response = await fetch(`${btcpay_private.url}/invoices`, options);
|
|
const text = await response.text();
|
|
|
|
if(response.status != 200) {
|
|
log.info(`BTCPAY error: ${response.status}, text: '${text}'`);
|
|
return api.error(403, 'BTCPay server failure');
|
|
} else {
|
|
const data = JSON.parse(text);
|
|
const sys_primary_id = data.id;
|
|
|
|
let payment = await Payment.insert({
|
|
system: 'btcpay',
|
|
status: 'pending',
|
|
user_id: api.user.id,
|
|
internal_id,
|
|
sys_primary_id,
|
|
sys_created_on: new Date(data.createdTime)
|
|
});
|
|
|
|
assert(payment, "Failed to store payment in the database. Email help@learnjsthehardway.com.");
|
|
|
|
return api.reply(200, {sys_primary_id, internal_id});
|
|
}
|
|
} else {
|
|
log.error(form._errors, "btcpay validation failure");
|
|
return api.validation_error(res, form);
|
|
}
|
|
} catch(error) {
|
|
log.error(error);
|
|
return api.error(403, 'BTCPay server failure');
|
|
}
|
|
}
|
|
|
|
get.authenticated = true;
|
|
|
|
export const post = async (req, res) => {
|
|
const api = new API(req, res);
|
|
const msg = req.body;
|
|
|
|
try{
|
|
log.debug(`btcpay POST Recieved message ${JSON.stringify(msg)}`);
|
|
|
|
const options = {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': btcpay_private.auth
|
|
},
|
|
}
|
|
|
|
let response = await fetch(`${btcpay_private.url}/invoices/${msg.sys_primary_id}`, options);
|
|
let data = await response.json();
|
|
|
|
// TODO: what are the error conditions on this response?
|
|
|
|
let waiter = defer("settled_waiting");
|
|
setTimeout(() => waiter.resolve(), 5000);
|
|
await waiter;
|
|
|
|
response = await fetch(`${btcpay_private.url}/invoices/${msg.sys_primary_id}`, options);
|
|
data = await response.json();
|
|
// TODO: what are the error conditions on this response?
|
|
|
|
console.log("BTCPAY COMPLETE WITH", data);
|
|
|
|
const sys_primary_id = data.id;
|
|
const status = data.status;
|
|
const btc_due = data.btcDue;
|
|
|
|
// BUG: this doesn't seem to actually mark it complete
|
|
if(status == 'paid') {
|
|
let count = await Payment.update({
|
|
sys_primary_id,
|
|
internal_id: msg.internal_id
|
|
}, {status: 'complete'});
|
|
|
|
assert(count === 1, "Failed to update payment database.");
|
|
}
|
|
|
|
api.reply(200, {sys_primary_id, status, btc_due});
|
|
} catch(error) {
|
|
log.error(error);
|
|
api.error(403, 'Failed to confirm payment');
|
|
}
|
|
}
|
|
|
|
post.authenticated = true;
|
|
|