{"record":{"id":"800bd86f9e5d729a","repo":"calcom/cal.diy","slug":"cal-diy-payment-not-found","errorCode":null,"errorMessage":"Cal.diy: payment not found","messagePattern":"Cal\\.diy: payment not found","errorType":"http","errorClass":"HttpCode","httpStatus":404,"severity":"error","filePath":"packages/app-store/btcpayserver/api/webhook.ts","lineNumber":66,"sourceCode":"    const bodyAsString = rawBody.toString();\r\n\r\n    const signature = req.headers[\"btcpay-sig\"] || req.headers[\"BTCPay-Sig\"];\r\n    if (!signature || typeof signature !== \"string\" || !signature.startsWith(\"sha256=\"))\r\n      throw new HttpCode({ statusCode: 401, message: \"Missing or invalid signature format\" });\r\n\r\n    const webhookData = btcpayWebhookSchema.safeParse(JSON.parse(bodyAsString));\r\n    if (!webhookData.success) return res.status(400).json({ message: \"Invalid webhook payload\" });\r\n\r\n    const data = webhookData.data;\r\n    if (!SUPPORTED_INVOICE_EVENTS.includes(data.type))\r\n      return res.status(200).send({ message: \"Webhook received but ignored\" });\r\n\r\n    const bookingPaymentRepository = new BookingPaymentRepository();\r\n    const payment = await bookingPaymentRepository.findByExternalIdIncludeBookingUserCredentials(\r\n      data.invoiceId,\r\n      appConfig.type\r\n    );\r\n    if (!payment) throw new HttpCode({ statusCode: 404, message: \"Cal.diy: payment not found\" });\r\n    if (payment.success) return res.status(200).send({ message: \"Payment already registered\" });\r\n    const key = payment.booking?.user?.credentials?.[0].key;\r\n    if (!key) throw new HttpCode({ statusCode: 404, message: \"Cal.diy: credentials not found\" });\r\n\r\n    const parsedKey = btcpayCredentialKeysSchema.safeParse(key);\r\n    if (!parsedKey.success)\r\n      throw new HttpCode({ statusCode: 400, message: \"Cal.diy: Invalid BTCPay credentials\" });\r\n\r\n    const { webhookSecret, storeId } = parsedKey.data;\r\n    if (storeId !== data.storeId)\r\n      throw new HttpCode({ statusCode: 400, message: \"Cal.diy: Store ID mismatch\" });\r\n\r\n    const expectedSignature = signature.split(\"=\")[1];\r\n    const computedSignature = verifyBTCPaySignature(rawBody, expectedSignature, webhookSecret);\r\n\r\n    if (computedSignature.length !== expectedSignature.length) {\r\n      throw new HttpCode({ statusCode: 400, message: \"signature mismatch\" });\r\n    }\r","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/packages/app-store/btcpayserver/api/webhook.ts#L48-L84","documentation":"After parsing the webhook payload, the handler looks up the booking payment by `data.invoiceId` (BTCPay invoice id) via `BookingPaymentRepository.findByExternalIdIncludeBookingUserCredentials`. If no payment row exists for that invoice id under the BTCPay app type, HttpCode 404 is thrown — the system received a payment event it has no local record of.","triggerScenarios":"BTCPay sends an `InvoiceSettled`/`InvoiceProcessing` event for an invoice that was never recorded locally: invoice created in BTCPay directly, payment row deleted, race where the event arrives before `PaymentService.create()` committed, or a webhook from a different Cal.diy instance.","commonSituations":"Manual invoice creation in BTCPay UI; webhook redelivery long after the booking/payment row was purged; multi-instance setup where webhooks hit the wrong environment; test/sandbox BTCPay store firing against production DB.","solutions":["Confirm the `invoiceId` in the webhook payload corresponds to a payment created by this Cal.diy instance (query `BookingPayment` by external id).","If redelivery is expected post-cleanup, return 200 acknowledged instead of 404 to stop BTCPay retrying.","Ensure webhooks are configured per-environment (sandbox vs production stores).","Verify `appConfig.type` matches the `appId` used when the payment was stored."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const existing = await bookingPaymentRepository.findByExternalIdIncludeBookingUserCredentials(invoiceId, appConfig.type);\nif (!existing) {\n  return res.status(200).json({ message: \"No local payment for this invoice\" });\n}","typeGuard":null,"tryCatchPattern":"try {\n  await handleWebhook(data);\n} catch (e) {\n  if (e instanceof HttpCode && e.statusCode === 404 && /payment not found/.test(e.message)) {\n    return res.status(200).json({ message: \"Ignored: unknown invoice\" });\n  }\n  throw e;\n}","preventionTips":["Scope webhooks per environment so sandbox invoices don't hit production.","Ensure `PaymentService.create()` commits the payment row before BTCPay can deliver events.","Make `appConfig.type` consistent across create and webhook paths.","Idempotently handle redeliveries instead of 404-ing after cleanup."],"tags":["webhook","payment","not-found","btcpay","data-integrity"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}