calcom/cal.diy · error · HttpCode
Cal.diy: payment not found
Error message
Cal.diy: payment not found
What it means
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.
Source
Thrown at packages/app-store/btcpayserver/api/webhook.ts:66
const bodyAsString = rawBody.toString();
const signature = req.headers["btcpay-sig"] || req.headers["BTCPay-Sig"];
if (!signature || typeof signature !== "string" || !signature.startsWith("sha256="))
throw new HttpCode({ statusCode: 401, message: "Missing or invalid signature format" });
const webhookData = btcpayWebhookSchema.safeParse(JSON.parse(bodyAsString));
if (!webhookData.success) return res.status(400).json({ message: "Invalid webhook payload" });
const data = webhookData.data;
if (!SUPPORTED_INVOICE_EVENTS.includes(data.type))
return res.status(200).send({ message: "Webhook received but ignored" });
const bookingPaymentRepository = new BookingPaymentRepository();
const payment = await bookingPaymentRepository.findByExternalIdIncludeBookingUserCredentials(
data.invoiceId,
appConfig.type
);
if (!payment) throw new HttpCode({ statusCode: 404, message: "Cal.diy: payment not found" });
if (payment.success) return res.status(200).send({ message: "Payment already registered" });
const key = payment.booking?.user?.credentials?.[0].key;
if (!key) throw new HttpCode({ statusCode: 404, message: "Cal.diy: credentials not found" });
const parsedKey = btcpayCredentialKeysSchema.safeParse(key);
if (!parsedKey.success)
throw new HttpCode({ statusCode: 400, message: "Cal.diy: Invalid BTCPay credentials" });
const { webhookSecret, storeId } = parsedKey.data;
if (storeId !== data.storeId)
throw new HttpCode({ statusCode: 400, message: "Cal.diy: Store ID mismatch" });
const expectedSignature = signature.split("=")[1];
const computedSignature = verifyBTCPaySignature(rawBody, expectedSignature, webhookSecret);
if (computedSignature.length !== expectedSignature.length) {
throw new HttpCode({ statusCode: 400, message: "signature mismatch" });
}
View on GitHub (pinned to 176037d0af)
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.
Defensive patterns
Strategy: validation
Validate before calling
const existing = await bookingPaymentRepository.findByExternalIdIncludeBookingUserCredentials(invoiceId, appConfig.type);
if (!existing) {
return res.status(200).json({ message: "No local payment for this invoice" });
} Try / catch
try {
await handleWebhook(data);
} catch (e) {
if (e instanceof HttpCode && e.statusCode === 404 && /payment not found/.test(e.message)) {
return res.status(200).json({ message: "Ignored: unknown invoice" });
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cal.diy: credentials not found
- Booking with uid ${uid} not found
- Event type with uid ${uid} not found
- Payment with uid ${uid} not found
- No users found or no team present for event type with uid ${
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/800bd86f9e5d729a.
Report an issue: GitHub.