juspay/hyperswitch · error · Error

Payment method create failed with status ${response.status}:

Error message

Payment method create failed with status ${response.status}: ${JSON.stringify(response.body)}

What it means

Thrown by the paymentMethodCreateCall (v2, port 8082) custom Cypress command: on success it asserts that payment_method_type, payment_method_subtype, and storage_type echo back from the response; any status outside the success branch hits the else-throw, which embeds both the status and JSON.stringify(response.body). Because the full body is included, the message is self-describing — it usually contains the server's validation error verbatim.

Source

Thrown at cypress-tests/cypress/support/commands.js:8375

    failOnStatusCode: false,
  }).then((response) => {
    if (response.status === 200) {
      globalState.set("paymentMethodId", response.body.id);
      expect(requestBody.customer_id, "customer_id").to.equal(
        response.body.customer_id
      );
      expect(requestBody.payment_method_type, "payment_method_type").to.equal(
        response.body.payment_method_type
      );
      expect(
        requestBody.payment_method_subtype,
        "payment_method_subtype"
      ).to.equal(response.body.payment_method_subtype);
      expect(requestBody.storage_type, "storage_type").to.equal(
        response.body.storage_type
      );
    } else {
      throw new Error(
        `Payment method create failed with status ${response.status}: ${JSON.stringify(response.body)}`
      );
    }
  });
});

Cypress.Commands.add("getRawPaymentMethodDetailsCall", (globalState) => {
  const apiKey = globalState.get("apiKey");
  const profileId = globalState.get("profileId");
  const paymentMethodId = globalState.get("paymentMethodId");

  cy.request({
    method: "GET",
    url: `${globalState.get("pmServiceUrl")}/v1/payment-methods/${paymentMethodId}?fetch_raw_detail=true`,
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      "X-Profile-Id": profileId,

View on GitHub (pinned to 9b8b89dc37)

Solutions

  1. Read the JSON body in the thrown message first — it contains the server's exact validation message; fix the named pmData field.
  2. Assert prerequisites before the call: globalState has apiKey, profileId, and customerId set (i.e. the customer-create step actually ran and succeeded).
  3. 401 -> refresh the API key; 404 -> confirm the v2 base URL/port (8082) and that the customer still exists in that environment.
  4. If a renamed payment_method_type/subtype/storage_type value: update the fixture to the current v2 enum values.
  5. 429/5xx -> retry after the service is healthy.

Example fix

// before: runs even when the customer step failed, producing an opaque 404
cy.paymentMethodCreateCall(globalState, pmData);

// after: fail fast with a clear cause when state is missing
const customerId = globalState.get('customerId');
if (!customerId) {
  throw new Error('paymentMethodCreateCall: customerId missing in globalState — run customerCreateCall first');
}
cy.paymentMethodCreateCall(globalState, pmData);
Defensive patterns

Strategy: validation

Validate before calling

// Run before paymentMethodCreateCall
function validatePaymentMethodCreateCall(globalState, pmData) {
  for (const key of ['apiKey', 'profileId', 'customerId']) {
    if (!globalState.get(key)) throw new Error(`paymentMethodCreateCall: ${key} missing in globalState — earlier step failed or skipped`);
  }
  for (const field of ['payment_method_type', 'payment_method_subtype', 'storage_type']) {
    if (!pmData?.[field]) throw new Error(`paymentMethodCreateCall: pmData.${field} is required`);
  }
}

Type guard

// Narrow the response before asserting the echo fields, so a 4xx body never reaches .to.equal()
function isPaymentMethodCreated(body) {
  return (
    typeof body === 'object' &&
    body !== null &&
    typeof body.payment_method_type === 'string' &&
    typeof body.payment_method_subtype === 'string' &&
    typeof body.storage_type === 'string'
  );
}

Try / catch

// The thrown message already carries status + full JSON body; extract them for routing:
// try { ... } catch (e) {
//   const m = e.message.match(/status (\d+): (.*)$/s);
//   if (!m) throw e;
//   const [, statusStr, bodyJson] = m;
//   const status = Number(statusStr);
//   const body = JSON.parse(bodyJson);
//   if (status === 400) throw new Error(`fix pmData: ${body.error?.message}`);
//   if (status === 401) throw new Error('refresh apiKey');
//   if (status === 404) throw new Error('wrong v2 base URL/port or customer missing');
//   throw e;
// }

Prevention

When it happens

Trigger: 400 when pmData is missing/invalid payment_method_type, payment_method_subtype, or storage_type; 401 with a bad apiKey; 404 when the endpoint path is wrong or the referenced customer/profile does not exist; 422/400 when the customerId in globalState was never set (earlier customerCreateCall failed or was skipped, leaving paymentMethodId/customerId undefined); 5xx from the v2 service.

Common situations: Test-ordering dependency: this command runs before customerCreateCall populated globalState, so the request targets a missing customer; using v1 credentials/base URL (8081) against the v2 service (8082); connector schema change renaming subtypes; expired API key in the environment; data cleanup between runs deleting the customer the test assumed existed.

Related errors


AI-assisted analysis of juspay/hyperswitch@9b8b89dc37 (2026-08-16). Data as JSON: /api/errors/47f74b7cc272fee6. Report an issue: GitHub.