【问题标题】:Stripe Checkout: moving from Node to Firebase FunctionsStripe Checkout:从 Node 迁移到 Firebase 函数
【发布时间】:2021-06-27 00:29:14
【问题描述】:

我的 Angular 11 应用使用 Stripe Checkout 和 Express 服务器来处理付款。 Angular 和 Node 一切正常。我想使用 Firebase 函数而不是 Node,但是当我调用我的 Firebase 函数时,我得到了错误:

IntegrationError: stripe.redirectToCheckout: 您必须提供 lineItems、items 或 sessionId 之一。

角度代码

checkout(): void {
    var stripe = Stripe(environment.stripe.key);

    var productName = 'T-shirt!!';
    var price = '2000';

    const options = {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json;charset=UTF-8'
        },
        body: JSON.stringify({
            productName: productName,
            price: price
        })
    };
    var url = 'http://localhost:4242/create-checkout-session';                      // Node server
    //var url = 'http://localhost:5000/MY_FIREBASE_PROJECT/us-central1/stripeTest'; // Firebase emulation
    fetch(url, options)
        .then(function (response) {
            return response.json();
        })
        .then(function (session) {
            return stripe.redirectToCheckout({ sessionId: session.id });
        })
        .then(function (result) {
            if (result.error) {                                                    // redirect fails due to browser or network error
                alert(result.error.message);
            }
        })
        .catch(function (error) {
            console.error('Error:', error);
        });
}

节点服务器

const express = require('express');
const app = express();
const stripe = require('stripe')('MY_SECRET_KEY')
const port = 4242;

app.use(express.json())                                                          // parse request body as JSON

app.use(function (req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');       // website you wish to allow to connect
    // res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');                                                                            // request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'POST');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');             
    next();                                                                      // pass to next layer of middleware
});

app.post('/create-checkout-session', async (req, res) => {
    var productName = req.body.productName;
    var price = req.body.price;

    console.log('body = ', req.body);
    console.log('price = ', req.body.price);

    const session = await stripe.checkout.sessions.create({
        payment_method_types: ['card'],
        line_items: [
            {
                price_data: {
                    currency: 'usd',
                    product_data: {
                        name: productName,
                    },
                    unit_amount: price,
                },
                quantity: 1,
            },
        ],
        mode: 'payment',
        success_url: 'http://localhost:4200/home?action=success',
        cancel_url: 'http://localhost:4200/home?action=cancel',
    });

    res.json({ id: session.id });
});
app.listen(port, () => console.log('Listening on port ' + port + '!'));

Firebase 功能

const stripe = require('stripe')('MY_SECRET_KEY')
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.stripeTest = functions.https.onCall((data, context) => {
    var productName = data['productName'];
    var price = data['price'];
    console.log('data: ', data);
    console.log('product name = ', productName);
    console.log('price = ', price);

    const session = stripe.checkout.sessions.create({
        payment_method_types: ['card'],
        line_items: [
            {
                price_data: {
                    currency: 'usd',
                    product_data: {
                        name: productName,
                    },
                    unit_amount: price,
                },
                quantity: 1,
            },
        ],
        mode: 'payment',
        success_url: 'http://localhost:4200/home?action=success',
        cancel_url: 'http://localhost:4200/home?action=cancel',
    }, async (err, data) => {});

    return { id: session.id };
})

我不明白这个问题,因为我传入了line_items。谢谢!

【问题讨论】:

    标签: node.js firebase google-cloud-platform google-cloud-functions stripe-payments


    【解决方案1】:

    session.idtypeof 'undefined',正如错误消息提示的那样。 这意味着,它必须与 data 一起传递到函数中(没有可用的服务器端会话)。

    还要验证属性名称,因为 Stripe API 至少不知道 line_items

    【讨论】:

    • 还有"line_items" !== "lineItems"
    • 确实,我没有仔细阅读...但只是试图从错误消息中理解。它是 .redirectToCheckout(),其中 sessionId 是唯一被传递的属性 - 因此没有太多空间用于替代原因。
    • 我尝试将 'line_items' 交换为 'lineItems',但它给出了同样的错误。另外,'line_items' 在 Node 代码中工作,直接来自 Stripe 的文档
    【解决方案2】:

    感谢大家帮助我到达这里。

    使用 Firebase 函数时,您需要使用 httpsCallable 而不是 fetch 从 Angular 进行调用...

    角度代码

    import { AngularFireFunctions } from '@angular/fire/functions';
    
    constructor(
        // ...
        private afFun: AngularFireFunctions) {
    
        afFun.useEmulator("localhost", 5000);
    }
    
    checkoutFirebase(): void {
        var stripe = Stripe(environment.stripe.key);
        this.afFun.httpsCallable("stripeTest")({ productName: 'T-shirt', price: '400' })
            .subscribe(result => {           // the result is your Stripe sessionId
                console.log({result});       
    
                stripe.redirectToCheckout({
                    sessionId: result,
                }).then(function (result) {
                    console.log(result.error.message);
                });
            });
    }
    

    然后我们需要在 Firebase 中将 async 添加到 onCall 中。

    Firebase 功能

    exports.stripeTest = functions.https.onCall(async (data, context) => {
        var productName = data['productName'];
        var price = data['price'];
    
        const session = await stripe.checkout.sessions.create({
            payment_method_types: ['card'],
            line_items: [
                {
                    price_data: {
                        currency: 'usd',
                        product_data: {
                            name: productName,
                        },
                        unit_amount: price,
                    },
                    quantity: 1,
                },
            ],
            mode: 'payment',
            success_url: 'http://localhost:4200/home?action=success',
            cancel_url: 'http://localhost:4200/home?action=cancel',
        });
    
        return session.id;
    })
    

    【讨论】:

      【解决方案3】:

      反弹 Ryan Loggerythm 的更新答案。

      在大约半小时左右摆弄 Stripe 的文档解决方案(似乎更倾向于让开发人员尝试演示而不是实际实施该技术)和 Medium 上的其他指南后,我加载了您的更新答案并查看并且看到它绝对完美地工作并且所有这些都在 2 个段落中,1 个用于 Angular,1 个用于 Firebase 功能,因此使用 Stripe 的 Checkout 产品而不是我们自己的结帐(或转向 Shopify 的)的全部意义: 交钥匙简单。

      与此同时,对于这个简单的解决方案,Stripe 的 API 文档感觉像是一个迷宫。也许 Ryan 应该是制作 Stripe 文档的人 ?

      【讨论】:

        猜你喜欢
        • 2020-12-30
        • 2019-10-02
        • 2019-04-17
        • 2022-08-17
        • 2015-10-21
        • 1970-01-01
        • 2017-06-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多