--- a/onepay-payment-gateway-for-woocommerce/index.php
+++ b/onepay-payment-gateway-for-woocommerce/index.php
@@ -3,7 +3,7 @@
Plugin Name: onepay Payment Gateway For WooCommerce
Plugin URI: https://github.com/onepay-srilanka/onepay-woocommerce
Description: onepay Payment Gateway allows you to accept payment on your Woocommerce store via Visa, MasterCard, AMEX, & Lanka QR services.
-Version: 1.1.2
+Version: 1.1.3
Author: onepay
Author URI: https://www.onepay.lk
License: GPLv3 or later
@@ -54,12 +54,11 @@
$this->redirect_page = $this->settings['redirect_page']; // Define the Redirect Page.
- $this->msg['message'] = '';
+ $this->msg['message'] = '';
$this->msg['class'] = '';
- add_action('init', array(&$this, 'check_onepay_response'));
add_action('woocommerce_api_' . strtolower(get_class($this)), array($this, 'check_onepay_response')); //update for woocommerce >2.0
if ( version_compare(WOOCOMMERCE_VERSION, '2.0.0', '>=' ) ) {
@@ -215,6 +214,7 @@
'sdk_type' => "woocommerce",
'authorization' => sanitize_text_field($this->auth_token),
'amount' => number_format(floatval($order -> get_total()), 2, '.', ''),
+
);
$result_body = json_encode($hash_args,JSON_UNESCAPED_SLASHES);
@@ -241,10 +241,10 @@
$is_correct=1;
}
- $this->liveurl .= "?hash=$hash_result";
+ $payment_url = $this->liveurl . "?hash=$hash_result";
- return ' <form action="'.$this->liveurl.'" method="post" id="onepay_payment_form">
+ return ' <form action="'.$payment_url.'" method="post" id="onepay_payment_form">
' . implode('', $onepay_args_array) . '
<input type="submit" class="button-alt" id="submit_onepay_payment_form" value="'.__('Pay via onepay', 'onepayipg').'" /> <a class="button cancel" href="'.$order->get_cancel_order_url().'">'.__('Cancel order & restore cart', 'onepayipg').'</a>
<script type="text/javascript">
@@ -303,104 +303,183 @@
**/
function check_onepay_response(){
+ // Validate required parameters
if( !isset($_REQUEST['merchant_transaction_id']) || !isset($_REQUEST['hash']) || !isset($_REQUEST['onepay_transaction_id']) ) {
- return; // Do nothing if these parameters are missing
+ wp_die( esc_html__('Invalid request parameters.', 'onepayipg'), esc_html__('Payment Error', 'onepayipg'), array( 'response' => 400 ) );
}
- global $woocommerce;
- echo '<p><strong>' . esc_html__('Thank you for your order.', 'onepayipg').'</strong><br/>' . esc_html__('You will be redirected soon....', 'onepay').'</p>';
-
+ global $woocommerce;
+ $order_id = absint($_REQUEST['merchant_transaction_id']);
+ $hash_string = sanitize_text_field($_REQUEST['hash']);
+ $onepay_transaction_id = sanitize_text_field($_REQUEST['onepay_transaction_id']);
+ $status = isset($_REQUEST['status']) ? (int)$_REQUEST['status'] : 0;
+
+ // Validate order ID
+ if( empty($order_id) ) {
+ wp_die( esc_html__('Invalid order ID.', 'onepayipg'), esc_html__('Payment Error', 'onepayipg'), array( 'response' => 400 ) );
+ }
- if( isset($_REQUEST['merchant_transaction_id']) && isset($_REQUEST['hash']) && isset($_REQUEST['onepay_transaction_id']) ){
- $order_id = sanitize_text_field($_REQUEST['merchant_transaction_id']);
- if($order_id != ''){
- try{
- $order = wc_get_order( $order_id );
- $status = (int)sanitize_text_field($_REQUEST['status']);
- $hash_string = sanitize_text_field($_REQUEST['hash']);
-
- $request_args = array(
- 'onepay_transaction_id' => sanitize_text_field($_REQUEST['onepay_transaction_id']),
- 'merchant_transaction_id' => sanitize_text_field($_REQUEST['merchant_transaction_id']),
- 'status' => $status
- );
+ // Get the order
+ $order = wc_get_order( $order_id );
+ if( !$order ) {
+ wp_die( esc_html__('Order not found.', 'onepayipg'), esc_html__('Payment Error', 'onepayipg'), array( 'response' => 404 ) );
+ }
+ // Verify the order belongs to this payment gateway
+ if( $order->get_payment_method() !== $this->id ) {
+ wp_die( esc_html__('Invalid payment method for this order.', 'onepayipg'), esc_html__('Payment Error', 'onepayipg'), array( 'response' => 400 ) );
+ }
- $json_string=json_encode($request_args);
-
-
- $verified = true;
- if ($hash_string) {
+ // Verify salt_string is configured
+ if( empty($this->salt_string) ) {
+ $order->add_order_note('Security Error: Hash salt not configured.');
+ wp_die( esc_html__('Payment gateway configuration error.', 'onepayipg'), esc_html__('Payment Error', 'onepayipg'), array( 'response' => 500 ) );
+ }
+ // Build request arguments for hash verification (matching gateway callback format)
+ // Gateway callback format matches: { onepay_transaction_id, merchant_transaction_id, status }
+ $request_args = array(
+ 'onepay_transaction_id' => $onepay_transaction_id,
+ 'merchant_transaction_id' => (string)$order_id,
+ 'status' => $status
+ );
-
+ // Gateway hash calculation (matching gateway's jsonToShaValidator function):
+ // JavaScript: JSON.stringify -> replace("'", """) -> replace(" ", "") -> remove newlines -> sha256
+ // Note: JavaScript replace() without 'g' flag only replaces first occurrence, but gateway code may vary
+
+ // Generate JSON (matching JavaScript JSON.stringify)
+ $json_string = json_encode($request_args, JSON_UNESCAPED_SLASHES);
+
+ if( empty($json_string) ) {
+ $order->add_order_note('Error: Failed to generate JSON for hash verification.');
+ wp_die( esc_html__('Payment verification error.', 'onepayipg'), esc_html__('Payment Error', 'onepayipg'), array( 'response' => 500 ) );
+ }
+
+ // Clean the JSON string to match gateway's format exactly
+ // Gateway JavaScript does: JSON.stringify -> replace("'", """) -> replace(" ", "") -> remove newlines
+ $cleaned_json = $json_string;
+
+ // Step 1: Replace single quotes with double quotes (JSON shouldn't have single quotes, but gateway does this)
+ $cleaned_json = str_replace("'", '"', $cleaned_json);
+
+ // Step 2: Remove all spaces
+ $cleaned_json = str_replace(' ', '', $cleaned_json);
+
+ // Step 3: Remove newlines and special characters
+ // JavaScript regex: /[rnx0Bx0Cu0085u2028u2029]+/g
+ // Remove: r, n, x0B, x0C, x85, u2028, u2029
+ // Use a simple approach that definitely works
+ $cleaned_json = str_replace(array("r", "n", "x0B", "x0C", "x85"), '', $cleaned_json);
+ // Remove Unicode separators if they exist (rare, but gateway checks for them)
+ $cleaned_json = str_replace(array("xE2x80xA8", "xE2x80xA9"), '', $cleaned_json); // UTF-8 encoded u2028 and u2029
+
+ // Safety check: ensure cleaned JSON is not empty
+ if( empty($cleaned_json) ) {
+ $order->add_order_note('Error: Cleaned JSON is empty. Original JSON: ' . $json_string);
+ wp_die( esc_html__('Payment verification error.', 'onepayipg'), esc_html__('Payment Error', 'onepayipg'), array( 'response' => 500 ) );
+ }
+
+ // Calculate hash (gateway does NOT use salt_string for callback verification)
+ $calculated_hash = hash('sha256', $cleaned_json);
- $json_hash_result = hash('sha256',$json_string);
-
+ // Verify hash using timing-safe comparison
+ $verified = false;
+ if( !empty($hash_string) && hash_equals($calculated_hash, $hash_string) ) {
+ $verified = true;
+ }
+ // Additional security validation (since callback hash doesn't use salt)
+ if( $verified ) {
+ // Verify order belongs to this gateway
+ if( $order->get_payment_method() !== $this->id ) {
+ $verified = false;
+ $order->add_order_note('Security Error: Order payment method mismatch.');
+ }
+
+ // Verify order total is valid
+ $order_total = floatval($order->get_total());
+ if( $order_total <= 0 ) {
+ $verified = false;
+ $order->add_order_note('Security Error: Invalid order total detected.');
+ }
+ }
- if (($hash_string != $json_hash_result)) {
- $verified = false;
-
- }
- }
-
- $trans_authorised = false;
-
- if( $order->status !=='completed' || $order->status !=='wc-completed' ){
- if($verified){
-
- if($status==1){
- $trans_authorised = true;
- $this->msg['message'] = "Thank you for shopping with us. Your account has been charged and your transaction is successful.";
- $this->msg['class'] = 'woocommerce-message';
- if($order->status == 'processing' || $order->status =='wc-processing'){
- $order->add_order_note('onepay transaction ID: '.sanitize_text_field($_REQUEST['onepay_transaction_id']));
- }else{
- $order->payment_complete();
- $order->add_order_note('onepay payment successful.<br/>onepay transaction ID: '.sanitize_text_field($_REQUEST['onepay_transaction_id']));
- $woocommerce->cart->empty_cart();
- }
- }else if($status==0){
- $trans_authorised = true;
- $this->msg['class'] = 'woocommerce-error';
- $this->msg['message'] = "Thank you for shopping with us. However, the transaction has been failed. We will keep you informed";
- $order->add_order_note('Transaction ERROR.'.sanitize_text_field($_REQUEST['onepay_transaction_id']));
- $order->update_status('failed');
- $woocommerce -> cart -> empty_cart();
- }
-
- }else{
- $this->msg['class'] = 'error';
- $this->msg['message'] = "Security Error. Illegal access detected.";
- $order->add_order_note('Checksum ERROR: '.json_encode($json_string));
- }
-
- if($trans_authorised==false){
- $order->update_status('failed');
- }
+ // Log hash verification details for debugging (only if verification failed)
+ if( !$verified ) {
+ $debug_info = array(
+ 'received_hash' => substr($hash_string, 0, 20) . '...',
+ 'calculated_hash' => substr($calculated_hash, 0, 20) . '...',
+ 'original_json' => $json_string,
+ 'cleaned_json' => $cleaned_json,
+ 'cleaned_length' => strlen($cleaned_json),
+ 'request_args' => $request_args
+ );
+ $order->add_order_note('Hash verification failed. Debug: ' . print_r($debug_info, true));
+ } else {
+ // Log successful verification for audit trail
+ $order->add_order_note('Hash verification successful. onepay transaction ID: ' . $onepay_transaction_id);
+ }
- }
+ // Check if order is already completed
+ $order_status = $order->get_status();
+ if( in_array($order_status, array('completed', 'processing', 'wc-completed', 'wc-processing'), true) ) {
+ // Order already processed, just redirect
+ if ( ($this->redirect_page == '' || $this->redirect_page == 0) ) {
+ $redirect_url = $this->get_return_url( $order );
+ } else {
+ $redirect_url = get_permalink( $this->redirect_page );
+ }
+ wp_redirect( esc_url_raw($redirect_url) );
+ exit;
+ }
- }catch(Exception $e){
- $msg = "Error";
+ $trans_authorised = false;
+
+ if( $verified ) {
+ if( $status == 1 ) {
+ // Payment successful
+ $trans_authorised = true;
+ $this->msg['message'] = esc_html__("Thank you for shopping with us. Your account has been charged and your transaction is successful.", 'onepayipg');
+ $this->msg['class'] = 'woocommerce-message';
+
+ if( in_array($order_status, array('processing', 'wc-processing'), true) ) {
+ $order->add_order_note('onepay transaction ID: ' . $onepay_transaction_id);
+ } else {
+ $order->payment_complete();
+ $order->add_order_note('onepay payment successful.<br/>onepay transaction ID: ' . $onepay_transaction_id);
+ $woocommerce->cart->empty_cart();
}
+ } else if( $status == 0 ) {
+ // Payment failed
+ $trans_authorised = true;
+ $this->msg['class'] = 'woocommerce-error';
+ $this->msg['message'] = esc_html__("Thank you for shopping with us. However, the transaction has been failed. We will keep you informed", 'onepayipg');
+ $order->add_order_note('Transaction ERROR. onepay transaction ID: ' . $onepay_transaction_id);
+ $order->update_status('failed');
+ $woocommerce->cart->empty_cart();
}
+ } else {
+ // Hash verification failed
+ $this->msg['class'] = 'error';
+ $this->msg['message'] = esc_html__("Security Error. Illegal access detected.", 'onepayipg');
+ $order->add_order_note('Checksum ERROR: Invalid hash verification. Received: ' . $hash_string . ', Expected: ' . $calculated_hash);
+ }
-
-
+ if( $trans_authorised == false && $verified == false ) {
+ $order->update_status('failed');
}
- if ( ($this->redirect_page == '' || $this->redirect_page == 0) && isset($_REQUEST['merchant_transaction_id']) ) {
-
- $redirect_url=$this->get_return_url( $order );
- } else if($this->redirect_page == '' || $this->redirect_page == 0) {
- $redirect_url = get_permalink( get_option('woocommerce_myaccount_page_id') );
- }else{
+
+ // Display message and redirect
+ echo '<p><strong>' . esc_html__('Thank you for your order.', 'onepayipg').'</strong><br/>' . esc_html__('You will be redirected soon....', 'onepay').'</p>';
+
+ if ( ($this->redirect_page == '' || $this->redirect_page == 0) ) {
+ $redirect_url = $this->get_return_url( $order );
+ } else {
$redirect_url = get_permalink( $this->redirect_page );
}
-
wp_redirect( esc_url_raw($redirect_url) );
exit;