Add tests for stripe (#1529)

* test for stripe

* first test for stripe

* tests for stripe

* remove comment

* fix import

* fix import

* remove todo
This commit is contained in:
Karolina Rakoczy 2021-10-28 14:14:07 +02:00 committed by GitHub
parent d0a6e10cec
commit d618b8e775
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 382 additions and 36 deletions

View file

@ -1,35 +1,42 @@
{ {
"clientData": "eyJ2ZXJzaW9uIjoiMS4wLjAiLCJkZXZpY2VGaW5nZXJwcmludCI6InpabUlDMEVYOHcwMDMwMDAwMDAwMDAwMDAwS1piSVFqNmt6czAwNjA4MjQxOTBjVkI5NGlLekJHS1IybWJMc3JmckFKUEdCY2JHSG0wMDZpdmJTdVlkRzBSMDAwMDBZVnhFcjAwMDAwVk1yM0h5dHhQNmlaQ3FuSTRsc2s6NDAiLCJwZXJzaXN0ZW50Q29va2llIjpbXSwiY29tcG9uZW50cyI6eyJ1c2VyQWdlbnQiOiJkYzJjNDM2MThlOTM0YjY2NmIwOWIxNmEzZDE4NmViZCIsIndlYmRyaXZlciI6MCwibGFuZ3VhZ2UiOiJwbC1QTCIsImNvbG9yRGVwdGgiOjMwLCJkZXZpY2VNZW1vcnkiOjgsInBpeGVsUmF0aW8iOjIsImhhcmR3YXJlQ29uY3VycmVuY3kiOjEyLCJzY3JlZW5XaWR0aCI6MTc5Miwic2NyZWVuSGVpZ2h0IjoxMTIwLCJhdmFpbGFibGVTY3JlZW5XaWR0aCI6MTc5MiwiYXZhaWxhYmxlU2NyZWVuSGVpZ2h0IjoxMDMwLCJ0aW1lem9uZU9mZnNldCI6LTEyMCwidGltZXpvbmUiOiJFdXJvcGUvV2Fyc2F3Iiwic2Vzc2lvblN0b3JhZ2UiOjEsImxvY2FsU3RvcmFnZSI6MSwiaW5kZXhlZERiIjoxLCJhZGRCZWhhdmlvciI6MCwib3BlbkRhdGFiYXNlIjoxLCJwbGF0Zm9ybSI6Ik1hY0ludGVsIiwicGx1Z2lucyI6ImMxMzU2NDM4NzUxNzZkZWU5MTkyZTE5ZDRkOGM3YTkwIiwiY2FudmFzIjoiZDYxNjRlNzA3ZWQ4NDdlNTE4OGE1YjUyMDhmNDI1OGIiLCJ3ZWJnbCI6IjcwM2ZjZWMyM2MxNjhlYjAzNmM0MjA3MzA1ODYzZmNlIiwid2ViZ2xWZW5kb3JBbmRSZW5kZXJlciI6IkludGVsIEluYy5+SW50ZWwoUikgVUhEIEdyYXBoaWNzIDYzMCIsImFkQmxvY2siOjAsImhhc0xpZWRMYW5ndWFnZXMiOjAsImhhc0xpZWRSZXNvbHV0aW9uIjowLCJoYXNMaWVkT3MiOjAsImhhc0xpZWRCcm93c2VyIjowLCJmb250cyI6IjI5MmVhMmNjZWNjZDAyYjAxYzBjNGMxZDQxMzIxNzVlIiwiYXVkaW8iOiIzOTRmNjQwMGY0NDg5NDIwNDIzZGYzMzY1OGU1NGJlNiIsImVudW1lcmF0ZURldmljZXMiOiJmMjA4Yzc3MWExNjBiNjRmNDkzOWFmYmE1OTY2YTRhZiJ9fQ==", "adyen": {
"encryptedExpiryMonth": "test_03", "clientData": "eyJ2ZXJzaW9uIjoiMS4wLjAiLCJkZXZpY2VGaW5nZXJwcmludCI6InpabUlDMEVYOHcwMDMwMDAwMDAwMDAwMDAwS1piSVFqNmt6czAwNjA4MjQxOTBjVkI5NGlLekJHS1IybWJMc3JmckFKUEdCY2JHSG0wMDZpdmJTdVlkRzBSMDAwMDBZVnhFcjAwMDAwVk1yM0h5dHhQNmlaQ3FuSTRsc2s6NDAiLCJwZXJzaXN0ZW50Q29va2llIjpbXSwiY29tcG9uZW50cyI6eyJ1c2VyQWdlbnQiOiJkYzJjNDM2MThlOTM0YjY2NmIwOWIxNmEzZDE4NmViZCIsIndlYmRyaXZlciI6MCwibGFuZ3VhZ2UiOiJwbC1QTCIsImNvbG9yRGVwdGgiOjMwLCJkZXZpY2VNZW1vcnkiOjgsInBpeGVsUmF0aW8iOjIsImhhcmR3YXJlQ29uY3VycmVuY3kiOjEyLCJzY3JlZW5XaWR0aCI6MTc5Miwic2NyZWVuSGVpZ2h0IjoxMTIwLCJhdmFpbGFibGVTY3JlZW5XaWR0aCI6MTc5MiwiYXZhaWxhYmxlU2NyZWVuSGVpZ2h0IjoxMDMwLCJ0aW1lem9uZU9mZnNldCI6LTEyMCwidGltZXpvbmUiOiJFdXJvcGUvV2Fyc2F3Iiwic2Vzc2lvblN0b3JhZ2UiOjEsImxvY2FsU3RvcmFnZSI6MSwiaW5kZXhlZERiIjoxLCJhZGRCZWhhdmlvciI6MCwib3BlbkRhdGFiYXNlIjoxLCJwbGF0Zm9ybSI6Ik1hY0ludGVsIiwicGx1Z2lucyI6ImMxMzU2NDM4NzUxNzZkZWU5MTkyZTE5ZDRkOGM3YTkwIiwiY2FudmFzIjoiZDYxNjRlNzA3ZWQ4NDdlNTE4OGE1YjUyMDhmNDI1OGIiLCJ3ZWJnbCI6IjcwM2ZjZWMyM2MxNjhlYjAzNmM0MjA3MzA1ODYzZmNlIiwid2ViZ2xWZW5kb3JBbmRSZW5kZXJlciI6IkludGVsIEluYy5+SW50ZWwoUikgVUhEIEdyYXBoaWNzIDYzMCIsImFkQmxvY2siOjAsImhhc0xpZWRMYW5ndWFnZXMiOjAsImhhc0xpZWRSZXNvbHV0aW9uIjowLCJoYXNMaWVkT3MiOjAsImhhc0xpZWRCcm93c2VyIjowLCJmb250cyI6IjI5MmVhMmNjZWNjZDAyYjAxYzBjNGMxZDQxMzIxNzVlIiwiYXVkaW8iOiIzOTRmNjQwMGY0NDg5NDIwNDIzZGYzMzY1OGU1NGJlNiIsImVudW1lcmF0ZURldmljZXMiOiJmMjA4Yzc3MWExNjBiNjRmNDkzOWFmYmE1OTY2YTRhZiJ9fQ==",
"encryptedExpiryYear": "test_2030", "encryptedExpiryMonth": "test_03",
"encryptedSecurityCodes": { "encryptedExpiryYear": "test_2030",
"unknown": "665", "encryptedSecurityCodes": {
"matches": "test_001" "unknown": "665",
}, "matches": "test_001"
"cards": {
"simpleCard": {
"brand": "visa",
"encryptedCardNumber": "test_4111111145551142"
}, },
"threeDSecureOneAuth": { "cards": {
"brand": "visa", "simpleCard": {
"encryptedCardNumber": "test_4212345678901237" "brand": "visa",
}, "encryptedCardNumber": "test_4111111145551142"
"threeDSecureTwoAuth": { },
"encryptedCardNumber": "test_5454545454545454", "threeDSecureOneAuth": {
"brand": "mc" "brand": "visa",
}, "encryptedCardNumber": "test_4212345678901237"
"errorCard": { },
"brand": "visa", "threeDSecureTwoAuth": {
"encryptedCardNumber": "test_5201282999005515" "encryptedCardNumber": "test_5454545454545454",
}, "brand": "mc"
"closeAccount" : { },
"brand": "visa", "errorCard": {
"encryptedCardNumber" : "test_5454541580311093" "brand": "visa",
}, "encryptedCardNumber": "test_5201282999005515"
"avs" : { },
"brand": "visa", "closeAccount": {
"encryptedCardNumber" : "test_4400000000000008" "brand": "visa",
"encryptedCardNumber": "test_5454541580311093"
},
"avs": {
"brand": "visa",
"encryptedCardNumber": "test_4400000000000008"
}
} }
},
"stripe":{
"simpleCardNumber": "5555555555554444",
"insufficientFundsCard": "4000000000009995",
"threeDSecureAuthCard": "4000002760003184"
} }
} }

View file

@ -24,6 +24,7 @@ export const urlList = {
shippingMethods: "shipping/", shippingMethods: "shipping/",
siteSettings: "site-settings/", siteSettings: "site-settings/",
staffMembers: "staff/", staffMembers: "staff/",
stripeApiPaymentMethods: "https://api.stripe.com/v1/payment_methods",
translations: "translations/", translations: "translations/",
vouchers: "discounts/vouchers/", vouchers: "discounts/vouchers/",
warehouses: "warehouses/", warehouses: "warehouses/",
@ -70,3 +71,6 @@ export const productTypeDetailsUrl = productTypeId =>
export const giftCardDetailsUrl = giftCardId => export const giftCardDetailsUrl = giftCardId =>
`${urlList.giftCards}${giftCardId}`; `${urlList.giftCards}${giftCardId}`;
export const stripeConfirmationUrl = id =>
`https://api.stripe.com/v1/payment_intents/${id}/confirm`;

View file

@ -42,7 +42,7 @@ filterTests({ definedTags: ["stagedOnly"] }, () => {
deleteProductsStartsWith(startsWith); deleteProductsStartsWith(startsWith);
deleteShippingStartsWith(startsWith); deleteShippingStartsWith(startsWith);
cy.fixture("cards").then(cardsResp => { cy.fixture("cards").then(cardsResp => {
paymentCards = cardsResp; paymentCards = cardsResp.adyen;
cardData = { cardData = {
clientData: paymentCards.clientData, clientData: paymentCards.clientData,
encryptedExpiryMonth: paymentCards.encryptedExpiryMonth, encryptedExpiryMonth: paymentCards.encryptedExpiryMonth,

View file

@ -0,0 +1,140 @@
/// <reference types="cypress"/>
/// <reference types="../../../support"/>
import {
addShippingMethod,
completeCheckout,
createCheckout
} from "../../../support/api/requests/Checkout";
import { getOrder } from "../../../support/api/requests/Order";
import { confirmThreeDSecure } from "../../../support/api/requests/stripe";
import { addStripePaymentAndGetConfirmationData } from "../../../support/api/utils/ordersUtils";
import { createProductWithShipping } from "../../../support/api/utils/products/productsUtils";
import { deleteShippingStartsWith } from "../../../support/api/utils/shippingUtils";
import filterTests from "../../../support/filterTests";
filterTests({ definedTags: ["stagedOnly"] }, () => {
describe("Stripe payments", () => {
const startsWith = "Stripe-";
const email = `example@example.com`;
let address;
let defaultChannel;
let shippingMethod;
let variantsList;
let checkout;
let paymentCards;
let cardData;
before(() => {
cy.clearSessionData().loginUserViaRequest();
deleteShippingStartsWith(startsWith);
cy.fixture("cards").then(({ stripe }) => {
paymentCards = stripe;
cardData = {
publicKey: paymentCards.publicApiKey,
cvc: 123,
expMonth: 10,
expYear: 50
};
});
createProductWithShipping({ name: startsWith }).then(values => {
address = values.address;
defaultChannel = values.defaultChannel;
shippingMethod = values.shippingMethod;
variantsList = values.variantsList;
});
});
beforeEach(() => {
cy.clearSessionData().loginUserViaRequest();
createCheckout({
channelSlug: defaultChannel.slug,
email,
variantsList,
address,
billingAddress: address,
auth: "token"
})
.then(({ checkout: checkoutResp }) => {
checkout = checkoutResp;
addShippingMethod(checkout.id, shippingMethod.id);
})
.then(({ checkout: checkoutResp }) => {
checkout = checkoutResp;
});
});
it("should purchase products with simple card", () => {
const simpleCard = cardData;
simpleCard.cardNumber = paymentCards.simpleCardNumber;
addStripePaymentAndGetConfirmationData({
card: simpleCard,
checkoutId: checkout.id,
amount: checkout.totalPrice.gross.amount
})
.then(() => {
completeCheckout(checkout.id);
})
.then(({ order }) => {
getOrder(order.id);
})
.then(order => {
expect(order.paymentStatus).to.eq("FULLY_CHARGED");
});
});
it("should not purchase products with card with insufficient funds", () => {
const simpleCard = cardData;
simpleCard.cardNumber = paymentCards.insufficientFundsCard;
addStripePaymentAndGetConfirmationData({
card: simpleCard,
checkoutId: checkout.id,
amount: checkout.totalPrice.gross.amount
}).then(resp => {
expect(resp.body.error.code).to.equal("card_declined");
});
});
it("should purchase products with 3D secure card", () => {
const threeDSecureCard = cardData;
threeDSecureCard.cardNumber = paymentCards.threeDSecureAuthCard;
addStripePaymentAndGetConfirmationData({
card: threeDSecureCard,
checkoutId: checkout.id,
amount: checkout.totalPrice.gross.amount
})
.then(resp => {
confirmThreeDSecure(resp.body.next_action.redirect_to_url.url);
})
.then(() => {
completeCheckout(checkout.id);
})
.then(({ order }) => {
getOrder(order.id);
})
.then(order => {
expect(order.paymentStatus).to.eq("FULLY_CHARGED");
});
});
it("should not purchase product when 3D secure not pass", () => {
const threeDSecureCard = cardData;
threeDSecureCard.cardNumber = paymentCards.threeDSecureAuthCard;
addStripePaymentAndGetConfirmationData({
card: threeDSecureCard,
checkoutId: checkout.id,
amount: checkout.totalPrice.gross.amount
})
.then(resp => {
confirmThreeDSecure(resp.body.next_action.redirect_to_url.url, false);
})
.then(() => {
completeCheckout(checkout.id);
})
.then(({ order }) => {
expect(order).to.not.be.ok;
});
});
});
});

View file

@ -67,6 +67,7 @@ filterTests({ definedTags: ["all"] }, () => {
it("Should upload saved image", () => { it("Should upload saved image", () => {
const name = "CyImages"; const name = "CyImages";
cy.clearSessionData().loginUserViaRequest();
loginDeleteProductsAndCreateNewOneWithNewDataAndDefaultChannel({ name }) loginDeleteProductsAndCreateNewOneWithNewDataAndDefaultChannel({ name })
.then(product => { .then(product => {
cy.visit(productDetailsUrl(product.id)) cy.visit(productDetailsUrl(product.id))

View file

@ -26,6 +26,8 @@ module.exports = async (on, config) => {
config.env.APP_MOUNT_URI = process.env.APP_MOUNT_URI; config.env.APP_MOUNT_URI = process.env.APP_MOUNT_URI;
config.env.mailHogUrl = process.env.CYPRESS_MAILHOG; config.env.mailHogUrl = process.env.CYPRESS_MAILHOG;
config.env.SHOP = await getShopInfo(process.env); config.env.SHOP = await getShopInfo(process.env);
config.env.STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
config.env.STRIPE_PUBLIC_KEY = process.env.STRIPE_PUBLIC_KEY;
on("before:browser:launch", (browser = {}, launchOptions) => { on("before:browser:launch", (browser = {}, launchOptions) => {
launchOptions.args.push("--proxy-bypass-list=<-loopback>"); launchOptions.args.push("--proxy-bypass-list=<-loopback>");

View file

@ -69,6 +69,7 @@ export function addShippingMethod(checkoutId, shippingMethodId) {
field field
} }
checkout{ checkout{
id
shippingMethod{ shippingMethod{
id id
name name
@ -107,6 +108,7 @@ export function addPayment({ checkoutId, gateway, token, amount }) {
gateway: "${gateway}" gateway: "${gateway}"
${tokenLine} ${tokenLine}
${amountLine} ${amountLine}
returnUrl: "https://qa.storefront.staging.saleor.cloud/checkout/payment-confirm"
}){ }){
paymentErrors{ paymentErrors{
field field

View file

@ -0,0 +1,106 @@
import { stripeConfirmationUrl, urlList } from "../../../fixtures/urlList";
import { getValueWithDefault } from "./utils/Utils";
const stripeAuthBearer = `Bearer ${Cypress.env("STRIPE_SECRET_KEY")}`;
const stripePublicKey = Cypress.env("STRIPE_PUBLIC_KEY");
export function getPaymentMethodStripeId({
cardNumber,
cvc,
expMonth,
expYear
}) {
return cy.request({
url: urlList.stripeApiPaymentMethods,
method: "POST",
form: true,
body: {
type: "card",
"card[number]": cardNumber,
"card[cvc]": cvc,
"card[exp_month]": expMonth,
"card[exp_year]": expYear,
pasted_fields: "number",
key: stripePublicKey
},
headers: {
Authorization: stripeAuthBearer
}
});
}
export function sendConfirmationToStripe(paymentMethodId, confirmationId) {
return cy.request({
method: "POST",
url: stripeConfirmationUrl(confirmationId),
form: true,
failOnStatusCode: false,
headers: {
Authorization: stripeAuthBearer
},
body: {
payment_method: paymentMethodId,
return_url: Cypress.config().baseUrl,
webauthn_uvpa_available: "true",
spc_eligible: "false",
key: stripePublicKey
}
});
}
export function confirmThreeDSecure(nextActionUrl, withSuccess = true) {
let returnUrl;
const paRes = getValueWithDefault(withSuccess, "success", "failure");
return cy
.request(nextActionUrl)
.then(resp => {
const { body } = new DOMParser().parseFromString(resp.body, "text/html");
const formUrl = body.querySelector('[id="form"]').getAttribute("action");
const source = body
.querySelector('[name="source"]')
.getAttribute("value");
returnUrl = body
.querySelector('[name="return_url"]')
.getAttribute("value");
const amount = body
.querySelector('[name="amount"]')
.getAttribute("value");
const currency = body
.querySelector('[name="currency"]')
.getAttribute("value");
const usage = body.querySelector('[name="usage"]').getAttribute("value");
const url = `${formUrl}?source=${source}&livemode=false&type=three_d_secure&pass_through=&return_url=${returnUrl}&amount=${amount}&currency=
${currency}&usage=${usage}`;
cy.request(url);
})
.then(() => {
cy.request({
url: returnUrl,
method: "POST",
form: true,
body: {
PaRes: "success",
MD: ""
}
});
})
.then(resp => {
const { body } = new DOMParser().parseFromString(resp.body, "text/html");
const formUrl = body.querySelector('[id="form"]').getAttribute("action");
const merchant = body
.querySelector('[name="merchant"]')
.getAttribute("value");
cy.request({
url: formUrl,
method: "POST",
form: true,
body: {
PaRes: paRes,
MD: "",
Merchant: merchant
}
});
});
}

View file

@ -1,5 +1,9 @@
import * as checkoutRequest from "../requests/Checkout"; import * as checkoutRequest from "../requests/Checkout";
import * as orderRequest from "../requests/Order"; import * as orderRequest from "../requests/Order";
import {
getPaymentMethodStripeId,
sendConfirmationToStripe
} from "../requests/stripe";
import { createProductInChannel } from "./products/productsUtils"; import { createProductInChannel } from "./products/productsUtils";
export function createWaitingForCaptureOrder({ export function createWaitingForCaptureOrder({
@ -172,6 +176,14 @@ export function addAdyenPayment(checkoutId, amount) {
amount amount
}); });
} }
export function addStripePayment(checkoutId, amount, token) {
return checkoutRequest.addPayment({
checkoutId,
gateway: "saleor.payments.stripe",
amount,
token
});
}
export function createAndCompleteCheckoutWithoutShipping({ export function createAndCompleteCheckoutWithoutShipping({
channelSlug, channelSlug,
@ -226,3 +238,25 @@ export function createOrderWithNewProduct({
}) })
.then(({ order, checkout }) => ({ order, checkout, variantsList })); .then(({ order, checkout }) => ({ order, checkout, variantsList }));
} }
export function addStripePaymentAndGetConfirmationData({
card,
checkoutId,
amount
}) {
let paymentMethodId;
return getPaymentMethodStripeId(card)
.then(resp => {
paymentMethodId = resp.body.id;
addStripePayment(checkoutId, amount, resp.body.id);
})
.then(() => {
checkoutRequest.completeCheckout(checkoutId);
})
.then(resp => {
const confirmationData = JSON.parse(resp.confirmationData);
sendConfirmationToStripe(paymentMethodId, confirmationData.id, false);
})
.then(resp => resp);
}

View file

@ -10,6 +10,7 @@ import {
import { deleteAttributesStartsWith } from "../attributes/attributeUtils"; import { deleteAttributesStartsWith } from "../attributes/attributeUtils";
import { deleteCollectionsStartsWith } from "../catalog/collectionsUtils"; import { deleteCollectionsStartsWith } from "../catalog/collectionsUtils";
import { getDefaultChannel } from "../channelsUtils"; import { getDefaultChannel } from "../channelsUtils";
import { createShipping } from "../shippingUtils";
export function createProductInChannel({ export function createProductInChannel({
name, name,
@ -107,15 +108,15 @@ export function deleteProductsStartsWith(startsWith) {
); );
} }
export function loginDeleteProductsAndCreateNewOneWithNewDataAndDefaultChannel({ export function deleteProductsAndCreateNewOneWithNewDataAndDefaultChannel({
name, name,
description = name description = name,
warehouseId
}) { }) {
let defaultChannel; let defaultChannel;
let collection; let collection;
let attribute; let attribute;
cy.clearSessionData().loginUserViaRequest();
deleteProductsStartsWith(name); deleteProductsStartsWith(name);
deleteCollectionsStartsWith(name); deleteCollectionsStartsWith(name);
return getDefaultChannel() return getDefaultChannel()
@ -136,8 +137,57 @@ export function loginDeleteProductsAndCreateNewOneWithNewDataAndDefaultChannel({
channelId: defaultChannel.id, channelId: defaultChannel.id,
name, name,
collectionId: collection.id, collectionId: collection.id,
description description,
warehouseId
}); });
}) })
.then(({ product: productResp }) => productResp); .then(({ product, variantsList }) => ({ product, variantsList }));
}
export function createProductWithShipping({ name }) {
let address;
let warehouse;
let shippingMethod;
let defaultChannel;
let shippingZone;
return cy
.fixture("addresses")
.then(addresses => {
address = addresses.usAddress;
getDefaultChannel();
})
.then(channelResp => {
defaultChannel = channelResp;
createShipping({
channelId: defaultChannel.id,
name,
address,
price: 10
});
})
.then(
({
warehouse: warehouseResp,
shippingZone: shippingZoneResp,
shippingMethod: shippingMethodResp
}) => {
warehouse = warehouseResp;
shippingMethod = shippingMethodResp;
shippingZone = shippingZoneResp;
deleteProductsAndCreateNewOneWithNewDataAndDefaultChannel({
name,
warehouseId: warehouse.id
});
}
)
.then(({ variantsList, product }) => ({
variantsList,
product,
warehouse,
shippingZone,
defaultChannel,
shippingMethod,
address
}));
} }