5

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?

  • "where the customer can complete the credit card payment" is this page should be provided by payment gateway or by Magento side? – joni jones Jan 10 '18 at 16:00
  • The page is provided by the payment provider/processor. – Lez Jan 17 '18 at 21:47
  • @Lez its depending upon payment gateway & its API which you are using. Please share some more details so i will provide step for that in details. – Yogesh Jan 18 '18 at 5:57
  • @Yogesh I've provided more details with an edit. Hopefully it's all clear now. – Lez Jan 18 '18 at 16:29
  • @Lez what logic you need how to get payment form data in Payment.php file ? – Yogesh Jan 20 '18 at 11:04
5
+100

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.

  • I think he need help in programming stuff @joni jones – Yogesh Jan 18 '18 at 9:06
  • @Yogesh, from questions section it's not clear. – joni jones Jan 18 '18 at 10:48
  • Thank you very much for your detailed answer! It helped clarify several doubts I had. I've expanded my question with an edit and a few more details. If you could take a look, would be much appreciated! – Lez Jan 18 '18 at 16:28
  • @Lez, please, see my updated answer. – joni jones Jan 18 '18 at 17:00
4

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

  • Hi i am accepting your wonderful answer. can you tell me how to pass some value in http header with this post data. – Sarvesh Tiwari Mar 6 '18 at 15:07
  • @ErSarveshVTiwari Hey, unfortunately I don't know the answer to that question. You can maybe check this question to find a solution: stackoverflow.com/questions/9516865/… – Lez Mar 6 '18 at 16:18
  • Please give me an example for var custom_controller_url = url.build('{frontname/path/action}'); – Mahesh Jul 26 '18 at 10:06
  • It depends on your controller. A live example could be something like: var paypal_redirect_url = url.build('paypal/paypal/redirect'). This basically just calls the controller url. If you are not sure what to put here, you should maybe check some M2 controller tutorials. – Lez Jul 26 '18 at 10:55
  • @Lez can you please provide me the code, tried with your instruction, not working for me,great help if you can help me in this – Mujahidh Nov 9 '18 at 10:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.