How to add more payment method

(just apply for ideas shop NOT ideas cart)

- First : override file checkout/checkout.htm of component Checkout :

+ create folder 'checkout' in /themes/your_theme/partials

+ create file 'checkout.htm' in /themes/your_theme/partials/checkout

+ copy code of '/plugins/ideas/shop/components/checkout/checkout.htm' to '/themes/your_theme/partials/checkout/checkout.htm'

+ add more payment method

<div class="checkbox">
    <label><input type="radio" name="payment_method" value="1" checked>
        <span class="text-grey">Cash on delivery</span></label><br/>

    {% if paypal == enable %}
    <label><input type="radio" name="payment_method" value="2">
        <span class="text-grey">Paypal</span></label><br/>
    {% endif %}

    {% if stripe == enable %}
    <label><input type="radio" name="payment_method" value="3">
        <span class="text-grey">Stripe</span></label><br/>
    {% endif %}

    <label><input type="radio" name="payment_method" value="4">
    <span class="text-grey">Other</span></label><br/>
</div>

=> notice: value is > 3 (because cod: 1, paypal: 2, stripe: 3)

=> we add more payment method by override view of 'Checkout' component

- Create a plugin

- Create one components in this plugin, for example : AnotherPayment :

<?php namespace Ideas\Shop\Components;

use Cms\Classes\ComponentBase;

class AnotherPayment extends ComponentBase
{

    public function componentDetails()
    {
        return [
            'name' => 'Another payment example',
            'description' => 'Another payment example for check out'
        ];
    }

    public function onRun()
    {
        $this->addJs('/plugins/ideas/shop/assets/components/js/another_payment.js');
    }
}

=> this will load file another_payment.js

- in file /themes/your_themes/pages/checkout.htm:

title = "Checkout"
url = "/checkout"
layout = "default"

[Ideas\Shop\Components\Checkout Checkout]
[Ideas\Shop\Components\UserExtend UserExtend]
[Ideas\Shop\Components\AnotherPayment AnotherPayment]

==

{% component 'Checkout::checkout' %}

=> notice: we load component 'Checkout' before component 'AnotherPayment'

=> so, when we view source of page checkout, there is some code like:

<script src="http://october.local/plugins/ideas/shop/assets/components/js/jquery.validate.js"></script>
<script src="http://october.local/plugins/ideas/shop/assets/components/js/checkout.js"></script>
<script src="http://october.local/plugins/ideas/shop/assets/vendor/alertable/jquery.alertable.js"></script>
<script src="http://october.local/plugins/ideas/shop/assets/components/js/user.js"></script>
<script src="http://october.local/plugins/ideas/shop/assets/components/js/another_payment.js"></script>

=> we have to load file checkout.js then load another_payment.js

- in file another_payment.js, we will handle params pass from event 'beforeSaveOrder' and 'afterSaveOrder' (defined in checkout.js), code example:

$(document).ready(function() {

    //call event beforeSaveOrder
    $(document).on('beforeSaveOrder', function(e, params) {
        console.log(params);
        console.log(params.params);
        //do something
        $('#checkout-div').waitMe({color: '#3c66c6'});
        $.saveOrder(params.params);
    });

    //call event afterSaveOrder
    $(document).on('afterSaveOrder', function(e, params) {
        console.log(params);
        console.log(params.params);
        console.log(params.orderId);
        //do something
        //delete cart
        var baseUrl = window.location.origin;
        sessionStorage.setItem('cart', '');
        window.location.href = baseUrl+'/'+$('#checkout_ok_url').val();

    });

});

 =>here we call function saveOrder() in checkout.js, of course, you can define another function to do whatever you want.

here is function to checkout in checkout.js

 $('#checkout-button').click(function() {
    ........
    var paymentMethodId = $('input[name="payment_method"]:checked').val();
    if (paymentMethodId == undefined) {
        $.notify('You have to choose a payment method');
    } else {
        ...........
        params.user_id = $('#user_id').val();
        params.shipping_cost = parseFloat($('#'+shipMoneyDiv).text());
        params.total = $.getTotalPriceFinal();
        params.order_status_id = 1;
        var cartRs = sessionStorage.getItem('cart');
        params.cart = JSON.parse(cartRs);
        params.payment_method_id = paymentMethodId;
        params.form_payment_not_login = $('#form-payment-not-login').serializeArray();
        params.shipping_rule_id = $('input[name="shipping_rule"]:checked').attr('attr-id');
        params.currency_code = $('#currency_code').text();
        params.coupon_id = $('#coupon_id').val();
        params.coupon_total = $('#coupon-reduce').text();
        params.comment = $('#comment').val();
        sessionStorage.setItem('orderParams', JSON.stringify(params));
        //just save order if it not stripe checkout
        if (params.payment_method_id == STRIPE) {
            $.checkoutStripe(params);
        } else if (params.payment_method_id == CASH_ON_DELIVERY
            || params.payment_method_id == PAYPAL) {
            $('#checkout-div').waitMe({color: waitMeColor});
            $.saveOrder(params);
        } else {
            // create hook for before save order for another payment
            $(document).trigger( "beforeSaveOrder", {params:params} );
        }

    }

});

=> notice this code : 

$(document).trigger( "beforeSaveOrder", {params:params} );

=> we trigger event 'beforeSaveOrder' if payment method is not one of Cod, paypal or stripe

 

The flow when checkout 

=> call function saveOrder() in checkout.js

=> call onSaveOrder() in /plugins/ideas/shop/components/Checkout.php:

/**
 * Save order get params
 */
public function saveOrderGetParams($params)
{
    $formPaymentInfo = $params['form_payment_not_login'];
    $formPaymentInfo = $this->convertFormPaymentInfo($formPaymentInfo);
    $orderId = \Ideas\Shop\Facades\Order::saveOrder($params, $formPaymentInfo);
    return $orderId;
}

/**
 * On save order
 */
public function onSaveOrder()
{
    $post = post();
    $orderId = $this->saveOrderGetParams($post);
    return response()->json($orderId);
}

=> from onSaveOrder(), it will call saveOrderGetParams() in same file

=> then, it call \Ideas\Shop\Facades\Order::saveOrder($params, $formPaymentInfo)

 /**
 * Save order
 */
public static function saveOrder($post, $formPaymentInfo)
{
    DB::beginTransaction();
    try {
        $order = self::saveOrderData($post, $formPaymentInfo);
        if ($post['coupon_id'] != 0) {
            self::saveCouponIdHistory($order['orderId'], $post);
        }
        DB::commit();
        //send mail
        self::sendOrderMail($order, $post);
        return $order['orderId'];
    } catch (\Exception $e) {
        DB::rollBack();
        return self::SAVE_ORDER_FAIL;
    }
}

=> As you can see, it will call 3 functions: 

self::saveOrderData($post, $formPaymentInfo)
self::saveCouponIdHistory($order['orderId'], $post)
self::sendOrderMail($order, $post)

=> 3 functions do 3 tasks: save order, save coupon history (if customer use coupon), send email

=> then it will return id of order (that saved to database)

=> then in checkout.js, it will run this code:

 //Save order
jQuery.saveOrder = function(params) {
    $.request('onSaveOrder', {
        data: params,
        success: function(orderId) {
            if (orderId != SAVE_ORDER_FAIL) {
                if (params.payment_method_id == CASH_ON_DELIVERY) {
                    sessionStorage.setItem('cart', '');
                    window.location.href = baseUrl+'/'+$('#checkout_ok_url').val();
                    return;
                }
                if (params.payment_method_id == PAYPAL) {
                    $.checkoutPaypal(params, orderId);
                    sessionStorage.setItem('cart', '');
                    return;
                }
                // create hook for after save order  for another payment
                $(document).trigger( "afterSaveOrder", {params:params, orderId:orderId} );
            } else {
                window.location.href = baseUrl+'/'+$('#checkout_fail_url').val();
            }
        }
    });
};

=> here we have event 'afterSaveOrder' to handle sessionStorage of cart