-减少传输到firestore的数据量。
尽管采取了这些行动,但问题仍然存在并显着影响我们的门票创建过程。 我将非常感谢您的经验中的任何建议或见解,以帮助确定这些延迟的原因并实施必要的解决方案。提前感谢您的协助和专业知识。
const { setGlobalOptions } = require('firebase-functions/v2');
setGlobalOptions({
region: "europe-west3",
});
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const stripe = require('stripe')('---');
const axios = require('axios');
const QRCode = require('qrcode');
const { Storage } = require('@google-cloud/storage');
const storage = new Storage();
const { firestore } = require('firebase-admin');
const cors = require('cors')({ origin: true });
admin.initializeApp();
exports.stripeWebhook = functions.https.onRequest((req, res) => {
cors(req, res, async () => {
let event;
try {
event = stripe.webhooks.constructEvent(
req.rawBody,
req.headers['stripe-signature'],
'---'
);
} catch (err) {
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
const data = extractDataFromSession(session);
// Parsing du champ tickets (JSON)
let ticketsArray = [];
try {
ticketsArray = JSON.parse(data.tickets);
console.log("Tickets extraits:", ticketsArray);
} catch (err) {
console.error("Erreur lors du parsing des tickets:", err.message);
return res.status(400).send("Tickets invalides dans les metadata.");
}
if (!Array.isArray(ticketsArray) || ticketsArray.length === 0) {
console.error("Aucun ticket trouvé.");
return res.status(400).send("Aucun ticket trouvé.");
}
// Réponse immédiate à Stripe (code 200)
res.status(200).send({ received: true });
// Traitement complet en arrière-plan (ne bloque pas la réponse)
(async () => {
try {
for (const ticket of ticketsArray) {
for (let i = 0; i < ticket.quantity; i++) {
const ticketData = {
locaImageUrl: data.localisationImageUrl,
garantis_: data.garantis_,
email: data.email,
sexe: data.sexe,
total: data.totalA,
uid: data.uid,
name: data.name,
namePro: data.namePro,
etc... etc
};
const qr = await uniqueCodeCreate(ticketData);
const publicUrl = await createAndUploadQRCode(ticketData, qr, 256, 'M');
await reservationAndProDetailsToFirestore(ticketData, publicUrl, qr);
await sendQrCodeEmail(ticketData, publicUrl, qr);
}
}
} catch (error) {
console.error("Erreur lors du traitement asynchrone:", error);
}
})();
} else {
res.status(400).send("Type d’événement non géré");
}
});
});
function extractDataFromSession(session) {
return {
localisationImageUrl: session.metadata.localisationImageUrl,
garantis_: session.metadata.garantis_,
email: session.metadata.email,
sexe: session.metadata.sexe,
total: session.metadata.total,
uid: session.metadata.uid,
etc..etc..etc
};
}
async function uniqueCodeCreate(data) {
const prenom = data.name;
const rng = Math.floor(Math.random() * ____);
const qr = ____;
return qr;
}
async function sendQrCodeEmail(data, publicUrl, qr) {
try {
const fraix = isNaN(parseFloat(data.ticketPriceFraix)) ? 0.0 : parseFloat(data.ticketPriceFraix);
const amount = parseFloat(data.ticketPrice);
const dateDebutTimestamp = admin.firestore.Timestamp.fromDate(new Date(data.dateDebut));
const url = '---';
// Envoi de la requête POST à l'API
const response = await axios.post(url, {
localisationImageUrl: "",
resaID: qr,
dateDebut: dateDebutTimestamp,
etc...etc...
});
// Vérification de la réponse
if (response.status !== 200) {
}
return { success: true, message: "Email sent successfully." };
} catch (error) {
let errorMessage = "Error sending QR code email";
if (error.response) {
console.error("Erreur dans la réponse:", error.response.status);
console.error("Détails de l'erreur:", error.response.data);
errorMessage += : Server responded with status: ${error.response.status};
} else if (error.request) {
console.error("Erreur dans la requête:", error.request);
errorMessage += ": No response received";
} else {
console.error('Erreur lors de la configuration de la requête:', error.message);
errorMessage += : Request setup failed: ${error.message};
}
return { success: false, message: errorMessage };
}
}
async function createAndUploadQRCode(data, qr, width = 256, errorCorrectionLevel = 'M') {
try {
const options = {
color: { dark: "#000000", light: "#0000" },
width: width,
errorCorrectionLevel: errorCorrectionLevel,
};
const qrCodeBuffer = await QRCode.toBuffer(qr, options);
const bucket = admin.storage().bucket();
const filePath = EventsFile/${---}/${data.---}/---/${---}.png;
const file = bucket.file(filePath);
await file.save(qrCodeBuffer, { contentType: 'image/png', public: true });
console.log("QR code uploadé.");
const publicUrl = https://storage.googleapis.com/${bucket.name}/${file.name};
return publicUrl;
} catch (error) {
throw error;
}
}
async function reservationAndProDetailsToFirestore(data, publicUrl, qr) {
try {
// Initialisation locale de Firestore
const firebaseFirestore = firestore();
const fraix = isNaN(parseFloat(data.ticketPriceFraix)) ? 0.0 : parseFloat(data.ticketPriceFraix);
const amount = parseFloat(data.ticketPrice);
const dateDebutTimestamp = admin.firestore.Timestamp.fromDate(new Date(data.dateDebut));
const dateFinTimestamp = admin.firestore.Timestamp.fromDate(new Date(data.dateFin));
const now = new Date();
const resaModel = {
...//...//...
};
const resaDetails = {
...//...//...
};
const historiqueDetails = {
...//...//...
};
const clientInfo = {
...//...//...
};
const historiqueClientDetails = {
...//...//...
};
const postRef = firebaseFirestore
.collection("--")
.doc(--)
.collection("--")
.doc(--);
const postUpdateData = {
'--': admin.firestore.FieldValue.increment(amount),
[--]: firestore.FieldValue.increment(-1),
[--]: firestore.FieldValue.increment(1)
};
if (data.sexe === '--') {
postUpdateData['--'] = firestore.FieldValue.increment(1);
} else if (data.-- === '--') {
postUpdateData['--'] = firestore.FieldValue.increment(1);
}
const batch = firebaseFirestore.batch();
// Ajout des écritures dans le batch :
batch.set(
firebaseFirestore.collection("--").doc(--).collection("-- --").doc(--),
);
batch.set(
firebaseFirestore.collection("--").doc(--).collection("Reservation").doc(--),
resaDetails
);
batch.set(
firebaseFirestore.collection("--").doc(--).collection("-- --").doc(--),
historiqueDetails
);
const clientDocRef = firebaseFirestore.collection("--").doc(--).collection("--").doc(--);
batch.set(clientDocRef, clientInfo, { merge: true });
batch.set(clientDocRef.collection("--").doc(--), historiqueClientDetails);
batch.update(postRef, { ...postUpdateData, Like: admin.firestore.FieldValue.increment(1) });
// Modification : retourner la promesse du commit
return batch.commit().then(() => {
console.timeEnd("batchCommit");
console.log("=== Fin de combinedReservationAndHistoryToFirestore ===");
return { success: true, message: "" };
});
} catch (error) {
console.error("", error);
return { success: false, message: "" };
}
}
HTTP Cloud功能在一旦将响应发送给呼叫者,它被认为是“完成的”。寄回响应后尝试的任何工作都可能永远不会执行,如果这样做,则在此处记录了
,并且某些网络请求可能会被阻止,从而导致其他问题。在您的情况下,您告诉云功能运行时,在调用以下任何一条行时,您的功能已完成:
res.status(200).send({ received: true });
or
res.status(400).send("Type d’événement non géré");
您应该将这些行对待这些行,就像它们在开始时具有
return
,在此之后可以运行更多代码。
如果以快速,及时的方式向您的功能的呼叫者发送响应很重要(Stripe推荐),接受请求,验证并将相关数据保存到实时数据库或云Firestore,然后将响应发送回呼叫者。在单独的云功能中,将其配置为收听该数据库添加,然后对其进行采取行动,以执行您需要执行的任何背景任务。
将您的代码捕获以实现这一目标超出了StackoverFlow的范围。但这是您需要执行的步骤:
HTTP请求处理程序Accept HTTP请求
估算请求
将请求数据保存到实时数据库或CloudFirestore
对呼叫者的确认书进行回信,理想情况下,提供有关在何处获得状态更新的信息。 (有关此的一般形式,请参见HTTP202接受
流)
background任务 trigger基于上面步骤3中的数据库调用triggerRTDB云函数或CloudFirestore Cloud函数
阅读请求数据执行背景任务
如果您期望高音量,并且不需要任务进度跟踪,则可以调用Pub/sub事件云功能
而不是覆盖的其他线程