Files
Amitsingh Tanwar 7ad5cd5531 feat(cypress): Cypress test for ucs (#8967)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
2025-09-02 15:23:25 +00:00

4215 lines
142 KiB
JavaScript

// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
// commands.js or your custom support file
import {
defaultErrorHandler,
extractIntegerAtEnd,
getValueByKey,
} from "../e2e/configs/Payment/Utils";
import { execConfig, validateConfig } from "../utils/featureFlags";
import * as RequestBodyUtils from "../utils/RequestBodyUtils";
import { isoTimeTomorrow, validateEnv } from "../utils/RequestBodyUtils.js";
import { handleRedirection } from "./redirectionHandler";
function logRequestId(xRequestId) {
if (xRequestId) {
cy.task("cli_log", "x-request-id -> " + xRequestId);
} else {
cy.task("cli_log", "x-request-id is not available in the response headers");
}
}
function validateErrorMessage(response, resData) {
if (resData.body.status !== "failed") {
expect(response.body.error_message, "error_message").to.be.null;
expect(response.body.error_code, "error_code").to.be.null;
}
}
//skip MIT using PMId if connector does not support MIT only
export function shouldSkipMitUsingPMId(connectorId) {
const skipConnectors = ["fiuu"];
return skipConnectors.includes(connectorId);
}
Cypress.Commands.add("healthCheck", (globalState) => {
const baseUrl = globalState.get("baseUrl");
const url = `${baseUrl}/health`;
cy.request({
method: "GET",
url: url,
headers: {
Accept: "application/json",
},
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.body).to.equal("health is good");
} else {
throw new Error(
`Health Check failed with status: \`${response.status}\` and body: \`${response.body}\``
);
}
});
});
});
Cypress.Commands.add(
"merchantCreateCallTest",
(merchantCreateBody, globalState) => {
const randomMerchantId = RequestBodyUtils.generateRandomString();
RequestBodyUtils.setMerchantId(merchantCreateBody, randomMerchantId);
globalState.set("merchantId", randomMerchantId);
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/accounts`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
body: merchantCreateBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
// Handle the response as needed
globalState.set("profileId", response.body.default_profile);
globalState.set("publishableKey", response.body.publishable_key);
globalState.set("merchantDetails", response.body.merchant_details);
});
});
}
);
Cypress.Commands.add("merchantRetrieveCall", (globalState) => {
const merchant_id = globalState.get("merchantId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/accounts/${merchant_id}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"], "content_headers").to.include(
"application/json"
);
expect(response.body.merchant_id, "merchant_id").to.equal(merchant_id);
expect(
response.body.payment_response_hash_key,
"payment_response_hash_key"
).to.not.be.null;
expect(response.body.publishable_key, "publishable_key").to.not.be.null;
cy.log("HI");
expect(response.body.default_profile, "default_profile").to.not.be.null;
expect(response.body.organization_id, "organization_id").to.not.be.null;
globalState.set("organizationId", response.body.organization_id);
if (globalState.get("publishableKey") === undefined) {
globalState.set("publishableKey", response.body.publishable_key);
}
});
});
});
Cypress.Commands.add("merchantDeleteCall", (globalState) => {
const merchant_id = globalState.get("merchantId");
cy.request({
method: "DELETE",
url: `${globalState.get("baseUrl")}/accounts/${merchant_id}`,
headers: {
Accept: "application/json",
"api-key": globalState.get("adminApiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.body.merchant_id).to.equal(merchant_id);
expect(response.body.deleted).to.equal(true);
});
});
});
Cypress.Commands.add("ListConnectorsFeatureMatrixCall", (globalState) => {
const baseUrl = globalState.get("baseUrl");
const url = `${baseUrl}/feature_matrix`;
cy.request({
method: "GET",
url: url,
headers: {
Accept: "application/json",
},
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.body).to.have.property("connectors").and.not.empty;
expect(response.body.connectors).to.be.an("array").and.not.empty;
response.body.connectors.forEach((item) => {
expect(item).to.have.property("description").and.not.empty;
expect(item).to.have.property("category").and.not.empty;
expect(item).to.have.property("integration_status").and.not.empty;
});
});
});
});
Cypress.Commands.add("merchantListCall", (globalState) => {
const organization_id = globalState.get("organizationId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/accounts/list?organization_id=${organization_id}`,
headers: {
Accept: "application/json",
"api-key": globalState.get("adminApiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
for (const key in response.body) {
expect(response.body[key]).to.have.property("merchant_id").and.not
.empty;
expect(response.body[key]).to.have.property("organization_id").and.not
.empty;
expect(response.body[key]).to.have.property("default_profile").and.not
.empty;
}
});
});
});
Cypress.Commands.add(
"merchantUpdateCall",
(merchantUpdateBody, globalState) => {
const merchant_id = globalState.get("merchantId");
const organization_id = globalState.get("organizationId");
const publishable_key = globalState.get("publishableKey");
const merchant_details = globalState.get("merchantDetails");
merchantUpdateBody.merchant_id = merchant_id;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/accounts/${merchant_id}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
body: merchantUpdateBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body.merchant_id).to.equal(merchant_id);
expect(response.body.publishable_key).to.equal(publishable_key);
expect(response.body.organization_id).to.equal(organization_id);
expect(response.body.merchant_details).to.not.equal(merchant_details);
});
});
}
);
Cypress.Commands.add(
"createBusinessProfileTest",
(createBusinessProfile, globalState, profilePrefix = "profile") => {
const apiKey = globalState.get("apiKey");
const baseUrl = globalState.get("baseUrl");
const connectorId = globalState.get("connectorId");
const merchantId = globalState.get("merchantId");
const profileName = `${profilePrefix}_${RequestBodyUtils.generateRandomString(connectorId)}`;
const url = `${baseUrl}/account/${merchantId}/business_profile`;
createBusinessProfile.profile_name = profileName;
cy.request({
method: "POST",
url: url,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": apiKey,
},
body: createBusinessProfile,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
globalState.set(`${profilePrefix}Id`, response.body.profile_id);
if (response.status === 200) {
expect(response.body.profile_id).to.not.to.be.null;
} else {
throw new Error(
`Business Profile call failed ${response.body.error.message}`
);
}
});
});
}
);
Cypress.Commands.add(
"UpdateBusinessProfileTest",
(
updateBusinessProfileBody,
is_connector_agnostic_mit_enabled,
collect_billing_details_from_wallet_connector,
collect_shipping_details_from_wallet_connector,
always_collect_billing_details_from_wallet_connector,
always_collect_shipping_details_from_wallet_connector,
globalState,
profilePrefix = "profile"
) => {
updateBusinessProfileBody.is_connector_agnostic_mit_enabled =
is_connector_agnostic_mit_enabled;
updateBusinessProfileBody.collect_shipping_details_from_wallet_connector =
collect_shipping_details_from_wallet_connector;
updateBusinessProfileBody.collect_billing_details_from_wallet_connector =
collect_billing_details_from_wallet_connector;
updateBusinessProfileBody.always_collect_billing_details_from_wallet_connector =
always_collect_billing_details_from_wallet_connector;
updateBusinessProfileBody.always_collect_shipping_details_from_wallet_connector =
always_collect_shipping_details_from_wallet_connector;
const apiKey = globalState.get("apiKey");
const merchantId = globalState.get("merchantId");
const profileId = globalState.get(`${profilePrefix}Id`);
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/account/${merchantId}/business_profile/${profileId}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": apiKey,
},
body: updateBusinessProfileBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
globalState.set(
"collectBillingDetails",
response.body.collect_billing_details_from_wallet_connector
);
globalState.set(
"collectShippingDetails",
response.body.collect_shipping_details_from_wallet_connector
);
globalState.set(
"alwaysCollectBillingDetails",
response.body.always_collect_billing_details_from_wallet_connector
);
globalState.set(
"alwaysCollectShippingDetails",
response.body.always_collect_shipping_details_from_wallet_connector
);
}
});
});
}
);
// API Key API calls
Cypress.Commands.add("apiKeyCreateTest", (apiKeyCreateBody, globalState) => {
// Define the necessary variables and constant
const apiKey = globalState.get("adminApiKey");
const baseUrl = globalState.get("baseUrl");
// We do not want to keep API Key forever,
// so we set the expiry to tomorrow as new merchant accounts are created with every run
const expiry = isoTimeTomorrow();
const keyIdType = "key_id";
const keyId = validateEnv(baseUrl, keyIdType);
const merchantId = globalState.get("merchantId");
const url = `${baseUrl}/api_keys/${merchantId}`;
// Update request body
apiKeyCreateBody.expiration = expiry;
cy.request({
method: "POST",
url: url,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": apiKey,
},
body: apiKeyCreateBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.body.merchant_id).to.equal(merchantId);
expect(response.body.description).to.equal(
apiKeyCreateBody.description
);
// API Key assertions are intentionally excluded to avoid being exposed in the logs
expect(response.body).to.have.property(keyIdType).and.to.include(keyId)
.and.to.not.be.empty;
globalState.set("apiKeyId", response.body.key_id);
globalState.set("apiKey", response.body.api_key);
cy.task("setGlobalState", globalState.data);
} else {
// to be updated
throw new Error(
`API Key create call failed with status ${response.status} and message: "${response.body.error.message}"`
);
}
});
});
});
Cypress.Commands.add("apiKeyUpdateCall", (apiKeyUpdateBody, globalState) => {
const merchantId = globalState.get("merchantId");
const apiKeyId = globalState.get("apiKeyId");
// We do not want to keep API Key forever,
// so we set the expiry to tomorrow as new merchant accounts are created with every run
const expiry = isoTimeTomorrow();
// Update request body
apiKeyUpdateBody.expiration = expiry;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/api_keys/${merchantId}/${apiKeyId}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
body: apiKeyUpdateBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.body.name).to.equal("Updated API Key");
expect(response.body.merchant_id).to.equal(merchantId);
// API Key assertions are intentionally excluded to avoid being exposed in the logs
expect(response.body.key_id).to.equal(apiKeyId);
} else {
// to be updated
throw new Error(
`API Key create call failed with status ${response.status} and message: "${response.body.error.message}"`
);
}
});
});
});
Cypress.Commands.add("apiKeyRetrieveCall", (globalState) => {
const merchant_id = globalState.get("merchantId");
const api_key_id = globalState.get("apiKeyId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/api_keys/${merchant_id}/${api_key_id}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body.name).to.equal("Updated API Key");
expect(response.body.key_id).to.equal(api_key_id);
expect(response.body.merchant_id).to.equal(merchant_id);
});
});
});
Cypress.Commands.add("apiKeyListCall", (globalState) => {
const merchant_id = globalState.get("merchantId");
const base_url = globalState.get("baseUrl");
cy.request({
method: "GET",
url: `${base_url}/api_keys/${merchant_id}/list`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body).to.be.an("array").and.not.empty;
for (const key in response.body) {
expect(response.body[key]).to.have.property("name").and.not.empty;
if (base_url.includes("sandbox") || base_url.includes("integ")) {
expect(response.body[key]).to.have.property("key_id").include("snd_")
.and.not.empty;
} else if (base_url.includes("localhost")) {
expect(response.body[key]).to.have.property("key_id").include("dev_")
.and.not.empty;
}
expect(response.body[key].merchant_id).to.equal(merchant_id);
}
});
});
});
Cypress.Commands.add("apiKeyDeleteCall", (globalState) => {
const merchant_id = globalState.get("merchantId");
const api_key_id = globalState.get("apiKeyId");
cy.request({
method: "DELETE",
url: `${globalState.get("baseUrl")}/api_keys/${merchant_id}/${api_key_id}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body.merchant_id).to.equal(merchant_id);
expect(response.body.key_id).to.equal(api_key_id);
expect(response.body.revoked).to.equal(true);
});
});
});
Cypress.Commands.add(
"createNamedConnectorCallTest",
(
connectorType,
createConnectorBody,
paymentMethodsEnabled,
globalState,
connectorName,
connectorLabel,
profilePrefix = "profile",
mcaPrefix = "merchantConnector"
) => {
const merchantId = globalState.get("merchantId");
const profileId = globalState.get(`${profilePrefix}Id`);
createConnectorBody.profile_id = profileId;
createConnectorBody.connector_type = connectorType;
createConnectorBody.connector_name = connectorName;
createConnectorBody.connector_label = connectorLabel;
createConnectorBody.payment_methods_enabled = paymentMethodsEnabled;
// readFile is used to read the contents of the file and it always returns a promise ([Object Object]) due to its asynchronous nature
// it is best to use then() to handle the response within the same block of code
cy.readFile(globalState.get("connectorAuthFilePath")).then(
(jsonContent) => {
const { authDetails } = getValueByKey(
JSON.stringify(jsonContent),
connectorName
);
createConnectorBody.connector_account_details =
authDetails.connector_account_details;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/account/${merchantId}/connectors`,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"api-key": globalState.get("apiKey"),
},
body: createConnectorBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(connectorName).to.equal(response.body.connector_name);
globalState.set(
`${mcaPrefix}Id`,
response.body.merchant_connector_id
);
} else {
cy.task(
"cli_log",
"response status -> " + JSON.stringify(response.status)
);
throw new Error(
`Connector Create Call Failed ${response.body.error.message}`
);
}
});
});
}
);
}
);
Cypress.Commands.add(
"createConnectorCallTest",
(
connectorType,
createConnectorBody,
payment_methods_enabled,
globalState,
profilePrefix = "profile",
mcaPrefix = "merchantConnector"
) => {
const api_key = globalState.get("apiKey");
const base_url = globalState.get("baseUrl");
const connector_id = globalState.get("connectorId");
const merchant_id = globalState.get("merchantId");
const profile_id = globalState.get(`${profilePrefix}Id`);
const url = `${base_url}/account/${merchant_id}/connectors`;
createConnectorBody.connector_type = connectorType;
createConnectorBody.profile_id = profile_id;
createConnectorBody.connector_name = connector_id;
createConnectorBody.payment_methods_enabled = payment_methods_enabled;
// readFile is used to read the contents of the file and it always returns a promise ([Object Object]) due to its asynchronous nature
// it is best to use then() to handle the response within the same block of code
cy.readFile(globalState.get("connectorAuthFilePath")).then(
(jsonContent) => {
const { authDetails, stateUpdate } = getValueByKey(
JSON.stringify(jsonContent),
connector_id,
extractIntegerAtEnd(profilePrefix)
);
if (stateUpdate) {
globalState.set(
"MULTIPLE_CONNECTORS",
stateUpdate.MULTIPLE_CONNECTORS
);
}
createConnectorBody.connector_account_details =
authDetails.connector_account_details;
if (authDetails && authDetails.metadata) {
createConnectorBody.metadata = {
...createConnectorBody.metadata, // Preserve existing metadata fields
...authDetails.metadata, // Merge with authDetails.metadata
};
}
cy.request({
method: "POST",
url: url,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": api_key,
},
body: createConnectorBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(globalState.get("connectorId")).to.equal(
response.body.connector_name
);
globalState.set(
`${mcaPrefix}Id`,
response.body.merchant_connector_id
);
} else {
cy.task(
"cli_log",
"response status -> " + JSON.stringify(response.status)
);
throw new Error(
`Connector Create Call Failed ${response.body.error.message}`
);
}
});
});
}
);
}
);
Cypress.Commands.add(
"createPayoutConnectorCallTest",
(connectorType, createConnectorBody, globalState) => {
const merchantId = globalState.get("merchantId");
const connectorName = globalState.get("connectorId");
createConnectorBody.connector_type = connectorType;
createConnectorBody.connector_name = connectorName;
createConnectorBody.connector_type = "payout_processor";
createConnectorBody.profile_id = globalState.get("profileId");
// readFile is used to read the contents of the file and it always returns a promise ([Object Object]) due to its asynchronous nature
// it is best to use then() to handle the response within the same block of code
cy.readFile(globalState.get("connectorAuthFilePath")).then(
(jsonContent) => {
const { authDetails } = getValueByKey(
JSON.stringify(jsonContent),
`${connectorName}_payout`
);
// If the connector does not have payout connector creds in creds file, set payoutsExecution to false
if (authDetails === null) {
globalState.set("payoutsExecution", false);
return false;
} else {
globalState.set("payoutsExecution", true);
}
createConnectorBody.connector_account_details =
authDetails.connector_account_details;
if (authDetails && authDetails.metadata) {
createConnectorBody.metadata = {
...createConnectorBody.metadata, // Preserve existing metadata fields
...authDetails.metadata, // Merge with authDetails.metadata
};
}
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/account/${merchantId}/connectors`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
body: createConnectorBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(globalState.get("connectorId")).to.equal(
response.body.connector_name
);
globalState.set(
"merchantConnectorId",
response.body.merchant_connector_id
);
} else {
cy.task(
"cli_log",
"response status -> " + JSON.stringify(response.status)
);
throw new Error(
`Connector Create Call Failed ${response.body.error.message}`
);
}
});
});
}
);
}
);
Cypress.Commands.add("connectorRetrieveCall", (globalState) => {
const merchant_id = globalState.get("merchantId");
const connector_id = globalState.get("connectorId");
const merchant_connector_id = globalState.get("merchantConnectorId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/account/${merchant_id}/connectors/${merchant_connector_id}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
"x-merchant-id": merchant_id,
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body.connector_name).to.equal(connector_id);
expect(response.body.merchant_connector_id).to.equal(
merchant_connector_id
);
});
});
});
Cypress.Commands.add("connectorDeleteCall", (globalState) => {
const merchant_id = globalState.get("merchantId");
const merchant_connector_id = globalState.get("merchantConnectorId");
cy.request({
method: "DELETE",
url: `${globalState.get("baseUrl")}/account/${merchant_id}/connectors/${merchant_connector_id}`,
headers: {
Accept: "application/json",
"api-key": globalState.get("adminApiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.body.merchant_id).to.equal(merchant_id);
expect(response.body.merchant_connector_id).to.equal(
merchant_connector_id
);
expect(response.body.deleted).to.equal(true);
});
});
});
Cypress.Commands.add(
"connectorUpdateCall",
(connectorType, updateConnectorBody, globalState) => {
const api_key = globalState.get("apiKey");
const base_url = globalState.get("baseUrl");
const connector_id = globalState.get("connectorId");
const merchant_id = globalState.get("merchantId");
const merchant_connector_id = globalState.get("merchantConnectorId");
const connectorLabel = `updated_${RequestBodyUtils.generateRandomString(connector_id)}`;
const url = `${base_url}/account/${merchant_id}/connectors/${merchant_connector_id}`;
updateConnectorBody.connector_type = connectorType;
updateConnectorBody.connector_label = connectorLabel;
cy.readFile(globalState.get("connectorAuthFilePath")).then(
(jsonContent) => {
const { authDetails } = getValueByKey(
JSON.stringify(jsonContent),
connector_id
);
if (authDetails && authDetails.metadata) {
updateConnectorBody.metadata = {
...updateConnectorBody.metadata, // Preserve existing metadata
...authDetails.metadata,
};
}
cy.request({
method: "POST",
url: url,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": api_key,
"x-merchant-id": merchant_id,
},
body: updateConnectorBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include(
"application/json"
);
expect(response.body.connector_name).to.equal(connector_id);
expect(response.body.merchant_connector_id).to.equal(
merchant_connector_id
);
expect(response.body.connector_label).to.equal(connectorLabel);
});
});
}
);
}
);
// Generic function to list all connectors
Cypress.Commands.add("connectorListByMid", (globalState) => {
const merchant_id = globalState.get("merchantId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/account/${merchant_id}/connectors`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
"X-Merchant-Id": merchant_id,
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body).to.be.an("array").and.not.empty;
response.body.forEach((item) => {
expect(item).to.not.have.property("metadata");
expect(item).to.not.have.property("additional_merchant_data");
expect(item).to.not.have.property("connector_wallets_details");
});
});
});
});
Cypress.Commands.add(
"createCustomerCallTest",
(customerCreateBody, globalState) => {
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/customers`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
body: customerCreateBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
globalState.set("customerId", response.body.customer_id);
expect(response.body.customer_id, "customer_id").to.not.be.empty;
expect(customerCreateBody.email, "email").to.equal(
response.body.email
);
expect(customerCreateBody.name, "name").to.equal(response.body.name);
expect(customerCreateBody.phone, "phone").to.equal(
response.body.phone
);
expect(customerCreateBody.metadata, "metadata").to.deep.equal(
response.body.metadata
);
expect(customerCreateBody.address, "address").to.deep.equal(
response.body.address
);
expect(
customerCreateBody.phone_country_code,
"phone_country_code"
).to.equal(response.body.phone_country_code);
} else if (response.status === 400) {
if (response.body.error.message.includes("already exists")) {
expect(response.body.error.code).to.equal("IR_12");
expect(response.body.error.message).to.equal(
"Customer with the given `customer_id` already exists"
);
}
}
});
});
}
);
Cypress.Commands.add("customerListCall", (globalState) => {
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/customers/list`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
for (const key in response.body) {
expect(response.body[key]).to.not.be.empty;
}
});
});
});
Cypress.Commands.add("customerRetrieveCall", (globalState) => {
const customer_id = globalState.get("customerId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/customers/${customer_id}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.body.customer_id).to.equal(customer_id).and.not.be.empty;
});
});
});
Cypress.Commands.add(
"customerUpdateCall",
(customerUpdateBody, globalState) => {
const customer_id = globalState.get("customerId");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/customers/${customer_id}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
body: customerUpdateBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.body.customer_id).to.equal(customer_id);
});
});
}
);
Cypress.Commands.add("ephemeralGenerateCall", (globalState) => {
const customer_id = globalState.get("customerId");
const merchant_id = globalState.get("merchantId");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/ephemeral_keys`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
body: { customer_id: customer_id },
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.body.customer_id).to.equal(customer_id);
expect(response.body.merchant_id).to.equal(merchant_id);
expect(response.body.id).to.exist.and.not.be.empty;
expect(response.body.secret).to.exist.and.not.be.empty;
});
});
});
Cypress.Commands.add("customerDeleteCall", (globalState) => {
const customer_id = globalState.get("customerId");
cy.request({
method: "DELETE",
url: `${globalState.get("baseUrl")}/customers/${customer_id}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.body.customer_id).to.equal(customer_id).and.not.be.empty;
expect(response.body.customer_deleted).to.equal(true);
expect(response.body.address_deleted).to.equal(true);
expect(response.body.payment_methods_deleted).to.equal(true);
});
});
});
Cypress.Commands.add(
"paymentMethodListTestLessThanEqualToOnePaymentMethod",
(resData, globalState) => {
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/account/payment_methods?client_secret=${globalState.get("clientSecret")}`,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"api-key": globalState.get("publishableKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body).to.have.property("currency");
if (resData["payment_methods"].length == 1) {
function getPaymentMethodType(obj) {
return obj["payment_methods"][0]["payment_method_types"][0][
"payment_method_type"
];
}
expect(getPaymentMethodType(resData)).to.equal(
getPaymentMethodType(response.body)
);
} else {
expect(0).to.equal(response.body["payment_methods"].length);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"paymentMethodListTestWithRequiredFields",
(data, globalState) => {
const apiKey = globalState.get("publishableKey");
const baseUrl = globalState.get("baseUrl");
const clientSecret = globalState.get("clientSecret");
const url = `${baseUrl}/account/payment_methods?client_secret=${clientSecret}`;
cy.request({
method: "GET",
url: url,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"api-key": apiKey,
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
const responsePaymentMethods = response.body["payment_methods"];
const responseRequiredFields =
responsePaymentMethods[0]["payment_method_types"][0][
"required_fields"
];
const expectedRequiredFields =
data["payment_methods"][0]["payment_method_types"][0][
"required_fields"
];
Object.keys(expectedRequiredFields).forEach((key) => {
const expectedField = expectedRequiredFields[key];
const responseField = responseRequiredFields[key];
expect(responseField).to.exist;
expect(responseField.required_field).to.equal(
expectedField.required_field
);
expect(responseField.display_name).to.equal(
expectedField.display_name
);
expect(responseField.field_type).to.deep.equal(
expectedField.field_type
);
expect(responseField.value).to.equal(expectedField.value);
});
} else {
throw new Error(
`List payment methods failed with status code "${response.status}" and error message "${response.body.error.message}"`
);
}
});
});
}
);
Cypress.Commands.add(
"paymentMethodListTestTwoConnectorsForOnePaymentMethodCredit",
(resData, globalState) => {
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/account/payment_methods?client_secret=${globalState.get("clientSecret")}`,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"api-key": globalState.get("publishableKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body).to.have.property("currency");
if (resData["payment_methods"].length > 0) {
function getPaymentMethodType(obj) {
return obj["payment_methods"][0]["payment_method_types"][0][
"card_networks"
][0]["eligible_connectors"]
.slice()
.sort();
}
const config_payment_method_type = getPaymentMethodType(resData);
const response_payment_method_type = getPaymentMethodType(
response.body
);
for (let i = 0; i < response_payment_method_type.length; i++) {
expect(config_payment_method_type[i]).to.equal(
response_payment_method_type[i]
);
}
} else {
expect(0).to.equal(response.body["payment_methods"].length);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"sessionTokenCall",
(sessionTokenBody, data, globalState) => {
const { Response: resData } = data || {};
sessionTokenBody.payment_id = globalState.get("paymentID");
sessionTokenBody.client_secret = globalState.get("clientSecret");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments/session_tokens`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("publishableKey"),
"x-merchant-domain": "hyperswitch - demo - store.netlify.app",
"x-client-platform": "web",
},
body: sessionTokenBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
const expectedTokens = resData.body.session_token;
const actualTokens = response.body.session_token;
// Verifying length of array
expect(actualTokens.length, "arrayLength").to.equal(
expectedTokens.length
);
// Verify specific fields in each session_token object
expectedTokens.forEach((expectedToken, index) => {
const actualToken = actualTokens[index];
// Check specific fields only
expect(actualToken.wallet_name, "wallet_name").to.equal(
expectedToken.wallet_name
);
expect(actualToken.connector, "connector").to.equal(
expectedToken.connector
);
});
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"createPaymentIntentTest",
(
createPaymentBody,
data,
authentication_type,
capture_method,
globalState
) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
if (
!createPaymentBody ||
typeof createPaymentBody !== "object" ||
!reqData.currency
) {
throw new Error(
"Invalid parameters provided to createPaymentIntentTest command"
);
}
const configInfo = execConfig(validateConfig(configs));
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
for (const key in reqData) {
createPaymentBody[key] = reqData[key];
}
createPaymentBody.authentication_type = authentication_type;
createPaymentBody.capture_method = capture_method;
createPaymentBody.customer_id = globalState.get("customerId");
createPaymentBody.profile_id = profile_id;
globalState.set("paymentAmount", createPaymentBody.amount);
globalState.set("setupFutureUsage", createPaymentBody.setup_future_usage);
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments`,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: createPaymentBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (resData.status === 200) {
expect(response.body).to.have.property("client_secret");
const clientSecret = response.body.client_secret;
globalState.set("clientSecret", clientSecret);
globalState.set("paymentID", response.body.payment_id);
// Store the actual setup_future_usage value from the response
globalState.set(
"actualSetupFutureUsage",
response.body.setup_future_usage
);
cy.log(clientSecret);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(
response.body[key],
`Expected ${resData.body[key]} but got ${response.body[key]}`
);
}
expect(response.body.payment_id, "payment_id").to.not.be.null;
expect(response.body.merchant_id, "merchant_id").to.not.be.null;
expect(createPaymentBody.amount, "amount").to.equal(
response.body.amount
);
expect(createPaymentBody.currency, "currency").to.equal(
response.body.currency
);
expect(createPaymentBody.capture_method, "capture_method").to.equal(
response.body.capture_method
);
expect(
createPaymentBody.authentication_type,
"authentication_type"
).to.equal(response.body.authentication_type);
expect(createPaymentBody.description, "description").to.equal(
response.body.description
);
expect(createPaymentBody.email, "email").to.equal(
response.body.email
);
expect(createPaymentBody.email, "customer.email").to.equal(
response.body.customer.email
);
expect(createPaymentBody.customer_id, "customer.id").to.equal(
response.body.customer.id
);
expect(createPaymentBody.metadata, "metadata").to.deep.equal(
response.body.metadata
);
expect(
createPaymentBody.setup_future_usage,
"setup_future_usage"
).to.equal(response.body.setup_future_usage);
// If 'shipping_cost' is not included in the request, the 'amount' in 'createPaymentBody' should match the 'amount_capturable' in the response.
if (typeof createPaymentBody?.shipping_cost === "undefined") {
expect(createPaymentBody.amount, "amount_capturable").to.equal(
response.body.amount_capturable
);
} else {
expect(
createPaymentBody.amount + createPaymentBody.shipping_cost,
"amount_capturable"
).to.equal(response.body.amount_capturable);
}
expect(response.body.amount_received, "amount_received").to.be.oneOf([
0,
null,
]);
expect(response.body.connector, "connector").to.be.null;
expect(createPaymentBody.capture_method, "capture_method").to.equal(
response.body.capture_method
);
expect(response.body.payment_method, "payment_method").to.be.null;
expect(response.body.payment_method_data, "payment_method_data").to.be
.null;
expect(response.body.merchant_connector_id, "merchant_connector_id")
.to.be.null;
expect(response.body.payment_method_id, "payment_method_id").to.be
.null;
expect(response.body.payment_method_id, "payment_method_status").to.be
.null;
expect(response.body.profile_id, "profile_id").to.not.be.null;
expect(
response.body.merchant_order_reference_id,
"merchant_order_reference_id"
).to.be.null;
expect(response.body.connector_mandate_id, "connector_mandate_id").to
.be.null;
validateErrorMessage(response, resData);
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add("paymentMethodsCallTest", (globalState) => {
const clientSecret = globalState.get("clientSecret");
const paymentIntentID = clientSecret.split("_secret_")[0];
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/account/payment_methods?client_secret=${clientSecret}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("publishableKey"),
},
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body).to.have.property("redirect_url");
expect(response.body).to.have.property("payment_methods");
if (
globalState.get("collectBillingDetails") === true ||
globalState.get("alwaysCollectBillingDetails") === true
) {
expect(
response.body.collect_billing_details_from_wallets,
"collectBillingDetailsFromWallets"
).to.be.true;
} else
expect(
response.body.collect_billing_details_from_wallets,
"collectBillingDetailsFromWallets"
).to.be.false;
if (
globalState.get("collectShippingDetails") === true ||
globalState.get("alwaysCollectShippingDetails") === true
) {
expect(
response.body.collect_shipping_details_from_wallets,
"collectShippingDetailsFromWallets"
).to.be.true;
} else
expect(
response.body.collect_shipping_details_from_wallets,
"collectShippingDetailsFromWallets"
).to.be.false;
globalState.set("paymentID", paymentIntentID);
cy.log(response);
});
});
});
Cypress.Commands.add("createPaymentMethodTest", (globalState, data) => {
const { Request: reqData, Response: resData } = data || {};
reqData.customer_id = globalState.get("customerId");
const merchant_id = globalState.get("merchantId");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payment_methods`,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"api-key": globalState.get("apiKey"),
},
body: reqData,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body.client_secret, "client_secret").to.include(
"_secret_"
).and.to.not.be.null;
expect(response.body.payment_method_id, "payment_method_id").to.not.be
.null;
expect(response.body.merchant_id, "merchant_id").to.equal(merchant_id);
expect(reqData.payment_method_type, "payment_method_type").to.equal(
response.body.payment_method_type
);
expect(reqData.payment_method, "payment_method").to.equal(
response.body.payment_method
);
expect(response.body.last_used_at, "last_used_at").to.not.be.null;
expect(reqData.customer_id, "customer_id").to.equal(
response.body.customer_id
);
globalState.set("paymentMethodId", response.body.payment_method_id);
} else {
defaultErrorHandler(response, resData);
}
});
});
});
Cypress.Commands.add("deletePaymentMethodTest", (globalState) => {
const apiKey = globalState.get("apiKey");
const baseUrl = globalState.get("baseUrl");
const paymentMethodId = globalState.get("paymentMethodId");
const url = `${baseUrl}/payment_methods/${paymentMethodId}`;
cy.request({
method: "DELETE",
url: url,
headers: {
Accept: "application/json",
"api-key": apiKey,
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body.payment_method_id).to.equal(paymentMethodId);
expect(response.body.deleted).to.be.true;
} else if (response.status === 500 && baseUrl.includes("localhost")) {
// delete payment method api endpoint requires tartarus (hyperswitch card vault) to be set up since it makes a call to the locker service to delete the payment method
expect(response.body.error.code).to.include("HE_00");
expect(response.body.error.message).to.include("Something went wrong");
} else {
throw new Error(
`Payment Method Delete Call Failed with error message: ${response.body.error.message}`
);
}
});
});
});
Cypress.Commands.add("setDefaultPaymentMethodTest", (globalState) => {
const payment_method_id = globalState.get("paymentMethodId");
const customer_id = globalState.get("customerId");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/customers/${customer_id}/payment_methods/${payment_method_id}/default`,
headers: {
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body).to.have.property(
"default_payment_method_id",
payment_method_id
);
expect(response.body).to.have.property("customer_id", customer_id);
} else if (response.status === 400) {
expect(response.body.error.message).to.equal(
"Payment Method is already set as default"
);
} else {
defaultErrorHandler(response);
}
});
});
});
Cypress.Commands.add(
"confirmCallTest",
(confirmBody, data, confirm, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const apiKey = globalState.get("publishableKey");
const baseUrl = globalState.get("baseUrl");
const configInfo = execConfig(validateConfig(configs));
const merchantConnectorId = globalState.get(
`${configInfo.merchantConnectorPrefix}Id`
);
const paymentIntentID = globalState.get("paymentID");
const profileId = globalState.get(`${configInfo.profilePrefix}Id`);
const url = `${baseUrl}/payments/${paymentIntentID}/confirm`;
confirmBody.client_secret = globalState.get("clientSecret");
confirmBody.confirm = confirm;
confirmBody.profile_id = profileId;
for (const key in reqData) {
confirmBody[key] = reqData[key];
}
cy.request({
method: "POST",
url: url,
headers: {
"Content-Type": "application/json",
"api-key": apiKey,
},
failOnStatusCode: false,
body: confirmBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("paymentID", paymentIntentID);
globalState.set("connectorId", response.body.connector);
expect(response.body.connector, "connector").to.equal(
globalState.get("connectorId")
);
expect(paymentIntentID, "payment_id").to.equal(
response.body.payment_id
);
expect(response.body.payment_method_data, "payment_method_data").to
.not.be.empty;
expect(merchantConnectorId, "connector_id").to.equal(
response.body.merchant_connector_id
);
expect(response.body.customer, "customer").to.not.be.empty;
expect(response.body.billing, "billing_address").to.not.be.empty;
expect(response.body.profile_id, "profile_id").to.equal(profileId).and
.to.not.be.null;
validateErrorMessage(response, resData);
if (response.body.capture_method === "automatic") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
if (
response.body.setup_future_usage === "off_session" &&
response.body.status === "succeeded"
) {
expect(
response.body.connector_mandate_id,
"connector_mandate_id"
).to.not.be.null;
}
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else if (response.body.capture_method === "manual") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
if (
response.body.setup_future_usage === "off_session" &&
response.body.status === "succeeded"
) {
expect(
response.body.connector_mandate_id,
"connector_mandate_id"
).to.not.be.null;
}
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"confirmBankRedirectCallTest",
(confirmBody, data, confirm, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const connectorId = globalState.get("connectorId");
const paymentIntentId = globalState.get("paymentID");
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
for (const key in reqData) {
confirmBody[key] = reqData[key];
}
confirmBody.client_secret = globalState.get("clientSecret");
confirmBody.confirm = confirm;
confirmBody.profile_id = profile_id;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments/${paymentIntentId}/confirm`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("publishableKey"),
},
failOnStatusCode: false,
body: confirmBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.headers["content-type"]).to.include(
"application/json"
);
globalState.set("paymentID", paymentIntentId);
globalState.set("connectorId", response.body.connector);
globalState.set("paymentMethodType", confirmBody.payment_method_type);
if (response.status === 200) {
validateErrorMessage(response, resData);
switch (response.body.authentication_type) {
case "three_ds":
if (
response.body.capture_method === "automatic" ||
response.body.capture_method === "manual"
) {
if (response.body.status !== "failed") {
// we get many statuses here, hence this verification
if (
connectorId === "adyen" &&
response.body.payment_method_type === "blik"
) {
expect(response.body)
.to.have.property("next_action")
.to.have.property("type")
.to.equal("wait_screen_information");
} else {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
}
} else if (response.body.status === "failed") {
expect(response.body.error_code).to.equal(
resData.body.error_code
);
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
break;
case "no_three_ds":
if (
response.body.capture_method === "automatic" ||
response.body.capture_method === "manual"
) {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
break;
default:
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"confirmBankTransferCallTest",
(confirmBody, data, confirm, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const paymentIntentID = globalState.get("paymentID");
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
for (const key in reqData) {
confirmBody[key] = reqData[key];
}
confirmBody.client_secret = globalState.get("clientSecret");
confirmBody.confirm = confirm;
confirmBody.profile_id = globalState.get(profile_id);
globalState.set("paymentMethodType", confirmBody.payment_method_type);
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments/${paymentIntentID}/confirm`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("publishableKey"),
},
failOnStatusCode: false,
body: confirmBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("paymentID", paymentIntentID);
validateErrorMessage(response, resData);
if (
response.body.capture_method === "automatic" ||
response.body.capture_method === "manual"
) {
switch (response.body.payment_method_type) {
case "pix":
expect(response.body)
.to.have.property("next_action")
.to.have.property("qr_code_url");
if (response.body.next_action.qr_code_url !== null) {
globalState.set(
"nextActionUrl", // This is intentionally kept as nextActionUrl to avoid issues during handleRedirection call,
response.body.next_action.qr_code_url
);
globalState.set("nextActionType", "qr_code_url");
} else {
globalState.set(
"nextActionUrl", // This is intentionally kept as nextActionUrl to avoid issues during handleRedirection call,
response.body.next_action.image_data_url
);
globalState.set("nextActionType", "image_data_url");
}
break;
case "ach":
if (
response.body.next_action
?.bank_transfer_steps_and_charges_details != null
) {
globalState.set(
"nextActionType",
"bank_transfer_steps_and_charges_details"
);
}
break;
default:
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
globalState.set("nextActionType", "redirect_to_url");
break;
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"confirmUpiCall",
(confirmBody, data, confirm, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const paymentId = globalState.get("paymentID");
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
for (const key in reqData) {
confirmBody[key] = reqData[key];
}
confirmBody.client_secret = globalState.get("clientSecret");
confirmBody.confirm = confirm;
confirmBody.profile_id = profile_id;
globalState.set("paymentMethodType", confirmBody.payment_method_type);
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments/${paymentId}/confirm`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("publishableKey"),
},
failOnStatusCode: false,
body: confirmBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
validateErrorMessage(response, resData);
if (
response.body.capture_method === "automatic" ||
response.body.capture_method === "manual"
) {
if (response.body.payment_method_type === "upi_collect") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
} else if (response.body.payment_method_type === "upi_intent") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("qr_code_fetch_url");
globalState.set(
"nextActionUrl",
response.body.next_action.qr_code_fetch_url
);
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"createConfirmPaymentTest",
(
createConfirmPaymentBody,
data,
authentication_type,
capture_method,
globalState
) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const merchant_connector_id = globalState.get(
`${configInfo.merchantConnectorPrefix}Id`
);
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
createConfirmPaymentBody.authentication_type = authentication_type;
createConfirmPaymentBody.capture_method = capture_method;
createConfirmPaymentBody.customer_id = globalState.get("customerId");
createConfirmPaymentBody.profile_id = profile_id;
for (const key in reqData) {
createConfirmPaymentBody[key] = reqData[key];
}
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: createConfirmPaymentBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
globalState.set("clientSecret", response.body.client_secret);
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("paymentAmount", createConfirmPaymentBody.amount);
globalState.set("paymentID", response.body.payment_id);
// Store the actual setup_future_usage value from the response
globalState.set(
"actualSetupFutureUsage",
response.body.setup_future_usage
);
expect(response.body.connector, "connector").to.equal(
globalState.get("connectorId")
);
expect(response.body.payment_id, "payment_id").to.equal(
globalState.get("paymentID")
);
expect(response.body.payment_method_data, "payment_method_data").to
.not.be.empty;
expect(response.body.merchant_connector_id, "connector_id").to.equal(
merchant_connector_id
);
expect(response.body.customer, "customer").to.not.be.empty;
expect(response.body.billing, "billing_address").to.not.be.empty;
expect(response.body.profile_id, "profile_id").to.not.be.null;
expect(response.body).to.have.property("status");
validateErrorMessage(response, resData);
if (response.body.capture_method === "automatic") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else {
throw new Error(
`Invalid authentication type: ${response.body.authentication_type}`
);
}
} else if (response.body.capture_method === "manual") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else {
throw new Error(
`Invalid authentication type: ${response.body.authentication_type}`
);
}
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
// This is consequent saved card payment confirm call test(Using payment token)
Cypress.Commands.add(
"saveCardConfirmCallTest",
(saveCardConfirmBody, data, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const merchant_connector_id = globalState.get(
`${configInfo.merchantConnectorPrefix}Id`
);
const paymentIntentID = globalState.get("paymentID");
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
// Add card_cvc if actual setup_future_usage is "on_session"
// This covers both explicit on_session and fallback cases
if (
globalState.get("actualSetupFutureUsage") === "on_session" &&
reqData.payment_method_data?.card?.card_cvc
) {
saveCardConfirmBody.card_cvc = reqData.payment_method_data.card.card_cvc;
}
saveCardConfirmBody.client_secret = globalState.get("clientSecret");
saveCardConfirmBody.payment_token = globalState.get("paymentToken");
saveCardConfirmBody.profile_id = profile_id;
// Include request data from config but exclude payment_method_data
if (reqData) {
const requestDataWithoutPMD = Object.fromEntries(
Object.entries(reqData).filter(
([key]) =>
key !== "payment_method_data" && key !== "customer_acceptance"
)
);
Object.assign(saveCardConfirmBody, requestDataWithoutPMD);
}
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments/${paymentIntentID}/confirm`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("publishableKey"),
},
failOnStatusCode: false,
body: saveCardConfirmBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("paymentID", paymentIntentID);
globalState.set("paymentID", paymentIntentID);
globalState.set("connectorId", response.body.connector);
expect(response.body.connector, "connector").to.equal(
globalState.get("connectorId")
);
expect(paymentIntentID, "payment_id").to.equal(
response.body.payment_id
);
expect(response.body.payment_method_data, "payment_method_data").to
.not.be.empty;
expect(merchant_connector_id, "connector_id").to.equal(
response.body.merchant_connector_id
);
expect(response.body.customer, "customer").to.not.be.empty;
if (reqData.billing !== null) {
expect(response.body.billing, "billing_address").to.not.be.empty;
}
expect(response.body.profile_id, "profile_id").to.not.be.null;
expect(response.body.payment_token, "payment_token").to.not.be.null;
validateErrorMessage(response, resData);
if (response.body.capture_method === "automatic") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
if (
response.body?.payment_method_id &&
response.body.payment_method_id !== null
) {
expect(
response.body.payment_method_status,
"payment_method_status"
).to.equal("active");
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
expect(response.body.customer_id).to.equal(
globalState.get("customerId")
);
if (
[
"partially_captured",
"requires_capture",
"succeeded",
].includes(response.body.status)
) {
expect(
response.body.payment_method_id,
"payment_method_id should exist for succeeded/requires_capture status"
).to.exist.and.to.be.a("string");
expect(
response.body.payment_method_id,
"payment_method_id"
).to.include("pm_");
expect(
response.body.payment_method_status,
"payment_method_status"
).to.equal("active");
}
} else {
// Handle other authentication types as needed
throw new Error(
`Invalid authentication type: ${response.body.authentication_type}`
);
}
} else if (response.body.capture_method === "manual") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
expect(response.body.customer_id).to.equal(
globalState.get("customerId")
);
} else {
// Handle other authentication types as needed
throw new Error(
`Invalid authentication type: ${response.body.authentication_type}`
);
}
} else {
// Handle other capture methods as needed
throw new Error(
`Invalid capture method: ${response.body.capture_method}`
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add("captureCallTest", (requestBody, data, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const paymentId = globalState.get("paymentID");
const profileId = globalState.get(`${configInfo.profilePrefix}Id`);
requestBody.profile_id = profileId;
for (const key in reqData) {
requestBody[key] = reqData[key];
}
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments/${paymentId}/capture`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.body.capture_method !== undefined) {
expect(response.body.payment_id).to.equal(paymentId);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
});
Cypress.Commands.add("voidCallTest", (requestBody, data, globalState) => {
const {
Configs: configs = {},
Response: resData,
Request: reqData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const payment_id = globalState.get("paymentID");
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
requestBody.profile_id = profile_id;
// Apply connector-specific request data (including cancellation_reason)
for (const key in reqData) {
requestBody[key] = reqData[key];
}
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments/${payment_id}/cancel`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
});
Cypress.Commands.add(
"retrievePaymentCallTest",
(globalState, data, autoretries = false, attempt = 1) => {
const { Configs: configs = {} } = data || {};
const configInfo = execConfig(validateConfig(configs));
const merchant_connector_id = globalState.get(
`${configInfo.merchantConnectorPrefix}Id`
);
const payment_id = globalState.get("paymentID");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/payments/${payment_id}?force_sync=true&expand_attempts=true`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.headers["content-type"]).to.include(
"application/json"
);
expect(response.body.payment_id).to.equal(payment_id);
expect(response.body.amount).to.equal(
globalState.get("paymentAmount")
);
expect(response.body.profile_id, "profile_id").to.not.be.null;
expect(response.body.billing, "billing_address").to.not.be.null;
expect(response.body.customer, "customer").to.not.be.empty;
if (
["succeeded", "processing", "requires_customer_action"].includes(
response.body.status
)
) {
expect(response.body.connector, "connector").to.equal(
globalState.get("connectorId")
);
expect(response.body.payment_method_data, "payment_method_data").to
.not.be.empty;
expect(response.body.payment_method, "payment_method").to.not.be
.null;
expect(
response.body.merchant_connector_id,
"connector_id"
).to.equal(merchant_connector_id);
}
if (
response.body.payment_method_id &&
typeof response.body.payment_method_id === "string"
) {
// Validate the payment_method_id format
expect(
response.body.payment_method_id,
"payment_method_id"
).to.include("pm_").and.to.not.be.null;
// Whenever, CIT Confirmations gets a payment status of `processing`, it does not yield the `payment_method_id` and hence the `paymentMethodId` in the `globalState` gets the value of `null`. And hence while confirming MIT, it yields an `error.message` of `"Json deserialize error: invalid type: null, expected a string at line 1 column 182"` which is basically because of the `null` value in `recurring_details.data` with `recurring_details.type` as `payment_method_id`. However, we get the `payment_method_id` while PSync, so we can assign it to the `globalState` here.
globalState.set("paymentMethodId", response.body.payment_method_id);
const allowedActiveStatuses = [
"succeeded",
"requires_capture",
"partially_captured",
];
// If capture method is manual, 'processing' status also means 'active'
// for the payment method's usability.
if (response.body.capture_method === "manual") {
allowedActiveStatuses.push("processing");
}
const expectedStatus = allowedActiveStatuses.includes(
response.body.status
)
? "active"
: "inactive";
// Validate the status
expect(
response.body.payment_method_status,
"payment_method_status"
).to.equal(expectedStatus);
}
if (autoretries) {
expect(response.body).to.have.property("attempts");
expect(response.body.attempts).to.be.an("array").and.not.empty;
expect(response.body.attempts.length).to.equal(attempt);
expect(response.body.attempts[0].attempt_id).to.include(
`${payment_id}_`
);
for (const key in response.body.attempts) {
if (
response.body.attempts[key].attempt_id ===
`${payment_id}_${attempt}` &&
response.body.status === "succeeded"
) {
expect(response.body.attempts[key].status).to.equal("charged");
} else if (
response.body.attempts[key].attempt_id ===
`${payment_id}_${attempt}` &&
response.body.status === "requires_customer_action"
) {
expect(response.body.attempts[key].status).to.equal(
"authentication_pending"
);
} else {
expect(response.body.attempts[key].status).to.equal("failure");
}
}
}
} else {
throw new Error(
`Retrieve Payment Call Failed with error code "${response.body.error.code}" error message "${response.body.error.message}"`
);
}
});
});
}
);
Cypress.Commands.add("refundCallTest", (requestBody, data, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const payment_id = globalState.get("paymentID");
// we only need this to set the delay. We don't need the return value
execConfig(validateConfig(configs));
for (const key in reqData) {
requestBody[key] = reqData[key];
}
requestBody.payment_id = payment_id;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/refunds`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("refundId", response.body.refund_id);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
expect(response.body.payment_id).to.equal(payment_id);
} else {
defaultErrorHandler(response, resData);
}
});
});
});
Cypress.Commands.add("syncRefundCallTest", (data, globalState) => {
const { Response: resData } = data || {};
const refundId = globalState.get("refundId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/refunds/${refundId}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
});
});
});
Cypress.Commands.add(
"citForMandatesCallTest",
(
requestBody,
data,
amount,
confirm,
capture_method,
payment_type,
globalState
) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
const merchant_connector_id = globalState.get(
`${configInfo.merchantConnectorPrefix}Id`
);
for (const key in reqData) {
requestBody[key] = reqData[key];
}
requestBody.amount = amount;
requestBody.capture_method = capture_method;
requestBody.confirm = confirm;
requestBody.customer_id = globalState.get("customerId");
requestBody.payment_type = payment_type;
requestBody.profile_id = profile_id;
globalState.set("paymentAmount", requestBody.amount);
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("paymentID", response.body.payment_id);
expect(response.body.payment_method_data, "payment_method_data").to
.not.be.empty;
expect(response.body.connector, "connector").to.equal(
globalState.get("connectorId")
);
expect(merchant_connector_id, "connector_id").to.equal(
response.body.merchant_connector_id
);
expect(response.body.customer, "customer").to.not.be.empty;
expect(response.body.profile_id, "profile_id").to.not.be.null;
if (
response.body.status !== "failed" &&
response.body.setup_future_usage === "off_session"
) {
expect(response.body.payment_method_id, "payment_method_id").to.not
.be.null;
}
if (requestBody.mandate_data === null) {
expect(response.body).to.have.property("payment_method_id");
globalState.set("paymentMethodId", response.body.payment_method_id);
} else {
expect(response.body).to.have.property("mandate_id");
globalState.set("mandateId", response.body.mandate_id);
}
if (response.body.capture_method === "automatic") {
expect(response.body).to.have.property("mandate_id");
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
if (
response.body.setup_future_usage === "off_session" &&
//Added this check to ensure mandate_id is null so that will get connector_mandate_id
response.body.mandate_id === null &&
response.body.status === "succeeded"
) {
expect(
response.body.connector_mandate_id,
"connector_mandate_id"
).to.not.be.null;
}
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else if (response.body.capture_method === "manual") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
globalState.set(
"nextActionUrl",
response.body.next_action.redirect_to_url
);
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.deep.equal(
response.body[key]
);
if (
response.body.setup_future_usage === "off_session" &&
response.body.mandate_id === null &&
response.body.status === "succeeded"
) {
expect(
response.body.connector_mandate_id,
"connector_mandate_id"
).to.not.be.null;
}
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"mitForMandatesCallTest",
(requestBody, data, amount, confirm, capture_method, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const profile_id = globalState.get(`${configInfo.profilePrefix}Id`);
for (const key in reqData) {
requestBody[key] = reqData[key];
}
const merchant_connector_id = globalState.get(
`${configInfo.merchantConnectorPrefix}Id`
);
requestBody.amount = amount;
requestBody.confirm = confirm;
requestBody.capture_method = capture_method;
requestBody.customer_id = globalState.get("customerId");
requestBody.mandate_id = globalState.get("mandateId");
requestBody.profile_id = profile_id;
globalState.set("paymentAmount", requestBody.amount);
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payments`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("paymentID", response.body.payment_id);
expect(response.body.payment_method_data, "payment_method_data").to
.not.be.empty;
expect(response.body.connector, "connector").to.equal(
globalState.get("connectorId")
);
expect(merchant_connector_id, "connector_id").to.equal(
response.body.merchant_connector_id
);
expect(response.body.customer, "customer").to.not.be.empty;
expect(response.body.profile_id, "profile_id").to.not.be.null;
expect(response.body.payment_method_id, "payment_method_id").to.not.be
.null;
if (response.body.capture_method === "automatic") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else if (response.body.capture_method === "manual") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
} else if (response.status === 400) {
if (response.body.error.message === "Mandate Validation Failed") {
expect(response.body.error.code).to.equal("HE_03");
expect(response.body.error.message).to.equal(
"Mandate Validation Failed"
);
expect(response.body.error.reason).to.equal(
"request amount is greater than mandate amount"
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"mitUsingPMId",
(
requestBody,
data,
amount,
confirm,
capture_method,
globalState,
connector_agnostic_mit
) => {
if (shouldSkipMitUsingPMId(globalState.get("connectorId"))) {
cy.log(
`Skipping mitUsingPMId for connector: ${globalState.get("connectorId")}`
);
return;
}
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const profileId = globalState.get(`${configInfo.profilePrefix}Id`);
const apiKey = globalState.get("apiKey");
const baseUrl = globalState.get("baseUrl");
const customerId = globalState.get("customerId");
const paymentMethodId = globalState.get("paymentMethodId");
const url = `${baseUrl}/payments`;
for (const key in reqData) {
requestBody[key] = reqData[key];
}
requestBody.amount = amount;
requestBody.capture_method = capture_method;
requestBody.confirm = confirm;
requestBody.customer_id = customerId;
requestBody.profile_id = profileId;
requestBody.recurring_details.data = paymentMethodId;
cy.request({
method: "POST",
url: url,
headers: {
"Content-Type": "application/json",
"api-key": apiKey,
},
failOnStatusCode: false,
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("paymentID", response.body.payment_id);
if (response.body.status === "failed") {
expect(
response.body.connector_transaction_id,
"connector_transaction_id"
).to.be.null;
} else {
expect(
response.body.connector_transaction_id,
"connector_transaction_id"
).to.not.be.null;
}
if (
response.body.payment_method_id &&
typeof response.body.payment_method_id === "string"
) {
expect(
response.body.payment_method_id,
"payment_method_id"
).to.include("pm_").and.to.not.be.null;
const allowedActiveStatuses = [
"succeeded",
"requires_capture",
"partially_captured",
];
if (allowedActiveStatuses.includes(response.body.status)) {
expect(response.body.status, "response status").to.be.oneOf(
allowedActiveStatuses
);
expect(
response.body.payment_method_status,
"payment_method_status for active status"
).to.equal("active");
if (connector_agnostic_mit) {
expect(
response.body.connector_mandate_id,
"connector_mandate_id for active status"
).to.be.null;
} else {
expect(
response.body.connector_mandate_id,
"connector_mandate_id for active status"
).to.exist.and.not.be.null;
}
} else {
expect(response.body.status, "response status").to.not.be.oneOf(
allowedActiveStatuses
);
expect(
response.body.payment_method_status,
"payment_method_status for inactive status"
).to.equal("inactive");
expect(
response.body.connector_mandate_id,
"connector_mandate_id for inactive status"
).to.be.null;
}
}
if (response.body.capture_method === "automatic") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else if (response.body.capture_method === "manual") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"mitUsingNTID",
(requestBody, data, amount, confirm, capture_method, globalState) => {
const {
Configs: configs = {},
Request: reqData,
Response: resData,
} = data || {};
const configInfo = execConfig(validateConfig(configs));
const profileId = globalState.get(`${configInfo.profilePrefix}Id`);
for (const key in reqData) {
requestBody[key] = reqData[key];
}
requestBody.amount = amount;
requestBody.confirm = confirm;
requestBody.capture_method = capture_method;
requestBody.profile_id = profileId;
const apiKey = globalState.get("apiKey");
const baseUrl = globalState.get("baseUrl");
const url = `${baseUrl}/payments`;
cy.request({
method: "POST",
url: url,
headers: {
"Content-Type": "application/json",
"api-key": apiKey,
},
failOnStatusCode: false,
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.headers["content-type"]).to.include(
"application/json"
);
globalState.set("paymentID", response.body.payment_id);
if (response.body.capture_method === "automatic") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else if (response.body.capture_method === "manual") {
if (response.body.authentication_type === "three_ds") {
expect(response.body)
.to.have.property("next_action")
.to.have.property("redirect_to_url");
const nextActionUrl = response.body.next_action.redirect_to_url;
cy.log(nextActionUrl);
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else if (response.body.authentication_type === "no_three_ds") {
for (const key in resData.body) {
expect(resData.body[key], [key]).to.equal(response.body[key]);
}
} else {
throw new Error(
`Invalid authentication type ${response.body.authentication_type}`
);
}
} else {
throw new Error(
`Invalid capture method ${response.body.capture_method}`
);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add("listMandateCallTest", (globalState) => {
const customerId = globalState.get("customerId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/customers/${customerId}/mandates`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
let i = 0;
for (i in response.body) {
if (response.body[i].mandate_id === globalState.get("mandateId")) {
expect(response.body[i].status).to.equal("active");
}
}
});
});
});
Cypress.Commands.add("revokeMandateCallTest", (globalState) => {
const mandateId = globalState.get("mandateId");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/mandates/revoke/${mandateId}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.body.status === 200) {
expect(response.body.status).to.equal("revoked");
} else if (response.body.status === 400) {
expect(response.body.reason).to.equal(
"Mandate has already been revoked"
);
}
});
});
});
Cypress.Commands.add(
"handleRedirection",
(globalState, expectedRedirection) => {
const connectorId = globalState.get("connectorId");
const nextActionUrl = globalState.get("nextActionUrl");
const expectedUrl = new URL(expectedRedirection);
const redirectionUrl = new URL(nextActionUrl);
handleRedirection(
"three_ds",
{ redirectionUrl, expectedUrl },
connectorId,
null
);
}
);
Cypress.Commands.add(
"handleBankRedirectRedirection",
(globalState, paymentMethodType, expectedRedirection) => {
const connectorId = globalState.get("connectorId");
const nextActionUrl = globalState.get("nextActionUrl");
const expectedUrl = new URL(expectedRedirection);
const redirectionUrl = new URL(nextActionUrl);
// explicitly restricting `sofort` payment method by adyen from running as it stops other tests from running
// trying to handle that specific case results in stripe 3ds tests to fail
if (!(connectorId == "adyen" && paymentMethodType == "sofort")) {
handleRedirection(
"bank_redirect",
{ redirectionUrl, expectedUrl },
connectorId,
paymentMethodType
);
}
}
);
Cypress.Commands.add(
"handleBankTransferRedirection",
(globalState, paymentMethodType, expectedRedirection) => {
const connectorId = globalState.get("connectorId");
const nextActionUrl = globalState.get("nextActionUrl");
const nextActionType = globalState.get("nextActionType");
const expectedUrl = new URL(expectedRedirection);
let redirectionUrl = null;
try {
redirectionUrl = new URL(nextActionUrl);
} catch {
/* banktransfer may not have redirection url */
}
handleRedirection(
"bank_transfer",
{ redirectionUrl, expectedUrl },
connectorId,
paymentMethodType,
{
nextActionType,
}
);
}
);
Cypress.Commands.add(
"handleUpiRedirection",
(globalState, paymentMethodType, expected_redirection) => {
const connectorId = globalState.get("connectorId");
const nextActionUrl = globalState.get("nextActionUrl");
const expectedUrl = new URL(expected_redirection);
const redirectionUrl = new URL(nextActionUrl);
handleRedirection(
"upi",
{ redirectionUrl, expectedUrl },
connectorId,
paymentMethodType
);
}
);
Cypress.Commands.add("listCustomerPMCallTest", (globalState, order = 0) => {
const apiKey = globalState.get("apiKey");
const baseUrl = globalState.get("baseUrl");
const customerId = globalState.get("customerId");
const url = `${baseUrl}/customers/${customerId}/payment_methods`;
cy.request({
method: "GET",
url: url,
headers: {
"api-key": apiKey,
"Content-Type": "application/json",
},
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
// Validate response has payment methods
if (response.body.customer_payment_methods[order]?.payment_token) {
const paymentToken =
response.body.customer_payment_methods[order].payment_token;
const cardInfo = response.body.customer_payment_methods[order].card;
const paymentMethodId =
response.body.customer_payment_methods[order].payment_method_id;
const lastUsedAt =
response.body.customer_payment_methods[order].last_used_at;
globalState.set("paymentMethodId", paymentMethodId);
globalState.set("paymentToken", paymentToken);
if (cardInfo) {
expect(cardInfo.expiry_year, "expiry_year").to.not.be.null;
expect(cardInfo.card_holder_name, "card_holder_name").to.not.be.null;
}
// Validate last_used_at timestamp
expect(new Date(lastUsedAt).getTime(), "last_used_at").to.be.lessThan(
Date.now()
).and.to.not.be.null;
// For order > 0, validate payment methods are ordered by last_used_at
if (order > 0) {
const prevLastUsedAt =
response.body.customer_payment_methods[0].last_used_at;
expect(
new Date(prevLastUsedAt).getTime(),
"last_used_at ordering"
).to.be.greaterThan(new Date(lastUsedAt).getTime());
}
} else {
expect(response.body)
.to.have.property("customer_payment_methods")
.to.be.an("array").and.empty;
}
// Validate other payment method properties
for (const arrayCount in response.body.customer_payment_methods) {
const paymentMethod =
response.body.customer_payment_methods[arrayCount];
expect(
globalState.get("customerId"),
`${arrayCount} customer_id`
).to.equal(paymentMethod.customer_id);
expect(
paymentMethod.payment_token,
`${arrayCount} payment_token`
).to.include("token_").and.not.be.null;
expect(
paymentMethod.payment_method_id,
`${arrayCount} payment_method_id`
).to.include("pm_").and.not.be.null;
expect(paymentMethod.payment_method, `${arrayCount} payment_method`).to
.not.be.null;
expect(
paymentMethod.payment_method_type,
`${arrayCount} payment_method_type`
).to.not.be.null;
expect(paymentMethod.last_used_at, `${arrayCount} last_used_at`).to.not
.be.null;
}
});
});
});
Cypress.Commands.add("listCustomerPMByClientSecret", (globalState) => {
const clientSecret = globalState.get("clientSecret");
const setupFutureUsage = globalState.get("setupFutureUsage");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/customers/payment_methods?client_secret=${clientSecret}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("publishableKey"),
},
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.body.customer_payment_methods[0]?.payment_token) {
const paymentToken =
response.body.customer_payment_methods[0].payment_token;
const paymentMethodId =
response.body.customer_payment_methods[0].payment_method_id;
globalState.set("paymentToken", paymentToken);
globalState.set("paymentMethodId", paymentMethodId);
expect(
response.body.customer_payment_methods[0].payment_method_id,
"payment_method_id"
).to.not.be.null;
if (setupFutureUsage === "off_session") {
expect(
response.body.customer_payment_methods[0].requires_cvv,
"requires_cvv"
).to.be.false;
} else if (setupFutureUsage === "on_session") {
expect(
response.body.customer_payment_methods[0].requires_cvv,
"requires_cvv"
).to.be.true;
}
} else {
// We only get an empty array if something's wrong. One exception is a 4xx when no customer exist but it is handled in the test
expect(response.body)
.to.have.property("customer_payment_methods")
.to.be.an("array").and.empty;
}
});
});
});
Cypress.Commands.add("listRefundCallTest", (requestBody, globalState) => {
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/refunds/list`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
body: requestBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body.data).to.be.an("array").and.not.empty;
});
});
});
Cypress.Commands.add(
"createConfirmPayoutTest",
(createConfirmPayoutBody, data, confirm, auto_fulfill, globalState) => {
const { Request: reqData, Response: resData } = data || {};
for (const key in reqData) {
createConfirmPayoutBody[key] = reqData[key];
}
createConfirmPayoutBody.auto_fulfill = auto_fulfill;
createConfirmPayoutBody.confirm = confirm;
createConfirmPayoutBody.customer_id = globalState.get("customerId");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payouts/create`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: createConfirmPayoutBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("payoutAmount", createConfirmPayoutBody.amount);
globalState.set("payoutID", response.body.payout_id);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"createConfirmWithTokenPayoutTest",
(createConfirmPayoutBody, data, confirm, auto_fulfill, globalState) => {
const { Request: reqData, Response: resData } = data || {};
for (const key in reqData) {
createConfirmPayoutBody[key] = reqData[key];
}
createConfirmPayoutBody.customer_id = globalState.get("customerId");
createConfirmPayoutBody.payout_token = globalState.get("paymentToken");
createConfirmPayoutBody.auto_fulfill = auto_fulfill;
createConfirmPayoutBody.confirm = confirm;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payouts/create`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: createConfirmPayoutBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("payoutAmount", createConfirmPayoutBody.amount);
globalState.set("payoutID", response.body.payout_id);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"createConfirmWithPayoutMethodIdTest",
(createConfirmPayoutBody, data, confirm, auto_fulfill, globalState) => {
const { Request: reqData, Response: resData } = data || {};
for (const key in reqData) {
createConfirmPayoutBody[key] = reqData[key];
}
createConfirmPayoutBody.customer_id = globalState.get("customerId");
createConfirmPayoutBody.auto_fulfill = auto_fulfill;
createConfirmPayoutBody.confirm = confirm;
createConfirmPayoutBody.payout_method_id = globalState.data.paymentMethodId;
delete createConfirmPayoutBody.payout_token;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payouts/create`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: createConfirmPayoutBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
globalState.set("payoutAmount", createConfirmPayoutBody.amount);
globalState.set("payoutID", response.body.payout_id);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"fulfillPayoutCallTest",
(payoutFulfillBody, data, globalState) => {
const { Response: resData } = data || {};
payoutFulfillBody.payout_id = globalState.get("payoutID");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/payouts/${globalState.get("payoutID")}/fulfill`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: payoutFulfillBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add(
"updatePayoutCallTest",
(payoutConfirmBody, data, auto_fulfill, globalState) => {
const { Response: resData } = data || {};
payoutConfirmBody.confirm = true;
payoutConfirmBody.auto_fulfill = auto_fulfill;
cy.request({
method: "PUT",
url: `${globalState.get("baseUrl")}/payouts/${globalState.get("payoutID")}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
body: payoutConfirmBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add("retrievePayoutCallTest", (globalState) => {
const payout_id = globalState.get("payoutID");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/payouts/${payout_id}`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
expect(response.body.payout_id).to.equal(payout_id);
expect(response.body.amount).to.equal(globalState.get("payoutAmount"));
});
});
});
// User API calls
// Below 3 commands should be called in sequence to login a user
Cypress.Commands.add("userLogin", (globalState) => {
const baseUrl = globalState.get("baseUrl");
const queryParams = `token_only=true`;
const signinBody = {
email: globalState.get("email"),
password: globalState.get("password"),
};
const url = `${baseUrl}/user/v2/signin?${queryParams}`;
cy.request({
method: "POST",
url: url,
headers: {
"Content-Type": "application/json",
},
body: signinBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
if (response.body.token_type === "totp") {
expect(response.body, "totp_token").to.have.property("token").and.to
.not.be.empty;
const totpToken = response.body.token;
if (!totpToken) {
throw new Error("No token received from login");
}
globalState.set("totpToken", totpToken);
}
} else {
throw new Error(
`User login call failed to get totp token with status: "${response.status}" and message: "${response.body.error.message}"`
);
}
});
});
});
Cypress.Commands.add("terminate2Fa", (globalState) => {
// Define the necessary variables and constant
const baseUrl = globalState.get("baseUrl");
const queryParams = `skip_two_factor_auth=true`;
const apiKey = globalState.get("totpToken");
const url = `${baseUrl}/user/2fa/terminate?${queryParams}`;
cy.request({
method: "GET",
url: url,
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
if (response.body.token_type === "user_info") {
expect(response.body, "user_info_token").to.have.property("token").and
.to.not.be.empty;
const userInfoToken = response.body.token;
if (!userInfoToken) {
throw new Error("No user info token received");
}
globalState.set("userInfoToken", userInfoToken);
}
} else {
throw new Error(
`2FA terminate call failed with status: "${response.status}" and message: "${response.body.error.message}"`
);
}
});
});
});
Cypress.Commands.add("userInfo", (globalState) => {
// Define the necessary variables and constant
const baseUrl = globalState.get("baseUrl");
const userInfoToken = globalState.get("userInfoToken");
const url = `${baseUrl}/user`;
if (!userInfoToken) {
throw new Error("No user info token available");
}
cy.request({
method: "GET",
url: url,
headers: {
Authorization: `Bearer ${userInfoToken}`,
"Content-Type": "application/json",
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.body, "merchant_id").to.have.property("merchant_id").and
.to.not.be.empty;
expect(response.body, "organization_id").to.have.property("org_id").and
.to.not.be.empty;
expect(response.body, "profile_id").to.have.property("profile_id").and
.to.not.be.empty;
globalState.set("merchantId", response.body.merchant_id);
globalState.set("organizationId", response.body.org_id);
globalState.set("profileId", response.body.profile_id);
globalState.set("userInfoToken", userInfoToken);
} else {
throw new Error(
`User login call failed to fetch user info with status: "${response.status}" and message: "${response.body.error.message}"`
);
}
});
});
});
// Specific to routing tests
Cypress.Commands.add("ListMcaByMid", (globalState) => {
const merchantId = globalState.get("merchantId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/account/${merchantId}/connectors`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
"X-Merchant-Id": merchantId,
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
globalState.set("profileId", response.body[0].profile_id);
globalState.set("stripeMcaId", response.body[0].merchant_connector_id);
globalState.set("adyenMcaId", response.body[1].merchant_connector_id);
globalState.set("bluesnapMcaId", response.body[3].merchant_connector_id);
});
});
});
Cypress.Commands.add(
"addRoutingConfig",
(routingBody, data, type, routing_data, globalState) => {
const { Request: reqData, Response: resData } = data || {};
for (const key in reqData) {
routingBody[key] = reqData[key];
}
// set profile id from env
routingBody.profile_id = globalState.get("profileId");
routingBody.algorithm.type = type;
routingBody.algorithm.data = routing_data;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/routing`,
headers: {
Authorization: `Bearer ${globalState.get("userInfoToken")}`,
"Content-Type": "application/json",
},
failOnStatusCode: false,
body: routingBody,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body).to.have.property("id");
globalState.set("routingConfigId", response.body.id);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);
Cypress.Commands.add("activateRoutingConfig", (data, globalState) => {
const { Response: resData } = data || {};
const routing_config_id = globalState.get("routingConfigId");
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/routing/${routing_config_id}/activate`,
headers: {
Authorization: `Bearer ${globalState.get("userInfoToken")}`,
"Content-Type": "application/json",
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body.id).to.equal(routing_config_id);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
});
Cypress.Commands.add("retrieveRoutingConfig", (data, globalState) => {
const { Response: resData } = data || {};
const routing_config_id = globalState.get("routingConfigId");
cy.request({
method: "GET",
url: `${globalState.get("baseUrl")}/routing/${routing_config_id}`,
headers: {
Authorization: `Bearer ${globalState.get("userInfoToken")}`,
"Content-Type": "application/json",
},
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
expect(response.body.id).to.equal(routing_config_id);
for (const key in resData.body) {
expect(resData.body[key]).to.equal(response.body[key]);
}
} else {
defaultErrorHandler(response, resData);
}
});
});
});
Cypress.Commands.add(
"updateGsmConfig",
(gsmBody, globalState, step_up_possible) => {
gsmBody.step_up_possible = step_up_possible;
cy.request({
method: "POST",
url: `${globalState.get("baseUrl")}/gsm/update`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("adminApiKey"),
},
body: gsmBody,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.body)
.to.have.property("message")
.to.equal("card_declined");
expect(response.body)
.to.have.property("connector")
.to.equal("stripe");
expect(response.body)
.to.have.property("step_up_possible")
.to.equal(step_up_possible);
}
});
});
}
);
Cypress.Commands.add("incrementalAuth", (globalState, data) => {
const { Request: reqData, Response: resData } = data || {};
const baseUrl = globalState.get("baseUrl");
const paymentId = globalState.get("paymentID");
const apiKey = globalState.get("apiKey");
const url = `${baseUrl}/payments/${paymentId}/incremental_authorization`;
cy.request({
method: "POST",
url: url,
headers: {
"api-key": apiKey,
"Content-Type": "application/json",
},
body: reqData,
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.body.amount_capturable, "amount_capturable").to.equal(
resData.body.amount_capturable
);
expect(
response.body.authorization_count,
"authorization_count"
).to.be.a("number").and.not.be.null;
expect(
response.body.incremental_authorization_allowed,
"incremental_authorization_allowed"
).to.be.true;
expect(
response.body.incremental_authorizations,
"incremental_authorizations"
).to.be.an("array").and.not.be.empty;
expect(response.body.payment_id, "payment_id").to.equal(paymentId);
expect(response.body.status, "status").to.equal(resData.body.status);
for (const key in response.body.incremental_authorizations) {
expect(response.body.incremental_authorizations[key], "amount")
.to.have.property("amount")
.to.be.a("number")
.to.equal(resData.body.amount).and.not.be.null;
if (
response.body.incremental_authorizations[key].status === "failure"
) {
expect(response.body.incremental_authorizations[key], "error_code")
.to.have.property("error_code")
.to.be.equal(
resData.body.incremental_authorizations[key].error_code
);
expect(
response.body.incremental_authorizations[key],
"error_message"
)
.to.have.property("error_message")
.to.be.equal(
resData.body.incremental_authorizations[key].error_message
);
expect(response.body.incremental_authorizations[key], "status")
.to.have.property("status")
.to.equal("failure");
} else {
expect(
response.body.incremental_authorizations[key],
"error_code"
).to.have.property("error_code").to.be.null;
expect(
response.body.incremental_authorizations[key],
"error_message"
).to.have.property("error_message").to.be.null;
expect(response.body.incremental_authorizations[key], "status")
.to.have.property("status")
.to.equal("success");
}
expect(
response.body.incremental_authorizations[key],
"previously_authorized_amount"
)
.to.have.property("previously_authorized_amount")
.to.be.a("number")
.to.equal(
response.body.incremental_authorizations[key]
.previously_authorized_amount
).and.not.be.null;
}
}
});
});
});
Cypress.Commands.add("setConfigs", (globalState, key, value, requestType) => {
if (!key || !requestType) {
throw new Error("Key and requestType are required parameters");
}
const REQUEST_CONFIG = {
CREATE: { method: "POST", useKey: false },
UPDATE: { method: "POST", useKey: true },
FETCH: { method: "GET", useKey: true },
DELETE: { method: "DELETE", useKey: true },
};
const config = REQUEST_CONFIG[requestType];
if (!config) {
throw new Error(`Invalid requestType: ${requestType}`);
}
const apiKey = globalState.get("adminApiKey");
const baseUrl = globalState.get("baseUrl");
const url = `${baseUrl}/configs/${config.useKey ? key : ""}`;
const getRequestBody = {
CREATE: () => ({ key, value }),
UPDATE: () => ({ value }),
};
const body = getRequestBody[requestType]?.() || undefined;
cy.request({
method: config.method,
url,
headers: {
"Content-Type": "application/json",
"api-key": apiKey,
},
...(body && { body }),
failOnStatusCode: false,
}).then((response) => {
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
if (response.status === 200) {
expect(response.body).to.have.property("key").to.equal(key);
expect(response.body).to.have.property("value").to.equal(value);
} else {
Cypress.log({
name: "setConfigs",
message: `Failed for key: ${key} → status ${response.status}, message: ${response.body?.error?.message}`,
});
}
});
});
});
Cypress.Commands.add("setupConfigs", (globalState, key, value) => {
cy.setConfigs(globalState, key, value, "DELETE");
cy.setConfigs(globalState, key, value, "CREATE");
});
// UCS Configuration Commands
Cypress.Commands.add("setupUCSConfigs", (globalState, connector) => {
cy.setupConfigs(globalState, "ucs_enabled", "true");
const merchantId = globalState.get("merchantId");
const rolloutConfigs = [
`ucs_rollout_config_${merchantId}_${connector}_card_Authorize`,
`ucs_rollout_config_${merchantId}_${connector}_card_SetupMandate`,
`ucs_rollout_config_${merchantId}_${connector}_card_PSync`,
];
rolloutConfigs.forEach((key) => {
cy.setConfigs(globalState, key, "1.0", "CREATE");
});
});
Cypress.Commands.add("cleanupUCSConfigs", (globalState, connector) => {
const merchantId = globalState.get("merchantId");
const rolloutConfigs = [
`ucs_rollout_config_${merchantId}_${connector}_card_Authorize`,
`ucs_rollout_config_${merchantId}_${connector}_card_SetupMandate`,
`ucs_rollout_config_${merchantId}_${connector}_card_PSync`,
];
rolloutConfigs.forEach((key) => {
cy.setConfigs(globalState, key, "1.0", "DELETE");
});
cy.setConfigs(globalState, "ucs_enabled", "true", "DELETE");
});
// DDC Race Condition Test Commands
Cypress.Commands.add(
"ddcServerSideRaceConditionTest",
(confirmData, globalState) => {
const ddcConfig = confirmData.DDCConfig;
const paymentId = globalState.get("paymentID");
const merchantId = globalState.get("merchantId");
const completeUrl = `${Cypress.env("BASEURL")}/payments/${paymentId}/${merchantId}${ddcConfig.completeUrlPath}`;
cy.request({
method: "GET",
url: completeUrl,
qs: {
[ddcConfig.collectionReferenceParam]: ddcConfig.firstSubmissionValue,
},
failOnStatusCode: false,
}).then((firstResponse) => {
if (
firstResponse.status === 400 &&
firstResponse.body?.error?.message?.includes("No eligible connector")
) {
throw new Error(
`Connector configuration issue detected. Response: ${JSON.stringify(firstResponse.body)}`
);
}
expect(firstResponse.status).to.be.oneOf([200, 302]);
cy.log(`First request status: ${firstResponse.status}`);
cy.request({
method: "GET",
url: completeUrl,
qs: {
[ddcConfig.collectionReferenceParam]: ddcConfig.secondSubmissionValue,
},
failOnStatusCode: false,
}).then((secondResponse) => {
cy.log(`Second request status: ${secondResponse.status}`);
expect(secondResponse.status).to.eq(ddcConfig.expectedError.status);
expect(secondResponse.body).to.deep.equal(ddcConfig.expectedError.body);
cy.log(
"✅ Server-side race condition protection verified - second submission properly rejected"
);
});
});
}
);
Cypress.Commands.add(
"ddcClientSideRaceConditionTest",
(confirmData, globalState) => {
const ddcConfig = confirmData.DDCConfig;
const paymentId = globalState.get("paymentID");
const merchantId = globalState.get("merchantId");
const nextActionUrl = `${Cypress.env("BASEURL")}${ddcConfig.redirectUrlPath}/${paymentId}/${merchantId}/${paymentId}_1`;
cy.intercept("GET", nextActionUrl, (req) => {
req.reply((res) => {
let modifiedHtml = res.body.toString();
modifiedHtml = modifiedHtml.replace(
"</body>",
ddcConfig.raceConditionScript + "</body>"
);
res.send(modifiedHtml);
});
}).as("ddcPageWithRaceCondition");
cy.intercept("GET", "**/redirect/complete/**").as("ddcSubmission");
const delayBeforeSubmission = ddcConfig.delayBeforeSubmission || 2000;
cy.visit(nextActionUrl);
cy.wait("@ddcPageWithRaceCondition");
cy.wait("@ddcSubmission");
cy.wait(delayBeforeSubmission);
cy.get("@ddcSubmission.all").should("have.length", 1);
cy.get("@ddcSubmission").then((interception) => {
const collectionRef =
interception.request.query[ddcConfig.collectionReferenceParam] || "";
cy.log(
`Single submission detected with ${ddcConfig.collectionReferenceParam}: "${collectionRef}"`
);
});
cy.log(
"✅ Client-side race condition protection verified - only one submission occurred"
);
}
);