Skip to content

Commit

Permalink
Stripe fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mrvautin committed Nov 11, 2021
1 parent 43ffdb1 commit 147af3e
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 216 deletions.
237 changes: 72 additions & 165 deletions lib/payments/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,59 @@ const { getId, sendEmail, getEmailTemplate } = require('../common');
const { getPaymentConfig } = require('../config');
const { emptyCart } = require('../cart');
const numeral = require('numeral');
const stripe = require('stripe')(getPaymentConfig().secretKey);
const router = express.Router();

router.post('/setup', async (req, res, next) => {
const stripeConfig = getPaymentConfig('stripe');
const stripe = require('stripe')(stripeConfig.secretKey);

const paymentIntent = await stripe.paymentIntents.create({
// Check for customer
const customers = await stripe.customers.list({
email: req.session.customerEmail
});

let stripeCustomer;
if(customers.data.length === 0){
// Create a Stripe customer if one doesn't exist
stripeCustomer = await stripe.customers.create({
email: req.session.customerEmail,
name: `${req.session.customerFirstname} ${req.session.customerLastname}`,
description: req.session.customerId.toString()
});
}else{
// Set customer if existing
stripeCustomer = customers.data[0];
}

// Set the Stripe customer to the session
req.session.stripeCustomer = stripeCustomer.id;

// Setup the Payment Intent
const intent = {
amount: numeral(req.session.totalCartAmount).format('0.00').replace('.', ''),
currency: stripeConfig.stripeCurrency,
payment_method_types: stripeConfig.paymentMethods
});
payment_method_types: stripeConfig.paymentMethods,
customer: stripeCustomer.id
};

// Default to a once off payment
let paymentType = 'single';

// If a subscription, set some values specific to storing method.
if(req.session.cartSubscription){
paymentType = 'subscription';
intent.setup_future_usage = 'off_session';
intent.payment_method_types = ['card'];
}

// Set the payment type
intent.metadata = {
paymentType
};

// Create payment intent
const paymentIntent = await stripe.paymentIntents.create(intent);

res.send({
clientSecret: paymentIntent.client_secret
});
Expand Down Expand Up @@ -96,7 +137,7 @@ router.get('/checkout_action', async (req, res, next) => {
orderStatus: paymentStatus,
orderDate: new Date(),
orderProducts: req.session.cart,
orderType: 'Single',
orderType: paymentIntent.metadata.paymentType,
transaction: transactionId
};

Expand All @@ -106,6 +147,32 @@ router.get('/checkout_action', async (req, res, next) => {
// get the new ID
const orderId = newOrder.insertedId;

// If a subscription payment
if(orderDoc.orderType === 'subscription'){
// Attach the payment method to the customer
await stripe.paymentMethods.attach(paymentIntent.payment_method, {
customer: paymentIntent.customer
});

// Update customers default payment method
await stripe.customers.update(paymentIntent.customer, {
invoice_settings: {
default_payment_method: paymentIntent.payment_method
}
});

// Create the subscription for the merchant
await stripe.subscriptions.create({
customer: paymentIntent.customer,
items: [{
price: req.session.cartSubscription
}],
metadata: {
orderId: orderId.toString()
}
});
}

// Update order to transaction
await db.transactions.updateOne({
_id: getId(transactionId)
Expand Down Expand Up @@ -157,164 +224,4 @@ router.get('/checkout_action', async (req, res, next) => {
res.redirect(`/payment/${orderId}`);
});

// Subscription hook from Stripe
router.all('/subscription_update', async (req, res, next) => {
const db = req.app.db;
const stripeSigSecret = getPaymentConfig('stripe').stripeWebhookSecret;
const stripeSig = req.headers['stripe-signature'];

let hook;
if(stripeSigSecret){
try{
hook = await stripe.webhooks.constructEvent(req.rawBody, stripeSig, stripeSigSecret);
console.info('Stripe Webhook received');
}catch(err){
return res.status(400).send(`Webhook Error: ${err.message}`);
}

if(!hook.data.object.customer){
return res.status(400).json({ message: 'Customer not found' });
}
}else{
hook = req.body;
}

const order = await db.orders.findOne({
orderCustomer: hook.data.object.customer,
orderType: 'Subscription'
});

if(!order){
return res.status(400).json({ message: 'Order not found' });
}

let orderStatus = 'Paid';
if(hook.type === 'invoice.payment_failed'){
orderStatus = 'Declined';
}

// Update order status
await db.orders.updateOne({
_id: getId(order._id),
orderType: 'Subscription'
}, {
$set: {
orderStatus: orderStatus
}
});

return res.status(200).json({ message: 'Status successfully updated' });
});

router.post('/checkout_action_subscription', async (req, res, next) => {
const db = req.app.db;
const config = req.app.config;

try{
const plan = await stripe.plans.retrieve(req.body.stripePlan);
if(!plan){
req.session.messageType = 'danger';
req.session.message = 'The plan connected to this product doesn\'t exist';
res.redirect('/checkout/payment');
return;
}
}catch(ex){
req.session.messageType = 'danger';
req.session.message = 'The plan connected to this product doesn\'t exist';
res.redirect('/checkout/payment');
return;
}

// Create customer
const customer = await stripe.customers.create({
source: req.body.stripeToken,
plan: req.body.stripePlan,
email: req.body.shipEmail,
name: `${req.body.shipFirstname} ${req.body.shipLastname}`,
phone: req.body.shipPhoneNumber
});

if(!customer){
req.session.messageType = 'danger';
req.session.message = 'Your subscripton has declined. Please try again';
req.session.paymentApproved = false;
req.session.paymentDetails = '';
res.redirect('/checkout/payment');
return;
}

// Check for a subscription
if(customer.subscriptions.data && customer.subscriptions.data.length === 0){
req.session.messageType = 'danger';
req.session.message = 'Your subscripton has declined. Please try again';
req.session.paymentApproved = false;
req.session.paymentDetails = '';
res.redirect('/checkout/payment');
return;
}

const subscription = customer.subscriptions.data[0];

// Create the new order document
const orderDoc = {
orderPaymentId: subscription.id,
orderPaymentGateway: 'Stripe',
orderPaymentMessage: subscription.collection_method,
orderTotal: req.session.totalCartAmount,
orderShipping: req.session.totalCartShipping,
orderItemCount: req.session.totalCartItems,
orderProductCount: req.session.totalCartProducts,
orderEmail: req.session.customerEmail,
orderCompany: req.session.customerCompany,
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: 'Subscription',
orderCustomer: customer.id
};

// insert order into DB
const order = await db.orders.insertOne(orderDoc);
const orderId = order.insertedId;

indexOrders(req.app)
.then(() => {
// set the results
req.session.messageType = 'success';
req.session.message = 'Your subscription was successfully created';
req.session.paymentEmailAddr = req.body.shipEmail;
req.session.paymentApproved = true;
req.session.paymentDetails = `<p><strong>Order ID: </strong>${orderId}</p><p><strong>Subscription ID: </strong>${subscription.id}</p>`;

// set payment results for email
const paymentResults = {
message: req.session.message,
messageType: req.session.messageType,
paymentEmailAddr: req.session.paymentEmailAddr,
paymentApproved: true,
paymentDetails: req.session.paymentDetails
};

// clear the cart
if(req.session.cart){
emptyCart(req, res, 'function');
}

// send the email with the response
sendEmail(req.session.paymentEmailAddr, `Your payment with ${config.cartTitle}`, getEmailTemplate(paymentResults));

// redirect to outcome
res.redirect(`/payment/${orderId}`);
});
});

module.exports = router;
Loading

0 comments on commit 147af3e

Please sign in to comment.