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.
127 lines
3.6 KiB
127 lines
3.6 KiB
2 years ago
|
import { Product, Payment } from '../../lib/models.js';
|
||
|
import { API, developer_admin } from "../../lib/api.js";
|
||
|
import logging from '../../lib/logging.js';
|
||
|
import dayjs from 'dayjs';
|
||
|
import { product as product_config, register_enabled } from "../../client/config.js";
|
||
|
import assert from 'assert';
|
||
|
import * as queues from "../../lib/queues.js";
|
||
|
import stripe_create from "stripe";
|
||
|
import { stripe_private } from "../../lib/config.js";
|
||
|
|
||
|
const log = logging.create("api/payments/stripe.js");
|
||
|
|
||
|
const stripe = stripe_create(stripe_private.secret);
|
||
|
|
||
|
const product = await Product.first({id: product_config.id});
|
||
|
|
||
|
const rules = {
|
||
|
"payment_intent": "required",
|
||
|
"payment_intent_client_secret": "required",
|
||
|
"redirect_status": "required"
|
||
|
};
|
||
|
|
||
|
const create_stripe_payment = async (user) => {
|
||
|
assert(product.price > 1, "Stripe requires the price to be greater than $1.");
|
||
|
|
||
|
const intent = await stripe.paymentIntents.create({
|
||
|
amount: product.price * 100,
|
||
|
currency: product.currency.toLowerCase(),
|
||
|
automatic_payment_methods: {
|
||
|
enabled: true,
|
||
|
},
|
||
|
});
|
||
|
|
||
|
assert(intent, "Failed to create stripe intent.");
|
||
|
|
||
|
let internal_id = Payment.gen_internal_id();
|
||
|
|
||
|
// TODO: see if we can use date-fns instead of dayjs here
|
||
|
let payment = await Payment.insert({
|
||
|
user_id: user.id,
|
||
|
system: 'stripe',
|
||
|
status: 'pending',
|
||
|
status_reason: "started",
|
||
|
internal_id,
|
||
|
sys_primary_id: intent.id,
|
||
|
sys_secondary_id: intent.client_secret,
|
||
|
sys_created_on: new Date()
|
||
|
});
|
||
|
|
||
|
assert(payment && payment.internal_id, "Failed to save payment.");
|
||
|
|
||
|
return [intent, payment];
|
||
|
}
|
||
|
|
||
|
export const post = async (req, res) => {
|
||
|
const api = new API(req, res);
|
||
|
|
||
|
if(!register_enabled) {
|
||
|
return api.error(500, {errors: { main: "Registration is disabled."}});
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
let clientSecret;
|
||
|
|
||
|
const pending = await Payment.first({user_id: api.user.id, status: "pending", system: "stripe"});
|
||
|
|
||
|
if(pending) {
|
||
|
// this already exists so send back the original secret
|
||
|
clientSecret = pending.sys_secondary_id;
|
||
|
} else {
|
||
|
// this is a new request so create it
|
||
|
const [intent, payment] = await create_stripe_payment(api.user);
|
||
|
clientSecret = intent.client_secret;
|
||
|
}
|
||
|
|
||
|
api.reply(200, { clientSecret });
|
||
|
} catch(error) {
|
||
|
log.error(error);
|
||
|
api.error(500, "Fatal error in the server. Tell Zed.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
post.authenticated = true;
|
||
|
|
||
|
export const get = async (req, res) => {
|
||
|
const api = new API(req, res);
|
||
|
|
||
|
if(!register_enabled) {
|
||
|
return api.error(500, {errors: { main: "Registration is disabled."}});
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
const msg = api.validate(rules);
|
||
|
|
||
|
if(!msg._valid) {
|
||
|
log.error(msg._errors, "Stripe validation failure");
|
||
|
return api.validation_error(res, msg);
|
||
|
} if(msg.redirect_status === "succeeded") {
|
||
|
// find the user's payment attempt using the stripe id
|
||
|
let payment = await Payment.first({
|
||
|
user_id: api.user.id,
|
||
|
system: "stripe",
|
||
|
sys_primary_id: msg.payment_intent
|
||
|
});
|
||
|
|
||
|
// redirect status is succeeded so this is complete
|
||
|
await Payment.update({id: payment.id}, {status: "complete"});
|
||
|
|
||
|
// send out all of the emails and everything
|
||
|
queues.send_welcome(api.user);
|
||
|
queues.send_receipt(api.user, payment.id, product.id);
|
||
|
|
||
|
// downside to stripe is they do a redirect to finish
|
||
|
return api.redirect("/client/#/payment/finished/");
|
||
|
} else {
|
||
|
// need to handle the other stripe redirect states
|
||
|
log.debug("STRIPE ERROR", msg);
|
||
|
return api.error(402, `Stripe error: ${JSON.stringify(msg)}`);
|
||
|
}
|
||
|
} catch(error) {
|
||
|
log.error(error);
|
||
|
return api.error(403, 'Stripe configuration failed');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get.authenticated = true;
|