0
votes

I'm trying to add a custom payment gateway in my Woocommerce website. The flow will be like this:

  1. After the user clicks on "Place order" button, he is redirected to the hosted payment page (HPP) of my payment processor. In order to achieve that, I must send some data (such as the merchant id, a hash, the order amount, etc.) through a hidden form which the payment processor needs to make the redirection to the HPP.
  2. In the HPP, there is a form where the user can introduce his card data.
  3. If everything is ok, the user is redirected to thank you page, otherwise he will be redirected to a fail page, or an error message shows up, I don't care.

This is what I have achieved so far: When the user clicks on "Place order", a new order with the status "on hold" is created and he is redirected to a page called 'prueba.php'. This page only contains the hidden form with the data the payment processor needs to redirect the user to their gateway. This form is submitted automatically once the page is loaded (I have the feeling that this isn't the safest thing to do since if you open the element inspector, you can see the hidden inputs and their values, but I didn't know any other better ways to make this work). This is what I have in the main file of the plugin:

add_action( 'plugins_loaded', 'custom_init_gateway_class' );
function custom_init_gateway_class() {

    class WC_My_Gateway extends WC_Payment_Gateway {
        /**
         * Constructor
         */
        public function __construct() {
            $this->id = 'mygateway';
            $this->has_fields = true;
            $this->method_title = 'My Gateway';
            $this->method_description = 'Description of payment gateway'; 
            $this->supports = array(
                'products'
            );

            // Method 
            $this->init_form_fields();
            $this->init_settings();
            $this->title = $this->get_option( 'title' );
            $this->description = $this->get_option( 'description' );
            $this->enabled = $this->get_option( 'enabled' );

            add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
        }

        /**
         * Plugin options
         */
        public function init_form_fields(){
            $this->form_fields = array(
                'enabled' => array(
                    'title' => __( 'Enable/Disable', 'woocommerce' ),
                    'type' => 'checkbox',
                    'label' => __( 'Enable Card Payment', 'woocommerce' ),
                    'default' => 'yes'
                ),
                'title' => array(
                    'title' => __( 'Title', 'woocommerce' ),
                    'type' => 'text',
                    'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
                    'default' => __( 'Credit Payment', 'woocommerce' ),
                    'desc_tip'      => true,
                ),
                'description' => array(
                    'title' => __( 'Customer Message', 'woocommerce' ),
                    'type' => 'textarea',
                    'default' => ''
                )
            );

        }

        /*
         * Processing the payments
         */
        public function process_payment( $order_id ) {
            global $woocommerce;
            $order = new WC_Order( $order_id );

            $order->update_status('on-hold', __( 'Awaiting card payment', 'woocommerce' ));

            return array(
                'result' => 'success',
                'redirect' => 'https://mydomain.example/prueba.php?price='.$order->get_total().'&currency='.$order->get_currency().'&order_id='.$order->get_id()
            );

        }

    }

}

function custom_add_gateway_class( $methods ) {
    $methods[] = 'WC_My_Gateway'; 
    return $methods;
}
add_filter( 'woocommerce_payment_gateways', 'custom_add_gateway_class' );

The hidden form in "prueba.php". In the input "MERCHANT_RESPONSE_URL" I must specify which url I want the payment processor to send the data to, after the payment is completed:

<form method="POST" action="https://paymentprocessorgateway.example" name="respuesta" style="display: none;">
        <input type="hidden" name="TIMESTAMP" value="<?php echo $timestamp; ?>">
        <input type="hidden" name="MERCHANT_ID" value="12345">
        <input type="hidden" name="AMOUNT" value="<?php echo $Amount; ?>">
        <input type="hidden" name="CURRENCY" value="<?php echo $Currency; ?>">
        <input type="hidden" name="SIGNATURE" value="<?php echo $hash; ?>">
        <input type="hidden" name="COMMENT1" value="<?php echo $order_id; ?>">
        <input type="hidden" name="MERCHANT_RESPONSE_URL" value="https://mydomain.example/response.php">
        <input type="submit" value="Click here to buy">
    </form>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script type="text/javascript">
        function sendForm() {   
            $("form").submit();  
        }
        sendForm();
    </script>

After this form is sent, the user is redirected to the payment processor gateway where he can write and send his card data, and then the processor sends some POST data to "response.php". In this page, I compare a hash I receive from the payment processor with a hash I generate myself (with some of the other POST values received from the payment processor).

What I'd like to achieve is redirecting the user to the thank you page and change the order status to 'completed' if everything is correct, or showing an error message if it isn't. Also, I would like that the order is created here (if the payment is correct), not when the client clicks on 'Place order' button (I thought that knowing the id of the order, I will be able to change its status wherever I needed, or that I could create a new order anywhere, but I must be doing everything wrong since I don't know very well how woocommerce and OOP PHP works). I tried writing this in "response.php", but the processor's gateway throws an error that says the transaction went well but it couldn't connect to the url specified in MERCHANT_RESPONSE_URL:

//Generating the hash with data received from processor
$respuesta = $_POST['TIMESTAMP'].".".$_POST['MERCHANT_ID'].".".$_POST['ORDER_ID'].".".$_POST['RESULT'];
$my_hash = sha1(sha1($respuesta);

global $woocommerce;
$order = wc_get_order( $order_id );

    if ($my_hash != $_POST['HASH']) { //$_POST['HASH'] is the hash sent by the processor.
            wc_add_notice(  'Please try again.', 'error' );
            return;
         } else {
            $order->payment_complete();

            $woocommerce->cart->empty_cart();

            return array(
                'result' => 'success',
                'redirect' => $this->get_return_url( $order )
            );
        }

If I delete the woocommerce related variables and use something like this to see if it's working, the alerts are printed correctly:

if ($my_hash != $_POST['HASH']) {
    echo '<script>alert("Wrong transaction");</script>';
} else {
    echo '<script>alert("Correct transaction");</script>';
}

It's like I can't use woocommerce related variables there. I guess I must attach this to some hook or something, but I don't know which or where, I have the feeling I have been making many mistakes along the way. If someone could throw some light on how to handle the response of the payment processor and integrate it with Woocommerce flow I would be eternally grateful.

1
See my comment below, not sure if you get notifications if I comment on my answer. - Snuwerd

1 Answers

0
votes

First of all. If you're supposed to submit a hidden form, I wouldnt worry about security when it comes to the hidden or not aspect. Anything that is client side can always be viewed/tampered with anyway, so thats not where security measures should be taken imo.

Your last block of code mentions $old_hash, but it is empty? At least, your code block doesn't set it. What part of this code isn't working you say? Did you try logging to something to like an error log in it to see if it is called? (Like error_log('message'); )

You mentioned that HPP sends back a hash to a page of your choice, so I'd say response.php is probably the page where the user is sent after paying.

(Also, it is almost bed time for me, so apologies if I stop responding.)