production secret added

This commit is contained in:
2026-03-24 14:16:27 -04:00
parent d772b7ec9c
commit 1300084481
5 changed files with 60 additions and 10 deletions

View File

@@ -7,14 +7,14 @@ initializeApp();
const db = getFirestore();
/**
* Shopdm Pay Webhook Handler
* Shopdm Pay Webhook Handler (v2)
*
* Receives payment.successful events from Shopdm Pay.
* Matches the invoice_id to a pending donation and creates
* a confirmed donor record.
*/
exports.shopdmPayWebhook = onRequest(
{ secrets: ["SHOPDM_MERCHANT_SECRET_DEV"] },
{ secrets: ["SHOPDM_MERCHANT_SECRET", "SHOPDM_MERCHANT_SECRET_DEV"], invoker: "public" },
async (req, res) => {
// Only accept POST requests
if (req.method !== "POST") {
@@ -24,8 +24,7 @@ exports.shopdmPayWebhook = onRequest(
const payload = req.body;
// Verify webhook signature
// Supports both dev and prod secrets
// Verify webhook signature using sorted keys + HMAC-SHA256 (per Shopdm Pay docs)
const secrets = [
process.env.SHOPDM_MERCHANT_SECRET,
process.env.SHOPDM_MERCHANT_SECRET_DEV,
@@ -39,10 +38,7 @@ exports.shopdmPayWebhook = onRequest(
return;
}
const sortedPayload = JSON.stringify(
payload,
Object.keys(payload).sort()
);
const sortedPayload = JSON.stringify(payload, Object.keys(payload).sort());
const isValid = secrets.some((secret) => {
const expected = crypto
@@ -57,14 +53,21 @@ exports.shopdmPayWebhook = onRequest(
res.status(401).send("Unauthorized");
return;
}
console.log("Webhook signature verified successfully");
}
// Only process successful payments
if (payload.event !== "payment.successful") {
if (payload.event !== "payment.successful" && payload.event !== "payment.success") {
res.status(200).send("Event ignored");
return;
}
// Determine environment from the live flag in the payload
const isLive = payload.live === true;
const webhookEnv = isLive ? "production" : "dev";
console.log(`Webhook environment: ${webhookEnv} (live=${payload.live})`);
const invoiceId = payload.metadata?.invoice_id;
if (!invoiceId) {
console.log("No invoice_id in webhook payload, skipping donor creation");
@@ -85,6 +88,16 @@ exports.shopdmPayWebhook = onRequest(
const pending = pendingDoc.data();
// Ensure the webhook environment matches the pending donation environment
const pendingEnv = pending.env || "production";
if (pendingEnv !== webhookEnv) {
console.warn(
`Environment mismatch: pending donation is ${pendingEnv} but webhook is ${webhookEnv}. Skipping.`
);
res.status(200).send("OK - environment mismatch");
return;
}
// Check if already processed (idempotency)
if (pending.status === "confirmed") {
console.log(`Donation ${invoiceId} already confirmed, skipping`);
@@ -103,6 +116,7 @@ exports.shopdmPayWebhook = onRequest(
message: pending.message || "",
anonymous: !!pending.anonymous,
status: "confirmed",
env: webhookEnv,
pendingDonationId: invoiceId,
paymentId: payload.data?.object_id || "",
date: FieldValue.serverTimestamp(),