diff --git a/assets/admin/js/test_payment_method_connection.js b/assets/admin/js/test_payment_method_connection.js index 1f101703..9b185a0e 100644 --- a/assets/admin/js/test_payment_method_connection.js +++ b/assets/admin/js/test_payment_method_connection.js @@ -91,13 +91,6 @@ function convertTpayChannelIdInputIntoSelect(channels) { tpayChannelIdFormType.innerHTML = ''; - const displayAllOption = document.createElement('option'); - displayAllOption.value = ''; - console.log(tpayChannelIdFormType.dataset); - displayAllOption.text = tpayChannelIdFormType.dataset.displayAllLabel; - - tpayChannelIdFormType.appendChild(displayAllOption); - for (const [id, name] of Object.entries(channels)) { const option = document.createElement('option'); option.value = id; diff --git a/config/config/sylius_fixtures.php b/config/config/sylius_fixtures.php index 08814a06..ebe78f4b 100644 --- a/config/config/sylius_fixtures.php +++ b/config/config/sylius_fixtures.php @@ -119,9 +119,9 @@ ], 'pbl_channel' => [ 'code' => 'tpay_pbl_channel', - 'name' => 'Pay-by-link one channel (Tpay)', - 'gatewayFactory' => 'tpay_pbl', - 'gatewayName' => 'tpay_pbl', + 'name' => 'Pay-by-link single channel (Tpay)', + 'gatewayFactory' => 'tpay_pbl_channel', + 'gatewayName' => 'tpay_pbl_channel', 'gatewayConfig' => $tpayConfig + [ 'tpay_channel_id' => '%env(string:TPAY_PBL_CHANNEL_ID)%', ], diff --git a/config/config/sylius_template_events.php b/config/config/sylius_template_events.php index f2737518..74e3894f 100644 --- a/config/config/sylius_template_events.php +++ b/config/config/sylius_template_events.php @@ -41,6 +41,10 @@ 'template' => '@CommerceWeaversSyliusTpayPlugin/shop/order/pay/_payByLink.html.twig', 'priority' => 10, ], + 'pay_by_link_channel' => [ + 'template' => '@CommerceWeaversSyliusTpayPlugin/shop/order/pay/_payByLinkChannel.html.twig', + 'priority' => 10, + ], 'google_pay' => [ 'template' => '@CommerceWeaversSyliusTpayPlugin/shop/order/pay/_google_pay_regulations.html.twig', 'priority' => 5, @@ -69,6 +73,10 @@ 'template' => '@CommerceWeaversSyliusTpayPlugin/shop/cart/complete/_payByLink.html.twig', 'priority' => 5, ], + 'pay_by_link_channel' => [ + 'template' => '@CommerceWeaversSyliusTpayPlugin/shop/cart/complete/_payByLinkChannel.html.twig', + 'priority' => 5, + ], 'visa_mobile' => [ 'template' => '@CommerceWeaversSyliusTpayPlugin/shop/cart/complete/_visaMobile.html.twig', 'priority' => 5, diff --git a/config/services/pay_by_link_channel_payment/form.php b/config/services/pay_by_link_channel_payment/form.php new file mode 100644 index 00000000..a650bf3e --- /dev/null +++ b/config/services/pay_by_link_channel_payment/form.php @@ -0,0 +1,9 @@ +import('form/**/*.php'); +}; diff --git a/config/services/pay_by_link_channel_payment/form/type.php b/config/services/pay_by_link_channel_payment/form/type.php new file mode 100644 index 00000000..3ccf51df --- /dev/null +++ b/config/services/pay_by_link_channel_payment/form/type.php @@ -0,0 +1,22 @@ +services(); + + $services->set('commerce_weavers_sylius_tpay.pay_by_link_channel_payment.form.type.gateway_configuration', GatewayConfigurationType::class) + ->args([ + service('translator'), + ]) + ->parent('commerce_weavers_sylius_tpay.form.type.abstract_tpay_gateway_configuration') + ->tag('sylius.gateway_configuration_type', ['label' => 'commerce_weavers_sylius_tpay.admin.gateway_name.tpay_channel_pbl', 'type' => GatewayFactory::NAME]) + ->tag('form.type') + ; +}; diff --git a/config/services/pay_by_link_channel_payment/payum.php b/config/services/pay_by_link_channel_payment/payum.php new file mode 100644 index 00000000..cbc99101 --- /dev/null +++ b/config/services/pay_by_link_channel_payment/payum.php @@ -0,0 +1,9 @@ +import('payum/**/*.php'); +}; diff --git a/config/services/pay_by_link_channel_payment/payum/action.php b/config/services/pay_by_link_channel_payment/payum/action.php new file mode 100644 index 00000000..1702d5c8 --- /dev/null +++ b/config/services/pay_by_link_channel_payment/payum/action.php @@ -0,0 +1,24 @@ +services(); + + $services->defaults() + ->public() + ; + + $services->set(CreatePayByLinkTransactionAction::class) + ->args([ + service('commerce_weavers_sylius_tpay.tpay.factory.create_pay_by_link_payment_payload'), + service('commerce_weavers_sylius_tpay.payum.factory.token.notify'), + ]) + ->tag('payum.action', ['factory' => GatewayFactory::NAME, 'alias' => 'cw.tpay_pbl.create_pay_by_link_channel_transaction']) + ; +}; diff --git a/config/services/pay_by_link_channel_payment/payum/factory.php b/config/services/pay_by_link_channel_payment/payum/factory.php new file mode 100644 index 00000000..2bd1b7b0 --- /dev/null +++ b/config/services/pay_by_link_channel_payment/payum/factory.php @@ -0,0 +1,19 @@ +services(); + + $services->set('commerce_weavers_sylius_tpay.pay_by_link_channel_payment.payum.factory.gateway', GatewayFactoryBuilder::class) + ->args([ + GatewayFactory::class, + ]) + ->tag('payum.gateway_factory_builder', ['factory' => GatewayFactory::NAME]) + ; +}; diff --git a/config/services/pay_by_link_payment/form/type.php b/config/services/pay_by_link_payment/form/type.php index ee5ac4fc..41645abd 100644 --- a/config/services/pay_by_link_payment/form/type.php +++ b/config/services/pay_by_link_payment/form/type.php @@ -12,9 +12,6 @@ $services = $container->services(); $services->set('commerce_weavers_sylius_tpay.pay_by_link_payment.form.type.gateway_configuration', GatewayConfigurationType::class) - ->args([ - service('translator'), - ]) ->parent('commerce_weavers_sylius_tpay.form.type.abstract_tpay_gateway_configuration') ->tag('sylius.gateway_configuration_type', ['label' => 'commerce_weavers_sylius_tpay.admin.gateway_name.tpay_pbl', 'type' => GatewayFactory::NAME]) ->tag('form.type') diff --git a/config/services/payum/action.php b/config/services/payum/action.php index 39a55e7b..52a92fd2 100644 --- a/config/services/payum/action.php +++ b/config/services/payum/action.php @@ -9,6 +9,7 @@ use CommerceWeavers\SyliusTpayPlugin\CardPayment\Payum\Factory\GatewayFactory as CardGatewayFactory; use CommerceWeavers\SyliusTpayPlugin\GooglePayPayment\Payum\Factory\GatewayFactory as GooglePayGatewayFactory; use CommerceWeavers\SyliusTpayPlugin\PayByLinkPayment\Payum\Factory\GatewayFactory as PayByLinkGatewayFactory; +use CommerceWeavers\SyliusTpayPlugin\PayByLinkChannelPayment\Payum\Factory\GatewayFactory as PayByLinkChannelGatewayFactory; use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\NotifyAction; use CommerceWeavers\SyliusTpayPlugin\Payum\Action\CaptureAction; use CommerceWeavers\SyliusTpayPlugin\Payum\Action\GetStatusAction; @@ -35,6 +36,7 @@ ->tag('payum.action', ['factory' => CardGatewayFactory::NAME, 'alias' => 'cw.tpay_card.capture']) ->tag('payum.action', ['factory' => GooglePayGatewayFactory::NAME, 'alias' => 'cw.tpay_google_pay.capture']) ->tag('payum.action', ['factory' => PayByLinkGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl.capture']) + ->tag('payum.action', ['factory' => PayByLinkChannelGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl_channel.capture']) ->tag('payum.action', ['factory' => RedirectGatewayFactory::NAME, 'alias' => 'cw.tpay_redirect.capture']) ->tag('payum.action', ['factory' => TpayGatewayFactory::NAME, 'alias' => 'cw.tpay.capture']) ; @@ -50,6 +52,7 @@ ->tag('payum.action', ['factory' => CardGatewayFactory::NAME, 'alias' => 'cw.tpay_card.notify']) ->tag('payum.action', ['factory' => GooglePayGatewayFactory::NAME, 'alias' => 'cw.tpay_google_pay.notify']) ->tag('payum.action', ['factory' => PayByLinkGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl.notify']) + ->tag('payum.action', ['factory' => PayByLinkChannelGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl_channel.notify']) ->tag('payum.action', ['factory' => RedirectGatewayFactory::NAME, 'alias' => 'cw.tpay_redirect.notify']) ->tag('payum.action', ['factory' => TpayGatewayFactory::NAME, 'alias' => 'cw.tpay.notify']) ; @@ -60,6 +63,7 @@ ->tag('payum.action', ['factory' => CardGatewayFactory::NAME, 'alias' => 'cw.tpay_card.get_status']) ->tag('payum.action', ['factory' => GooglePayGatewayFactory::NAME, 'alias' => 'cw.tpay_google_pay.get_status']) ->tag('payum.action', ['factory' => PayByLinkGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl.get_status']) + ->tag('payum.action', ['factory' => PayByLinkChannelGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl_channel.get_status']) ->tag('payum.action', ['factory' => RedirectGatewayFactory::NAME, 'alias' => 'cw.tpay_redirect.get_status']) ->tag('payum.action', ['factory' => TpayGatewayFactory::NAME, 'alias' => 'cw.tpay.get_status']) ; @@ -70,6 +74,7 @@ ->tag('payum.action', ['factory' => CardGatewayFactory::NAME, 'alias' => 'cw.tpay_card.partial_refund']) ->tag('payum.action', ['factory' => GooglePayGatewayFactory::NAME, 'alias' => 'cw.tpay_google_pay.partial_refund']) ->tag('payum.action', ['factory' => PayByLinkGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl.partial_refund']) + ->tag('payum.action', ['factory' => PayByLinkChannelGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl_channel.partial_refund']) ->tag('payum.action', ['factory' => RedirectGatewayFactory::NAME, 'alias' => 'cw.tpay_redirect.partial_refund']) ->tag('payum.action', ['factory' => TpayGatewayFactory::NAME, 'alias' => 'cw.tpay.partial_refund']) ; @@ -80,6 +85,7 @@ ->tag('payum.action', ['factory' => CardGatewayFactory::NAME, 'alias' => 'cw.tpay_card.refund']) ->tag('payum.action', ['factory' => GooglePayGatewayFactory::NAME, 'alias' => 'cw.tpay_google_pay.refund']) ->tag('payum.action', ['factory' => PayByLinkGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl.refund']) + ->tag('payum.action', ['factory' => PayByLinkChannelGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl_channel.refund']) ->tag('payum.action', ['factory' => RedirectGatewayFactory::NAME, 'alias' => 'cw.tpay_redirect.refund']) ->tag('payum.action', ['factory' => TpayGatewayFactory::NAME, 'alias' => 'cw.tpay.refund']) ; @@ -90,6 +96,7 @@ ->tag('payum.action', ['factory' => CardGatewayFactory::NAME, 'alias' => 'cw.tpay_card.resolve_next_route']) ->tag('payum.action', ['factory' => GooglePayGatewayFactory::NAME, 'alias' => 'cw.tpay_google_pay.resolve_next_route']) ->tag('payum.action', ['factory' => PayByLinkGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl.resolve_next_route']) + ->tag('payum.action', ['factory' => PayByLinkChannelGatewayFactory::NAME, 'alias' => 'cw.tpay_pbl_channel.resolve_next_route']) ->tag('payum.action', ['factory' => RedirectGatewayFactory::NAME, 'alias' => 'cw.tpay_redirect.resolve_next_route']) ->tag('payum.action', ['factory' => TpayGatewayFactory::NAME, 'alias' => 'cw.tpay.resolve_next_route']) ; diff --git a/migrations/Version20250120115438.php b/migrations/Version20250120115438.php new file mode 100644 index 00000000..eb47b8ce --- /dev/null +++ b/migrations/Version20250120115438.php @@ -0,0 +1,43 @@ +addSql(" + UPDATE sylius_gateway_config + SET + gateway_name = 'tpay_pbl_channel', + factory_name = 'tpay_pbl_channel' + WHERE gateway_name = 'tpay_pbl' + AND JSON_EXTRACT(config, '$.tpay_channel_id') IS NOT NULL + "); + } + + public function down(Schema $schema): void + { + $this->addSql(" + UPDATE sylius_gateway_config + SET + gateway_name = 'tpay_pbl', + factory_name = 'tpay_pbl' + WHERE gateway_name = 'tpay_pbl_channel' + AND JSON_EXTRACT(config, '$.tpay_channel_id') IS NOT NULL + "); + } +} diff --git a/src/PayByLinkChannelPayment/Form/Type/GatewayConfigurationType.php b/src/PayByLinkChannelPayment/Form/Type/GatewayConfigurationType.php new file mode 100644 index 00000000..70ca58e5 --- /dev/null +++ b/src/PayByLinkChannelPayment/Form/Type/GatewayConfigurationType.php @@ -0,0 +1,44 @@ +add( + 'tpay_channel_id', + TextType::class, + [ + 'empty_data' => '', + 'label' => 'commerce_weavers_sylius_tpay.admin.gateway_configuration.tpay_channel_id', + 'validation_groups' => ['sylius'], + 'constraints' => [ + new NotBlank(allowNull: false, groups: ['sylius']), + ], + 'help' => 'commerce_weavers_sylius_tpay.admin.gateway_configuration.tpay_channel_id_help', + 'required' => false, + ], + ) + ; + } +} diff --git a/src/PayByLinkChannelPayment/Payum/Action/CreatePayByLinkTransactionAction.php b/src/PayByLinkChannelPayment/Payum/Action/CreatePayByLinkTransactionAction.php new file mode 100644 index 00000000..fd5d643f --- /dev/null +++ b/src/PayByLinkChannelPayment/Payum/Action/CreatePayByLinkTransactionAction.php @@ -0,0 +1,70 @@ +notifyTokenFactory->create($model, $gatewayName, $localeCode); + + $this->do( + fn () => $this->api->transactions()->createTransaction( + $this->createPayByLinkPayloadFactory->createFrom($model, $notifyToken->getTargetUrl(), $localeCode), + ), + onSuccess: function (array $response) use ($paymentDetails) { + $paymentDetails->setTransactionId($response['transactionId']); + $paymentDetails->setStatus($response['status']); + $paymentDetails->setPaymentUrl($response['transactionPaymentUrl']); + }, + onFailure: fn () => $paymentDetails->setStatus(PaymentInterface::STATE_FAILED), + ); + } + + protected function postExecute(PaymentInterface $model, PaymentDetails $paymentDetails, string $gatewayName, string $localeCode): void + { + if ($paymentDetails->getPaymentUrl() !== null) { + throw new HttpRedirect($paymentDetails->getPaymentUrl()); + } + } + + public function supports($request): bool + { + if (!$request instanceof CreateTransaction) { + return false; + } + + $model = $request->getModel(); + + if (!$model instanceof PaymentInterface) { + return false; + } + + $paymentDetails = PaymentDetails::fromArray($model->getDetails()); + + return $paymentDetails->getType() === PaymentType::PAY_BY_LINK; + } +} diff --git a/src/PayByLinkChannelPayment/Payum/Factory/GatewayFactory.php b/src/PayByLinkChannelPayment/Payum/Factory/GatewayFactory.php new file mode 100644 index 00000000..abb95791 --- /dev/null +++ b/src/PayByLinkChannelPayment/Payum/Factory/GatewayFactory.php @@ -0,0 +1,17 @@ +getGatewayConfig(); - if (null === $gatewayConfig || GatewayName::PAY_BY_LINK !== $gatewayConfig->getFactoryName()) { + if (null === $gatewayConfig || (GatewayName::PAY_BY_LINK !== $gatewayConfig->getFactoryName() && GatewayName::PAY_BY_LINK_CHANNEL !== $gatewayConfig->getFactoryName())) { return true; } diff --git a/src/PayByLinkPayment/ContextProvider/BankListContextProvider.php b/src/PayByLinkPayment/ContextProvider/BankListContextProvider.php index b5595385..d2518602 100644 --- a/src/PayByLinkPayment/ContextProvider/BankListContextProvider.php +++ b/src/PayByLinkPayment/ContextProvider/BankListContextProvider.php @@ -4,6 +4,7 @@ namespace CommerceWeavers\SyliusTpayPlugin\PayByLinkPayment\ContextProvider; +use CommerceWeavers\SyliusTpayPlugin\PayByLinkChannelPayment\Payum\Factory\GatewayFactory as PayByLinkChannelGatewayFactory; use CommerceWeavers\SyliusTpayPlugin\PayByLinkPayment\Payum\Factory\GatewayFactory as PayByLinkGatewayFactory; use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\OrderAwareValidTpayChannelListProviderInterface; use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\ValidTpayChannelListProviderInterface; @@ -40,7 +41,8 @@ public function provide(array $templateContext, TemplateBlock $templateBlock): a if ( null === $paymentMethod || null === $gatewayConfig || - PayByLinkGatewayFactory::NAME !== $gatewayConfig->getFactoryName() + PayByLinkGatewayFactory::NAME !== $gatewayConfig->getFactoryName() && + PayByLinkChannelGatewayFactory::NAME !== $gatewayConfig->getFactoryName() ) { return $templateContext; } @@ -62,8 +64,7 @@ public function provide(array $templateContext, TemplateBlock $templateBlock): a $templateContext['banks'] = null === $order ? $this->validTpayChannelListProvider->provide() - : $this->orderAwareValidTpayChannelListProvider->provide($order) - ; + : $this->orderAwareValidTpayChannelListProvider->provide($order); } return $templateContext; @@ -72,9 +73,8 @@ public function provide(array $templateContext, TemplateBlock $templateBlock): a public function supports(TemplateBlock $templateBlock): bool { return - 'pay_by_link' === $templateBlock->getName() && - in_array($templateBlock->getEventName(), self::SUPPORTED_TEMPLATE_BLOCK_EVENT_NAMES, true) - ; + ('pay_by_link' === $templateBlock->getName() || 'pay_by_link_channel' === $templateBlock->getName()) && + in_array($templateBlock->getEventName(), self::SUPPORTED_TEMPLATE_BLOCK_EVENT_NAMES, true); } private function resolvePaymentMethod(array $templateContext): ?PaymentMethodInterface diff --git a/src/PayByLinkPayment/Form/Type/GatewayConfigurationType.php b/src/PayByLinkPayment/Form/Type/GatewayConfigurationType.php index b7d315b5..12e605dc 100644 --- a/src/PayByLinkPayment/Form/Type/GatewayConfigurationType.php +++ b/src/PayByLinkPayment/Form/Type/GatewayConfigurationType.php @@ -7,39 +7,13 @@ use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\DecryptGatewayConfigListenerInterface; use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\EncryptGatewayConfigListenerInterface; use CommerceWeavers\SyliusTpayPlugin\Form\Type\AbstractTpayGatewayConfigurationType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Contracts\Translation\TranslatorInterface; final class GatewayConfigurationType extends AbstractTpayGatewayConfigurationType { public function __construct( DecryptGatewayConfigListenerInterface $decryptGatewayConfigListener, EncryptGatewayConfigListenerInterface $encryptGatewayConfigListener, - private readonly TranslatorInterface $translator, ) { parent::__construct($decryptGatewayConfigListener, $encryptGatewayConfigListener); } - - public function buildForm(FormBuilderInterface $builder, array $options): void - { - parent::buildForm($builder, $options); - - $builder - ->add( - 'tpay_channel_id', - TextType::class, - [ - 'empty_data' => '', - 'label' => 'commerce_weavers_sylius_tpay.admin.gateway_configuration.tpay_channel_id', - 'help' => 'commerce_weavers_sylius_tpay.admin.gateway_configuration.tpay_channel_id_help', - 'required' => false, - 'attr' => [ - 'placeholder' => $this->translator->trans('commerce_weavers_sylius_tpay.admin.gateway_configuration.tpay_display_all_channels', domain: 'messages'), - 'data-display-all-label' => $this->translator->trans('commerce_weavers_sylius_tpay.admin.gateway_configuration.tpay_display_all_channels', domain: 'messages'), - ], - ], - ) - ; - } } diff --git a/src/Tpay/GatewayName.php b/src/Tpay/GatewayName.php index b641e7a4..b287eecd 100644 --- a/src/Tpay/GatewayName.php +++ b/src/Tpay/GatewayName.php @@ -16,6 +16,8 @@ class GatewayName public const PAY_BY_LINK = 'tpay_pbl'; + public const PAY_BY_LINK_CHANNEL = 'tpay_pbl_channel'; + public const REDIRECT = 'tpay_redirect'; public const VISA_MOBILE = 'tpay_visa_mobile'; @@ -31,6 +33,7 @@ public static function all(): array self::CARD, self::GOOGLE_PAY, self::PAY_BY_LINK, + self::PAY_BY_LINK_CHANNEL, self::REDIRECT, self::VISA_MOBILE, ]; diff --git a/templates/shop/cart/complete/_payByLink.html.twig b/templates/shop/cart/complete/_payByLink.html.twig index 0188e246..006e299e 100644 --- a/templates/shop/cart/complete/_payByLink.html.twig +++ b/templates/shop/cart/complete/_payByLink.html.twig @@ -1,5 +1,5 @@ {% set payment = order.lastCartPayment() %} -{% if payment is not null and payment.method.gatewayConfig.gatewayName == 'tpay_pbl' %} +{% if payment is not null and payment.method.gatewayConfig.gatewayName == 'tpay_pbl'%} {% include '@CommerceWeaversSyliusTpayPlugin/shop/payment/_payByLink.html.twig' %} {% endif %} diff --git a/templates/shop/cart/complete/_payByLinkChannel.html.twig b/templates/shop/cart/complete/_payByLinkChannel.html.twig new file mode 100644 index 00000000..ea4b7b33 --- /dev/null +++ b/templates/shop/cart/complete/_payByLinkChannel.html.twig @@ -0,0 +1,5 @@ +{% set payment = order.lastCartPayment() %} + +{% if payment is not null and payment.method.gatewayConfig.gatewayName == 'tpay_pbl_channel'%} + {% include '@CommerceWeaversSyliusTpayPlugin/shop/payment/_payByLinkChannel.html.twig' %} +{% endif %} diff --git a/templates/shop/order/pay/_payByLink.html.twig b/templates/shop/order/pay/_payByLink.html.twig index 60fbb378..39c62e8b 100644 --- a/templates/shop/order/pay/_payByLink.html.twig +++ b/templates/shop/order/pay/_payByLink.html.twig @@ -1,4 +1,4 @@ -{% if method.gatewayConfig.gatewayName == 'tpay_pbl' and form.vars.value.state != 'cart' %} +{% if (method.gatewayConfig.gatewayName == 'tpay_pbl' or method.gatewayConfig.gatewayName == 'tpay_pbl_channel') and form.vars.value.state != 'cart' %} diff --git a/templates/shop/order/pay/_payByLinkChannel.html.twig b/templates/shop/order/pay/_payByLinkChannel.html.twig new file mode 100644 index 00000000..309c18fd --- /dev/null +++ b/templates/shop/order/pay/_payByLinkChannel.html.twig @@ -0,0 +1,5 @@ +{% if method.gatewayConfig.gatewayName == 'tpay_pbl_channel' and form.vars.value.state != 'cart' %} + +{% endif %} diff --git a/templates/shop/payment/_payByLink.html.twig b/templates/shop/payment/_payByLink.html.twig index 4f346c78..47068202 100644 --- a/templates/shop/payment/_payByLink.html.twig +++ b/templates/shop/payment/_payByLink.html.twig @@ -8,10 +8,8 @@ {% endfor %} - {# many PBL payment methods support #} -
{% if not form.tpay.tpay_channel_id.rendered %} - {{ form_row(form.tpay.tpay_channel_id, {'value': defaultTpayChannelId}) }} + {{ form_row(form.tpay.tpay_channel_id) }} {% endif %} {% include '@CommerceWeaversSyliusTpayPlugin/shop/partial/_policies.html.twig' %} diff --git a/templates/shop/payment/_payByLinkChannel.html.twig b/templates/shop/payment/_payByLinkChannel.html.twig new file mode 100644 index 00000000..3aff7350 --- /dev/null +++ b/templates/shop/payment/_payByLinkChannel.html.twig @@ -0,0 +1,8 @@ +
+
+ {% if not form.tpay.tpay_channel_id.rendered %} + {{ form_row(form.tpay.tpay_channel_id, {'value': defaultTpayChannelId}) }} + {% endif %} + + {% include '@CommerceWeaversSyliusTpayPlugin/shop/partial/_policies.html.twig' %} +
diff --git a/tests/Api/DataFixtures/shop/paying_for_orders_by_card/payment_method.yml b/tests/Api/DataFixtures/shop/paying_for_orders_by_card/payment_method.yml index 8efa5bd0..6aeb7b34 100644 --- a/tests/Api/DataFixtures/shop/paying_for_orders_by_card/payment_method.yml +++ b/tests/Api/DataFixtures/shop/paying_for_orders_by_card/payment_method.yml @@ -27,6 +27,13 @@ Sylius\Component\Core\Model\PaymentMethod: translations: - '@tpay_pbl_payment_method_translation' channels: [ '@channel_web' ] + tpay_pbl_channel: + code: 'tpay_pbl_channel' + enabled: true + gatewayConfig: '@gateway_tpay_pbl_channel' + translations: + - '@tpay_pbl_payment_method_translation' + channels: [ '@channel_web' ] tpay_visa_mobile: code: 'tpay_visa_mobile' enabled: true @@ -69,6 +76,15 @@ Sylius\Bundle\PayumBundle\Model\GatewayConfig: client_secret: 'encrypted_' production_mode: false encrypted: true + gateway_tpay_pbl_channel: + gatewayName: 'tpay_pbl_channel' + factoryName: 'tpay_pbl_channel' + config: + client_id: 'encrypted_' + client_secret: 'encrypted_' + tpay_channel_id: 'encrypted_' + production_mode: false + encrypted: true gateway_tpay_visa_mobile: gatewayName: 'tpay_visa_mobile' factoryName: 'tpay_visa_mobile' diff --git a/tests/Api/Shop/PayingForOrdersByPblChannelTest.php b/tests/Api/Shop/PayingForOrdersByPblChannelTest.php new file mode 100644 index 00000000..2477f383 --- /dev/null +++ b/tests/Api/Shop/PayingForOrdersByPblChannelTest.php @@ -0,0 +1,161 @@ +setUpOrderPlacer(); + } + + public function test_it_returns_violation_if_bank_is_not_available(): void + { + $this->loadFixturesFromDirectory('shop/paying_for_orders_by_card'); + + $order = $this->doPlaceOrder('t0k3n', paymentMethodCode: 'tpay_pbl_channel'); + + $this->client->request( + Request::METHOD_POST, + sprintf('/api/v2/shop/orders/%s/pay', $order->getTokenValue()), + server: self::CONTENT_TYPE_HEADER, + content: json_encode([ + 'successUrl' => 'https://example.com/success', + 'failureUrl' => 'https://example.com/failure', + 'tpayChannelId' => '3' + ]), + ); + + $response = $this->client->getResponse(); + + $this->assertResponseViolations($response, [ + [ + 'propertyPath' => 'tpayChannelId', + 'message' => 'Channel with provided id is not available.', + ] + ]); + } + + public function test_it_returns_violation_if_channel_is_not_a_bank(): void + { + $this->loadFixturesFromDirectory('shop/paying_for_orders_by_card'); + + $order = $this->doPlaceOrder('t0k3n', paymentMethodCode: 'tpay_pbl_channel'); + + $this->client->request( + Request::METHOD_POST, + sprintf('/api/v2/shop/orders/%s/pay', $order->getTokenValue()), + server: self::CONTENT_TYPE_HEADER, + content: json_encode([ + 'successUrl' => 'https://example.com/success', + 'failureUrl' => 'https://example.com/failure', + 'tpayChannelId' => '2' + ]), + ); + + $response = $this->client->getResponse(); + + $this->assertResponseViolations($response, [ + [ + 'propertyPath' => 'tpayChannelId', + 'message' => 'Channel with provided id is not a bank.', + ] + ]); + } + + public function test_paying_with_pbl_based_payment_type(): void + { + $this->loadFixturesFromDirectory('shop/paying_for_orders_by_card'); + + $order = $this->doPlaceOrder('t0k3n', paymentMethodCode: 'tpay_pbl_channel'); + + $this->client->request( + Request::METHOD_POST, + sprintf('/api/v2/shop/orders/%s/pay', $order->getTokenValue()), + server: self::CONTENT_TYPE_HEADER, + content: json_encode([ + 'successUrl' => 'https://example.com/success', + 'failureUrl' => 'https://example.com/failure', + 'tpayChannelId' => '1' + ]), + ); + + $response = $this->client->getResponse(); + + $this->assertResponseCode($response, Response::HTTP_OK); + $this->assertResponse($response, 'shop/paying_for_orders_by_pbl/test_paying_with_pay_by_link_based_payment_type'); + } + + public function test_it_handles_tpay_error_while_paying_with_pay_by_link_based_payment_type(): void + { + $this->loadFixturesFromDirectory('shop/paying_for_orders_by_card'); + + $order = $this->doPlaceOrder('t0k3n', paymentMethodCode: 'tpay_pbl_channel'); + + $this->client->request( + Request::METHOD_POST, + sprintf('/api/v2/shop/orders/%s/pay', $order->getTokenValue()), + server: self::CONTENT_TYPE_HEADER, + content: json_encode([ + 'successUrl' => 'https://example.com/success', + 'failureUrl' => 'https://example.com/failure', + 'tpayChannelId' => '991' + ]), + ); + + $response = $this->client->getResponse(); + + $this->assertResponseCode($response, 424); + $this->assertStringContainsString( + 'An error occurred while processing your payment. Please try again or contact store support.', + $response->getContent(), + ); + } + + private function doPlaceOrder( + string $tokenValue, + string $email = 'sylius@example.com', + string $productVariantCode = 'MUG_BLUE', + string $shippingMethodCode = 'UPS', + string $paymentMethodCode = 'tpay', + int $quantity = 1, + ?\DateTimeImmutable $checkoutCompletedAt = null, + + ): OrderInterface { + $this->checkSetUpOrderPlacerCalled(); + + $this->pickUpCart($tokenValue); + $this->addItemToCart($productVariantCode, $quantity, $tokenValue); + $cart = $this->updateCartWithAddressAndCouponCode($tokenValue, $email); + $this->dispatchShippingMethodChooseCommand( + $tokenValue, + $shippingMethodCode, + (string)$cart->getShipments()->first()->getId(), + ); + $this->dispatchPaymentMethodChooseCommand( + $tokenValue, + $paymentMethodCode, + (string)$cart->getLastPayment()->getId(), + ); + + $order = $this->dispatchCompleteOrderCommand($tokenValue); + + $this->setCheckoutCompletedAt($order, $checkoutCompletedAt); + + return $order; + } +} diff --git a/tests/E2E/Admin/PaymentMethod/TestPaymentMethodConnectionTest.php b/tests/E2E/Admin/PaymentMethod/TestPaymentMethodConnectionTest.php index 1ddbf6bb..8e6ea900 100644 --- a/tests/E2E/Admin/PaymentMethod/TestPaymentMethodConnectionTest.php +++ b/tests/E2E/Admin/PaymentMethod/TestPaymentMethodConnectionTest.php @@ -23,7 +23,7 @@ protected function setUp(): void public function test_it_checks_if_payment_method_connection(): void { - $this->client->request('GET', '/admin/payment-methods/new/tpay_pbl'); + $this->client->request('GET', '/admin/payment-methods/new/tpay_pbl_channel'); $this->client ->findElement(WebDriverBy::id('sylius_payment_method_gatewayConfig_config_client_id')) diff --git a/tests/E2E/Resources/fixtures/common/payment_method.yaml b/tests/E2E/Resources/fixtures/common/payment_method.yaml index 8a9886e0..08c56c55 100644 --- a/tests/E2E/Resources/fixtures/common/payment_method.yaml +++ b/tests/E2E/Resources/fixtures/common/payment_method.yaml @@ -106,13 +106,13 @@ Sylius\Bundle\PayumBundle\Model\GatewayConfig: production_mode: false encrypted: true gateway_tpay_pbl_one_channel: - gatewayName: 'tpay_pbl' - factoryName: 'tpay_pbl' + gatewayName: 'tpay_pbl_channel' + factoryName: 'tpay_pbl_channel' config: client_id: 'encrypted_' client_secret: 'encrypted_' tpay_channel_id: '1' - type: 'pay_by_link' + type: 'pay_by_link_channel' production_mode: false encrypted: true gateway_tpay_pbl_one_channel_with_amount_min_20_constraint: diff --git a/tests/Unit/PayByLinkChannelPayment/Payum/Action/CreatePayByLinkTransactionActionTest.php b/tests/Unit/PayByLinkChannelPayment/Payum/Action/CreatePayByLinkTransactionActionTest.php new file mode 100644 index 00000000..c169c48d --- /dev/null +++ b/tests/Unit/PayByLinkChannelPayment/Payum/Action/CreatePayByLinkTransactionActionTest.php @@ -0,0 +1,193 @@ +api = $this->prophesize(TpayApi::class); + $this->createPayByLinkPayloadFactory = $this->prophesize(CreatePayByLinkPayloadFactoryInterface::class); + $this->tokenFactory = $this->prophesize(GenericTokenFactoryInterface::class); + $this->notifyTokenFactory = $this->prophesize(NotifyTokenFactoryInterface::class); + } + + public function test_it_supports_create_transaction_requests_with_a_valid_payment_model(): void + { + $payment = $this->prophesize(PaymentInterface::class); + $payment->getDetails()->willReturn(['tpay' => ['tpay_channel_id' => '1']]); + + $request = $this->prophesize(CreateTransaction::class); + $request->getModel()->willReturn($payment); + + $isSupported = $this->createTestSubject()->supports($request->reveal()); + + $this->assertTrue($isSupported); + } + + public function test_it_does_not_support_non_create_transaction_requests(): void + { + $payment = $this->prophesize(PaymentInterface::class); + $payment->getDetails()->willReturn([]); + + $request = $this->prophesize(Capture::class); + $request->getModel()->willReturn($payment); + + $isSupported = $this->createTestSubject()->supports($request->reveal()); + + $this->assertFalse($isSupported); + } + + public function test_it_does_not_support_requests_with_non_payment_model(): void + { + $nonPaymentModel = new \stdClass(); + + $request = $this->prophesize(CreateTransaction::class); + $request->getModel()->willReturn($nonPaymentModel); + + $isSupported = $this->createTestSubject()->supports($request->reveal()); + + $this->assertFalse($isSupported); + } + + public function test_it_does_not_support_requests_with_payment_model_not_containing_tpay_pbl_channel_id(): void + { + $payment = $this->prophesize(PaymentInterface::class); + $payment->getDetails()->willReturn(['tpay' => ['blik' => '123456']]); + + $request = $this->prophesize(CreateTransaction::class); + $request->getModel()->willReturn($payment); + + $isSupported = $this->createTestSubject()->supports($request->reveal()); + + $this->assertFalse($isSupported); + } + + public function test_it_creates_a_payment_and_redirects_to_a_payment_page(): void + { + $this->expectException(HttpRedirect::class); + + $order = $this->prophesize(OrderInterface::class); + $order->getLocaleCode()->willReturn('pl_PL'); + + $payment = $this->prophesize(PaymentInterface::class); + $payment->getOrder()->willReturn($order); + $payment->getDetails()->willReturn([]); + + $token = $this->prophesize(TokenInterface::class); + $token->getGatewayName()->willReturn('tpay'); + + $request = $this->prophesize(CreateTransaction::class); + $request->getModel()->willReturn($payment); + $request->getToken()->willReturn($token); + + $notifyToken = $this->prophesize(TokenInterface::class); + $notifyToken->getTargetUrl()->willReturn('https://cw.org/notify'); + + $transactions = $this->prophesize(TransactionsApi::class); + $transactions->createTransaction(['factored' => 'payload'])->willReturn([ + 'result' => 'success', + 'status' => 'pending', + 'transactionId' => 'tr4ns4ct!0n_id', + 'transactionPaymentUrl' => 'https://tpay.org/pay', + ]); + + $this->api->transactions()->willReturn($transactions); + + $payment->setDetails( + $this->getExpectedDetails(transaction_id: 'tr4ns4ct!0n_id', status: 'pending', payment_url: 'https://tpay.org/pay'), + )->shouldBeCalled(); + + $this->notifyTokenFactory->create($payment, 'tpay', 'pl_PL')->willReturn($notifyToken); + + $this->createPayByLinkPayloadFactory + ->createFrom($payment, 'https://cw.org/notify', 'pl_PL') + ->willReturn(['factored' => 'payload']) + ; + + $this->createTestSubject()->execute($request->reveal()); + } + + public function test_it_marks_payment_as_failed_if_tpay_throws_an_exception(): void + { + $order = $this->prophesize(OrderInterface::class); + $order->getLocaleCode()->willReturn('pl_PL'); + + $payment = $this->prophesize(PaymentInterface::class); + $payment->getOrder()->willReturn($order); + $payment->getDetails()->willReturn([]); + + $token = $this->prophesize(TokenInterface::class); + $token->getGatewayName()->willReturn('tpay'); + + $request = $this->prophesize(CreateTransaction::class); + $request->getModel()->willReturn($payment); + $request->getToken()->willReturn($token); + + $notifyToken = $this->prophesize(TokenInterface::class); + $notifyToken->getTargetUrl()->willReturn('https://cw.org/notify'); + + $transactions = $this->prophesize(TransactionsApi::class); + $transactions->createTransaction(['factored' => 'payload'])->willThrow(new TpayException('Something went wrong')); + + $this->api->transactions()->willReturn($transactions); + + $payment->setDetails( + $this->getExpectedDetails(status: 'failed'), + )->shouldBeCalled(); + + $this->notifyTokenFactory->create($payment, 'tpay', 'pl_PL')->willReturn($notifyToken); + + $this->createPayByLinkPayloadFactory + ->createFrom($payment, 'https://cw.org/notify', 'pl_PL') + ->willReturn(['factored' => 'payload']) + ; + + $this->createTestSubject()->execute($request->reveal()); + } + + private function createTestSubject(): CreatePayByLinkTransactionAction + { + $action = new CreatePayByLinkTransactionAction( + $this->createPayByLinkPayloadFactory->reveal(), + $this->notifyTokenFactory->reveal(), + ); + + $action->setApi($this->api->reveal()); + $action->setGenericTokenFactory($this->tokenFactory->reveal()); + + return $action; + } +} diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 45c50300..29ba309e 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -7,6 +7,7 @@ commerce_weavers_sylius_tpay: tpay_card: 'Card (Tpay)' tpay_google_pay: 'Google Pay (Tpay)' tpay_pbl: 'Pay-by-Link (Tpay)' + tpay_channel_pbl: 'Pay-by-link Single Channel (Tpay)' tpay_redirect: 'Redirect to Tpay' tpay_visa_mobile: 'Visa Mobile (Tpay)' gateway_configuration: diff --git a/translations/messages.pl.yaml b/translations/messages.pl.yaml index 9840eb52..67020a94 100644 --- a/translations/messages.pl.yaml +++ b/translations/messages.pl.yaml @@ -7,6 +7,7 @@ commerce_weavers_sylius_tpay: tpay_card: 'Karta (Tpay)' tpay_google_pay: 'Google Pay (Tpay)' tpay_pbl: 'Pay-by-Link (Tpay)' + tpay_channel_pbl: 'Pay-by-link kanaƂ (Tpay)' tpay_redirect: 'Przekierowanie do Tpay' tpay_visa_mobile: 'Visa Mobile (Tpay)' gateway_configuration: