Skip to content

Service Processor

The Service Processor is responsible for handling place order requests. It matches the given payment method code to an optional corresponding Place Order Service.

If no match is found, it falls back to the default service provided by the PlaceOrderServiceProvider which can be found at \Hyva\Checkout\Model\Magewire\Payment\PlaceOrderServiceProvider.

Happy Flow

The high-level happy flow when the Place Order button is pressed is as follows:

  1. A request is made to the method \Hyva\Checkout\Magewire\Main::placeOrder().
  2. Since version 1.1.13, the placeOrder() method accepts hyva_checkout session data, which is automatically passed through to the Place Order Service as Hyva\Checkout\Model\Magewire\Payment\AbstractOrderData.
  3. The processors, if permitted by the Place Order Service canPlaceOrder() method, attempt to convert the session data and run the Place Order Service's placeOrder() method, catching any exceptions that are thrown.
  4. When the order is placed successfully, an order:place:success window event is pushed into the Evaluation batch, which is then dispatched on the frontend immediately after.
  5. If the Place Order Service permits the processor to redirect afterward, a Redirect evaluation result is added to the Evaluation Batch. This result includes the redirect URL provided by the Place Order Service's getRedirectUrl() method.

Since 1.1.13, the \Hyva\Checkout\Model\Magewire\Payment\PlaceOrderServiceProvider has been marked as @internal which means this object should not be used by any other than the Main checkout component.

Register a Place Order Service

Ensuring that a specific place order service is used for a certain payment method requires registration and can be achieved as follows.

<!-- File: etc/frontend/di.xml -->

<type name="Hyva\Checkout\Model\Magewire\Payment\PlaceOrderServiceProvider">
    <arguments>
        <argument name="placeOrderServiceList" xsi:type="array">
            <item name="foo" xsi:type="object">
                My\Example\Model\Payment\PlaceOrderService\FooPlaceOrderService
            </item>
        </argument>
    </arguments>
</type>

For more details, please refer to Place Order Service API - Example scenario.

Exception Handling

The service processor automatically dispatches an order:place:error and order:place:{payment_method_code}:error window event which by default is not being listened to by any default frontend components.

Accordingly, if the handleException method of the used order place service re-throws the exception, the processor will set a default message: "Something went wrong while processing your order. Please try again." This message can be customized by allowing the handleException() method to throw an exception of type \Magento\Framework\Exception\LocalizedException.

Additionally, the exception message is logged. Subsequently, an error message Evaluation result is added to the batch, which will be automatically dispatched and displayed on the frontend, notifying the customer that something went wrong.

For more details, please refer to Abstract Layer - Handle Exception.

Example scenario

You have bound a custom Place Order Service to a specific payment method with the code foo. This method attempts to place the order via a custom placeOrder() method, but throws an exception due to an API call to an external service returning a 403 response.

In this scenario, we assume the quote has not yet been transferred into an order object. Therefore, we need to catch the error on the frontend within the payment method template itself.

Using the Messenger component

The messenger component is a template used for a messenger block. This template automatically turns the block into a messenger listening component responsible for displaying error messages in a unified manner, without requiring you to handle any business logic or specific styling.

The messenger needs to be injected manually via layout XML, as the platform cannot determine where you want these messages to be displayed.

<block name="checkout.shipping.method.foo" as="foo_foo">
    <block name="component-messenger-guest-details"
           template="Hyva_Checkout::page/messenger.phtml"
    >
        <arguments>
            <argument name="event_prefix" xsi:type="string">
                order:place
            </argument>
        </arguments>
    </block>
</block>

The event_prefix makes the messenger automatically listen for any {event_prefix}:error events, which, as previously mentioned, will be automatically dispatched on the window by the order place service processor.

Next we need to show a message which shows a bit more specific what went wrong. We'll do this via our custom place order service including our own handleException().

<?php

public function handleException(\Exception $exception, \Magewirephp\Magewire\Component $component, \Magento\Quote\Model\Quote $quote): void
{
    if ($exception instanceof \My\Example\Exceptions\AuthenticationFailureException) {
        throw new \Magento\Framework\Exception\LocalizedException(
            __('Unable to authenticate to the payment gateway. Please try again.')
        );
    }

    // Use the default behaviour.
    parent::handleException($exception, $component, $quote);
}

Now imagine you want to add custom data. You could either include it in the message or use an evaluation result, which provides more flexibility to meet your specific needs.

This is done by adding another result to the Main component batch, which can hold an unlimited number of results.

<?php

public function handleException(\Exception $exception, \Magewirephp\Magewire\Component $component, \Magento\Quote\Model\Quote $quote): void
{
    if ($exception instanceof \My\Example\Exceptions\AuthenticationFailureException) {
        $component->getEvaluationResultBatch()
            ->push($component->getEvaluationResultBatch()->getFactory()
                ->createEvent('my:custom:event')
                ->withDetails([
                    'code' => $exception->getCode(),
                    'gateway' => $exception->getGateway()
                ])
            );

        throw new \Magento\Framework\Exception\LocalizedException(
            __('Unable to authenticate to the payment gateway. Please try again.')
        );
    }

    parent::handleException($exception, $component, $quote);
}

In this case, my:custom:event will be dispatched onto the window with custom details to work with.

Using a custom event listener

Even though the Messenger component is our best practice for displaying error and warning messages in a unified manner, it is still possible to build more customized solutions.

This can help clarify the situation for your customers and provide guidance on how they can potentially resolve the issue.

<script>
    window.addEventListener('my:custom:event', event => {
        // Custom logic.
    })
</script>

Using other evaluation results

The Evaluation API is as flexible as you need it to be. You can inject custom results or use existing ones, which we continually improve and expand to handle various situations.

Using these results is identical to how you would use them in a custom component. The only difference is that you need to add them to a pre-created batch that resides on the $component.

Additionally, the batch object has a getFactory() method that can be used to create result objects.

<?php

public function handleException(\Exception $exception, \Magewirephp\Magewire\Component $component, \Magento\Quote\Model\Quote $quote): void
{
    // Get the factory.
    $factory = $component->getEvaluationResultBatch()->getFactory();
    // Create the required result.
    $executable = $factory->createExecutable('foo_exception');
    // Push it onto the stack.
    $component->getEvaluationResultBatch()->push($executable);

    //...
}

For more details, please refer to Evaluation API - Evaluation result types.