diff --git a/src/Components/ApplePayDirect/ApplePayDirect.php b/src/Components/ApplePayDirect/ApplePayDirect.php index 4f2670bde..46e7c38c7 100644 --- a/src/Components/ApplePayDirect/ApplePayDirect.php +++ b/src/Components/ApplePayDirect/ApplePayDirect.php @@ -31,6 +31,7 @@ use Shopware\Core\Checkout\Order\OrderEntity; use Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct; use Shopware\Core\Framework\Validation\DataBag\DataBag; +use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; class ApplePayDirect @@ -457,7 +458,7 @@ public function createPayment(OrderEntity $order, string $shopwareReturnUrl, str # This is required for a smooth checkout with our already validated Apple Pay transaction. $this->paymentHandler->setToken($paymentToken); - $paymentData = $this->molliePayments->startMolliePayment(ApplePayPayment::PAYMENT_METHOD_NAME, $asyncPaymentTransition, $context, $this->paymentHandler); + $paymentData = $this->molliePayments->startMolliePayment(ApplePayPayment::PAYMENT_METHOD_NAME, $asyncPaymentTransition, $context, $this->paymentHandler, new RequestDataBag()); # now also update the custom fields of our order # we want to have the mollie metadata in the diff --git a/src/Facade/MolliePaymentDoPay.php b/src/Facade/MolliePaymentDoPay.php index 171d97b7d..3547ee2df 100644 --- a/src/Facade/MolliePaymentDoPay.php +++ b/src/Facade/MolliePaymentDoPay.php @@ -30,6 +30,7 @@ use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection; use Shopware\Core\Checkout\Order\OrderEntity; use Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct; +use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Twig\Environment; use Twig\Extension\CoreExtension; @@ -135,10 +136,11 @@ public function __construct(OrderDataExtractor $extractor, MollieOrderBuilder $o * @param AsyncPaymentTransactionStruct $transactionStruct * @param SalesChannelContext $salesChannelContext * @param PaymentHandler $paymentHandler + * @param RequestDataBag $dataBag * @throws ApiException * @return MolliePaymentPrepareData */ - public function startMolliePayment(string $paymentMethod, AsyncPaymentTransactionStruct $transactionStruct, SalesChannelContext $salesChannelContext, PaymentHandler $paymentHandler): MolliePaymentPrepareData + public function startMolliePayment(string $paymentMethod, AsyncPaymentTransactionStruct $transactionStruct, SalesChannelContext $salesChannelContext, PaymentHandler $paymentHandler, RequestDataBag $dataBag): MolliePaymentPrepareData { $settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannelId()); @@ -172,6 +174,16 @@ public function startMolliePayment(string $paymentMethod, AsyncPaymentTransactio $mollieOrderId = $orderCustomFields->getMollieOrderId(); + $bancomatPayPhoneNumber = $dataBag->get('mollieBancomatPayPhone'); + + if ($bancomatPayPhoneNumber !== null) { + ## we need to pass the custom fields now, so we can use them in create order and display the number on failed orders + $orderCustomFields->setBancomatPayPhoneNumber($bancomatPayPhoneNumber); + $order->setCustomFields($orderCustomFields->toArray()); + $this->updaterOrderCustomFields->updateOrder($order->getId(), $orderCustomFields, $salesChannelContext->getContext()); + } + + # now let's check if we have another payment attempt for an existing order. # this is the case, if we already have a Mollie Order ID in our custom fields. # in this case, we just add a new payment (transaction) to the existing order in Mollie. @@ -211,6 +223,7 @@ public function startMolliePayment(string $paymentMethod, AsyncPaymentTransactio # We just try to create the customer before we create the actual order. $this->createCustomerAtMollie($order, $salesChannelContext); + # let's create our real Mollie order # for this payment in Shopware. $molliePaymentData = $this->createMollieOrder($order, $paymentMethod, $transactionStruct, $salesChannelContext, $paymentHandler); @@ -232,6 +245,7 @@ public function startMolliePayment(string $paymentMethod, AsyncPaymentTransactio $orderCustomFields->setSubscriptionData($subscriptionId, ''); } + /** * @var OrderLineItemCollection $orderLineItems */ diff --git a/src/Handler/Method/BancomatPayment.php b/src/Handler/Method/BancomatPayment.php new file mode 100644 index 000000000..05f05ac12 --- /dev/null +++ b/src/Handler/Method/BancomatPayment.php @@ -0,0 +1,33 @@ + $orderData + * @param OrderEntity $orderEntity + * @param SalesChannelContext $salesChannelContext + * @param CustomerEntity $customer + * @return array + */ + public function processPaymentMethodSpecificParameters(array $orderData, OrderEntity $orderEntity, SalesChannelContext $salesChannelContext, CustomerEntity $customer): array + { + $orderCustomFields = new OrderAttributes($orderEntity); + $orderData['billingAddress']['phone'] = $orderCustomFields->getBancomatPayPhoneNumber(); + return $orderData; + } +} diff --git a/src/Handler/PaymentHandler.php b/src/Handler/PaymentHandler.php index 2d95b9c07..0247d07c8 100644 --- a/src/Handler/PaymentHandler.php +++ b/src/Handler/PaymentHandler.php @@ -118,7 +118,8 @@ public function pay(AsyncPaymentTransactionStruct $transaction, RequestDataBag $ $this->paymentMethod, $transaction, $salesChannelContext, - $this + $this, + $dataBag ); $paymentUrl = $paymentData->getCheckoutURL(); diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/bancomat-plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/bancomat-plugin.js new file mode 100644 index 000000000..b08b04aed --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/bancomat-plugin.js @@ -0,0 +1,35 @@ +import Plugin from '@shopware-storefront-sdk/plugin-system/plugin.class'; + +export default class MollieBancomatPlugin extends Plugin { + + init() { + + const phoneField = document.getElementById('mollieBancomatPayPhone'); + if (phoneField === null) { + + return; + } + + const inputFieldWrapper = document.querySelector('.mollie-bancomat-pay'); + + const errorMessageElement = document.querySelector('.mollie-bancomat-pay [data-form-validation-invalid-phone="true"]'); + + phoneField.addEventListener('focus',(e)=>{ + inputFieldWrapper.classList.add('was-validated'); + e.target.removeAttribute('invalid'); + errorMessageElement.classList.add('d-none'); + }); + + + phoneField.addEventListener('blur', (e) => { + const form = e.target.form; + if(form.reportValidity() === false){ + e.target.setAttribute('invalid',true); + errorMessageElement.classList.remove('d-none'); + } + return form.reportValidity(); + + }) + + } +} \ No newline at end of file diff --git a/src/Resources/app/storefront/src/register.js b/src/Resources/app/storefront/src/register.js index 9de86e174..61abbca7f 100644 --- a/src/Resources/app/storefront/src/register.js +++ b/src/Resources/app/storefront/src/register.js @@ -5,6 +5,7 @@ import MollieApplePayDirect from './mollie-payments/plugins/apple-pay-direct.plu import MollieApplePayPaymentMethod from './mollie-payments/plugins/apple-pay-payment-method.plugin'; import MollieCreditCardMandateManage from './mollie-payments/plugins/creditcard-mandate-manage.plugin'; import MolliePosTerminalPlugin from './mollie-payments/plugins/pos-terminal.plugin'; +import MollieBancomatPlugin from './mollie-payments/plugins/bancomat-plugin'; export default class MolliRegistration { @@ -42,6 +43,8 @@ export default class MolliRegistration { // POS Terminal // ----------------------------------------------------------------------------- pluginManager.register('MolliePosTerminal', MolliePosTerminalPlugin, '[data-mollie-template-pos-terminal]'); + + pluginManager.register('MollieBancomatPlugin',MollieBancomatPlugin); } } diff --git a/src/Resources/app/storefront/src/scss/checkout/payment-selection.scss b/src/Resources/app/storefront/src/scss/checkout/payment-selection.scss index 6536b54ca..e9000d92f 100644 --- a/src/Resources/app/storefront/src/scss/checkout/payment-selection.scss +++ b/src/Resources/app/storefront/src/scss/checkout/payment-selection.scss @@ -9,6 +9,10 @@ margin-top: 15px; } + .mollie-bancomat-pay{ + margin-top:15px; + } + .mollie-pos-terminals { margin-top: 15px; } @@ -27,6 +31,10 @@ } } + .mollie-bancomat-pay{ + margin-top:15px; + } + .mollie-pos-terminals { margin-top: 15px; diff --git a/src/Resources/config/services/handlers.xml b/src/Resources/config/services/handlers.xml index 68ff045d9..dd3e77fea 100644 --- a/src/Resources/config/services/handlers.xml +++ b/src/Resources/config/services/handlers.xml @@ -190,6 +190,13 @@ + + + + + + + diff --git a/src/Resources/snippet/de_DE/mollie-payments.de-DE.json b/src/Resources/snippet/de_DE/mollie-payments.de-DE.json index bd1bd667e..a25f8be5d 100644 --- a/src/Resources/snippet/de_DE/mollie-payments.de-DE.json +++ b/src/Resources/snippet/de_DE/mollie-payments.de-DE.json @@ -43,6 +43,10 @@ "deleteButton": "Löschen", "deleteConfirmation": "Sind Sie sicher, dass Sie die Zahlart löschen möchten?", "title": "Bestätigung zum Löschen" + }, + "bancomatPay": { + "headLine": "Telefonnummer", + "phoneErrorMessage": "Die Telefonnummer ist erforderlich. Zum Beispiel +31208202070" } }, "messages": { diff --git a/src/Resources/snippet/en_GB/mollie-payments.en-GB.json b/src/Resources/snippet/en_GB/mollie-payments.en-GB.json index d47fd4a9e..c90c86622 100644 --- a/src/Resources/snippet/en_GB/mollie-payments.en-GB.json +++ b/src/Resources/snippet/en_GB/mollie-payments.en-GB.json @@ -43,6 +43,10 @@ "deleteButton": "Delete", "deleteConfirmation": "Are you sure you want to delete?", "title": "Delete Confirmation" + }, + "bancomatPay": { + "headLine": "Phone number", + "phoneErrorMessage": "Phone Number is required. For example +31208202070" } }, "messages": { diff --git a/src/Resources/snippet/nl_NL/mollie-payments.nl-NL.json b/src/Resources/snippet/nl_NL/mollie-payments.nl-NL.json index a8ca26978..e73d3d682 100644 --- a/src/Resources/snippet/nl_NL/mollie-payments.nl-NL.json +++ b/src/Resources/snippet/nl_NL/mollie-payments.nl-NL.json @@ -43,6 +43,10 @@ "deleteButton": "Verwijderen", "deleteConfirmation": "Weet je zeker dat je dat wilt verwijderen?", "title": "Bevestiging verwijderen" + }, + "bancomatPay": { + "headLine": "Telefoonnummer", + "phoneErrorMessage": "Telefoonnummer is vereist. Bijvoorbeeld +31208202070" } }, "messages": { diff --git a/src/Resources/views/storefront/component/payment/component/bancomat-fields.html.twig b/src/Resources/views/storefront/component/payment/component/bancomat-fields.html.twig new file mode 100644 index 000000000..070bfe636 --- /dev/null +++ b/src/Resources/views/storefront/component/payment/component/bancomat-fields.html.twig @@ -0,0 +1,17 @@ +{% if context.paymentMethod.translated.customFields.mollie_payment_method_name == 'bancomatpay' %} +
+ + + + + {{ "molliePayments.components.bancomatPay.phoneErrorMessage"|trans|striptags }} + + +
+{% endif %} \ No newline at end of file diff --git a/src/Resources/views/storefront/component/payment/payment-fields.html.twig b/src/Resources/views/storefront/component/payment/payment-fields.html.twig index a9b5d6eb7..48cd7d061 100644 --- a/src/Resources/views/storefront/component/payment/payment-fields.html.twig +++ b/src/Resources/views/storefront/component/payment/payment-fields.html.twig @@ -35,5 +35,7 @@ {% sw_include '@MolliePayments/storefront/component/payment/component/ideal-fields.html.twig' %} {% elseif payment.translated.customFields.mollie_payment_method_name == 'pointofsale' %} {% sw_include '@MolliePayments/storefront/component/payment/component/pos-fields.html.twig' %} + {% elseif payment.translated.customFields.mollie_payment_method_name == 'bancomatpay' %} + {% sw_include '@MolliePayments/storefront/component/payment/component/bancomat-fields.html.twig' %} {% endif %} {% endblock %} diff --git a/src/Resources/views/storefront/component/payment/payment-method.html.twig b/src/Resources/views/storefront/component/payment/payment-method.html.twig index cc2b689dc..4ea8b365b 100644 --- a/src/Resources/views/storefront/component/payment/payment-method.html.twig +++ b/src/Resources/views/storefront/component/payment/payment-method.html.twig @@ -41,5 +41,7 @@ {% sw_include '@MolliePayments/storefront/component/payment/component/ideal-fields.html.twig' %} {% elseif payment.translated.customFields.mollie_payment_method_name == 'pointofsale' %} {% sw_include '@MolliePayments/storefront/component/payment/component/pos-fields.html.twig' %} + {% elseif payment.translated.customFields.mollie_payment_method_name == 'bancomatpay' %} + {% sw_include '@MolliePayments/storefront/component/payment/component/bancomat-fields.html.twig' %} {% endif %} {% endblock %} diff --git a/src/Service/PaymentMethodService.php b/src/Service/PaymentMethodService.php index 9ea8e9b8f..ee11f6733 100644 --- a/src/Service/PaymentMethodService.php +++ b/src/Service/PaymentMethodService.php @@ -4,6 +4,7 @@ use Kiener\MolliePayments\Compatibility\VersionCompare; use Kiener\MolliePayments\Handler\Method\ApplePayPayment; +use Kiener\MolliePayments\Handler\Method\BancomatPayment; use Kiener\MolliePayments\Handler\Method\BanContactPayment; use Kiener\MolliePayments\Handler\Method\BankTransferPayment; use Kiener\MolliePayments\Handler\Method\BelfiusPayment; @@ -441,6 +442,7 @@ public function getPaymentHandlers(): array PosPayment::class, TwintPayment::class, BlikPayment::class, + BancomatPayment::class, // IngHomePayPayment::class, // not allowed anymore // DirectDebitPayment::class, // only allowed when updating subsriptions, aka => not allowed anymore ]; diff --git a/src/Struct/Order/OrderAttributes.php b/src/Struct/Order/OrderAttributes.php index a9f0375b1..eca8bad18 100644 --- a/src/Struct/Order/OrderAttributes.php +++ b/src/Struct/Order/OrderAttributes.php @@ -104,6 +104,11 @@ class OrderAttributes */ private $bankBic; + /** + * @var string + */ + private $bancomatPayPhoneNumber; + /** * @param OrderEntity $order */ @@ -128,6 +133,7 @@ public function __construct(OrderEntity $order) $this->bankAccount = $this->getCustomFieldValue($order, 'bankAccount'); $this->bankBic = $this->getCustomFieldValue($order, 'bankBic'); $this->timezone = $this->getCustomFieldValue($order, 'timezone'); + $this->bancomatPayPhoneNumber = $this->getCustomFieldValue($order, 'bancomatPayPhoneNumber'); } /** @@ -444,6 +450,24 @@ public function setBankTransferDetails(?stdClass $details) } } + /** + * @return string + */ + public function getBancomatPayPhoneNumber(): string + { + return $this->bancomatPayPhoneNumber; + } + + /** + * @param string $bancomatPayPhoneNumber + * @return void + */ + public function setBancomatPayPhoneNumber(string $bancomatPayPhoneNumber): void + { + $this->bancomatPayPhoneNumber = $bancomatPayPhoneNumber; + } + + /** * @return array */ @@ -528,6 +552,9 @@ public function toArray(): array $mollieData['bankBic'] = $this->bankBic; } + if ($this->bancomatPayPhoneNumber !== '') { + $mollieData['bancomatPayPhoneNumber'] = $this->bancomatPayPhoneNumber; + } return [ 'mollie_payments' => $mollieData, ]; diff --git a/tests/Cypress/cypress/e2e/storefront/payment-methods/bancomat.cy.js b/tests/Cypress/cypress/e2e/storefront/payment-methods/bancomat.cy.js new file mode 100644 index 000000000..33f0b75d3 --- /dev/null +++ b/tests/Cypress/cypress/e2e/storefront/payment-methods/bancomat.cy.js @@ -0,0 +1,41 @@ +import Devices from "Services/utils/Devices"; +import Session from "Services/utils/Session" +// ------------------------------------------------------ +import PaymentAction from "Actions/storefront/checkout/PaymentAction"; +import DummyBasketScenario from "Scenarios/DummyBasketScenario"; +// ------------------------------------------------------ + + +const devices = new Devices(); +const session = new Session(); + +const paymentAction = new PaymentAction(); +const scenarioDummyBasket = new DummyBasketScenario(1); + +const device = devices.getFirstDevice(); + + +describe('Bancomat Pay', () => { + + context(devices.getDescription(device), () => { + + before(function () { + devices.setDevice(device); + }) + + beforeEach(() => { + session.resetBrowserSession(); + devices.setDevice(device); + }); + + it('C2775016: Bancomat Pay is existing in checkout', () => { + + scenarioDummyBasket.execute(); + + paymentAction.switchPaymentMethod('Bancomat Pay'); + }) + + }) + +}) + diff --git a/tests/PHPUnit/Service/MollieApi/Builder/Payments/BancomatOrderBuilderTest.php b/tests/PHPUnit/Service/MollieApi/Builder/Payments/BancomatOrderBuilderTest.php new file mode 100644 index 000000000..bf7c1fffe --- /dev/null +++ b/tests/PHPUnit/Service/MollieApi/Builder/Payments/BancomatOrderBuilderTest.php @@ -0,0 +1,76 @@ +router->method('generate')->willReturn($redirectWebhookUrl); + + $this->paymentHandler = new BancomatPayment( + $this->loggerService, + new FakeContainer() + ); + + $transactionId = Uuid::randomHex(); + $amountTotal = 27.0; + $taxStatus = CartPrice::TAX_STATE_GROSS; + $currencyISO = 'EUR'; + + $currency = new CurrencyEntity(); + $currency->setId(Uuid::randomHex()); + $currency->setIsoCode($currencyISO); + + $orderNumber = 'foo number'; + $lineItems = $this->getDummyLineItems(); + + $order = $this->getOrderEntity($amountTotal, $taxStatus, $currencyISO, $lineItems, $orderNumber); + + $phoneNumber = '+1234567'; + $orderAttributes = new OrderAttributes($order); + $orderAttributes->setBancomatPayPhoneNumber($phoneNumber); + $order->setCustomFields($orderAttributes->toArray()); + + $actual = $this->builder->buildOrderPayload($order, $transactionId, $this->paymentHandler::PAYMENT_METHOD_NAME, $this->salesChannelContext, $this->paymentHandler, []); + + $expectedOrderLifeTime = (new DateTime())->setTimezone(new DateTimeZone('UTC')) + ->modify(sprintf('+%d day', $this->expiresAt)) + ->format('Y-m-d'); + + $expectedBillingAddress = $this->getExpectedTestAddress($this->address, $this->email); + $expectedBillingAddress['phone'] = $phoneNumber; + + $expected = [ + 'amount' => (new MollieOrderPriceBuilder())->build($amountTotal, $currencyISO), + 'locale' => $this->localeCode, + 'method' => $this->paymentHandler::PAYMENT_METHOD_NAME, + 'orderNumber' => $orderNumber, + 'payment' => ['webhookUrl' => $redirectWebhookUrl], + 'redirectUrl' => $redirectWebhookUrl, + 'webhookUrl' => $redirectWebhookUrl, + 'lines' => $this->getExpectedLineItems($taxStatus, $lineItems, $currency), + 'billingAddress' => $expectedBillingAddress, + 'shippingAddress' => $this->getExpectedTestAddress($this->address, $this->email), + 'expiresAt' => $expectedOrderLifeTime + ]; + + self::assertSame($expected, $actual); + } +} diff --git a/tests/PHPUnit/Service/PaymentMethodServiceTest.php b/tests/PHPUnit/Service/PaymentMethodServiceTest.php index f7796943f..84a932984 100644 --- a/tests/PHPUnit/Service/PaymentMethodServiceTest.php +++ b/tests/PHPUnit/Service/PaymentMethodServiceTest.php @@ -3,6 +3,7 @@ namespace Kiener\MolliePayments\Tests\Service; use Kiener\MolliePayments\Handler\Method\ApplePayPayment; +use Kiener\MolliePayments\Handler\Method\BancomatPayment; use Kiener\MolliePayments\Handler\Method\BanContactPayment; use Kiener\MolliePayments\Handler\Method\BankTransferPayment; use Kiener\MolliePayments\Handler\Method\BelfiusPayment; @@ -131,6 +132,7 @@ public function testSupportedMethods(): void PosPayment::class, TwintPayment::class, BlikPayment::class, + BancomatPayment::class, ]; $handlers = $this->paymentMethodService->getPaymentHandlers();