Environment: Express, express-session, Stripe
In the following simplified example when a user requests the home page express-session
assigns the user a session cookie. Refreshing the page retains the same session id as does visiting the success or fail routes. Clicking the upgrade button takes the client to a Stripe shopping cart screen that also keeps the same session id. However once the user is at the Stripe shopping cart if the user makes a successful purchase he is forwarded to the success route and the session id is over-written by Stripe. In the full version this is a problem because the user would be logged in and this causes the user to be automatically logged out after the successful purchase. I'm not sure why this is happening or how to stop it.
app.js
const bodyParser = require('body-parser');
require('dotenv').config();
const express = require('express');
const session = require('express-session');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();
app.set('view engine', 'ejs');
app.use(express.static('views'));
app.use(
session({
maxAge: 24 * 60 * 60 * 1000,
name: 'randomName',
resave: false,
saveUninitialized: true,
secret: 'randomSecret',
cookie: {
sameSite: true,
secure: false
}
})
);
app.get('/', function(req, res) {
req.session.userValues = true;
console.log(req.session);
res.render('index', { stripePublicKey: process.env.STRIPE_PUBLIC_KEY });
});
app.get('/success', function(req, res) {
console.log(req.session);
res.render('success');
});
app.get('/fail', function(req, res) {
console.log(req.session);
res.render('fail');
});
app.post('/create-checkout-session', bodyParser.raw({ type: 'application/json' }), async function(req, res) {
console.log(req.session);
const session = await stripe.checkout.sessions.create({
submit_type: 'auto',
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: 'usd',
product_data: {
name: 'name of product',
description: 'description of product'
},
unit_amount: 100
},
quantity: 1,
}
],
locale: 'en',
mode: 'payment',
success_url: 'http://localhost:8080/success',
cancel_url: 'http://localhost:8080/fail'
});
res.json({ id: session.id });
});
app.listen(8080, function() {
console.log('listening on port 8080');
});
index.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upgrade</title>
<script>var stripePublicKey = '<%- stripePublicKey %>';</script>
<script defer src="https://js.stripe.com/v3/"></script>
<script defer src="checkout.js"></script>
</head>
<body>
<button id="checkout-button">upgrade premium</button>
</body>
</html>
checkout.js
var stripe = Stripe(stripePublicKey);
var checkoutButton = document.getElementById('checkout-button');
checkoutButton.addEventListener('click', checkoutSequence);
function checkoutSequence() {
fetch('/create-checkout-session', {
method: 'POST',
})
.then(function(response) {
return response.json();
})
.then(function(session) {
console.log(session);
return stripe.redirectToCheckout({ sessionId: session.id });
})
.then(function(result) {
if (result.error) {
alert(result.error.message);
}
})
.catch(function(error) {
console.error('Error:', error);
});
}
randomName
cookie that was set when the app first loaded was still there when checkout redirected to both the/success
and/fail
urls. Even with the sameSite option set totrue
(i.e., SameSite=Strict). How are you checking if the cookie is there or not? One thing to be mindful of is that cookie is an http-only cookie which has certain levels of protection that prevent it from being read from client-side JS. – Eyewash