Checkout workflows and metadata store described

A word about order statuses

The plugin never updates any order statuses during the order creation (i.e. never from process_payment). This happens at first when orders are signed/booked or when callbacks from Resurs Bank arrives.

Simplified and Hosted (Standard integrations)

The Simplified Flow and Resurs Hosted Flow are both considered to work with "woocommerce standard". Those cases is very much simpler to implement sin ce they allow WooCommerce to handle most of the API calls. The both images are marked with different colors, depending on who's in charge. The red parts in the illustrations is marked as "Resurs features".

Purchasing with simplified and hosted flow

This payment flow is internally the easiest to integrate with. If you check the diagram here, you can probably already guess that we have to do very few things to make this integration to work. This is a step by step explanation, what happens during the purchase.

With all customer data filled in, it is about to complete the order.

  1. Purchase button is clicked. Button itself is rendered by WooCommerce. WooCommerce is now starting to handle everything internally, until it has an order id. If the process failed, for example due to empty stocks or other things that can happen, the order won't simply proceed.
  2. When WooCommerce is ready to hand over the purchase to externally queued plugins, this happens in process_payment() which is neither an action nor a filter, but a child function that inherits from WC_Payment_Gateway.
  3. In this plugin process_payment is used for both simplified, hosted and Resurs Checkout, with a slight difference for RCO. Since RCO (see below) is mostly done with all requests that WooCommerce normally handles first, huge parts are skipped in this process. For the other two flows, processing the payment is split up in three parts:
    1: Prepare order (preparing the ecommerce API).
    2: Create order (sending the order to the ecommerce API).
    3: Log, handle and return what happened in step 2. Success or fail will be handled here.

    During this three-step process, a bunch of metadata are strored about the order. This metadata are passed through the whole flow as long as possible to help callbacks and the customer to find back home after a successful or failed order.
  4. a) If Resurs Bank approves step 3, the plugin tells woocommerce that the creation has been successful and returns a "successUrl" back to the store, just like the ones that Resurs Bank has.
    b) If Resurs Bank approves step 3, but requires signing, the result will still be successful, but the redirect given back to WooCommerce is the URL for signing.
    From this point, the customer are redirected to a signing page and when signing is finished, Resurs is sending back the customer to a in-between landing page, where another step takes place to complete the missing parts of the order (bookSignedPayment). This happens in ResursDefault.php at setFinalSigning() that is called from a special built function named getApiRequire(). When this procedure is finished, customer will be redirected back to the real "Thank you page" at step 5.

    If something here goes wrong or the payment are cancelled, the redirecturl will instead point back to a cancellation page - in this case the most common place is back to the cart, where an error are shown. Such cases ends up here instead of step 5.
  5. Woocommerce has now sent the customer to a "Thank you"-page. At this point we are waiting for callbacks to handle the Resurs payment furthermore, even if much of this also happens during the bookSignedPayment process. As the signed payment very much is synchronous set, statuses are beginning to be handled here, while still waiting for callbacks.

To not loose data during this procedure, data that is required for the order to complete is stored as metadata in the current order that WooCommerce returned at step 2. This data is also used during parts of the redirects where success/fail-data is stored as a base64-encoded jsonstring.

Resurs Checkout

Resurs Checkbank are said to make life easiers for merchants. This is mostly true. Integrators however, many times has to think backwards as Resurs Bank's influence are happening much earlier in the flow.

At this point, everything starts with the Resurs Checkout iframe, instead of the internal WooCommerce flow - I.E. the iframe that is injected will work very much as the intended customer forms. The darker red box marks out where WooCommerce is actually first will be part of the order creation process. The order creation is still mostly handled by WooCommerce when it is allowed to begin. This is done with frontend scripts where the frontend features collects anything in the customer data that isn't specifically RCO belongnings. Otherwise we'll be in the way for other plugins with own customer features.

Purchasing with Resurs Checkout

The full payment process is still handled from Resurs Checkout via a function named process_payment, but only partially. The exact means of this will be shown in the step-by-step section below. Process_payment is a heritage from the parent woocommerce-process that normally handles payment processing before Resurs Bank is kicking in. We can't do that with Resurs Checkout as most of the purchas happens in the iframe. At least not without tweaking this procedure. This is how it's done.

We expect that the iframe is hereby ready for a purchase with all fields ready, filled in and done. All data is till stored as a one time variable in the browser which will be submitted to backend with an ajax call. Reloading the browser also means that data is reset.

  1. Purchase button is clicked. For RCOv2 we use $ResursCheckout.onSubmit().  From that point we call, by ajax, resursbank_checkout_create_order. For RCOv1 it works similarly but with another click-checker based on message events beween the main window and the iframe.
  2. resursbank_checkout_create_order resides in src/Module/PluginApi.php but in the API class it has been created with a camelCase transformed method: checkoutCreateOrder().
  3. checkoutCreateOrder() stores customer data in session as we are at this moment outside the WooCommerce flow.
  4. At first, checkoutCreateOrder() is firing up self::getPreparedRcoOrder() where customer data is collected and handled. In this feature we are discovering whether RCOv1 or RCOv2 is used. Secondly, this function also renders proper POST-data for WooCommerce to use.
  5. Secondly, self::getCreatedOrder() is firing up. At this moment we are about to hand over the order to WooCommerce again. This is done by marking the session with order_awaiting_payment as the order still needs a payment.
  6. Thirdly, from getCreatedOrder() we are now firing up the last step at the front-end handler: $order->process_checkout(). This function contains an extra layer for the frontend scripts to handle if something goes wrong.
    Note: If this was simplified hosted flow, this process has already been running at step 1-2 (see the other flow above).
  7. At this moment, we no longer have responsibility for the order, until WooCommerce executes our plugin's internal process_payment() again. In simplified and hosted flow, this part of the plugin is always executed before Resurs Bank order creation.
    You are now at the last step of succession.
  8. So, we're back in process_payment() again. At this moment, there are still metadata missing for the order, since the creation process is very much reverse. The first steps is about asking WooCommerce to create and handle an order, from which we later can achieve an order id.

    So, since the order are already handled by Resurs Bank, most of this section can now be skipped. For example, this is the first moment that we as above said, can get use of a real order id. Therefore most of the data- and metas is now filling in the missing gap of information. The the plugin prepares a success- or failurl return to pass over - to someone. For this flow this data is no longer handed over too WooCommerce entirely. This part matches mostly step 4 in simplified/hosted flow description. So let's head to step 9 to see how the urls are handled.

    RCO vs regular flows
            if (Data::getCheckoutType() !== ResursDefault::TYPE_RCO) {
                $return = $this->processResursOrder($order);
            } elseif (Data::getCheckoutType() === ResursDefault::TYPE_RCO) {
                // Rules applicable on RCO only.
                $return['result'] = 'success';
                $return['redirect'] = WC_Payment_Gateway::get_return_url($order);
            }
  9. The final moment in the long procedure has now a catch.
    Since we are now running through a RCO-iframe process, the response created to WooCommerce will actually never reach the regular internal flow properly. At least, this data will never reach the browser, the same way as in the normal flows. It is instead handed over to the Resurs Checkout front-end scripts in the browser, where we can now tell the iframe that we are finally done with our parts. The rest of the purchase process is up to Resurs Bank. When the order is processed at Resurs Bank, the same success and fail-url is now instead handled by Resurs Bank. Therefore Resurs Bank is instead responsible for redirecting the customer back or further to a payment provider or bank signing. When this is done, Resurs Bank returns the customer either to the "Thank you"-success page or a "customer failed/cancelled the payment"-page.

Orderview Metadata

Payments successful - best practices

The current solution may sometimes have some glitches when it comes to synchronizing payments in the RCO iframe. Depending on how you act in the checkout there might be desynch between what's in the browser memory and what's actually being sent to the backend side. For developers in general, the best way to maintain synchronized orders is to actually resynchronize them after a successful payment, by using Resurs Bank getPayment. This plugin handles that problem for you.

Payment failures

Sometimes when you enter an order to handle or check out, what went wrong you may meet this message:

It is usually shown in the admin interface when payments gone wrong in the checkout. This often happens when customers abort their payments before they are completed, which results in a non existent payment at Resurs Bank. Sometimes this also happens during payment signing, where the customer never leaves the signing window or for example never enters their card information when they are directed to an external payment provider. When running in RCO mode (Resurs Checkout) this means we have a created order in WooCommerce, but no created payment at Resurs Bank, that renders this kind of behaviour.

But now, we have a new state that partially (mostly because mindreading is not yet possible) handles this kind of error as it is very non-informative. If customers fail in an early state - meaning they never leave the main checkout view (or they fail to proceed to signing/payment provider for whatever reason possible) and instead are initially rejected by the iframe itself - the content in the metadata will now also change to mirror what happened in the checkout. In a customer point of view, errors than can occur now, looks like this:

In the iframe

In WooCommerce

If this scenario ever occurs, the plugin covers this with some addon-metadata, which will become available for the merchant in this form:

Table

As you can see here, the message in the red box now clarifies that the current customer actually failed the payment at least once. The default error message that occurs on errors generating code 8, 3, or 404 (meaning "payment does not exist") will in this state be replaced by a closer explanation why Resurs has no purchase registered. There are also extra meta data in the "blue box", which states this:

Meta KeyExplained
rco_rejected_onceThe purchase has at least been rejected once (fail/deny). If there actually is an order anyway, you could also be aware that the customer first tried something that didn't work out properly. This is an indicator that tells us it happened some time during the purchase process.
rco_rejectedEach rejection that has occurred during the current payment and which kind of state, with datestamps. Refers to rco_rejected_once. If it happened more than once you'll see that here.
rco_reject_messageThis one is extra interesting. This is the message that has been shown on the customer view. You can see in the above images where the text says "Please contact customer service"? That's the message stored in metadata, just to mark up that the customer was notified.

Collecting meta- and session data

When customers are proceeding in the checkouts we collect as much data as possible. Most of those procedures can be found in ResursDefault which is the primary class that extends into WC_Payment_Gateway and the data that the ending customer will bring through the orderflow, like signing, etc contains encoded data about the current order. In the regular flows (simplified and hosted) both Resurs payment id and the order id created by WooCommerce will be injected into an apiData-container. The apiData-container is based on a base64 encoded json-object.

As soon as the customer lands on the "thank you"-page (successUrl), the data that was injected from the beggining in the apiData-container, can  again be base64-decoded.

The API Data

Most of the data transferred to Resurs Bank and plugin endpoints are stored through ResursDefault.php. A private variable internally stores everything needed in the purchase in $apiData, which is returned as a base64-string when ready to deliver to processes that needs it. And instead of storing an unlimited amount of metadata to the orders, each order also gets this collection of data in one single metadata-string - encoded to base64. When needed, for example in the API calls, this data is extracted from base64, into the json object it was stored as. This makes it possible to maintain the content and character set as it was when first initialized. What the apiData contains can be different, depending on the flow, but here are some examples.

VariableDescription
checkoutTypeDuring the purchase, the checkout type that was used.
paymentMethodDuring the purchase, the payment method that was used.
wc_order_idThe order number given by WooCommerce.
preferred_idOur preferred payment id for Resurs Bank.
isReturningCustomerA flag that is set depending on where the customer is and has been.

What if...? The ending customer never reach the "thank you" page?

The data that is necessary for WooCommerce and the plugin to process, that was described above as the apiData-container, will also be injected into the signing data object for where the bookPayment takes place.

This process also takes place in ResursDefault, in the setSigningUrl like this:

    /**
     * Generate signing url for success/thank you page.
     * @param null $params
     * @return string
     * @since 0.0.1.0
     */
    private function getSigningUrl($params = null)
    {
        $wcApi = WooCommerce::getWcApiUrl();
        $signingBaseUrl = add_query_arg('apiDataId', $this->apiDataId, $wcApi);
        $signingBaseUrl = add_query_arg('apiData', $this->getApiData((array)$params, true), $signingBaseUrl);

        Data::setDeveloperLog(
            __FUNCTION__,
            sprintf(
                __('ECom setSigning: %s', 'trbwc'),
                $signingBaseUrl
            )
        );
        Data::setDeveloperLog(
            __FUNCTION__,
            sprintf(
                __('ECom setSigning parameters: %s', 'trbwc'),
                print_r($this->getApiData((array)$params), true)
            )
        );

        return $signingBaseUrl;
    }

The data that is stored at this moment is also stored as metaData:

    /**
     * Generic method to handle metadata depending on which direction the checkout is set into.
     * RCO needs this, but from the frontend calls. Simplified and hosted is the "regular" way,
     * so it's easier to handle.
     *
     * @throws Exception
     * @since 0.0.1.0
     */
    public function setOrderCheckoutMeta($order)
    {
        Data::setOrderMeta($order, 'checkoutType', Data::getCheckoutType());
        Data::setOrderMeta($order, 'apiDataId', $this->apiDataId);
        Data::setOrderMeta($order, 'orderSigningPayload', $this->getApiData());
        Data::setOrderMeta($order, 'resursReference', $this->getPaymentId());
        if (!empty(Data::getPaymentMethodBySession())) {
            Data::setOrderMeta($order, 'paymentMethod', Data::getPaymentMethodBySession());
        }
    }

As you can see. we give the necessary finalization data to both front-end and back-end systems which also means that the order information is at this moment already store as metadata for the current order. If the customer happens to get problems or closes their browser long before the successurl is called, but still has signed the order, this gives the chance for Resurs callbacks to finalize the last parts. The callbacks does not contain as much as the above, but still an order number that can be connected to the stored meta data.