面对 firebase 和 stripe webhooks 的问题

问题描述 投票:0回答:0

我正在从 codecanyon 重新申请一个项目,当我尝试在其中设置条带付款时,我遇到了一些问题我是 stripe 和 firebase 的新手,即使按照文档给定的问题也没有解决。

我在 friebase stripeWebhook 函数中获取的日志

{ "textPayload": "error TypeError: Cannot read properties of undefined (reading 'data')", "insertId": "64579c3a00090291aaae197c", "resource": { "type": "cloud_function", "labels": { "region": "us-central1", "project_id": "doctor-app-57d5a", "function_name": "stripeWebhook" } }, "timestamp": "2023-05-07T12:40:26.590481Z", "labels": { "instance_id": "00c61b117c339eb21c36ca478c21da086a8cad9d70243f1bd85a81514efef99af71c2c7f5a0f19be12f16a8d14b8fcaf19a660988e2d1488d71f", "execution_id": "qd9n9rwpctsj" }, "logName": "projects/doctor-app-57d5a/logs/cloudfunctions.googleapis.com%2Fcloud-functions", "trace": "projects/doctor-app-57d5a/traces/04069e50580a94c2fe0eea1b931f6265", "receiveTimestamp": "2023-05-07T12:40:26.893559408Z" }

如果有人可以帮助我解决问题,我也准备付款我只是希望这个问题得到解决

下面是文件命名stripe-functions.js的代码

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.refundStripe = exports.stripeWebhook = exports.purchaseTimeslot = void 0;
const functions = require("firebase-functions");
const stripe_1 = require("stripe");
const collections_1 = require("./collections");
const constants_1 = require("./constants");
const firebase_admin_1 = require("firebase-admin");
const payment_functions_1 = require("./payment-functions");
const notification_function_1 = require("./notification-function");
const collections_2 = require("./collections");
const timeslot_functions_1 = require("./timeslot-functions");
const stripe = new stripe_1.default(process.env.STRIPE_SECRET_KEY, {
    apiVersion: "2020-08-27",
});
/**
 *
 * function when client purchase timeslot, by default we use Stripe payment gateway
 * you can change this to your payment gateway by following the stripe structure of purchaing the timeslot
 */
exports.purchaseTimeslot = functions.https.onCall(async (request, response) => {
    try {
        const choosedTimeSlot = (await collections_1.timeSlotCol.doc(request.timeSlotId).get()).data();
        if (!choosedTimeSlot) {
            throw "choosed timeslot is not found";
        }
        const paymentIntent = await stripe.paymentIntents.create({
            amount: choosedTimeSlot.price * 100,
            currency: constants_1.CURRENCY,
            payment_method_types: ["card"],
        });
        await collections_1.orderCol.add({
            createdAt: firebase_admin_1.firestore.Timestamp.fromDate(new Date()),
            timeSlotId: request.timeSlotId,
            userId: request.userId,
            charged: false,
            stripePaymentId: paymentIntent.id,
            status: collections_1.OrderStatus.notPay,
            paymentMethod: payment_functions_1.PaymentMethod.Stripe,
        });
        return paymentIntent.client_secret;
    }
    catch (error) {
        throw error;
    }
});
/**
 * this function is for stripe webhook, when client successfully purchase the timeslot, stripe will call this webhook
 * make sure you have setup the stripe webhook by following this tutorial {@link https://halodoctor.netlify.app/docs/payment-gateway/stripe-webook}
 */
exports.stripeWebhook = functions.https.onRequest(async (request, response) => {
    var _a, _b;
    let event;
    try {
        if (process.env.STRIPE_WEBHOOK_SECRET == undefined) {
            throw new Error("Stripe webhook secret is undefined, please check .env file");
        }
        const stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
        event = stripe.webhooks.constructEvent(request.rawBody, request.headers["stripe-signature"], stripeWebhookSecret);
    }
    catch (error) {
        console.error("Webhook signature verification failed");
        response.sendStatus(400);
        return;
    }
    switch (event.type) {
        case "payment_intent.succeeded":
            try {
                const amount = request.body.data.object.amount_received / 100;
                const currency = request.body.data.object.currency;
                const linkReceipt = request.body.data.object.charges.data[0].receipt_url;
                let order = await collections_1.orderCol
                    .where("stripePaymentId", "==", request.body.data.object.id)
                    .get();
                let orderRef = order.docs[0];
                orderRef.ref.update({
                    charged: true,
                    amount: amount,
                    status: payment_functions_1.PaymentStatus.PaymentSuccess,
                    linkReceipt: linkReceipt,
                    currency: currency,
                });
                //Get user info who book this timeslot
                let bookByWho = (await collections_1.usersCol.doc(orderRef.data().userId).get()).data();
                const timeSlot = await collections_1.timeSlotCol
                    .doc(orderRef.data().timeSlotId)
                    .get();
                const timeSlotData = timeSlot.data();
                if (!timeSlotData) {
                    throw "selected timeslot is null or undefined";
                }
                const doctor = await collections_1.doctorCol.doc(timeSlotData.doctorId).get();
                timeSlot.ref.update({
                    charged: true,
                    available: false,
                    bookByWho: {
                        userId: orderRef.data().userId,
                        displayName: bookByWho === null || bookByWho === void 0 ? void 0 : bookByWho.displayName,
                        photoUrl: (bookByWho === null || bookByWho === void 0 ? void 0 : bookByWho.photoUrl) ? bookByWho === null || bookByWho === void 0 ? void 0 : bookByWho.photoUrl : "",
                    },
                    status: "booked",
                    doctor: {
                        doctorName: (_a = doctor.data()) === null || _a === void 0 ? void 0 : _a.doctorName,
                        doctorPicture: (_b = doctor.data()) === null || _b === void 0 ? void 0 : _b.doctorPicture,
                    },
                    purchaseTime: firebase_admin_1.firestore.Timestamp.fromDate(new Date()),
                });
                await (0, notification_function_1.orderedTimeslotNotification)(doctor.id);
                await (0, notification_function_1.paymentSuccessNotification)(orderRef.data().userId, orderRef.id);
                console.log("payment success");
                break;
            }
            catch (error) {
                console.log("error " + error);
            }
        case "payment_intent.canceled":
            console.log("failed payment ");
            break;
        // ... handle other event types
        default:
            console.log(`Unhandled event type ${event.type}`);
    }
    // Return a 200 response to acknowledge receipt of the event
    response.send();
});
/**
by default we use stripe refund system if you use other payment gateway you need to change this function to
you can just follow the structure of the function
*/
exports.refundStripe = functions.https.onCall(async (request, response) => {
    try {
        const { timeSlotId } = request;
        const orderRef = await collections_1.orderCol
            .where("timeSlotId", "==", request.timeSlotId)
            .get();
        const order = orderRef.docs[0];
        const refund = await stripe.refunds.create({
            payment_intent: order.data().stripePaymentId,
        });
        if (refund.status === "succeeded") {
            let refundData = await collections_2.refundCol.add({
                createdAt: firebase_admin_1.firestore.Timestamp.fromDate(new Date()),
                timeSlotId: request.timeSlotId,
                paymentId: order.data().stripePaymentId,
                status: refund.status,
                amount: refund.amount,
                refundId: refund.id,
                currency: refund.currency,
            });
            await (0, timeslot_functions_1.refundTimeslot)(timeSlotId, refundData.id);
            console.log("refund success : " + JSON.stringify(refund));
        }
        else {
            throw "refund failed";
        }
    }
    catch (err) {
        throw err;
    }
});
//# sourceMappingURL=stripe-functions.js.map

还有一个文件命名为 stripe-function.js

"use strict";
const functions = require("firebase-functions");
const stripe = require("stripe")(functions.config().stripe.token);
//const stripe = require('stripe')('testing');
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
const notificationFunction = require("./notification-function");
const timeslotFunction = require("./timeslot-function");
const { firestore } = require("firebase-admin");
exports.purchaseTimeslot = functions.https.onCall(async (request, response) => {
    try {
        let purchasedTimeSlot = await db
            .collection("DoctorTimeslot")
            .doc(request.timeSlotId)
            .get();
        purchasedTimeSlot = purchasedTimeSlot.data();
        const paymentIntent = await stripe.paymentIntents.create({
            amount: purchasedTimeSlot.price * 100,
            currency: "usd",
            payment_method_types: ["card"],
        });
        console.log("user id request : " + request.userId);
        db.collection("Order").add({
            createdAt: firestore.Timestamp.fromDate(new Date()),
            timeSlotId: request.timeSlotId,
            userId: request.userId,
            charged: false,
            stripePaymentId: paymentIntent.id,
            status: "notPay",
        });
        return paymentIntent.client_secret;
    }
    catch (e) {
        throw e;
    }
});
exports.stripeWebhook = functions.https.onRequest(async (request, response) => {
    let event;
    try {
        const stripeWebhookSecret = functions.config().stripe.webhook_secret;
        event = stripe.webhooks.constructEvent(request.rawBody, request.headers["stripe-signature"], stripeWebhookSecret);
        console.log("Webhook signature verification done");
    }
    catch (error) {
        console.error("Webhook signature verification failed");
        return response.sendStatus(400);
    }
    // Handle payment successfully event
    switch (event.type) {
        case "payment_intent.succeeded":
            try {
                console.log('Paymentintent was successful: ', paymentIntent.id)
                const amount = request.body.data.object.amount_received / 100;
                const currency = request.body.data.object.currency;
                const linkReceipt = request.body.data.object.charges.data[0].receipt_url;
                //Update Order
                let order = await db
                    .collection('Order')
                    .where("stripePaymentId", "==", request.body.data.object.id)
                    .get()
                    .then(async (querySnapshot) => {
                    let orderData = {};
                    querySnapshot.forEach(function (doc) {
                        console.log(doc.id, " => ", doc.data());
                        doc.ref.update({
                            charged: true,
                            amount: amount,
                            status: "payment_success",
                            linkReceipt: linkReceipt,
                            currency: currency,
                        });
                        orderData = doc.data();
                    });
                    return orderData;
                });
                //Get user info who book this timeslot
                let bookByWho = await db.collection("Users").doc(order.userId).get();
                //Update DoctorTimeslot
                let timeSlotRef = await db
                    .collection("DoctorTimeslot")
                    .doc(order.timeSlotId)
                    .get();
                //Get doctor detail data
                let doctor = await db
                    .collection("Doctors")
                    .doc(timeSlotRef.data().doctorId)
                    .get();
                await timeSlotRef.ref.update({
                    charged: true,
                    available: false,
                    bookByWho: {
                        userId: order.userId,
                        displayName: bookByWho.data().displayName,
                        photoUrl: bookByWho.data().photoUrl
                            ? bookByWho.data().photoUrl
                            : "",
                    },
                    status: "booked",
                    doctor: {
                        doctorName: doctor.data().doctorName,
                        doctorPicture: doctor.data().doctorPicture,
                    },
                    purchaseTime: firestore.Timestamp.fromDate(new Date()),
                });
                //send notification to doctor
                await notificationFunction.orderedTimeslotNotification(doctor.id);
                console.log("payment success");
                break;
            }
            catch (error) {
                console.log("error the error is here" + error);
            }
        case "payment_intent.canceled":
            const session = event.data.object;
            // Then define and call a function to handle the event checkout.session.async_payment_succeeded
            console.log("failed payment ");
            break;
        // ... handle other event types
        default:
            console.log(`Unhandled event type ${event.type}`);
    }
    // Return a 200 response to acknowledge receipt of the event
    response.send();
});
exports.refundTimeslot = functions.https.onCall(async (request, response) => {
    try {
        console.log("timeslot id : " + request.timeSlotId);
        let orderSnapshot = await db
            .collection("Order")
            .where("timeSlotId", "==", request.timeSlotId)
            .get();
        let order = orderSnapshot.docs[0];
        console.log("order obejct : " + JSON.stringify(order));
        console.log("stripe payment id : " + order.data().stripePaymentId);
        const refund = await stripe.refunds.create({
            payment_intent: order.data().stripePaymentId,
        });
        console.log("refund object : " + JSON.stringify(refund));
        if (refund.status == "succeeded") {
            let refundData = await db.collection("Refund").add({
                createdAt: firestore.Timestamp.fromDate(new Date()),
                timeSlotId: request.timeSlotId,
                stripePaymentId: order.data().stripePaymentId,
                status: refund.status,
                amount: refund.amount,
                refundId: refund.id,
                currency: refund.currency,
            });
            await timeslotFunction.refundTimeslot(request.timeSlotId, refundData.id);
            console.log("refund success : " + JSON.stringify(refund));
        }
        else {
            throw "refund failed";
        }
    }
    catch (e) {
        throw e;
    }
});
//# sourceMappingURL=stripe-function.js.map

firebase google-cloud-functions stripe-payments webhooks payment-gateway
© www.soinside.com 2019 - 2024. All rights reserved.