Question

The issue

I'm trying to integrate an online credit card payment service in Magento 2.2, but it's tough to understand all the docs on this topic at first, so I'm looking for some guidance.

What I need to achieve is to post several parameters like shop ID, secret_key order ID, order value, success url, error url etc. to the payment providers URL (form), where the customer can complete the credit card payment.

The provider handles all the security related stuff like storing and processing CC data.

This needs to happen when the customer presses the "Place order" button on checkout if this payment method is selected.

After the payment is complete, the customer should get redirected to the success URL or error URL.

My attempts & research

I've created a working payment method so far and I've created a simple payment gateway based on:

And I've looked at Paypal Express to see how they do it -> As far as I can tell, it calls a controller in Module_Paypal/Controllers/Express/AbstractExpress/Start.php with the "Continue to Paypal" button.

Haven't gone into the details of Paypal's approach cause I'm just not sure if this is the right way to go about this...

Questions

  1. What is the recommended way to post data and redirect customer to an external Payment processor/provider after an order is placed?

  2. Should I go about it as per the official docs - to create a payment gateway? Or is there a better/easier approach?

  3. If I need to create a payment gateway, do I need to use an authorize or capture command? (taking into account that I only need an order placed, no invoice processing is necessary)

  4. Depending on the approach, I would like some guidance as to how to call the payment provider's payment-form URL, post data to it and redirect the customer to it after the "Place order" button is clicked.

  5. Also any suggestions on how to call checkout/onepage/success if the transaction was successful for the customer? Or should I redirect to a custom controller if the payment was (un)successful?

EDIT

There is no complex API documentation, all I have to do is call the payment provider's URL with the post method and pass some parameters to it.

For example, a simple html form would do the trick like this:

<form name="payment-provider-authorize-form" action="https://payment-provider.com/authorize-form" method="post" >
    <input type="hidden" name="shop_id" value="$shop_id" />
    <input type="hidden" name="order_id" value="$cart_id" />
    <input type="hidden" name="amount" value="$total" />
    <input type="hidden" name="success_url" value="$success_url" />
    ...
    <input type="submit" value="Buy with Custom Payment provider" />
</form>

I'm currently thinking of:

  • redirecting the customer to the payment provider's url with $.post() inside the afterPlaceOrder function and getting the post data from system config through window.checkoutConfig
  • or redirecting to a custom controller with the afterPlaceOrder function, getting all post parameters there and somehow redirecting to payment provider's url (if this is the way to go, a small code example on how to do this in the controller would be very helpful)

And again I'm still not sure where Magento's Payment gateway commands like authorize fit into all of this. Do I even need them? Seems like they are meant for more complex integrations and aren't necessary in this situation...

@Joni Jones, could you please expand on your answer to question 4?

How would I post data with $.mage.redirect(window.checkoutConfig.payment...)? My first thought was using $.post().

Also is the Web API set-payment-information call really necessary even if I use the placeOrderAfter function?

Was it helpful?

Solution

As @Yogesh said in the comment, it depends on payment gateway and it's API. I see at least few possible solutions with different variations and without API documentation for payment gateway, it would be just assumption.

If shop ID, secret_key, order ID, order value, success url, error url are not secure data you can retrieve them from the server, add to your payment form and submit a simple form to the payment gateway. In a similar way, the Transparent Redirect implemented in Magento. enter image description here

As you mentioned in your question, PayPal Express Checkout looks similar to Transparent Redirect but uses a different approach. enter image description here

Otherwise, if needed data is secure, you need to submit a request to Magento controller and after that from controller redirect to payment gateway.

Now, according to your questions:

  1. Again, it depends on payment gateway API and kind of data, which should be posted. You can redirect from a controller side or from the payment form after place order action, Magento provides afterPlaceOrder() method from Magento_Checkout/js/view/payment/default.js which triggers after placeOrder() method.
  2. You should use official Magento dev docs documentation because now it's only one supported way how to implement payment integrations.
  3. It depends on payment gateway API. If payment gateway has authorize and settle actions, possible you would need to implement authorize or sale(authorize&capture) commands, because when Magento places an order it calls authorize or capture (can be used for sale, this post describes how to implement it) payment actions. Your integration (according to the provided description) looks different and I suppose you don't need these commands.
  4. It looks similar to Continue to PayPal button action. You need to call Magento WEB API set-payment-information and on the success callback redirect the customer to the payment gateway.

    setPaymentMethodAction(messageContainer).done( function () { $.mage.redirect(...); } );

    UPD: to redirect customer via post you need to submit a form, the $.post() will just perform a request to the payment gateway without redirection. The set-payment-information required because this request sets selected payment method into a quote and without it, the order creation behavior might be unexpected because the quote can be in an inconsistent state.

  5. According to required data (success url and error url), you will need at least two controllers. As I understand, the payment gateway will return customer to them. In the success controller, you will need to do everything that you need and redirect to checkout/onepage/success. In the error controller, you will need to process errors from the payment gateway and redirect customer to some page, like shopping cart or other page.

Hopes, my explanation is clearly enough and will help you.

OTHER TIPS

Here's my final solution to help others that are struggling with this:

Part 1: Create a basic module/payment method (http://devdocs.magento.com/guides/v2.2/howdoi/checkout/checkout_payment.html & https://www.mageplaza.com/magento-2-create-payment-method/)

Part 2: I didn't go with the authorize/capture commands approach, but with redirecting the customer with the afterPlaceOrder function.

Part 3: In your /app/code/{vendor}/{module}/view/frontend/web/js/view/payment/method-renderer/{custom-method.js}

define(
    [
        'jquery',
        'Magento_Checkout/js/view/payment/default',
        'mage/url',
        'Magento_Customer/js/customer-data',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Checkout/js/model/full-screen-loader',
        '{Vendor_Module}/js/{module}/form-builder'
    ],
    function ($, Component, url, customerData, errorProcessor, fullScreenLoader, formBuilder) {
        'use strict';
        return Component.extend({
            redirectAfterPlaceOrder: false, //This is important, so the customer isn't redirected to success.phtml by default
            defaults: {
                template: '{Vendor_Module}/payment/{module}'
            },
            getMailingAddress: function () {
                return window.checkoutConfig.payment.checkmo.mailingAddress;
            },

            afterPlaceOrder: function () {
                var custom_controller_url = url.build('{frontname/path/action}'); //your custom controller url
                $.post(custom_controller_url, 'json')
                .done(function (response) {
                    customerData.invalidate(['cart']);
                    formBuilder(response).submit(); //this function builds and submits the form
                })
                .fail(function (response) {
                    errorProcessor.process(response, this.messageContainer);
                })
                .always(function () {
                    fullScreenLoader.stopLoader();
                });
            }

        });
    }
);

What this does is to call your custom controller after the customer clicked the Place Order button.

In the custom controller, you get all the form data that you need to post.

After that you call the form-builder function and submit/redirect the customer to the payment provider's URL.

Part 4: Custom controller (snippet of the important parts):

namespace {Vendor}\{Module}\Controller\{CustomPaymentMethodName};

use Magento\Checkout\Model\Session;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Sales\Model\Order;

class PostData extends \Magento\Framework\App\Action\Action
...
public function __construct(
        Context $context,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, 
        Session $checkoutSession, 
        \Magento\Framework\Locale\Resolver $store,
        \Magento\Framework\UrlInterface $urlBuilder,
        \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, 
        \Magento\Directory\Model\CountryFactory $countryFactory
    ) {
        parent::__construct($context);
        $this->scopeConfig = $scopeConfig; //Used for getting data from System/Admin config
        $this->checkoutSession = $checkoutSession; //Used for getting the order: $order = $this->checkoutSession->getLastRealOrder(); And other order data like ID & amount
        $this->store = $store; //Used for getting store locale if needed $language_code = $this->store->getLocale();
        $this->urlBuilder = $urlBuilder; //Used for creating URLs to other custom controllers, for example $success_url = $this->urlBuilder->getUrl('frontname/path/action');
        $this->resultJsonFactory = $resultJsonFactory; //Used for returning JSON data to the afterPlaceOrder function ($result = $this->resultJsonFactory->create(); return $result->setData($post_data);)
    }

public function execute()
    {
    //Your custom code for getting the data the payment provider needs
    ...
    //Structure your return data so the form-builder.js can build your form correctly
    $post_data = array(
        'action' => $form_action_url,
        'fields' => array (
            'shop_id' => $shop_id,
            'order_id' => $order_id,
            'api_key' => $api_key,
            //add all the fields you need
        )
    );
    $result = $this->resultJsonFactory->create();

    return $result->setData($post_data); //return data in JSON format
}

Part 5: Create the form builder:

define(
    [
        'jquery',
        'underscore',
        'mage/template'
    ],
    function ($, _, mageTemplate) {
        'use strict';

        /* This is the form template used to generate the form, from your Controller JSON data */
        var form_template =
            '<form action="<%= data.action %>" method="POST" hidden enctype="application/x-www-form-urlencoded">' +
            '<% _.each(data.fields, function(val, key){ %>' +
            '<input value="<%= val %>" name="<%= key %>" type="hidden">' +
            '<% }); %>' +
            '</form>';

        return function (response) {
            var form = mageTemplate(form_template);
            var final_form = form({
                data: {
                    action: response.action, //notice the "action" key is the form action you return from controller
                    fields: response.fields //fields are inputs of the form returned from controller
                }
            });
            return $(final_form).appendTo($('[data-container="body"]')); //Finally, append the form to the <body> element (or more accurately to the [data-container="body"] element)
        };
    }
);

Part 6: Create custom success/failure controllers, blocks & templates for the success and failure pages which payment providers usually request for returning the customer to your shop.

I'd recommend following this tutorial: https://www.mageplaza.com/magento-2-module-development/how-to-create-controllers-magento-2.html

As per my research on Payflow pro magento 2.3.3 send data to payment gateway with following way.

vendor/magento/module-payment/view/frontend/web/js/transparent.js

_postPaymentToGateway: function (data) {
    var tmpl,
        iframeSelector = '[data-container="' + this.options.gateway + '-transparent-iframe"]';
    tmpl = this.hiddenFormTmpl({
        data: {
            target: $(iframeSelector).attr('name'),
            action: this.options.cgiUrl,
            inputs: data
        }
    });
    $(tmpl).appendTo($(iframeSelector)).submit();
}

After success order with above function Post data to payment gateway.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top