diff --git a/apps/web/app/(ee)/api/shopify/integration/webhook/route.ts b/apps/web/app/(ee)/api/shopify/integration/webhook/route.ts index a9ad369d102..f51475c3199 100644 --- a/apps/web/app/(ee)/api/shopify/integration/webhook/route.ts +++ b/apps/web/app/(ee)/api/shopify/integration/webhook/route.ts @@ -1,8 +1,8 @@ import { captureWebhookLog } from "@/lib/api-logs/capture-webhook-log"; import { isLocalDev } from "@/lib/api/environment"; import { prisma } from "@/lib/prisma"; -import { log } from "@dub/utils"; import { waitUntil } from "@vercel/functions"; +import { logAndRespond } from "app/(ee)/api/cron/utils"; import crypto from "crypto"; import { appUninstalled } from "./app-uninstalled"; import { customersDataRequest } from "./customers-data-request"; @@ -27,6 +27,12 @@ export const POST = async (req: Request) => { const headers = req.headers; const topic = headers.get("x-shopify-topic") || ""; const signature = headers.get("x-shopify-hmac-sha256") || ""; + const shopDomain = headers.get("x-shopify-shop-domain") || ""; + + console.info("Webhook event", { + shopDomain, + topic, + }); if (!isLocalDev) { // Verify signature @@ -36,19 +42,21 @@ export const POST = async (req: Request) => { .digest("base64"); if (generatedSignature !== signature) { - return new Response(`[Shopify] Invalid webhook signature. Skipping...`, { - status: 401, - }); + return logAndRespond( + "Shopify webhook signature verification failed. Skipping...", + { + status: 401, + }, + ); } } // Check if topic is relevant if (!relevantTopics.has(topic)) { - return new Response(`[Shopify] Unsupported topic: ${topic}. Skipping...`); + return logAndRespond(`Unsupported topic: ${topic}. Skipping...`); } const event = JSON.parse(data); - const shopDomain = headers.get("x-shopify-shop-domain") || ""; // Find workspace const workspace = await prisma.project.findUnique({ @@ -63,11 +71,15 @@ export const POST = async (req: Request) => { }); if (!workspace) { - return new Response( - `[Shopify] Workspace not found for shop: ${shopDomain}. Skipping...`, + return logAndRespond( + `Workspace not found for shop: ${shopDomain}. Skipping...`, ); } + console.info("Workspace found", { + workspaceId: workspace.id, + }); + const requestLog = { workspaceId: workspace.id, method: req.method, @@ -111,14 +123,7 @@ export const POST = async (req: Request) => { break; } } catch (error) { - await log({ - message: `Shopify webhook failed. Error: ${error.message}`, - type: "errors", - }); - - const response = new Response( - `[Shopify] Webhook handler failed. View logs`, - ); + const response = new Response("Webhook handler failed. View logs."); waitUntil( captureWebhookLog({ diff --git a/apps/web/app/(ee)/api/shopify/pixel/route.ts b/apps/web/app/(ee)/api/shopify/pixel/route.ts index 1ff35bb7750..c8ae4a715c6 100644 --- a/apps/web/app/(ee)/api/shopify/pixel/route.ts +++ b/apps/web/app/(ee)/api/shopify/pixel/route.ts @@ -23,6 +23,11 @@ export const POST = async (req: Request) => { }); } + console.info("Shopify pixel event", { + clickId, + checkoutToken, + }); + // Rate limit the request const ip = process.env.VERCEL === "1" ? ipAddress(req) : LOCALHOST_IP; const { success } = await ratelimit().limit(`shopify-track-pixel:${ip}`); @@ -39,6 +44,7 @@ export const POST = async (req: Request) => { const clickEvent = await getClickEvent({ clickId }); if (!clickEvent) { + console.warn(`Click event not found for the clickId ${clickId}`); clickId = undefined; } } diff --git a/apps/web/lib/integrations/shopify/checkout-cache.ts b/apps/web/lib/integrations/shopify/checkout-cache.ts index 3fd53217eb0..543228e2c40 100644 --- a/apps/web/lib/integrations/shopify/checkout-cache.ts +++ b/apps/web/lib/integrations/shopify/checkout-cache.ts @@ -65,11 +65,28 @@ export async function tryDispatchShopifyOrderJob({ checkoutToken: string; checkout: ShopifyCheckoutCacheItem; }) { + const logContext = { + checkoutToken, + workspaceId: checkout.workspaceId, + clickId: checkout.clickId, + hasClickId: Boolean(checkout.clickId), + hasOrder: Boolean(checkout.order), + dispatched: Boolean(checkout.dispatched), + }; + if (!checkout.order || !checkout.workspaceId || !checkout.clickId) { + console.info( + "Shopify order dispatch skipped: checkout incomplete", + logContext, + ); return false; } if (checkout.dispatched) { + console.info( + "Shopify order dispatch skipped: already dispatched", + logContext, + ); return false; } @@ -79,6 +96,7 @@ export async function tryDispatchShopifyOrderJob({ const claimed = Boolean(claim); if (!claimed) { + console.info("Shopify order dispatch skipped: claim lost", logContext); return false; } @@ -94,11 +112,17 @@ export async function tryDispatchShopifyOrderJob({ }, ); } catch (error) { + console.error("Shopify order dispatch failed, releasing claim", { + ...logContext, + error, + }); await redis.hdel(key, "dispatched"); throw error; } await shopifyCheckoutCache.delete(checkoutToken); + console.info("Shopify order job dispatched", logContext); + return true; }