signalapp/Signal-Server · error · BadRequestException
Invalid payment method
Error message
Invalid payment method
What it means
SubscriptionController.createPaymentMethod throws BadRequestException("Invalid payment method") for the UNKNOWN payment type, i.e. the client sent a paymentMethodType that could not be resolved to a known enum member.
Solutions
- Check the paymentMethodType string against the current enum values (CARD, SEPA_DEBIT, IDEAL, PAYPAL, GOOGLE_PLAY_BILLING, APPLE_APP_STORE)
- Fix typos or casing in the request body
- Update the client SDK if the server added payment types the client doesn't know
Example fix
// before
{ "paymentMethodType": "credit_card" }
// after
{ "paymentMethodType": "CARD" } Defensive patterns
Strategy: validation
Validate before calling
const VALID = ['CARD','SEPA_DEBIT','IDEAL','PAYPAL','GOOGLE_PLAY_BILLING','APPLE_APP_STORE'];
if (!VALID.includes(paymentMethodType)) throw new Error(`Unknown paymentMethodType: ${paymentMethodType}`); Type guard
const isKnownPaymentType = (t) => ['CARD','SEPA_DEBIT','IDEAL','PAYPAL','GOOGLE_PLAY_BILLING','APPLE_APP_STORE'].includes(t);
Prevention
- Validate payment type strings against the enum before sending
- Keep the client's enum in sync with the server's PaymentMethodType
- Never send undefined/null paymentMethodType
When it happens
Trigger: POSTing create_payment_method with a paymentMethodType string that deserializes to PaymentMethodType.UNKNOWN (unrecognized/misspelled value or empty field).
Common situations: Typos in the payment type string; client SDK older than newly added enum values; serialization sending null/missing field that maps to UNKNOWN.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- cannot create payment methods with payment type
- The PAYPAL payment type must use…
- Operation cannot be performed with the
- cannot use play billing for one-time donations
- 400 Bad Request (INVALID_ARGUMENTS)
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/4786ecb9e8b7f0da.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java:292
@Parameter(description="A base64-encoded donation permit retrieved from POST /v1/donation/permit")
@HeaderParam(HeaderUtils.DONATION_PERMIT)
final Optional<DonationPermitHeader> donationPermitHeader,
@PathParam("subscriberId") String subscriberId,
@QueryParam("type") @DefaultValue("CARD") PaymentMethod paymentMethodType,
@HeaderParam(HttpHeaders.USER_AGENT) @Nullable final String userAgentString) throws SubscriptionException {
SubscriberCredentials subscriberCredentials =
SubscriberCredentials.process(authenticatedAccount, subscriberId, clock);
final CustomerAwareSubscriptionPaymentProcessor customerAwareSubscriptionPaymentProcessor = switch (paymentMethodType) {
// Today, we always choose stripe to process non-paypal payment types, however we could use braintree to process
// other types (like CARD) in the future.
case CARD, SEPA_DEBIT, IDEAL -> stripeManager;
case GOOGLE_PLAY_BILLING, APPLE_APP_STORE ->
throw new BadRequestException("cannot create payment methods with payment type " + paymentMethodType);
case PAYPAL -> throw new BadRequestException("The PAYPAL payment type must use create_payment_method/paypal");
case UNKNOWN -> throw new BadRequestException("Invalid payment method");
};
SubscriptionsUtil.recordDonationPermitPresent(donationPermitHeader.isPresent(), "createPaymentMethod", userAgentString);
final boolean spendSuccessful = donationPermitHeader.map(
permitHeader -> {
try {
return SubscriptionsUtil.verifyAndSpendDonationPermit(permitHeader.permit(), donationPermitsManager, clock);
} catch (VerificationFailedException e) {
return false;
}
})
.orElse(false);
if (!spendSuccessful) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
final String token = subscriptionManager.addPaymentMethodToCustomer(View on GitHub (pinned to 100ab61c82)