在数据库中存储Stripe支付的最佳实践是什么?

20

我想知道在数据库中存储Stripe付款数据的"公约"或最佳实践是否存在。网上缺乏真正的信息,由于我是独立开展这项工作的,所以我想向其他人寻求反馈意见。

我的网站基本设置是有一个商店和几个产品。还有一个选项可以订阅,每月收到这些产品。

因此,我设置了两个帖子路线。一个用于购买产品,另一个用于订阅。它们使用Stripe API。对于订单,我使用stripe.charge,而对于订阅,则创建一个来自stripe.customer的客户并订阅计划。

对于stripe.charges,我将返回的对象保存到数据库中。这包含所有充电数据。如果charge succeeded,则发送charge.succeeded作为Webhook。我不存储这些数据。也许我应该只在这里存储充电ID,而不是整个充电对象。我将其存储如下:

订单路线

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);
}
};

订阅方面稍微有些复杂,因为创建的客户对象不包含任何收费ID。所以我将客户ID保存到用户模型中。如果订阅成功处理,Stripe会触发6个Webhook。 我存储customer.subscription.createdcustomer.createdcharge.succeededinvoice.created。我像这样存储客户对象:

订阅路由

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);
}
};

如您所见,我在我的Mongoose User模型上设置了一个布尔变量hasSubscription。如果它为真,则不创建客户,因此也不会设置订阅。如果它为假,则我保存从创建的客户对象中获取的客户ID。然后在Webhook路由中,我将上述4个事件保存到正确的用户中,通过匹配客户ID来进行匹配。我认为在这里可以保存更少的数据,可能记录任何订阅和取消的记录。我按以下方式保存Webhook事件:

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…

 }

为了保险起见,我存储了比实际需要更多的数据。我曾在某处读到建议充分存储数据有助于合规性。但由于Stripe将所有内容都存储在仪表板中,我能理解你的观点:一个简单的标识信息如付款ID就足够了。

我仍处于测试模式,但我认为Stripe只会发送卡片的最后四位和到期年份:

exp_year: 2042 last4: '4242'

关于将机密信息保存到数据库中:我没有看到任何更敏感的信息被泄露。卡本身是由Stripe创建的ID引用的。


你能澄清一下你所说的“支付数据”是什么意思吗?你是指像产品/库存管理、订单历史或卡片详细信息之类的吗?我有点迷茫。我认为,无论你的回答如何,你最好依靠Webhook事件来获取这些信息,并异步更新本地数据存储。 - korben
5
通常情况下,您可以存储API返回的任何数据。重要的是,您不允许用户的支付信息(如卡号、银行账号等)进入您的数据库,而只能使用他们的令牌/ID进行操作。至于为自己使用存储什么数据,除了不存储您实际上不需要的数据外,没有真正的“最佳实践”。即使在某些时候您“需要”这些数据,您是否需要将其存储或者只是获取即可? 在大多数情况下,您可以只存储收费ID或相应的内容。 - Jake T.
抱歉之前表述不够清晰 - 我已经编辑了我的原始帖子。 - HPJM
1个回答

9
条纹安全文档中,提到了可以安全存储的超出范围的卡片数据。杰克T.的评论真正回答了这个问题——从API返回的所有内容都可以安全存储。
卡类型、过期日期和最后四位数字不受PCI合规性的限制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接