Stripe Checkout:从 Node 迁移到 Firebase Functions
Stripe Checkout: moving from Node to Firebase Functions
我的 Angular 11 应用使用 Stripe Checkout 和 Express 服务器来处理付款。 Angular & Node.js 一切正常。我想使用 Firebase Functions 而不是 Node,但是当我调用我的 Firebase Functions 时,出现错误:
IntegrationError: stripe.redirectToCheckout: You must provide one of lineItems, items, or sessionId.
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);
});
}
节点服务器:
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
。谢谢!
session.id
是 typeof 'undefined'
,如错误消息提示的那样。
这意味着,它必须与 data
一起传递到函数中(没有可用的服务器端会话)。
还要验证 属性 名称,因为至少 line_items
对 Stripe API 是未知的。
感谢大家帮助我来到这里。
使用 Firebase Functions 时,您需要使用 httpsCallable
而不是 fetch
...
从 Angular 进行调用
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;
})
反弹 Ryan Loggerythm 的更新答案。
在大约半小时左右摆弄 Stripe 的文档解决方案(这似乎更适合让开发人员试用演示而不是实际实施该技术)和 Medium 上的其他指南后,我加载了您更新的答案你瞧,它绝对完美无瑕,所有这些都在 2 段内,1 段用于 Angular,1 段用于 Firebase 函数,因此使用 Stripe 的 Checkout 产品而不是制作我们自己的结帐(或转向 Shopify 的):交钥匙简单。
与此同时,对于这个简单的解决方案,Stripe 的 API 文档感觉就像是一个迷宫。也许 Ryan 应该是制作 Stripe 文档的人
我的 Angular 11 应用使用 Stripe Checkout 和 Express 服务器来处理付款。 Angular & Node.js 一切正常。我想使用 Firebase Functions 而不是 Node,但是当我调用我的 Firebase Functions 时,出现错误:
IntegrationError: stripe.redirectToCheckout: You must provide one of lineItems, items, or sessionId.
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);
});
}
节点服务器:
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
。谢谢!
session.id
是 typeof 'undefined'
,如错误消息提示的那样。
这意味着,它必须与 data
一起传递到函数中(没有可用的服务器端会话)。
还要验证 属性 名称,因为至少 line_items
对 Stripe API 是未知的。
感谢大家帮助我来到这里。
使用 Firebase Functions 时,您需要使用 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;
})
反弹 Ryan Loggerythm 的更新答案。
在大约半小时左右摆弄 Stripe 的文档解决方案(这似乎更适合让开发人员试用演示而不是实际实施该技术)和 Medium 上的其他指南后,我加载了您更新的答案你瞧,它绝对完美无瑕,所有这些都在 2 段内,1 段用于 Angular,1 段用于 Firebase 函数,因此使用 Stripe 的 Checkout 产品而不是制作我们自己的结帐(或转向 Shopify 的):交钥匙简单。
与此同时,对于这个简单的解决方案,Stripe 的 API 文档感觉就像是一个迷宫。也许 Ryan 应该是制作 Stripe 文档的人