mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
test(cypress): add cypress tests for void payment in v2 (#9646)
This commit is contained in:
1
.github/workflows/cypress-tests-runner.yml
vendored
1
.github/workflows/cypress-tests-runner.yml
vendored
@ -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
|
||||
|
||||
@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -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,
|
||||
};
|
||||
|
||||
3
cypress-tests-v2/cypress/fixtures/void_payment.json
Normal file
3
cypress-tests-v2/cypress/fixtures/void_payment.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"cancellation_reason": "requested_by_customer"
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user