Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PISHPS-338: custom product compatibility #820

Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
NTR: PISHPS-338: Add compatiblity with custom products
Vitalij Mik committed Sep 2, 2024
commit 63389719160d987dcfb81fe632c1faf025bd5058
66 changes: 66 additions & 0 deletions src/Checkout/Cart/ExpressCartItemAddRoute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Checkout\Cart;

use Kiener\MolliePayments\Service\Cart\CartBackupService;
use Kiener\MolliePayments\Service\CartServiceInterface;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
use Shopware\Core\Checkout\Cart\SalesChannel\AbstractCartItemAddRoute;
use Shopware\Core\Checkout\Cart\SalesChannel\CartResponse;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\Request;

class ExpressCartItemAddRoute extends AbstractCartItemAddRoute
{
/**
* @var AbstractCartItemAddRoute
*/
private $cartItemAddRoute;

/**
* @var CartBackupService
*/

private $cartBackupService;
/**
* @var CartServiceInterface
*/
private $cartService;

public function __construct(AbstractCartItemAddRoute $cartItemAddRoute, CartBackupService $cartBackupService, CartServiceInterface $cartService)
{
$this->cartItemAddRoute = $cartItemAddRoute;
$this->cartBackupService = $cartBackupService;
$this->cartService = $cartService;
}

public function getDecorated(): AbstractCartItemAddRoute
{
return $this->cartItemAddRoute;
}

public function add(Request $request, Cart $cart, SalesChannelContext $context, ?array $items): CartResponse
{
//we have to create a new request from global variables, because the request is not set here in the route
$request = Request::createFromGlobals();

$isExpressCheckout = (bool)$request->get('isExpressCheckout', false);
if ($isExpressCheckout === false) {
return $this->getDecorated()->add($request, $cart, $context, $items);
}

if (!$this->cartBackupService->isBackupExisting($context)) {
$this->cartBackupService->backupCart($context);
}

$cart = $this->cartService->getCalculatedMainCart($context);

# clear existing cart and also update it to save it
$cart->setLineItems(new LineItemCollection());
$this->cartService->updateCart($cart);

return $this->getDecorated()->add($request, $cart, $context, $items);
}
}
6 changes: 0 additions & 6 deletions src/Components/ApplePayDirect/ApplePayDirect.php
Original file line number Diff line number Diff line change
@@ -355,12 +355,6 @@ public function prepareCustomer(string $firstname, string $lastname, string $ema
}


# we clear our cart backup now
# we are in the user redirection process where a restoring wouldn't make sense
# because from now on we would end on the cart page where we could even switch payment method.
$this->cartBackupService->clearBackup($context);


$applePayID = $this->getActiveApplePayID($context);

# if we are not logged in,
Original file line number Diff line number Diff line change
@@ -203,29 +203,29 @@ public function pay(RequestDataBag $data, SalesChannelContext $context): StoreAp
if (empty($paymentToken)) {
throw new \Exception('PaymentToken not found!');
}
try {
# make sure to create a customer if necessary
# then update to our apple pay payment method
# and return the new context
$newContext = $this->applePay->prepareCustomer(
$firstname,
$lastname,
$email,
$street,
$zipcode,
$city,
$countryCode,
$phone,
$paymentToken,
$context
);

# we only start our TRY/CATCH here!
# we always need to throw exceptions on an API level
# but if something BELOW breaks, we want to navigate to the error page.
# customers are ready, data is ready, but the handling has a problem.

# make sure to create a customer if necessary
# then update to our apple pay payment method
# and return the new context
$newContext = $this->applePay->prepareCustomer(
$firstname,
$lastname,
$email,
$street,
$zipcode,
$city,
$countryCode,
$phone,
$paymentToken,
$context
);

# we only start our TRY/CATCH here!
# we always need to throw exceptions on an API level
# but if something BELOW breaks, we want to navigate to the error page.
# customers are ready, data is ready, but the handling has a problem.

try {
# create our new Shopware Order
$order = $this->applePay->createOrder($newContext);

Original file line number Diff line number Diff line change
@@ -95,12 +95,11 @@ class ApplePayDirectControllerBase extends AbstractStoreFrontController
* @param OrderService $orderService
* @throws \Exception
*/
public function __construct(ApplePayDirect $applePay, RouterInterface $router, LoggerInterface $logger, CartBackupService $cartBackup, ?FlashBag $sessionFlashBag, FlowBuilderFactory $flowBuilderFactory, FlowBuilderEventFactory $flowBuilderEventFactory, CustomerRepositoryInterface $repoCustomers, OrderService $orderService)
public function __construct(ApplePayDirect $applePay, RouterInterface $router, LoggerInterface $logger, CartBackupService $cartBackup, FlowBuilderFactory $flowBuilderFactory, FlowBuilderEventFactory $flowBuilderEventFactory, CustomerRepositoryInterface $repoCustomers, OrderService $orderService)
{
$this->applePay = $applePay;
$this->router = $router;
$this->logger = $logger;
$this->flashBag = $sessionFlashBag;
$this->cartBackupService = $cartBackup;
$this->repoCustomers = $repoCustomers;
$this->orderService = $orderService;
@@ -332,7 +331,7 @@ public function startPayment(SalesChannelContext $context, Request $request): Re
# we clear our cart backup now
# we are in the user redirection process where a restoring wouldnt make sense
# because from now on we would end on the cart page where we could even switch payment method.
$this->cartBackupService->clearBackup($context);
// $this->cartBackupService->clearBackup($context);


$email = (string)$request->get('email', '');
@@ -375,10 +374,8 @@ public function startPayment(SalesChannelContext $context, Request $request): Re
# if we have an error here, we have to redirect to the confirm page
$returnUrl = $this->getCheckoutConfirmPage($this->router);
# also add an error for our target page
if ($this->flashBag !== null) {
$this->flashBag->add('danger', $this->trans(self::SNIPPET_ERROR));
}

$this->addFlash('danger', $this->trans(self::SNIPPET_ERROR));
return new RedirectResponse($returnUrl);
}
}
@@ -420,10 +417,8 @@ public function finishPayment(SalesChannelContext $context, Request $request): R
# if we have an error here, we have to redirect to the confirm page
$returnUrl = $this->getCheckoutConfirmPage($this->router);
# also add an error for our target page
if ($this->flashBag !== null) {
$this->flashBag->add('danger', $this->trans(self::SNIPPET_ERROR));
}

$this->addFlash('danger', $this->trans(self::SNIPPET_ERROR));
return new RedirectResponse($returnUrl);
}

@@ -463,9 +458,7 @@ public function finishPayment(SalesChannelContext $context, Request $request): R
$returnUrl = $this->getEditOrderPage($order->getId(), $this->router);

# also add an error for our target page
if ($this->flashBag !== null) {
$this->flashBag->add('danger', $this->trans(self::SNIPPET_ERROR));
}
$this->addFlash('danger', $this->trans(self::SNIPPET_ERROR));

# fire our custom storefront event
$this->fireFlowBuilderStorefrontEvent(self::FLOWBUILDER_FAILED, $order, $context->getContext());

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import HttpClient from "./HttpClient";

export default class ApplePaySessionFactory {
/**
*
* @type {number}
*/
APPLE_PAY_VERSION = 3;

constructor() {
this.client = new HttpClient();
}

/**
*
* @param isProductMode
* @param country
* @param currency
* @param shopSlug
* @param withPhone
* @returns {ApplePaySession}
*/
create(isProductMode, country, currency, withPhone, shopSlug) {

const me = this;
var shippingFields = [
'name',
'email',
'postalAddress',
];

if (withPhone === 1) {
shippingFields.push('phone');
}

var request = {
countryCode: country,
currencyCode: currency,
requiredShippingContactFields: shippingFields,
supportedNetworks: [
'amex',
'maestro',
'masterCard',
'visa',
'vPay',
],
merchantCapabilities: ['supports3DS'],
total: {
label: '',
amount: 0,
},
};

// eslint-disable-next-line no-undef
const session = new ApplePaySession(this.APPLE_PAY_VERSION, request);

session.onvalidatemerchant = function (event) {
me.client.post(
shopSlug + '/mollie/apple-pay/validate',
JSON.stringify({
validationUrl: event.validationURL,
}),
(validationData) => {
if (validationData.success === false) {
throw new Error('Validation failed for URL: ' + event.validationURL);
}
const data = JSON.parse(validationData.session);
session.completeMerchantValidation(data);
},
() => {
session.abort();
}
);
};

session.onshippingcontactselected = function (event) {

var countryCode = '';

if (event.shippingContact.countryCode !== undefined) {
countryCode = event.shippingContact.countryCode;
}

me.client.post(
shopSlug + '/mollie/apple-pay/shipping-methods',
JSON.stringify({
countryCode: countryCode,
}),
(data) => {
if (data.success) {
session.completeShippingContactSelection(
// eslint-disable-next-line no-undef
ApplePaySession.STATUS_SUCCESS,
data.shippingmethods,
data.cart.total,
data.cart.items
);
} else {
session.completeShippingContactSelection(
// eslint-disable-next-line no-undef
ApplePaySession.STATUS_FAILURE,
[],
{
label: '',
amount: 0,
pending: true,
},
[]
);
}
},
() => {
session.abort();
}
);
};

session.onshippingmethodselected = function (event) {

me.client.post(
shopSlug + '/mollie/apple-pay/set-shipping',
JSON.stringify({
identifier: event.shippingMethod.identifier,
}),
(data) => {
if (data.success) {
session.completeShippingMethodSelection(
// eslint-disable-next-line no-undef
ApplePaySession.STATUS_SUCCESS,
data.cart.total,
data.cart.items
);
} else {
session.completeShippingMethodSelection(
// eslint-disable-next-line no-undef
ApplePaySession.STATUS_FAILURE,
{
label: '',
amount: 0,
pending: true,
},
[]
);
}
},
() => {
session.abort();
}
);
};

session.onpaymentauthorized = function (event) {
var paymentToken = event.payment.token;
paymentToken = JSON.stringify(paymentToken);

// complete the session and notify the
// devices and the system that everything worked
// eslint-disable-next-line no-undef
session.completePayment(ApplePaySession.STATUS_SUCCESS);

// now finish our payment by filling a form
// and submitting it along with our payment token
me.finishPayment(shopSlug + '/mollie/apple-pay/start-payment', paymentToken, event.payment);
};

session.oncancel = function () {

// if we are in product mode
// we should restore our original cart
if (isProductMode) {
me.client.post(shopSlug + '/mollie/apple-pay/restore-cart');
}
};

return session;
}

/**
*
* @param checkoutURL
* @param paymentToken
* @param payment
*/
finishPayment(checkoutURL, paymentToken, payment) {
const createInput = function (name, val) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = val;

return input;
}

const form = document.createElement('form');
form.action = checkoutURL;
form.method = 'POST';

let street = payment.shippingContact.addressLines[0];

if (payment.shippingContact.addressLines.length > 1) {
street += ' ' + payment.shippingContact.addressLines[1];
}

// add billing data
form.insertAdjacentElement('beforeend', createInput('email', payment.shippingContact.emailAddress));
form.insertAdjacentElement('beforeend', createInput('lastname', payment.shippingContact.familyName));
form.insertAdjacentElement('beforeend', createInput('firstname', payment.shippingContact.givenName));
form.insertAdjacentElement('beforeend', createInput('street', street));
form.insertAdjacentElement('beforeend', createInput('postalCode', payment.shippingContact.postalCode));
form.insertAdjacentElement('beforeend', createInput('city', payment.shippingContact.locality));

if (payment.shippingContact.phoneNumber !== undefined && payment.shippingContact.phoneNumber.length > 0) {
form.insertAdjacentElement('beforeend', createInput('phone', payment.shippingContact.phoneNumber));
}

form.insertAdjacentElement('beforeend', createInput('countryCode', payment.shippingContact.countryCode));
// also add our payment token
form.insertAdjacentElement('beforeend', createInput('paymentToken', paymentToken));

document.body.insertAdjacentElement('beforeend', form);

form.submit();
}
}
7 changes: 7 additions & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
@@ -231,6 +231,13 @@
<argument type="service" id="Shopware\Core\Content\Mail\Service\MailSender"/>
</service>

<service id="Kiener\MolliePayments\Checkout\Cart\ExpressCartItemAddRoute" decorates="Shopware\Core\Checkout\Cart\SalesChannel\CartItemAddRoute" public="true">
<argument type="service" id=".inner"/>
<argument type="service" id="Kiener\MolliePayments\Service\Cart\CartBackupService"/>
<argument type="service" id="Kiener\MolliePayments\Service\CartService"/>
</service>


</services>

</container>
1 change: 0 additions & 1 deletion src/Resources/config/services/controller.xml
Original file line number Diff line number Diff line change
@@ -182,7 +182,6 @@
<argument type="service" id="router"/>
<argument type="service" id="mollie_payments.logger"/>
<argument type="service" id="Kiener\MolliePayments\Service\Cart\CartBackupService"/>
<argument type="service" id="session.flash_bag" on-invalid="null"/>
<argument type="service" id="Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderFactory"/>
<argument type="service"
id="Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderEventFactory"/>
6 changes: 6 additions & 0 deletions src/Resources/config/services/subscriber.xml
Original file line number Diff line number Diff line change
@@ -71,5 +71,11 @@
<tag name="kernel.event_subscriber" />
</service>

<service id="Kiener\MolliePayments\Subscriber\ExpressCartRestoreSubscriber">
<argument type="service" id="Kiener\MolliePayments\Service\Cart\CartBackupService"/>
<tag name="kernel.event_subscriber" />
</service>


</services>
</container>
43 changes: 43 additions & 0 deletions src/Subscriber/ExpressCartRestoreSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Subscriber;

use Kiener\MolliePayments\Service\Cart\CartBackupService;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Register\CheckoutRegisterPageLoadedEvent;
use Shopware\Storefront\Page\PageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ExpressCartRestoreSubscriber implements EventSubscriberInterface
{
/**
* @var CartBackupService
*/
private $cartBackupService;

public function __construct(CartBackupService $cartBackupService)
{
$this->cartBackupService = $cartBackupService;
}

public static function getSubscribedEvents(): array
{
return [
CheckoutFinishPageLoadedEvent::class => 'onRestoreBackup',
CheckoutConfirmPageLoadedEvent::class => 'onRestoreBackup',
CheckoutRegisterPageLoadedEvent::class => 'onRestoreBackup'
];
}

public function onRestoreBackup(PageLoadedEvent $event): void
{
$context = $event->getSalesChannelContext();

if ($this->cartBackupService->isBackupExisting($context)) {
$this->cartBackupService->restoreCart($context);
$this->cartBackupService->clearBackup($context);
}
}
}