Skip to content

Commit

Permalink
MOL-1280: Fix retry route and add cypress tests (#683)
Browse files Browse the repository at this point in the history
* MOL-1280: Fix retry route and add cypress tests

* MOL-1280: Update cypress ID

* MOL-1280: cleanup cypress test

---------

Co-authored-by: Vitalij Mik <[email protected]>
  • Loading branch information
BlackScorp and Vitalij Mik authored Jan 15, 2024
1 parent 9de44f9 commit 32b0be3
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 97 deletions.
44 changes: 42 additions & 2 deletions src/Controller/Storefront/Payment/MollieFailureControllerBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,22 @@
use Kiener\MolliePayments\Exception\MissingOrderInTransactionException;
use Kiener\MolliePayments\Exception\MollieOrderCouldNotBeFetchedException;
use Kiener\MolliePayments\Factory\MollieApiFactory;
use Kiener\MolliePayments\Service\CustomerService;
use Kiener\MolliePayments\Service\Mollie\MolliePaymentStatus;
use Kiener\MolliePayments\Service\Mollie\OrderStatusConverter;
use Kiener\MolliePayments\Service\MollieApi\Order as MollieServiceOrder;
use Kiener\MolliePayments\Service\Order\OrderStateService;
use Kiener\MolliePayments\Service\SettingsService;
use Kiener\MolliePayments\Service\TransactionService;
use Kiener\MolliePayments\Service\Transition\TransactionTransitionServiceInterface;
use Kiener\MolliePayments\Struct\Order\OrderAttributes;
use Kiener\MolliePayments\Struct\OrderLineItemEntity\OrderLineItemEntityAttributes;
use Mollie\Api\Exceptions\ApiException;
use Mollie\Api\Resources\Order;
use Psr\Log\LoggerInterface;
use Shopware\Core\Checkout\Cart\Exception\OrderNotFoundException;
use Shopware\Core\Checkout\Order\Aggregate\OrderCustomer\OrderCustomerEntity;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Checkout\Order\OrderStates;
Expand Down Expand Up @@ -87,6 +92,16 @@ class MollieFailureControllerBase extends StorefrontController
*/
private $orderStatusConverter;

/**
* @var SettingsService
*/
private $settingsService;

/**
* @var CustomerService
*/
private $customerService;

/**
* @param RouterInterface $router
* @param CompatibilityGatewayInterface $compatibilityGateway
Expand All @@ -99,7 +114,7 @@ class MollieFailureControllerBase extends StorefrontController
* @param MollieServiceOrder $mollieOrderService
* @param OrderStatusConverter $orderStatusConverter
*/
public function __construct(RouterInterface $router, CompatibilityGatewayInterface $compatibilityGateway, MollieApiFactory $apiFactory, OrderStateService $orderStateService, TransactionService $transactionService, LoggerInterface $logger, TransactionTransitionServiceInterface $transactionTransitionService, FlowBuilderFactoryInterface $flowBuilderFactory, MollieServiceOrder $mollieOrderService, OrderStatusConverter $orderStatusConverter)
public function __construct(RouterInterface $router, CompatibilityGatewayInterface $compatibilityGateway, MollieApiFactory $apiFactory, OrderStateService $orderStateService, TransactionService $transactionService, LoggerInterface $logger, TransactionTransitionServiceInterface $transactionTransitionService, FlowBuilderFactoryInterface $flowBuilderFactory, MollieServiceOrder $mollieOrderService, OrderStatusConverter $orderStatusConverter, SettingsService $settingsService, CustomerService $customerService)
{
$this->router = $router;
$this->compatibilityGateway = $compatibilityGateway;
Expand All @@ -112,6 +127,8 @@ public function __construct(RouterInterface $router, CompatibilityGatewayInterfa
$this->orderStatusConverter = $orderStatusConverter;

$this->eventDispatcher = $flowBuilderFactory->createDispatcher();
$this->settingsService = $settingsService;
$this->customerService = $customerService;
}

/**
Expand Down Expand Up @@ -240,7 +257,30 @@ public function retry(SalesChannelContext $context, string $transactionId): Redi
# if its a failed status, then we have to create a new payment
# otherwise no payment would exist, and we are not able to redirect to the payment screen
if (MolliePaymentStatus::isFailedStatus('', $paymentStatus)) {
$mollieOrder->createPayment([]);
$settings = $this->settingsService->getSettings($context->getSalesChannelId());
$paymentData = [];

if ($settings->isSubscriptionsEnabled()) {
/** @var OrderLineItemCollection $lineItems */
$lineItems = $order->getLineItems();
/** @var OrderCustomerEntity $customer */
$customer = $order->getOrderCustomer();
/** @var string $customerId */
$customerId = $customer->getCustomerId();

# mollie customer ID is required for recurring payments, see https://docs.mollie.com/reference/v2/orders-api/create-order-payment
$mollieCustomerId = $this->customerService->getMollieCustomerId($customerId, $context->getSalesChannelId(), $context->getContext());

foreach ($lineItems as $lineItem) {
$attributes = new OrderLineItemEntityAttributes($lineItem);
if ($attributes->isSubscriptionProduct()) {
$paymentData['sequenceType'] = 'first';
$paymentData['customerId'] = $mollieCustomerId;
break;
}
}
}
$mollieOrder->createPayment($paymentData);
}

$redirectUrl = (string)$orderAttributes->getMolliePaymentUrl();
Expand Down
3 changes: 2 additions & 1 deletion src/Facade/MolliePaymentFinalize.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ public function finalize(AsyncPaymentTransactionStruct $transactionStruct, Sales
if ($this->settingsService->getMollieCypressMode() && $orderAttributes->isTypeSubscription()) {
if ($mollieOrder->payments() !== null && count($mollieOrder->payments()) > 0) {
$paymentDetails = new MolliePaymentDetails();
$mandateId = $paymentDetails->getMandateId($mollieOrder->payments()[0]);
$lasMolliePayment = count($mollieOrder->payments()) -1;
$mandateId = $paymentDetails->getMandateId($mollieOrder->payments()[$lasMolliePayment]);
$this->subscriptionManager->confirmSubscription($order, $mandateId, $salesChannelContext->getContext());
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/config/compatibility/controller.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
<argument type="service" id="Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderFactory"/>
<argument type="service" id="Kiener\MolliePayments\Service\MollieApi\Order"/>
<argument type="service" id="Kiener\MolliePayments\Service\Mollie\OrderStatusConverter"/>
<argument type="service" id="Kiener\MolliePayments\Service\SettingsService"/>
<argument type="service" id="Kiener\MolliePayments\Service\CustomerService"/>
<call method="setContainer">
<argument type="service" id="service_container"/>
</call>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/config/compatibility/controller_6.5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
<argument type="service" id="Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderFactory"/>
<argument type="service" id="Kiener\MolliePayments\Service\MollieApi\Order"/>
<argument type="service" id="Kiener\MolliePayments\Service\Mollie\OrderStatusConverter"/>
<argument type="service" id="Kiener\MolliePayments\Service\SettingsService"/>
<argument type="service" id="Kiener\MolliePayments\Service\CustomerService"/>
<call method="setContainer">
<argument type="service" id="service_container"/>
</call>
Expand Down
1 change: 1 addition & 0 deletions src/Service/TransactionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function getTransactionById($transactionId, $versionId = null, Context $c
}

$transactionCriteria->addAssociation('order.currency');
$transactionCriteria->addAssociation('order.lineItems');

/** @var OrderTransactionCollection $transactions */
$transactions = $this->orderTransactionRepository->search(
Expand Down
212 changes: 118 additions & 94 deletions tests/Cypress/cypress/e2e/storefront/subscriptions/subscription.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,112 +75,31 @@ describe('Subscription', () => {
});

describe('Storefront + Administration', function () {
it('C2339889: Purchase subscription after failed payment and verify data in Administration', () =>{
purchaseSubscriptionAndGoToPayment();

it('C4066: Purchase subscription and verify data in Administration', () => {

configAction.setupPlugin(true, false, false, true);
configAction.updateProducts('', true, 3, 'weeks');

dummyUserScenario.execute();
cy.visit('/');
topMenu.clickOnSecondCategory();
listing.clickOnFirstProduct();

// we have to see the subscription indicator
// and the add to basket button should show that we can subscribe
cy.contains('Subscription product');
cy.contains('.btn', 'Subscribe');
// we also want to see the translated interval
cy.contains('Every 3 weeks');

pdp.addToCart(2);

// ------------------------------------------------------------------------------------------------------

// verify our warning information in our offcanvas
cy.contains('Not all payments methods are available when ordering subscription products');

checkout.goToCheckoutInOffCanvas();

// ------------------------------------------------------------------------------------------------------

// verify our warning information on the cart page
cy.contains('Not all payments methods are available when ordering subscription products');
// we also want to see the translated interval
cy.contains('Every 3 weeks');

// now open our payment methods and verify
// that some of them are not available
// this is a check to at least see that it does something
// we also verify that we see all available methods (just to also check if mollie is even configured correctly).
if (shopware.isVersionGreaterEqual(6.4)) {
paymentAction.showAllPaymentMethods();
} else {
paymentAction.openPaymentsModal();
}

assertAvailablePaymentMethods();
molliePayment.selectFailed();

if (shopware.isVersionLower(6.4)) {
paymentAction.closePaymentsModal();
}
cy.url().should('include', '/payment/failed');
cy.get('.container-main .btn-primary').click();
cy.url().should('include','/checkout/select-method');
cy.get('.grid-button-creditcard[value="creditcard"]').click();

paymentAction.switchPaymentMethod('Card');

shopware.prepareDomainChange();
checkout.placeOrderOnConfirm();

mollieSandbox.initSandboxCookie();
mollieCreditCardForm.enterValidCard();
mollieCreditCardForm.submitForm();
molliePayment.selectPaid();

cy.url().should('include', '/checkout/finish');
cy.contains('Thank you for your order');


// ------------------------------------------------------------------------------------------------------

adminLogin.login();
adminOrders.openOrders();
adminOrders.openLastOrder();

// our latest order must have a subscription "badge"
repoOrdersDetails.getSubscriptionBadge().should('exist');

// ------------------------------------------------------------------------------------------------------

// verify that we have found a new subscription entry
// attention, this will not be 100% accurate if we have a persisting server
// or multiple subscription tests, but for now it has to work
adminSubscriptions.openSubscriptions();
adminSubscriptions.openSubscription(0);

// ------------------------------------------------------------------------------------------------------
assertValidSubscriptionInAdmin();
})

repoAdminSubscriptonDetails.getMollieCustomerIdField().should('be.visible');
it('C4066: Purchase subscription and verify data in Administration', () => {
purchaseSubscriptionAndGoToPayment();

vueJs.textField(repoAdminSubscriptonDetails.getMollieCustomerIdField()).containsValue('cst_');
vueJs.textField(repoAdminSubscriptonDetails.getCreatedAtField()).notEmptyValue();
molliePayment.selectPaid();

vueJs.textField(repoAdminSubscriptonDetails.getStatusField()).equalsValue('Active');
vueJs.textField(repoAdminSubscriptonDetails.getCanceledAtField()).emptyValue();
vueJs.textField(repoAdminSubscriptonDetails.getMollieSubscriptionIdField()).containsValue('sub_');
vueJs.textField(repoAdminSubscriptonDetails.getMandateField()).containsValue('mdt_');
vueJs.textField(repoAdminSubscriptonDetails.getNextPaymentAtField()).notEmptyValue();
vueJs.textField(repoAdminSubscriptonDetails.getLastRemindedAtField()).emptyValue();

// just do a contains, because card-titles are just different
// across shopware versions, and in the end, we just need to make sure we see this exact string
cy.contains("History (2)");

// oldest history entry
cy.contains(repoAdminSubscriptonDetails.getHistoryStatusToSelector(1), 'pending', {matchCase: false});
cy.contains(repoAdminSubscriptonDetails.getHistoryCommentSelector(1), 'created');
// latest history entry
cy.contains(repoAdminSubscriptonDetails.getHistoryStatusFromSelector(0), 'pending', {matchCase: false});
cy.contains(repoAdminSubscriptonDetails.getHistoryStatusToSelector(0), 'active', {matchCase: false});
cy.contains(repoAdminSubscriptonDetails.getHistoryCommentSelector(0), 'confirmed');
assertValidSubscriptionInAdmin();
})

});
Expand Down Expand Up @@ -413,6 +332,111 @@ describe('Subscription', () => {
})


function purchaseSubscriptionAndGoToPayment(){
configAction.setupPlugin(true, false, false, true);
configAction.updateProducts('', true, 3, 'weeks');

dummyUserScenario.execute();
cy.visit('/');
topMenu.clickOnSecondCategory();
listing.clickOnFirstProduct();

// we have to see the subscription indicator
// and the add to basket button should show that we can subscribe
cy.contains('Subscription product');
cy.contains('.btn', 'Subscribe');
// we also want to see the translated interval
cy.contains('Every 3 weeks');

pdp.addToCart(2);

// ------------------------------------------------------------------------------------------------------

// verify our warning information in our offcanvas
cy.contains('Not all payments methods are available when ordering subscription products');

checkout.goToCheckoutInOffCanvas();

// ------------------------------------------------------------------------------------------------------

// verify our warning information on the cart page
cy.contains('Not all payments methods are available when ordering subscription products');
// we also want to see the translated interval
cy.contains('Every 3 weeks');

// now open our payment methods and verify
// that some of them are not available
// this is a check to at least see that it does something
// we also verify that we see all available methods (just to also check if mollie is even configured correctly).
if (shopware.isVersionGreaterEqual(6.4)) {
paymentAction.showAllPaymentMethods();
} else {
paymentAction.openPaymentsModal();
}

assertAvailablePaymentMethods();

if (shopware.isVersionLower(6.4)) {
paymentAction.closePaymentsModal();
}

paymentAction.switchPaymentMethod('Card');

shopware.prepareDomainChange();
checkout.placeOrderOnConfirm();

mollieSandbox.initSandboxCookie();
mollieCreditCardForm.enterValidCard();
mollieCreditCardForm.submitForm();
}

function assertValidSubscriptionInAdmin(){
cy.url().should('include', '/checkout/finish');
cy.contains('Thank you for your order');
// ------------------------------------------------------------------------------------------------------

adminLogin.login();
adminOrders.openOrders();
adminOrders.openLastOrder();

// our latest order must have a subscription "badge"
repoOrdersDetails.getSubscriptionBadge().should('exist');

// ------------------------------------------------------------------------------------------------------

// verify that we have found a new subscription entry
// attention, this will not be 100% accurate if we have a persisting server
// or multiple subscription tests, but for now it has to work
adminSubscriptions.openSubscriptions();
adminSubscriptions.openSubscription(0);

// ------------------------------------------------------------------------------------------------------

repoAdminSubscriptonDetails.getMollieCustomerIdField().should('be.visible');

vueJs.textField(repoAdminSubscriptonDetails.getMollieCustomerIdField()).containsValue('cst_');
vueJs.textField(repoAdminSubscriptonDetails.getCreatedAtField()).notEmptyValue();

vueJs.textField(repoAdminSubscriptonDetails.getStatusField()).equalsValue('Active');
vueJs.textField(repoAdminSubscriptonDetails.getCanceledAtField()).emptyValue();
vueJs.textField(repoAdminSubscriptonDetails.getMollieSubscriptionIdField()).containsValue('sub_');
vueJs.textField(repoAdminSubscriptonDetails.getMandateField()).containsValue('mdt_');
vueJs.textField(repoAdminSubscriptonDetails.getNextPaymentAtField()).notEmptyValue();
vueJs.textField(repoAdminSubscriptonDetails.getLastRemindedAtField()).emptyValue();

// just do a contains, because card-titles are just different
// across shopware versions, and in the end, we just need to make sure we see this exact string
cy.contains("History (2)");

// oldest history entry
cy.contains(repoAdminSubscriptonDetails.getHistoryStatusToSelector(1), 'pending', {matchCase: false});
cy.contains(repoAdminSubscriptonDetails.getHistoryCommentSelector(1), 'created');
// latest history entry
cy.contains(repoAdminSubscriptonDetails.getHistoryStatusFromSelector(0), 'pending', {matchCase: false});
cy.contains(repoAdminSubscriptonDetails.getHistoryStatusToSelector(0), 'active', {matchCase: false});
cy.contains(repoAdminSubscriptonDetails.getHistoryCommentSelector(0), 'confirmed');
}

function assertAvailablePaymentMethods() {
cy.contains('Pay later').should('not.exist');
cy.contains('paysafecard').should('not.exist');
Expand Down

0 comments on commit 32b0be3

Please sign in to comment.