calcom/cal.diy · warning · Error

BTCPay Server does not support automatic refunds for Bitcoin

Error message

BTCPay Server does not support automatic refunds for Bitcoin payments

What it means

`PaymentService.refund()` is intentionally not implemented for BTCPay and always throws this Error. Bitcoin payments cannot be automatically reversed through BTCPay Server's API, so the contract method is a deliberate no-op that signals unsupported behavior rather than silently failing.

Source

Thrown at packages/app-store/btcpayserver/lib/PaymentService.ts:158

              isPaid: false,
              attendee: { name: bookerName, email: bookerEmail },
            },
          }
        ),
      });
      if (!paymentData) throw new Error("Failed to store Payment data");
      return paymentData;
    } catch (error) {
      log.error("BTCPay server: Payment could not be created", bookingId, safeStringify(error));
      throw new Error(ErrorCode.PaymentCreationFailure);
    }
  }

  async update(): Promise<Payment> {
    throw new Error("Method not implemented.");
  }
  async refund(): Promise<Payment> {
    throw new Error("BTCPay Server does not support automatic refunds for Bitcoin payments");
  }

  async collectCard(
    _payment: Pick<Prisma.PaymentUncheckedCreateInput, "amount" | "currency">,
    _bookingId: number,
    _bookerEmail: string,
    _paymentOption: PaymentOption
  ): Promise<Payment> {
    throw new Error("Method not implemented");
  }

  chargeCard(
    _payment: Pick<Prisma.PaymentUncheckedCreateInput, "amount" | "currency">,
    _bookingId: number
  ): Promise<Payment> {
    throw new Error("Method not implemented.");
  }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Before calling `refund()`, check the payment app's capability flag (or `instanceof`/slug check) and skip/branch for BTCPay.
  2. Surface a user-facing message that BTCPay/Bitcoin refunds must be handled manually on-chain.
  3. In a generic refund dispatcher, catch this Error and mark the refund as `manual_action_required` rather than failing the workflow.
  4. Add a `supportsRefund` capability on the payment adapter interface so callers can detect this without try/catch.

Example fix

// before
await paymentService.refund();

// after
if (paymentService.appSlug === "btcpayserver") {
  await markRefundRequiresManualAction(paymentId);
} else {
  await paymentService.refund();
}
Defensive patterns

Strategy: type-guard

Validate before calling

const supportsRefund = (slug: string) => slug !== "btcpayserver";
if (!supportsRefund(paymentAppSlug)) {
  await markRefundRequiresManualAction(paymentId);
  return;
}

Type guard

function supportsAutoRefund(appSlug: string): boolean {
  return appSlug !== "btcpayserver";
}

Try / catch

try {
  await paymentService.refund();
} catch (e) {
  if (e instanceof Error && /does not support automatic refunds/.test(e.message)) {
    await markRefundRequiresManualAction(paymentId);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Any code path that calls `.refund()` on the BTCPay payment adapter — e.g. a booking cancellation flow that automatically refunds payments, or a generic refund API endpoint that doesn't filter by payment method capability.

Common situations: A cancellation/refund workflow added without checking each payment app's refund support; a generic admin tool iterating over payment adapters and invoking refund uniformly; UI button that doesn't hide for Bitcoin payments.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/f670764555148cf4. Report an issue: GitHub.