I'm trying to add a custom payment gateway in my Woocommerce website. The flow will be like this:
- 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.
- In the HPP, there is a form where the user can introduce his card data.
- 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().'¤cy='.$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.