I was wondering if there was some kind of "convention" or otherwise a best practice for storing Stripe payment data in the database. There is a real lack of information online and since I am doing it on my own I wanted to get some feedback from others.
The basic setup of my site is that there is a store with several products. There is also an option to subscribe, to receive these products monthly.
So I set up two post routes. One for buying products, and one for subscribing. These use the Stripe API. For orders, I use stripe.charge
, and for subscriptions, I create a customer from stripe.customer
and subscribe them to a plan.
For stripe.charges
, I save the object returned into the database. This contains all of the charge data. There is a webhook sent as charge.succeeded
, if the charge succeeded
. I do not store this data. Maybe I should just store the charge id here, instead of the entire charge object. I store it as below:
ORDER ROUTE
module.exports = async (req, res) => {
try {
const charge = await stripe.charges.create({
amount: req.session.cart.totalPrice,
currency: 'usd',
source: req.body.id
});
const order = await new Order({
user: req.user,
cart: req.session.cart,
stripeCharge: charge
}).save();
req.session.cart = new Cart({});
res.sendStatus(200);
} catch (e) {
console.log(e);
}
};
With subscriptions, it’s a little more complex, because the created customer object does not contain any charge id. So I save the customer id to that user model. Stripe fires 6 webhooks if the subscription is processed fine. I store customer.subscription.created
, customer.created
, charge.succeeded
, and invoice.created
. I store the customer object like this:
SUBSCRIPTION ROUTE
module.exports = async (req, res) => {
try {
if(!req.user.hasSubscription) {
const customer = await stripe.customers.create({
email: req.body.email,
source: req.body.id,
plan: 'pro'
});
await new Subscription({
user: req.user,
customerId: customer.id
}).save();
req.user.hasSubscription = true;
await req.user.save();
res.send(req.user);
}
} catch (e) {
console.log(e);
}
};
As you can see, I have a boolean set up on my Mongoose User
model, hasSubscription
. If this is true, no customer is created, and so no subscription is set up. If it is false, I save the customer id from the customer object created. Then in the webhook route, I save the 4 events above to the correct user, matching by customer id. I could get away with saving less data here I think, possibly making a record of any subscriptions, and any cancellations. I save the webhook events as below:
WEBHOOKS ROUTE
module.exports = async (req, res) => {
try {
const data = {};
if (req.body.type === 'customer.subscription.created') {
await Subscription.findOneAndUpdate({ customerId:
req.body.data.object.customer }, {
$set: {
'stripe_data.customer_subscription_created':
req.body.data.object
}
}, {
new: true
});
res.sendStatus(200);
};
…//OTHER STRIPE EVENTS SIMILAR TO ABOVE…
}
I am storing more data than I likely need just to be on the safe side, and I read somewhere that suggested storing adequate data was good for legal compliance. But since Stripe stores everything in the dashboard, I can see your point that a simple piece of identifying information like the charge id would be enough.
I am still in test mode, but I think Stripe only sends back the last 4 digits of the card, and the expiry year like so:
exp_year: 2042
last4: '4242'
With regards to saving confidential information to the database: I do not see any more sensitive information being exposed. The card itself is referenced by an id that Stripe creates.
According to Patrick Collison in his interview on Techzing, Stripe uses MySQL and Mongo. Show activity on this post. Redis should be included in the list, as for Brandur's blog, an engineer at Stripe https://brandur.org/redis-cluster. Also a lot of content about Postgres in his blog.
Pull reports in StripeUse predefined SQL queries or write new custom queries right inside the Stripe Dashboard–no data engineering required.
The Customer resource is a core entity within Stripe. Use it to store all of the profile, billing, and tax information required to bill a customer for subscriptions and one-off invoices.
Stripe forces HTTPS for all services using TLS (SSL), including our public website and the Dashboard to ensure secure connections: Stripe.
In Stripes security docs, it mentions Out-of-scope card data that can be safely stored
. Jake T.'s comment is really the answer -- everything returned from the API is okay for you to store.
Card type, expiration date, and last four digits are not subject to PCI compliance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With