Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for storing Stripe payments in database?

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.

like image 909
HPJM Avatar asked Mar 06 '18 03:03

HPJM


People also ask

What DB does Stripe use?

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.

Does Stripe use SQL?

Pull reports in StripeUse predefined SQL queries or write new custom queries right inside the Stripe Dashboard–no data engineering required.

Does Stripe store customer data?

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.

Does Stripe require SSL?

Stripe forces HTTPS for all services using TLS (SSL), including our public website and the Dashboard to ensure secure connections: Stripe.


1 Answers

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.

like image 148
wallyjaw Avatar answered Sep 30 '22 16:09

wallyjaw