Quick_Payment/server.js

116 lines
3.6 KiB
JavaScript

require('dotenv').config();
const path = require('path');
const express = require('express');
const { savePayment, listPayments, countPayments } = require('./db');
const { createPaymentLink } = require('./payment-api');
const app = express();
const PORT = process.env.PORT || 3000;
const PRODUCTION = process.env.PRODUCTION;
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
function toNumber(v) {
const n = parseFloat(v);
return Number.isFinite(n) ? n : 0;
}
const ALLOWED_CURRENCIES = ['USD', 'AUD'];
// --- Create payment link ---------------------------------------------------
app.post('/api/payments', async (req, res) => {
const { customerName, email, phone, address, currency, items } = req.body || {};
// All customer fields are required.
const customerFields = { customerName, email, phone, address };
const CUSTOMER_LABELS = {
customerName: 'Customer name',
email: 'Email',
phone: 'Phone number',
address: 'Address',
};
const missingCustomer = Object.keys(CUSTOMER_LABELS).filter(
(k) => !customerFields[k] || !String(customerFields[k]).trim()
);
if (missingCustomer.length) {
return res.status(400).json({
error: `Missing required field(s): ${missingCustomer.map((k) => CUSTOMER_LABELS[k]).join(', ')}.`,
});
}
const cleanItems = Array.isArray(items) ? items : [];
if (cleanItems.length === 0) {
return res.status(400).json({ error: 'At least one item is required.' });
}
const cleanCurrency = ALLOWED_CURRENCIES.includes(currency) ? currency : 'AUD';
const normItems = cleanItems.map((it) => ({
sku: (it.sku || '').trim(),
name: (it.name || '').trim(),
price: toNumber(it.price),
quantity: toNumber(it.quantity),
condition: (it.condition || '').trim(),
}));
// Each item requires SKU, a positive price, a positive quantity and a condition.
for (let i = 0; i < normItems.length; i++) {
const it = normItems[i];
const missing = [];
if (!it.sku) missing.push('SKU');
if (!(it.price > 0)) missing.push('price');
if (!(it.quantity > 0)) missing.push('quantity');
if (!it.condition) missing.push('condition');
if (missing.length) {
return res
.status(400)
.json({ error: `Item ${i + 1} is missing required field(s): ${missing.join(', ')}.` });
}
}
const total = normItems.reduce((sum, it) => sum + it.price * it.quantity, 0);
// Sequential quote number based on how many payments already exist:
// first ever -> QT000001, when history already has 9 -> QT000010.
const quoteNumber = `QT${String(countPayments() + 1).padStart(6, '0')}`;
try {
const dataPayment = await createPaymentLink({
customerName,
email,
phone,
address,
currency: cleanCurrency,
quoteNumber,
items: normItems,
total,
});
const saved = savePayment({
customer_name: customerName,
email: email || '',
phone: phone || '',
address: address || '',
items_json: JSON.stringify(normItems),
total,
currency: cleanCurrency,
payment_link: dataPayment.link,
status: 'created',
});
res.json({ ok: true, payment: saved, link: dataPayment.link, raw: dataPayment.raw });
} catch (err) {
console.error('createPaymentLink error:', err);
res.status(502).json({ error: err.message || 'Failed to create payment link.' });
}
});
// --- History ---------------------------------------------------------------
app.get('/api/payments', (_req, res) => {
res.json({ ok: true, payments: listPayments() });
});
app.listen(PORT, () => {
console.log(`Quick Payment server running: http://localhost:${PORT}`);
});