calcom/cal.diy · error · HttpCode
invoice amount does not match payment amount
Error message
invoice amount does not match payment amount
What it means
Thrown when the verified Alby invoice's amount (in satoshis) differs from the stored payment.amount. This is a tamper/idempotency guard: a validly-signed invoice whose value does not match what cal.com recorded, or a unit mismatch between satoshis and the stored amount.
Source
Thrown at packages/app-store/alby/api/webhook.ts:89
},
});
if (!payment) throw new HttpCode({ statusCode: 204, message: "Payment not found" });
const key = payment.booking?.user?.credentials?.[0].key;
if (!key) throw new HttpCode({ statusCode: 204, message: "Credentials not found" });
const parseCredentials = albyCredentialKeysSchema.safeParse(key);
if (!parseCredentials.success) {
console.error(parseCredentials.error);
throw new HttpCode({ statusCode: 500, message: "Credentials not valid" });
}
const credentials = parseCredentials.data;
const albyInvoice = await parseInvoice(bodyAsString, parsedHeaders, credentials.webhook_endpoint_secret);
if (!albyInvoice) throw new HttpCode({ statusCode: 204, message: "Invoice not found" });
if (albyInvoice.amount !== payment.amount) {
throw new HttpCode({ statusCode: 400, message: "invoice amount does not match payment amount" });
}
const traceContext = distributedTracing.createTrace("alby_webhook", {
meta: { paymentId: payment.id, bookingId: payment.bookingId },
});
return await handlePaymentSuccess({
paymentId: payment.id,
bookingId: payment.bookingId,
appSlug: "alby",
traceContext,
});
} catch (_err) {
const err = getServerErrorFromUnknown(_err);
console.error(`Webhook Error: ${err.message}`);
return res.status(err.statusCode).send({
message: err.message,
stack: IS_PRODUCTION ? undefined : err.cause?.stack,
});View on GitHub (pinned to 176037d0af)
Solutions
- Verify the unit contract: the value passed to LightningAddress.requestInvoice({ satoshi }) must equal what is stored in payment.amount.
- Reject and log the mismatch; do not mark the booking paid.
- Ensure each new invoice creates a new payment row rather than reusing an old one.
Defensive patterns
Strategy: validation
Validate before calling
if (albyInvoice.amount !== payment.amount) {
// do not mark paid; log both amounts and the payment uid for forensics
} Prevention
- Keep payment.amount in the same unit (satoshi) used for requestInvoice.
- Create a fresh payment row for each regenerated invoice.
- Treat any mismatch as potential tampering and alert.
When it happens
Trigger: payment.amount was stored in a different unit than satoshis (e.g. cents or currency minor units); invoice amount changed between creation and payment; a stale payment row was reused for a regenerated invoice.
Common situations: Currency-conversion bug converting fiat to sats incorrectly; amount stored as cents while the invoice is denominated in sats; concurrent invoice regeneration producing a different amount.
Related errors
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/c505431b6b95cfda.
Report an issue: GitHub.