test(cypress): add cypress tests for void payment in v2 (#9646)

This commit is contained in:
Ayush Anand
2025-10-07 22:14:37 +05:30
committed by GitHub
parent 286e18b226
commit d98adb2e03
9 changed files with 522 additions and 174 deletions

View File

@ -655,6 +655,7 @@ jobs:
env:
CYPRESS_BASEURL: "http://localhost:8080"
ROUTER__SERVER__WORKERS: 4
PAYMENTS_CONNECTORS: ${{ env.PAYMENTS_CONNECTORS }}
shell: bash -leuo pipefail {0}
continue-on-error: true
# We aren't specifying `command` and `jobs` arguments currently

View File

@ -5,7 +5,7 @@ import { getCustomExchange } from "./_Reusable";
const successfulNo3DSCardDetails = {
card_number: "4111111111111111",
card_exp_month: "08",
card_exp_year: "25",
card_exp_year: "28",
card_holder_name: "joseph Doe",
card_cvc: "999",
};
@ -13,7 +13,7 @@ const successfulNo3DSCardDetails = {
const successfulThreeDSTestCardDetails = {
card_number: "4111111111111111",
card_exp_month: "10",
card_exp_year: "25",
card_exp_year: "28",
card_holder_name: "morino",
card_cvc: "999",
};
@ -52,6 +52,36 @@ const multiUseMandateData = {
},
};
const billingAddress = {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "US",
first_name: "joseph",
last_name: "Doe",
},
email: "example@example.com",
};
const shippingAddress = {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "US",
first_name: "joseph",
last_name: "Doe",
},
email: "example@example.com",
};
export const payment_methods_enabled = [
{
payment_method_type: "bank_debit",
@ -368,19 +398,7 @@ export const connectorDetails = {
pix: {},
},
},
billing: {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "BR",
first_name: "john",
last_name: "doe",
},
},
billing: billingAddress,
currency: "BRL",
},
}),
@ -409,19 +427,7 @@ export const connectorDetails = {
},
},
},
billing: {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "NL",
first_name: "john",
last_name: "doe",
},
},
billing: billingAddress,
},
}),
Giropay: getCustomExchange({
@ -439,19 +445,7 @@ export const connectorDetails = {
},
},
},
billing: {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "DE",
first_name: "john",
last_name: "doe",
},
},
billing: billingAddress,
},
}),
Sofort: getCustomExchange({
@ -466,19 +460,7 @@ export const connectorDetails = {
},
},
},
billing: {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "DE",
first_name: "john",
last_name: "doe",
},
},
billing: billingAddress,
},
}),
Eps: getCustomExchange({
@ -492,19 +474,7 @@ export const connectorDetails = {
},
},
},
billing: {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "AT",
first_name: "john",
last_name: "doe",
},
},
billing: billingAddress,
},
}),
Przelewy24: getCustomExchange({
@ -545,27 +515,20 @@ export const connectorDetails = {
},
},
},
billing: {
address: {
line1: "1467",
line2: "Harrison Street",
line3: "Harrison Street",
city: "San Fransico",
state: "California",
zip: "94122",
country: "PL",
first_name: "john",
last_name: "doe",
},
},
billing: billingAddress,
},
}),
},
card_pm: {
PaymentIntent: getCustomExchange({
Request: {
amount_details: {
order_amount: 1001,
currency: "USD",
},
billing: billingAddress,
shipping: shippingAddress,
},
Response: {
status: 200,
body: {
@ -573,6 +536,7 @@ export const connectorDetails = {
},
},
}),
PaymentIntentOffSession: getCustomExchange({
Request: {
currency: "USD",
@ -608,24 +572,36 @@ export const connectorDetails = {
}),
No3DSManualCapture: getCustomExchange({
Request: {
payment_method: "card",
payment_method_data: {
card: successfulNo3DSCardDetails,
},
currency: "USD",
payment_method_type: "card",
payment_method_subtype: "credit",
customer_acceptance: null,
setup_future_usage: "on_session",
},
Response: {
status: 200,
body: {
status: "requires_capture",
},
},
}),
No3DSAutoCapture: getCustomExchange({
Request: {
payment_method: "card",
payment_method_data: {
card: successfulNo3DSCardDetails,
},
currency: "USD",
payment_method_type: "card",
payment_method_subtype: "credit",
customer_acceptance: null,
setup_future_usage: "on_session",
shipping: shippingAddress,
},
Response: {
status: 200,
body: {
status: "succeeded",
},
},
}),
Capture: getCustomExchange({
@ -634,7 +610,6 @@ export const connectorDetails = {
payment_method_data: {
card: successfulNo3DSCardDetails,
},
currency: "USD",
customer_acceptance: null,
},
}),
@ -659,8 +634,28 @@ export const connectorDetails = {
error: {
type: "invalid_request",
message:
"You cannot cancel this payment because it has status succeeded",
code: "IR_16",
"This Payment could not be PaymentsCancel because it has a status of requires_payment_method. The expected state is requires_capture, partially_captured_and_capturable, partially_authorized_and_requires_capture",
code: "IR_14",
},
},
},
}),
VoidAfterConfirm: getCustomExchange({
Request: {},
Response: {
status: 200,
body: {
status: "cancelled",
},
},
ResponseCustom: {
status: 400,
body: {
error: {
type: "invalid_request",
message:
"This Payment could not be PaymentsCancel because it has a status of succeeded. The expected state is requires_capture, partially_captured_and_capturable, partially_authorized_and_requires_capture",
code: "IR_14",
},
},
},
@ -671,7 +666,6 @@ export const connectorDetails = {
payment_method_data: {
card: successfulNo3DSCardDetails,
},
currency: "USD",
customer_acceptance: null,
},
ResponseCustom: {
@ -970,7 +964,7 @@ export const connectorDetails = {
error: {
error_type: "invalid_request",
message: "Json deserialize error: invalid card number length",
code: "IR_06"
code: "IR_06",
},
},
},
@ -1077,8 +1071,9 @@ export const connectorDetails = {
body: {
error: {
error_type: "invalid_request",
message: "Json deserialize error: unknown variant `United`, expected one of `AED`, `AFN`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN`, `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BTN`, `BWP`, `BYN`, `BZD`, `CAD`, `CDF`, `CHF`, `CLP`, `CNY`, `COP`, `CRC`, `CUP`, `CVE`, `CZK`, `DJF`, `DKK`, `DOP`, `DZD`, `EGP`, `ERN`, `ETB`, `EUR`, `FJD`, `FKP`, `GBP`, `GEL`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD`, `HKD`, `HNL`, `HRK`, `HTG`, `HUF`, `IDR`, `ILS`, `INR`, `IQD`, `IRR`, `ISK`, `JMD`, `JOD`, `JPY`, `KES`, `KGS`, `KHR`, `KMF`, `KPW`, `KRW`, `KWD`, `KYD`, `KZT`, `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LYD`, `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRU`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN`, `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD`, `OMR`, `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG`, `QAR`, `RON`, `RSD`, `RUB`, `RWF`, `SAR`, `SBD`, `SCR`, `SDG`, `SEK`, `SGD`, `SHP`, `SLE`, `SLL`, `SOS`, `SRD`, `SSP`, `STN`, `SVC`, `SYP`, `SZL`, `THB`, `TJS`, `TMT`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS`, `UAH`, `UGX`, `USD`, `UYU`, `UZS`, `VES`, `VND`, `VUV`, `WST`, `XAF`, `XCD`, `XOF`, `XPF`, `YER`, `ZAR`, `ZMW`, `ZWL`",
code: "IR_06"
message:
"Json deserialize error: unknown variant `United`, expected one of `AED`, `AFN`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN`, `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BTN`, `BWP`, `BYN`, `BZD`, `CAD`, `CDF`, `CHF`, `CLP`, `CNY`, `COP`, `CRC`, `CUP`, `CVE`, `CZK`, `DJF`, `DKK`, `DOP`, `DZD`, `EGP`, `ERN`, `ETB`, `EUR`, `FJD`, `FKP`, `GBP`, `GEL`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD`, `HKD`, `HNL`, `HRK`, `HTG`, `HUF`, `IDR`, `ILS`, `INR`, `IQD`, `IRR`, `ISK`, `JMD`, `JOD`, `JPY`, `KES`, `KGS`, `KHR`, `KMF`, `KPW`, `KRW`, `KWD`, `KYD`, `KZT`, `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LYD`, `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRU`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN`, `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD`, `OMR`, `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG`, `QAR`, `RON`, `RSD`, `RUB`, `RWF`, `SAR`, `SBD`, `SCR`, `SDG`, `SEK`, `SGD`, `SHP`, `SLE`, `SLL`, `SOS`, `SRD`, `SSP`, `STN`, `SVC`, `SYP`, `SZL`, `THB`, `TJS`, `TMT`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS`, `UAH`, `UGX`, `USD`, `UYU`, `UZS`, `VES`, `VND`, `VUV`, `WST`, `XAF`, `XCD`, `XOF`, `XPF`, `YER`, `ZAR`, `ZMW`, `ZWL`",
code: "IR_06",
},
},
},
@ -1105,8 +1100,9 @@ export const connectorDetails = {
body: {
error: {
error_type: "invalid_request",
message: "Json deserialize error: unknown variant `auto`, expected one of `automatic`, `manual`, `manual_multiple`, `scheduled`",
code: "IR_06"
message:
"Json deserialize error: unknown variant `auto`, expected one of `automatic`, `manual`, `manual_multiple`, `scheduled`",
code: "IR_06",
},
},
},
@ -1132,8 +1128,9 @@ export const connectorDetails = {
body: {
error: {
error_type: "invalid_request",
message: "Json deserialize error: unknown variant `this_supposed_to_be_a_card`, expected one of `card`, `card_redirect`, `pay_later`, `wallet`, `bank_redirect`, `bank_transfer`, `crypto`, `bank_debit`, `reward`, `real_time_payment`, `upi`, `voucher`, `gift_card`, `open_banking`, `mobile_payment`",
code: "IR_06"
message:
"Json deserialize error: unknown variant `this_supposed_to_be_a_card`, expected one of `card`, `card_redirect`, `pay_later`, `wallet`, `bank_redirect`, `bank_transfer`, `crypto`, `bank_debit`, `reward`, `real_time_payment`, `upi`, `voucher`, `gift_card`, `open_banking`, `mobile_payment`",
code: "IR_06",
},
},
},
@ -1202,7 +1199,8 @@ export const connectorDetails = {
body: {
error: {
type: "invalid_request",
message: "A payment token or payment method data or ctp service details is required",
message:
"A payment token or payment method data or ctp service details is required",
code: "IR_06",
},
},

View File

@ -5,7 +5,7 @@ const connectorDetails = {
};
export default function getConnectorDetails(connectorId) {
let x = mergeDetails(connectorId);
const x = mergeDetails(connectorId);
return x;
}
@ -61,9 +61,9 @@ export const should_continue_further = (res_data) => {
}
if (
res_data.body.error !== undefined ||
res_data.body.error_code !== undefined ||
res_data.body.error_message !== undefined
res_data.Response.body.error !== undefined ||
res_data.Response.body.error_code !== undefined ||
res_data.Response.body.error_message !== undefined
) {
return false;
} else {
@ -89,9 +89,12 @@ export function defaultErrorHandler(response, response_data) {
if (typeof response.body.error === "object") {
for (const key in response_data.body.error) {
// Check if the error message is a Json deserialize error
let apiResponseContent = response.body.error[key];
let expectedContent = response_data.body.error[key];
if (typeof apiResponseContent === "string" && apiResponseContent.includes("Json deserialize error")) {
const apiResponseContent = response.body.error[key];
const expectedContent = response_data.body.error[key];
if (
typeof apiResponseContent === "string" &&
apiResponseContent.includes("Json deserialize error")
) {
expect(apiResponseContent).to.include(expectedContent);
} else {
expect(apiResponseContent).to.equal(expectedContent);

View File

@ -46,9 +46,7 @@ with `getCustomExchange`, if 501 response is expected, there is no need to pass
// Const to get default PaymentExchange object
const getDefaultExchange = () => ({
Request: {
currency: "EUR",
},
Request: {},
Response: {
status: 501,
body: {
@ -62,9 +60,7 @@ const getDefaultExchange = () => ({
});
const getUnsupportedExchange = () => ({
Request: {
currency: "EUR",
},
Request: {},
Response: {
status: 400,
body: {

View File

@ -40,12 +40,11 @@ describe("[Payment] [No 3DS] [Payment Method: Card]", () => {
let req_data = data["Request"];
let res_data = data["Response"];
cy.paymentIntentCreateCall(
globalState,
fixtures.createPaymentBody,
req_data,
res_data,
"no_three_ds",
"automatic",
globalState
"automatic"
);
});
@ -59,7 +58,7 @@ describe("[Payment] [No 3DS] [Payment Method: Card]", () => {
];
let req_data = data["Request"];
let res_data = data["Response"];
cy.paymentIntentConfirmCall(
cy.paymentConfirmCall(
fixtures.confirmBody,
req_data,
res_data,
@ -97,12 +96,11 @@ describe("[Payment] [No 3DS] [Payment Method: Card]", () => {
let req_data = data["Request"];
let res_data = data["Response"];
cy.paymentIntentCreateCall(
globalState,
fixtures.createPaymentBody,
req_data,
res_data,
"no_three_ds",
"automatic",
globalState
"automatic"
);
});
@ -116,7 +114,7 @@ describe("[Payment] [No 3DS] [Payment Method: Card]", () => {
];
let req_data = data["Request"];
let res_data = data["Response"];
cy.paymentIntentConfirmCall(
cy.paymentConfirmCall(
fixtures.confirmBody,
req_data,
res_data,

View File

@ -0,0 +1,207 @@
/*
V2 Void/Cancel Payment Tests
Test scenarios:
1. Void payment in requires_capture state
2. Void payment in requires_payment_method state
3. Void payment in succeeded state (should fail)
*/
import * as fixtures from "../../../fixtures/imports";
import State from "../../../utils/State";
import getConnectorDetails, {
should_continue_further,
} from "../../configs/Payment/Utils";
let globalState;
describe("[Payment] [Void/Cancel] [Payment Method: Card]", () => {
context("[Payment] [Void] [Requires Capture State]", () => {
let should_continue = true;
before("seed global state", () => {
cy.task("getGlobalState").then((state) => {
globalState = new State(state);
});
});
beforeEach(function () {
if (!should_continue) {
this.skip();
}
});
after("flush global state", () => {
cy.task("setGlobalState", globalState.data);
});
it("Create payment intent", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["PaymentIntent"];
const req_data = data["Request"];
const res_data = data["Response"];
cy.paymentIntentCreateCall(
globalState,
req_data,
res_data,
"no_three_ds",
"manual"
);
if (should_continue) should_continue = should_continue_further(data);
});
it("Confirm payment intent", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["No3DSManualCapture"];
const req_data = data["Request"];
cy.paymentConfirmCall(globalState, req_data, data);
if (should_continue) should_continue = should_continue_further(data);
});
it("Void payment intent", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["VoidAfterConfirm"];
cy.paymentVoidCall(globalState, fixtures.void_payment_body, data);
if (should_continue) should_continue = should_continue_further(data);
});
});
context(
"[Payment] [Void] [Requires Payment Method State - Should Fail]",
() => {
let should_continue = true;
before("seed global state", () => {
cy.task("getGlobalState").then((state) => {
globalState = new State(state);
});
});
beforeEach(function () {
if (!should_continue) {
this.skip();
}
});
after("flush global state", () => {
cy.task("setGlobalState", globalState.data);
});
it("Create payment intent", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["PaymentIntent"];
const req_data = data["Request"];
const res_data = data["Response"];
cy.paymentIntentCreateCall(
globalState,
req_data,
res_data,
"no_three_ds",
"manual"
);
if (should_continue) should_continue = should_continue_further(data);
});
it("Void payment intent - should fail", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["Void"];
// Use the ResponseCustom which contains the error response
const void_data = {
...data,
Response: data.ResponseCustom,
};
cy.paymentVoidCall(
globalState,
fixtures.void_payment_body,
void_data
);
if (should_continue) should_continue = should_continue_further(data);
});
}
);
context("[Payment] [Void] [Succeeded State - Should Fail]", () => {
let should_continue = true;
before("seed global state", () => {
cy.task("getGlobalState").then((state) => {
globalState = new State(state);
});
});
beforeEach(function () {
if (!should_continue) {
this.skip();
}
});
after("flush global state", () => {
cy.task("setGlobalState", globalState.data);
});
it("Create payment intent", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["PaymentIntent"];
const req_data = data["Request"];
const res_data = data["Response"];
cy.paymentIntentCreateCall(
globalState,
req_data,
res_data,
"no_three_ds",
"automatic"
);
if (should_continue) should_continue = should_continue_further(data);
});
it("Confirm payment intent", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["No3DSAutoCapture"];
const req_data = data["Request"];
cy.paymentConfirmCall(globalState, req_data, data);
if (should_continue) should_continue = should_continue_further(data);
});
it("Void payment intent - should fail", () => {
const data = getConnectorDetails(globalState.get("connectorId"))[
"card_pm"
]["VoidAfterConfirm"];
// Use the ResponseCustom which contains the error response
const void_data = {
...data,
Response: data.ResponseCustom,
};
cy.paymentVoidCall(
globalState,
fixtures.void_payment_body,
void_data
);
if (should_continue) should_continue = should_continue_further(data);
});
});
});

View File

@ -4,6 +4,7 @@ import merchant_account_body from "./merchant_account.json";
import merchant_connector_account_body from "./merchant_connector_account.json";
import organization_body from "./organization.json";
import routing_body from "./routing.json";
import void_payment_body from "./void_payment.json";
export {
api_key_body,
@ -12,4 +13,5 @@ export {
merchant_connector_account_body,
organization_body,
routing_body,
void_payment_body,
};

View File

@ -0,0 +1,3 @@
{
"cancellation_reason": "requested_by_customer"
}

View File

@ -27,7 +27,10 @@
// cy.task can only be used in support files (spec files or commands file)
import { nanoid } from "nanoid";
import { getValueByKey } from "../e2e/configs/Payment/Utils.js";
import {
defaultErrorHandler,
getValueByKey,
} from "../e2e/configs/Payment/Utils.js";
import { isoTimeTomorrow, validateEnv } from "../utils/RequestBodyUtils.js";
function logRequestId(xRequestId) {
@ -55,7 +58,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
},
body: organizationCreateBody,
failOnStatusCode: false,
@ -91,7 +94,7 @@ Cypress.Commands.add("organizationRetrieveCall", (globalState) => {
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
},
failOnStatusCode: false,
}).then((response) => {
@ -135,7 +138,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
},
body: organizationUpdateBody,
failOnStatusCode: false,
@ -185,7 +188,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
"X-Organization-Id": organization_id,
},
body: merchantAccountCreateBody,
@ -230,7 +233,7 @@ Cypress.Commands.add("merchantAccountRetrieveCall", (globalState) => {
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
},
failOnStatusCode: false,
}).then((response) => {
@ -274,7 +277,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
},
body: merchantAccountUpdateBody,
failOnStatusCode: false,
@ -324,7 +327,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
"x-merchant-id": merchant_id,
...customHeaders,
},
@ -369,7 +372,7 @@ Cypress.Commands.add("businessProfileRetrieveCall", (globalState) => {
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
failOnStatusCode: false,
@ -411,7 +414,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
body: businessProfileUpdateBody,
@ -503,7 +506,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
body: mcaCreateBody,
@ -551,7 +554,7 @@ Cypress.Commands.add("mcaRetrieveCall", (globalState) => {
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
failOnStatusCode: false,
@ -611,7 +614,7 @@ Cypress.Commands.add(
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
body: mcaUpdateBody,
@ -671,7 +674,7 @@ Cypress.Commands.add("apiKeyCreateCall", (apiKeyCreateBody, globalState) => {
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
body: apiKeyCreateBody,
@ -718,7 +721,7 @@ Cypress.Commands.add("apiKeyRetrieveCall", (globalState) => {
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
failOnStatusCode: false,
@ -768,7 +771,7 @@ Cypress.Commands.add("apiKeyUpdateCall", (apiKeyUpdateBody, globalState) => {
url: url,
headers: {
"Content-Type": "application/json",
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
...customHeaders,
},
body: apiKeyUpdateBody,
@ -1176,7 +1179,7 @@ Cypress.Commands.add("merchantAccountsListCall", (globalState) => {
method: "GET",
url: url,
headers: {
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
"Content-Type": "application/json",
},
failOnStatusCode: false,
@ -1218,7 +1221,7 @@ Cypress.Commands.add("businessProfilesListCall", (globalState) => {
method: "GET",
url: url,
headers: {
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
"Content-Type": "application/json",
...customHeaders,
},
@ -1261,7 +1264,7 @@ Cypress.Commands.add("mcaListCall", (globalState, service_type) => {
method: "GET",
url: url,
headers: {
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
"Content-Type": "application/json",
...customHeaders,
},
@ -1323,7 +1326,7 @@ Cypress.Commands.add("apiKeysListCall", (globalState) => {
method: "GET",
url: url,
headers: {
"Authorization": `admin-api-key=${api_key}`,
Authorization: `admin-api-key=${api_key}`,
"Content-Type": "application/json",
...customHeaders,
},
@ -1354,23 +1357,23 @@ Cypress.Commands.add("apiKeysListCall", (globalState) => {
// Payment API calls
// Update the below commands while following the conventions
// Below is an example of how the payment intent create call should look like (update the below command as per the need)
Cypress.Commands.add(
"paymentIntentCreateCall",
(
globalState,
paymentRequestBody,
paymentResponseBody
/* Add more variables based on the need*/
) => {
"paymentVoidCall",
(globalState, voidRequestBody, data) => {
const { Request: reqData = {}, Response: resData } = data || {};
// Define the necessary variables and constants at the top
// Also construct the URL here
const api_key = globalState.get("apiKey");
const base_url = globalState.get("baseUrl");
const profile_id = globalState.get("profileId");
const url = `${base_url}/v2/payments/create-intent`;
const payment_id = globalState.get("paymentID");
const url = `${base_url}/v2/payments/${payment_id}/cancel`;
// Update request body if needed
paymentRequestBody = {};
// Apply connector-specific request data (including cancellation_reason)
for (const key in reqData) {
voidRequestBody[key] = reqData[key];
}
// Pass Custom Headers
const customHeaders = {
@ -1381,7 +1384,59 @@ Cypress.Commands.add(
method: "POST",
url: url,
headers: {
"api-key": api_key,
Authorization: `api-key=${api_key}`,
"Content-Type": "application/json",
...customHeaders,
},
body: voidRequestBody,
failOnStatusCode: false,
}).then((response) => {
// Logging x-request-id is mandatory
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(
"paymentIntentCreateCall",
(
globalState,
paymentRequestBody,
paymentResponseBody,
authentication_type,
capture_method
) => {
// Define the necessary variables and constants at the top
// Also construct the URL here
const api_key = globalState.get("apiKey");
const base_url = globalState.get("baseUrl");
const profile_id = globalState.get("profileId");
const url = `${base_url}/v2/payments/create-intent`;
// Set capture_method and authentication_type as parameters (like V1)
paymentRequestBody.authentication_type = authentication_type;
paymentRequestBody.capture_method = capture_method;
// Pass Custom Headers
const customHeaders = {
"x-profile-id": profile_id,
};
cy.request({
method: "POST",
url: url,
headers: {
Authorization: `api-key=${api_key}`,
"Content-Type": "application/json",
...customHeaders,
},
@ -1391,28 +1446,113 @@ Cypress.Commands.add(
// Logging x-request-id is mandatory
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
// Update the assertions based on the need
expect(response.body).to.deep.equal(paymentResponseBody);
} else if (response.status === 400) {
// Add 4xx validations here
expect(response.body).to.deep.equal(paymentResponseBody);
} else if (response.status === 500) {
// Add 5xx validations here
expect(response.body).to.deep.equal(paymentResponseBody);
} else {
// If status code is other than the ones mentioned above, default should be thrown
throw new Error(
`Payment intent create call failed with status ${response.status} and message: "${response.body.error.message}"`
);
}
});
}
);
Cypress.Commands.add("paymentIntentConfirmCall", (globalState) => {});
Cypress.Commands.add("paymentIntentRetrieveCall", (globalState) => {});
// Validate the payment create response - V2 uses different ID format
expect(response.body).to.have.property("id").and.to.be.a("string").and
.not.be.empty;
expect(response.body).to.have.property("status");
// templates for future use
Cypress.Commands.add("", () => {
cy.request({}).then((response) => {});
// Store the payment ID for future use
globalState.set("paymentID", response.body.id);
// Log payment creation success
cy.task(
"cli_log",
`Payment created with ID: ${response.body.id}, Status: ${response.body.status}`
);
if (paymentResponseBody && paymentResponseBody.body) {
for (const key in paymentResponseBody.body) {
if (paymentResponseBody.body[key] !== null) {
expect(response.body[key]).to.equal(
paymentResponseBody.body[key]
);
}
}
}
} else {
defaultErrorHandler(response, paymentResponseBody);
}
});
});
}
);
Cypress.Commands.add(
"paymentConfirmCall",
(globalState, paymentConfirmRequestBody, data) => {
const { Request: reqData = {}, Response: resData } = data || {};
// Define the necessary variables and constants at the top
const api_key = globalState.get("apiKey");
const base_url = globalState.get("baseUrl");
const profile_id = globalState.get("profileId");
const payment_id = globalState.get("paymentID");
const url = `${base_url}/v2/payments/${payment_id}/confirm-intent`;
// Apply connector-specific request data
for (const key in reqData) {
paymentConfirmRequestBody[key] = reqData[key];
}
// Pass Custom Headers
const customHeaders = {
"x-profile-id": profile_id,
};
cy.request({
method: "POST",
url: url,
headers: {
Authorization: `api-key=${api_key}`,
"Content-Type": "application/json",
...customHeaders,
},
body: paymentConfirmRequestBody,
failOnStatusCode: false,
}).then((response) => {
// Logging x-request-id is mandatory
logRequestId(response.headers["x-request-id"]);
cy.wrap(response).then(() => {
expect(response.headers["content-type"]).to.include("application/json");
if (response.status === 200) {
// Validate the payment confirm response
expect(response.body).to.have.property("id").and.to.be.a("string").and
.not.be.empty;
expect(response.body).to.have.property("status");
globalState.set("paymentID", response.body.id);
// Validate response body against expected data
if (resData && resData.body) {
for (const key in resData.body) {
// Skip validation if expected value is null or undefined
if (resData.body[key] == null) {
continue;
}
// Only validate if the field exists in the response and has a non-null value
if (
response.body.hasOwnProperty(key) &&
response.body[key] != null
) {
// Use deep equal for object comparison, regular equal for primitives
if (
typeof resData.body[key] === "object" &&
typeof response.body[key] === "object"
) {
expect(response.body[key]).to.deep.equal(resData.body[key]);
} else {
expect(response.body[key]).to.equal(resData.body[key]);
}
}
}
}
} else {
defaultErrorHandler(response, resData);
}
});
});
}
);