calcom/cal.diy · error · HttpCode
Cal.diy: Store ID mismatch
Error message
Cal.diy: Store ID mismatch
What it means
The webhook payload's `storeId` is compared against the `storeId` stored in the credential. A mismatch means the event came from a different BTCPay store than the one this user configured, so it is rejected with HttpCode 400 to prevent cross-store payment attribution.
Source
Thrown at packages/app-store/btcpayserver/api/webhook.ts:77
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" });
}
const isValid = crypto.timingSafeEqual(
Buffer.from(computedSignature, "hex"),
Buffer.from(expectedSignature, "hex")
);
if (!isValid) throw new HttpCode({ statusCode: 400, message: "signature mismatch" });
const traceContext = distributedTracing.createTrace("btcpayserver_webhook", {
meta: { paymentId: payment.id, bookingId: payment.bookingId },
});
await handlePaymentSuccess({
paymentId: payment.id,
View on GitHub (pinned to 176037d0af)
Solutions
- Open the BTCPay integration settings in Cal.diy and update the store id to match the store that owns the invoice.
- Confirm the BTCPay webhook is scoped to a single store, not server-wide.
- If multiple stores must be supported, configure separate credentials/webhook endpoints per store.
- Compare `data.storeId` from the payload against the value shown in BTCPay's store settings to identify the drift.
Defensive patterns
Strategy: validation
Validate before calling
if (storeId !== data.storeId) {
return res.status(400).json({ message: `Store mismatch: webhook=${data.storeId} credential=${storeId}` });
} Try / catch
try {
processWebhook(data, credential);
} catch (e) {
if (e instanceof HttpCode && /Store ID mismatch/.test(e.message)) {
return res.status(400).json({ message: e.message });
}
throw e;
} Prevention
- Scope BTCPay webhooks per store, not server-wide.
- Copy the store id from BTCPay's store settings into the credential verbatim.
- Add a multi-store support matrix to onboarding docs.
- Reject mismatched-store events with 400 so BTCPay doesn't retry indefinitely.
When it happens
Trigger: A single BTCPay Server has multiple stores and the webhook endpoint receives events from a store other than the one linked to the user's credential; the user reconfigured their store id in BTCPay without updating the Cal.diy credential; webhook URL shared across stores.
Common situations: Multi-store BTCPay accounts; webhook configured at server level rather than per-store; copy-paste error when entering the store id during setup; store recreated with a new id after deletion.
Related errors
- signature mismatch
- Missing or invalid signature format
- Webhook URL is not allowed: ${validation.error}
- Bad Request
- Payment not for cal.com
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/8f95e03ffad1179c.
Report an issue: GitHub.