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

  1. Verify the unit contract: the value passed to LightningAddress.requestInvoice({ satoshi }) must equal what is stored in payment.amount.
  2. Reject and log the mismatch; do not mark the booking paid.
  3. 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

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.