简体   繁体   English

Stripe Checkout:从 Node 转移到 Firebase 函数

[英]Stripe Checkout: moving from Node to Firebase Functions

My Angular 11 app uses Stripe Checkout with an Express server to handle the payment.我的 Angular 11 应用程序使用带有 Express 服务器的Stripe Checkout来处理付款。 Everything works fine with Angular & Node. Angular 和 Node.js 一切正常。 I'd like to use Firebase Functions instead of Node, but when I call my Firebase Function, I get the error:我想使用 Firebase 函数而不是 Node,但是当我调用我的 Firebase Function 时,出现错误:

IntegrationError: stripe.redirectToCheckout: You must provide one of lineItems, items, or sessionId. IntegrationError:stripe.redirectToCheckout:您必须提供 lineItems、items 或 sessionId 之一。

Angular code : Angular 代码

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

Node server :节点服务器

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 Function : Firebase Function :

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

I don't understand the issue because I pass in line_items .我不明白这个问题,因为我传入了line_items Thanks!谢谢!

session.id is typeof 'undefined' , as the error message hints for. session.idtypeof 'undefined' ,如错误消息提示的那样。 Which means, that it would have to be passed into the function along with data (there are no server-side sessions available).这意味着,它必须与data一起传递到 function(没有可用的服务器端会话)。

Also verify the property names, because at least line_items is unknown to Stripe API.还要验证属性名称,因为 Stripe line_items至少不知道 line_items。

Thanks everyone for helping me get here.感谢大家帮助我到达这里。

When using Firebase Functions, you need to make your call from Angular with httpsCallable instead of fetch ...使用 Firebase 函数时,您需要使用httpsCallable而不是fetch从 Angular 进行调用...

Angular code : 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);
            });
        });
}

Then we need to add async to the onCall in Firebase.然后我们需要在onCall中添加async

Firebase Function : Firebase Function

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

Rebounding Ryan Loggerythm's updated answer.反弹 Ryan Loggerythm 的更新答案。

After about half an hour or so of fiddling with Stripe's docs solutions (which seem more geared towards getting devs to try out the demo instead of actually implementing the technology) and other guides on Medium, I loaded up your updated answer and lo and behold it worked absolutely flawlessly and all that within 2 paragraphs, 1 for Angular, 1 for Firebase functions, hence the whole point of using Stripe's Checkout product instead of making our own checkout (or turning towards Shopify's): turnkey simplicity.在大约半小时左右摆弄 Stripe 的文档解决方案(这似乎更适合让开发人员尝试演示而不是实际实施该技术)和 Medium 上的其他指南后,我加载了您更新的答案并看到它绝对完美地工作,所有这些都在 2 段内,1 段用于 Angular,1 段用于 Firebase 功能,因此使用 Stripe 的 Checkout 产品而不是我们自己结帐(或转向 Shopify 的)的全部意义在于:交钥匙简单性。

Meanwhile Stripe's API docs felt like a maze for this simple solution.与此同时,对于这个简单的解决方案,Stripe 的 API 文档感觉就像是一个迷宫。 Maybe Ryan should be the one making Stripe's documentation也许 Ryan 应该是制作 Stripe 文档的人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM