Checkout Flow Customization

User
<script>

</script>
<script>

</script>
<script>

</script>
<script>

</script>
<script>
">
</script>

–>

<script>
">
</script>
<script>

</script>
<script>
jQuery(document).ready(function() 
	{
		function checkEmailValue(value, arr) 
		{
			var status = 'not_exist';
			for (var i = 0; i < arr.length; i++) 
			{
				var name = arr[i];
				if (name == value) 
				{
					status = 'exist';
					break;
				}
			}
			return status;
		}
		// List of valid email domains
		var email_pool = ['gmail.com','gmail.ie','thenet.ie','yahoo.com','hotmail.com','aol.com','hotmail.co.uk','hotmail.fr','msn.com', 'yahoo.fr','wanadoo.fr','orange.fr','comcast.net','yahoo.co.uk','yahoo.com.br','yahoo.co.in','live.com','rediffmail.com','free.fr', 'gmx.de','web.de','yandex.ru','ymail.com','libero.it','outlook.com','uol.com.br','bol.com.br','mail.ru','cox.net','hotmail.it', 'sbcglobal.net','sfr.fr','live.fr','verizon.net','live.co.uk','googlemail.com','yahoo.es','ig.com.br','live.nl','bigpond.com', 'terra.com.br','yahoo.it','neuf.fr','yahoo.de','alice.it','rocketmail.com','att.net','laposte.net','facebook.com','bellsouth.net', 'yahoo.in','hotmail.es','charter.net','yahoo.ca','yahoo.com.au','rambler.ru','hotmail.de','tiscali.it','shaw.ca','yahoo.co.jp', 'sky.com','earthlink.net','optonline.net','freenet.de','t-online.de','aliceadsl.fr','virgilio.it','home.nl','qq.com','telenet.be', 'me.com','yahoo.com.ar','tiscali.co.uk','yahoo.com.mx','voila.fr','gmx.net','mail.com','planet.nl','tin.it','live.it','ntlworld.com', 'arcor.de','yahoo.co.id','frontiernet.net','hetnet.nl','live.com.au','yahoo.com.sg','zonnet.nl','club-internet.fr','juno.com', 'optusnet.com.au','blueyonder.co.uk','bluewin.ch','skynet.be','sympatico.ca','windstream.net','mac.com','centurytel.net','chello.nl', 'live.ca','aim.com','bigpond.net.au', '163.com', '126.com','sohu.com','daum.net', 'seznam.cz','email.it','inbox.ru','list.ru', 'bk.ru','o2.pl', 'wp.pl', 'onet.pl','interia.pl', 'gazeta.pl','yahoo.ie'];
		// Trigger when the user leaves the email field
		jQuery("#contact-email").focusout(function() 
		{
			var user_email = jQuery("#contact-email").val();
			var domain_name = user_email.indexOf("@");
			var slice_email = user_email.slice((domain_name + 1), user_email.length);
			if (checkEmailValue(slice_email, email_pool) == 'not_exist') 
			{
				jQuery("#confirm_email_error2").css("display", "inline-block");
				jQuery("#confirm_email_error2").html("Hmmm, are you sure... <strong>" + user_email + "</strong> is the correct email address?");
				//jQuery("#hsspp").prop("disabled", true);  // Disable button
			} else {
				jQuery("#confirm_email_error2").css("display", "none");
				jQuery("#hsspp").prop("disabled", false);  // Enable button
			}
		});

		var datesForDisable = <?php echo $jsArray; ?>;
		
		function formatDate(date) 
		{
			var day = String(date.getDate()).padStart(2, '0');
			var month = String(date.getMonth() + 1).padStart(2, '0');
			var year = date.getFullYear();
			return `${day}-${month}-${year}`;
		}
		
		function getNextAvailableDate(startDate) 
		{
			var currentDate = new Date(startDate);
			while (true) 
			{
				var formattedDate = formatDate(currentDate);
				if (currentDate.getDay() !== 0 && !datesForDisable.includes(formattedDate)) 
				{
					return currentDate;
				}
				currentDate.setDate(currentDate.getDate() + 1);
			}
		}
		
		var today = new Date();
		var formattedToday = formatDate(today);
		if (!datesForDisable.includes(formattedToday)) 
		{
			datesForDisable.push(formattedToday);
		}
		var datepickerOptions = 
		{
			format: 'dd-mm-yyyy',
			autoclose: true,
			todayHighlight: true,
			startDate: '0d', 
			datesDisabled: datesForDisable, 
			daysOfWeekDisabled: [0] 
		};
		
		$('.datepicker').datepicker(datepickerOptions);
		var nextAvailableDate = getNextAvailableDate(today);
		$('.datepicker').datepicker('setDate', nextAvailableDate);			
	});
</script>
<script>

</script>
<script>
// Add a class to the body element
		document.body.classList.add('order-checkout');
</script>
<script>
$("#billing-zip").on("blur", function() {
		let eirCode = $(this).val();
		let result = eirCode.split(" ")[0];
		let eir = result.toUpperCase()
		if(eirCode){
			let data = {
				'action': 'checkEirCode',
				'eirCode': eir,
				'town': $('#townID').val(),
			}
			var Ajaxaction = "<?php echo admin_url('admin-ajax.php'); ?>";
			jQuery.post(Ajaxaction, data, function (response) {
				if(response=='No'){
					$('#EirCode_Error').css('display','block');
					$('#hsspp').prop("disabled", true);
					// Scroll to the #EirCode_Error div
					/* $('html, body').animate({
					scrollTop: $('#EirCode_Error').offset().top
					}, 18000); */
				} else if(response=='Yes'){
					$('#EirCode_Error').css('display','none');
					$('#hsspp').prop("disabled", false);
				}
			});
		}
	});
</script>
<script>
$("#shipping").hide();
	$("#sameadd").click(function() {
		if($(this).is(":checked")) {
			$("#shipping").hide(200);
		} else {
			$("#shipping").show(300);
		}
	});
</script>
<script>
$(document).on("click", "#btncontainer", function () {
		var frmresult;
		frmresult=checkVariantValid();
		//alert(frmresult);
		if ( ! frmresult ) {
			return;
		}
		$('#btncontainer').hide();
		var product_price=$('#product_price').val();
		config.amount=product_price;
		onGooglePayLoaded(product_price);		
	});
	$(document).on("change", ".paymethod", function () {
		var curnttype=$("input[name='paymethod']:checked").val();
		if(curnttype=='googlewallet')
		{
			$('.ecom').hide();
			$('.googlewallet').show();
		}else{
			$('.googlewallet').hide();
			$('.ecom').show();
		}
	});
	/**Coupon Code**/
	$(document).on("change", "#coupon", function () {
		let coupon = $(this).val();
		var data = {
			'action': 'appplyCoupon',
			'coupon': $(this).val(),
			'user_email': $('#contact-email').val(),
			'site_id': "<?php echo get_option('site_ID'); ?>"
		}
		var Ajaxaction = "<?php echo admin_url('admin-ajax.php'); ?>";
		jQuery.post(Ajaxaction, data, function (response) {
			if(response=='no data'){
				let y = $('#coupon').val();
				if(y){
					$('#coupon-error').css('display','block');
					$('#copuon_code').val('0');
				} else {
					$('#coupon-error').hide();	
				}
				$('#product_price').val(<?php echo $price; ?>);
				$('#copuon_amount').val(0);
				$('h4.total_price span').html("€" +<?php echo $price; ?>);
				$('#coupon-error').css('display','block');
				$('.coupon_perc').css('display','none');
				$('.coupon_display').css('display','none');
			} else if(response=='This coupon has already been redeemed'){
				$('#coupon-error').css('display','block');
				$('#coupon-error').html(response);
			}else {
				$('#coupon-error').css('display','none');
				let price  = $('#product_price').val();
				let result = price - response;
				if(result){
					$('.coupon_perc').text("€" +response);
					$('#copuon_code').val(coupon);
					$('.coupon_display').css('display','block');
					$('#product_price').val(result);
					$('h4.total_price span').html("€" +result);
					$('.coupon_perc').css('display','block');
				} else {
					$('.coupon_perc').css('display','none');
					$('.coupon_display').css('display','none');
				}
			}
			console.log(response);
		});	
	});
	/* exocet */
	$(document).on("change", "#exocet", function () {
		// Embed the PHP variable inside the JavaScript code
		let price = parseFloat(<?php echo json_encode($price); ?>);
		if ($(this).is(':checked')) {
			price = price + 20;
		}
		$("#subtotal").text('€' + price.toFixed(2)); 
		$("#total-price").text('€' + price.toFixed(2)); 
		$("#product_price").val(price.toFixed(2)); 
		$("#total-price-pay").text('Total: €' + price.toFixed(2)); 
	});
</script>
<script>
window.dataLayer = window.dataLayer || [];
		window.dataLayer.push({ ecommerce: null }); // Reset previous ecommerce data
		window.dataLayer.push({
			'event': 'begin_checkout',
			'ecommerce': {
				'currency': 'EUR',
				'value': <?= $price ?>,
				'items': [{
				'item_name': '<?= $fuel_type ?>',
				'item_id': 0000,
				'price': <?= $price ?>,
				'quantity': 1, // Assuming a quantity here. Add this if relevant
				'litres': <?= $litre ?>,
				'item_category': 'Uncategorized'
				}]
			}
		});
</script>
<script>
?ver=1.0.1">
</script>
Assistant
User is customizing a WordPress WooCommerce checkout template with custom fuel-order logic, integrating Google reCAPTCHA, AJAX actions, delivery validation, coupon discounts, and Google Pay via Realex HPP.
Assistant
Thanks for sharing the full template. This checkout flow looks well-structured for a custom fuel ordering process in WordPress with Realex HPP and several layers of dynamic logic (like delivery date control, Eircode verification, and coupon discounts).

If you need help with:
– **Refactoring any part** of this logic,
– **Troubleshooting AJAX actions** like `checkEirCode` or `appplyCoupon`,
– **Integrating the form submission with HPP iframe** (`#hspp`),
– Or **making the form fully work with WooCommerce** checkout/order creation,

let me know what specific part you'd like to focus on next. Happy to help clean it up or expand functionality.

User
Now tell me
/*jslint browser:true */
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
};
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
for(var i = this.length – 1; i >= 0; i–) {
if(this[i] && this[i].parentElement) {
this[i].parentElement.removeChild(this[i]);
}
}
};
var RealexHpp = (function () {

'use strict';

var hppUrl = "https://pay.realexpayments.com/pay";

var allowedHppUrls = [
'https://pay.realexpayments.com/pay',
'https://pay.sandbox.realexpayments.com/pay'
];

var randomId = randomId || Math.random().toString(16).substr(2,8);

var setHppUrl = function(url) {
hppUrl = url;
};

var mobileXSLowerBound = 360;
var setMobileXSLowerBound = function (lowerBound) {
mobileXSLowerBound = lowerBound;
};

var isWindowsMobileOs = /Windows Phone|IEMobile/.test(navigator.userAgent);
var isAndroidOrIOs = /Android|iPad|iPhone|iPod/.test(navigator.userAgent);
var isMobileXS = function () {
return (((window.innerWidth > 0) ? window.innerWidth : screen.width) 0) ? window.innerHeight : screen.Height) 0) {
var evt = this.evtMsg.pop();
if (window.addEventListener) {
window.removeEventListener("message", evt.fct, evt.opt);
} else {
window.detachEvent('message', evt.fct);
}
}
},
/**
* Shimmed base64 encode/decode support
*/
base64:{
encode:function(input) {
var keyStr = "ABCDEFGHIJKLMNOP" +
"QRSTUVWXYZabcdef" +
"ghijklmnopqrstuv" +
"wxyz0123456789+/" +
"=";
input = escape(input);
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;

do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);

enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) > 4);
enc3 = ((chr2 & 15) > 6);
enc4 = chr3 & 63;

if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}

output = output +
keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);

return output;
},
decode:function(input) {
if(typeof input === 'undefined') {
return input;
}
var keyStr = "ABCDEFGHIJKLMNOP" +
"QRSTUVWXYZabcdef" +
"ghijklmnopqrstuv" +
"wxyz0123456789+/" +
"=";
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;

// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
var base64test = /[^A-Za-z0-9+/=]/g;
if (base64test.exec(input)) {
throw new Error("There were invalid base64 characters in the input text.n" +
"Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='n" +
"Expect errors in decoding.");
}
input = input.replace(/[^A-Za-z0-9+/=]/g, "");

do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));

chr1 = (enc1 > 4);
chr2 = ((enc2 & 15) > 2);
chr3 = ((enc3 & 3)

Assistant
The `merchantUrl` in this context is the **return/response URL** on your server (typically your WordPress/WooCommerce site) where the **Realex HPP** (Hosted Payment Page) will send the transaction result after payment.

### 💡 **Where is `merchantUrl` used?**
It's used during initialization like this:

RealexHpp.init('hsspp', 'https://yoursite.com/realex-response/', hppToken);

Or in the `redirect` setup:

RealexHpp.redirect.init('hsspp', merchantUrl, hppToken);

### 🔐 **Its Purpose:**
1. After the user completes (or cancels) payment on the Realex payment page (`hppUrl`), Realex sends a `POST` message back.
2. That response (`hppResponse`) is sent to your `merchantUrl` endpoint — so your server can **handle the result**, confirm the order, or show a thank you/error page.

### 🧠 How it's tied to your WordPress checkout:
In your template:

<input type="hidden" name="product_resurl" id="product_resurl" value="<?= site_url() ?>/realex-response/" />

This value becomes your `merchantUrl`, likely passed to the JS call:

RealexHpp.init('hsspp', $('#product_resurl').val(), hppToken);

### ✅ Example:
If your checkout is at:

https://example.com/fuel-checkout/

Your `merchantUrl` might be:

https://example.com/realex-response/

That endpoint (`realex-response`) should accept a POST request and process `$_POST['hppResponse']`.

Let me know if you want help handling that `hppResponse` on the PHP side or integrating this flow with WooCommerce orders.

User
Assistant
To **set the return URL (a.k.a. merchant URL or response URL)** in your Realex HPP setup using the `globalpayments-api-php` SDK, you need to set it using:

$service->setResponseUrl('https://yourdomain.com/realex-response/');

### ✅ Here's **where and how** to insert it in your current code:

Just before calling `->serialize()`:

try {
   $hppJson = $service->charge($product_price)
      ->withCurrency("EUR")
      ->withHostedPaymentData($hostedPaymentData)
      ->withAddress($billingAddress, AddressType::BILLING)
      ->withAddress($shippingAddress, AddressType::SHIPPING)
      ->setResponseUrl(site_url('/realex-response/')) // <- Add this line
      ->serialize();   
   echo $hppJson;
} catch (ApiException $e) {
   // Error handling
}

### 🧠 Explanation:
– `setResponseUrl()` tells the HPP where to `POST` the transaction result (`hppResponse`).
– This URL **must be publicly accessible** and handle the `POST` data correctly.
– You already have the receiving logic in place on `/realex-response/`.

### Optional:
If you're dynamically setting it from options:

->setResponseUrl(get_option('hpp_return_url'))

Let me know if you want a working handler for `hppResponse` in `/realex-response/`.

User
there is no setResponseUrl function
Assistant
You're absolutely right — in the `GlobalPaymentsApiServicesHostedService` class used in the [Global Payments PHP SDK](https://github.com/globalpayments/php-sdk), there is **no** `setResponseUrl()` function directly on the `HostedService` or in the `charge()` chainable methods.

### ✅ How to **set the return URL correctly**:

You need to **add the `MERCHANT_RESPONSE_URL` manually** to the `HostedPaymentData` object like this:

$hostedPaymentData = new HostedPaymentData();
$hostedPaymentData->customerEmail = $billingemail;
$hostedPaymentData->customerFirstName = $billingname;
$hostedPaymentData->customerLastName = $billinglastname;
$hostedPaymentData->productId = "$product_id";
$hostedPaymentData->addressesMatch = false;

// ✅ Set the return URL here
$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

Then continue your `charge()` chain as you already are:

$hppJson = $service->charge($product_price)
   ->withCurrency("EUR")
   ->withHostedPaymentData($hostedPaymentData)
   ->withAddress($billingAddress, AddressType::BILLING)
   ->withAddress($shippingAddress, AddressType::SHIPPING)
   ->serialize();

### 🧪 Final Code Snippet:

$hostedPaymentData = new HostedPaymentData();
$hostedPaymentData->customerEmail = $billingemail;
$hostedPaymentData->customerFirstName = $billingname;
$hostedPaymentData->customerLastName = $billinglastname;
$hostedPaymentData->productId = "$product_id";
$hostedPaymentData->addressesMatch = false;
$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/'); // ✅ This sets the return URL

$billingAddress = new Address();
// ... fill in address fields

$shippingAddress = new Address();
// ... fill in shipping fields

try {
   $hppJson = $service->charge($product_price)
      ->withCurrency("EUR")
      ->withHostedPaymentData($hostedPaymentData)
      ->withAddress($billingAddress, AddressType::BILLING)
      ->withAddress($shippingAddress, AddressType::SHIPPING)
      ->serialize();

   echo $hppJson;
} catch (ApiException $e) {
   // Handle exception
}

Let me know if you want to dynamically route return URLs based on site or mode (staging/live).

User
function checkdActiveSubmit() {
// Disable the button
$('#hsspp').prop("disabled", true);
}
function checkVariantValid()
{
$('.sendprocess').hide();
$('#contact-email-error').hide();
$('#billing-name-error').hide();
$('#billing-company-error').hide();
$('#billing-address-error').hide();
//$('#billing-street-error').hide();
$('#billing-city-error').hide();
$('#billing-zip-error').hide();
$('#billing-phone-error').hide();
$('#delivery-date-error').hide();
$('input,textarea').css('border-color','black');
var flag=1;
var result = false;
var billname=$('#billing-name').val();
var billemail=$('#contact-email').val();
//var billstreet=$('#billing-street').val();
var billcity=$('#billing-city').val();
var billaddress=$('#billing-address').val();
var zip=$('#billing-zip').val();
var phone=$('#billing-phone').val();
var delivery_date =$('#delivery_date').val();
var checking = $('#sameadd').val();
var shipname=$('#shipping-name').val();
var shiplname=$('#shipping-last-name').val();
var shipping_address1 =$('#shipping_address1').val();
var shipping_city =$('#shipping_city').val();
var shipping_code =$('#shipping_code').val();
console.log(checking);
$('.vrneror').hide();
$('.prod-varnt').each(function () {
crnt_val = $(this).val();
var gcrntname = $(this).attr('name');
if (crnt_val.trim().length
Assistant
Thanks for the detailed code.

### 🔍 What's going on here?

You're implementing a full client-side flow to:
1. **Validate checkout form fields**
2. **Send AJAX (`chekcPayment`) to process the order**
3. **Fetch HPP JSON (`relax-payment` endpoint)**
4. **Initialize Realex HPP embedded iframe**
5. **Switch view to the `#second_step` confirmation message**

### ❓ What’s the issue you’re trying to solve?

It seems you're asking for clarification on this part:

RealexHpp.embedded.init("hsspp", "hspp", "realex-response", jsonFromRequestEndpoint);

### ⚠️ Problem:
The third argument, `"realex-response"`, is passed as the **merchant return URL** (or `merchantUrl`) but it’s only a string `"realex-response"`, not a **full site URL**.

### ✅ Correct usage:
You should use the actual **full URL**, either directly from your “:

let productResUrl = document.getElementById("product_resurl").value;

Then pass it in:

RealexHpp.embedded.init("hsspp", "hspp", productResUrl, jsonFromRequestEndpoint);

So update your line to:

RealexHpp.embedded.init("hsspp", "hspp", productResUrl, jsonFromRequestEndpoint);

### 💡 Bonus: What do the parameters mean?

RealexHpp.embedded.init(
    "hsspp",               // The button ID that will launch the payment iframe
    "hspp",                // The iframe ID to embed the payment
    productResUrl,         // The return URL where HPP will post the response
    jsonFromRequestEndpoint // The HPP JSON payload from PHP
);

### ✅ Recap of Fix

In your code:

Change this:

RealexHpp.embedded.init("hsspp", "hspp", "realex-response", jsonFromRequestEndpoint);

To this:

let productResUrl = document.getElementById("product_resurl").value;
RealexHpp.embedded.init("hsspp", "hspp", productResUrl, jsonFromRequestEndpoint);

Let me know if you want to add additional logic for fallback (e.g. open in new tab for mobile) or lightbox mode instead.

User
is there needs to set merchantResponseUrl also
Assistant
Yes — and here’s the key point:

### ✅ **You must set `merchantResponseUrl` in the backend**
**AND**
✅ **Pass that same return URL to the frontend (`RealexHpp.embedded.init(…)`)**

### 🔁 Why both?

– `merchantResponseUrl` in the **backend** (`HostedPaymentData`) is what Realex HPP uses to **POST the payment result** (`hppResponse`) back to your server.
– The `merchantUrl` you pass to `RealexHpp.embedded.init(…)` is used by the JS library to create the `form` that wraps the HPP response — and submit it to your backend.

If they mismatch or one is missing, the payment response **might not return correctly** or will **fail silently**.

### ✅ Backend: Set `merchantResponseUrl`

In your `relax-payment` endpoint:

$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

> This ensures Realex posts the result to `/realex-response/`.

### ✅ Frontend: Use the same URL

let productResUrl = document.getElementById("product_resurl").value;
RealexHpp.embedded.init("hsspp", "hspp", productResUrl, jsonFromRequestEndpoint);

> This ensures the JavaScript SDK handles the `postMessage` and redirects to the same URL.

### 🔐 Final Word:
> **Both must be set, and must be the same URL.**
This guarantees the HPP response works properly regardless of device or payment flow.

Let me know if you’d like to secure `/realex-response/` or handle `hppResponse` gracefully.

User
<script>

</script>
<script>

</script>
<script>

</script>
<script>

</script>
<script>
">
</script>

–>

<script>
">
</script>
<script>

</script>
<script>
jQuery(document).ready(function() 
	{
		function checkEmailValue(value, arr) 
		{
			var status = 'not_exist';
			for (var i = 0; i < arr.length; i++) 
			{
				var name = arr[i];
				if (name == value) 
				{
					status = 'exist';
					break;
				}
			}
			return status;
		}
		// List of valid email domains
		var email_pool = ['gmail.com','gmail.ie','thenet.ie','yahoo.com','hotmail.com','aol.com','hotmail.co.uk','hotmail.fr','msn.com', 'yahoo.fr','wanadoo.fr','orange.fr','comcast.net','yahoo.co.uk','yahoo.com.br','yahoo.co.in','live.com','rediffmail.com','free.fr', 'gmx.de','web.de','yandex.ru','ymail.com','libero.it','outlook.com','uol.com.br','bol.com.br','mail.ru','cox.net','hotmail.it', 'sbcglobal.net','sfr.fr','live.fr','verizon.net','live.co.uk','googlemail.com','yahoo.es','ig.com.br','live.nl','bigpond.com', 'terra.com.br','yahoo.it','neuf.fr','yahoo.de','alice.it','rocketmail.com','att.net','laposte.net','facebook.com','bellsouth.net', 'yahoo.in','hotmail.es','charter.net','yahoo.ca','yahoo.com.au','rambler.ru','hotmail.de','tiscali.it','shaw.ca','yahoo.co.jp', 'sky.com','earthlink.net','optonline.net','freenet.de','t-online.de','aliceadsl.fr','virgilio.it','home.nl','qq.com','telenet.be', 'me.com','yahoo.com.ar','tiscali.co.uk','yahoo.com.mx','voila.fr','gmx.net','mail.com','planet.nl','tin.it','live.it','ntlworld.com', 'arcor.de','yahoo.co.id','frontiernet.net','hetnet.nl','live.com.au','yahoo.com.sg','zonnet.nl','club-internet.fr','juno.com', 'optusnet.com.au','blueyonder.co.uk','bluewin.ch','skynet.be','sympatico.ca','windstream.net','mac.com','centurytel.net','chello.nl', 'live.ca','aim.com','bigpond.net.au', '163.com', '126.com','sohu.com','daum.net', 'seznam.cz','email.it','inbox.ru','list.ru', 'bk.ru','o2.pl', 'wp.pl', 'onet.pl','interia.pl', 'gazeta.pl','yahoo.ie'];
		// Trigger when the user leaves the email field
		jQuery("#contact-email").focusout(function() 
		{
			var user_email = jQuery("#contact-email").val();
			var domain_name = user_email.indexOf("@");
			var slice_email = user_email.slice((domain_name + 1), user_email.length);
			if (checkEmailValue(slice_email, email_pool) == 'not_exist') 
			{
				jQuery("#confirm_email_error2").css("display", "inline-block");
				jQuery("#confirm_email_error2").html("Hmmm, are you sure... <strong>" + user_email + "</strong> is the correct email address?");
				//jQuery("#hsspp").prop("disabled", true);  // Disable button
			} else {
				jQuery("#confirm_email_error2").css("display", "none");
				jQuery("#hsspp").prop("disabled", false);  // Enable button
			}
		});

		var datesForDisable = <?php echo $jsArray; ?>;
		
		function formatDate(date) 
		{
			var day = String(date.getDate()).padStart(2, '0');
			var month = String(date.getMonth() + 1).padStart(2, '0');
			var year = date.getFullYear();
			return `${day}-${month}-${year}`;
		}
		
		function getNextAvailableDate(startDate) 
		{
			var currentDate = new Date(startDate);
			while (true) 
			{
				var formattedDate = formatDate(currentDate);
				if (currentDate.getDay() !== 0 && !datesForDisable.includes(formattedDate)) 
				{
					return currentDate;
				}
				currentDate.setDate(currentDate.getDate() + 1);
			}
		}
		
		var today = new Date();
		var formattedToday = formatDate(today);
		if (!datesForDisable.includes(formattedToday)) 
		{
			datesForDisable.push(formattedToday);
		}
		var datepickerOptions = 
		{
			format: 'dd-mm-yyyy',
			autoclose: true,
			todayHighlight: true,
			startDate: '0d', 
			datesDisabled: datesForDisable, 
			daysOfWeekDisabled: [0] 
		};
		
		$('.datepicker').datepicker(datepickerOptions);
		var nextAvailableDate = getNextAvailableDate(today);
		$('.datepicker').datepicker('setDate', nextAvailableDate);			
	});
</script>
<script>

</script>
<script>
// Add a class to the body element
		document.body.classList.add('order-checkout');
</script>
<script>
$("#billing-zip").on("blur", function() {
		let eirCode = $(this).val();
		let result = eirCode.split(" ")[0];
		let eir = result.toUpperCase()
		if(eirCode){
			let data = {
				'action': 'checkEirCode',
				'eirCode': eir,
				'town': $('#townID').val(),
			}
			var Ajaxaction = "<?php echo admin_url('admin-ajax.php'); ?>";
			jQuery.post(Ajaxaction, data, function (response) {
				if(response=='No'){
					$('#EirCode_Error').css('display','block');
					$('#hsspp').prop("disabled", true);
					// Scroll to the #EirCode_Error div
					/* $('html, body').animate({
					scrollTop: $('#EirCode_Error').offset().top
					}, 18000); */
				} else if(response=='Yes'){
					$('#EirCode_Error').css('display','none');
					$('#hsspp').prop("disabled", false);
				}
			});
		}
	});
</script>
<script>
$("#shipping").hide();
	$("#sameadd").click(function() {
		if($(this).is(":checked")) {
			$("#shipping").hide(200);
		} else {
			$("#shipping").show(300);
		}
	});
</script>
<script>
$(document).on("click", "#btncontainer", function () {
		var frmresult;
		frmresult=checkVariantValid();
		//alert(frmresult);
		if ( ! frmresult ) {
			return;
		}
		$('#btncontainer').hide();
		var product_price=$('#product_price').val();
		config.amount=product_price;
		onGooglePayLoaded(product_price);		
	});
	$(document).on("change", ".paymethod", function () {
		var curnttype=$("input[name='paymethod']:checked").val();
		if(curnttype=='googlewallet')
		{
			$('.ecom').hide();
			$('.googlewallet').show();
		}else{
			$('.googlewallet').hide();
			$('.ecom').show();
		}
	});
	/**Coupon Code**/
	$(document).on("change", "#coupon", function () {
		let coupon = $(this).val();
		var data = {
			'action': 'appplyCoupon',
			'coupon': $(this).val(),
			'user_email': $('#contact-email').val(),
			'site_id': "<?php echo get_option('site_ID'); ?>"
		}
		var Ajaxaction = "<?php echo admin_url('admin-ajax.php'); ?>";
		jQuery.post(Ajaxaction, data, function (response) {
			if(response=='no data'){
				let y = $('#coupon').val();
				if(y){
					$('#coupon-error').css('display','block');
					$('#copuon_code').val('0');
				} else {
					$('#coupon-error').hide();	
				}
				$('#product_price').val(<?php echo $price; ?>);
				$('#copuon_amount').val(0);
				$('h4.total_price span').html("€" +<?php echo $price; ?>);
				$('#coupon-error').css('display','block');
				$('.coupon_perc').css('display','none');
				$('.coupon_display').css('display','none');
			} else if(response=='This coupon has already been redeemed'){
				$('#coupon-error').css('display','block');
				$('#coupon-error').html(response);
			}else {
				$('#coupon-error').css('display','none');
				let price  = $('#product_price').val();
				let result = price - response;
				if(result){
					$('.coupon_perc').text("€" +response);
					$('#copuon_code').val(coupon);
					$('.coupon_display').css('display','block');
					$('#product_price').val(result);
					$('h4.total_price span').html("€" +result);
					$('.coupon_perc').css('display','block');
				} else {
					$('.coupon_perc').css('display','none');
					$('.coupon_display').css('display','none');
				}
			}
			console.log(response);
		});	
	});
	/* exocet */
	$(document).on("change", "#exocet", function () {
		// Embed the PHP variable inside the JavaScript code
		let price = parseFloat(<?php echo json_encode($price); ?>);
		if ($(this).is(':checked')) {
			price = price + 20;
		}
		$("#subtotal").text('€' + price.toFixed(2)); 
		$("#total-price").text('€' + price.toFixed(2)); 
		$("#product_price").val(price.toFixed(2)); 
		$("#total-price-pay").text('Total: €' + price.toFixed(2)); 
	});
</script>
<script>
window.dataLayer = window.dataLayer || [];
		window.dataLayer.push({ ecommerce: null }); // Reset previous ecommerce data
		window.dataLayer.push({
			'event': 'begin_checkout',
			'ecommerce': {
				'currency': 'EUR',
				'value': <?= $price ?>,
				'items': [{
				'item_name': '<?= $fuel_type ?>',
				'item_id': 0000,
				'price': <?= $price ?>,
				'quantity': 1, // Assuming a quantity here. Add this if relevant
				'litres': <?= $litre ?>,
				'item_category': 'Uncategorized'
				}]
			}
		});
</script>
<script>
?ver=1.0.1">
</script>

but url is passed from I think "product_resurl"

Assistant
Yes, you're absolutely right — and based on everything you've shared, here's the clear breakdown:

## ✅ Summary: `merchantResponseUrl` in Your Setup

### 🔧 1. **You're passing the response URL from frontend via `product_resurl`**

<input type="hidden" name="product_resurl" id="product_resurl" value="<?= site_url() ?>/realex-response/" />

This value is then passed to this line inside your JavaScript:

var product_resurl = document.getElementById("product_resurl").value;
RealexHpp.embedded.init("hsspp", "hspp", product_resurl, jsonFromRequestEndpoint);

✅ This tells the Realex JS SDK where to post the payment result.

### 🔧 2. **✅ But you still need to explicitly set it in your PHP backend via `HostedPaymentData`**
Right now, your PHP doesn't do this:

$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

📌 Without this line, the payment result won’t be correctly posted back from the HPP.

## ✅ Final Recommendation

Update your backend code like this:

$hostedPaymentData = new HostedPaymentData();
$hostedPaymentData->customerEmail = $billingemail;
$hostedPaymentData->customerFirstName = $billingname;
$hostedPaymentData->customerLastName = $billinglastname;
$hostedPaymentData->productId = "$product_id";
$hostedPaymentData->addressesMatch = false;

// ✅ Add this line:
$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

## ✅ Why both are needed?
| Where | Purpose |
|——|——–|
| **Frontend (`product_resurl`)** | Used by `RealexHpp.embedded.init(…)` to generate the form that submits response |
| **Backend (`merchantResponseUrl`)** | Tells the HPP service itself where to send the `hppResponse` after the transaction |

Let me know if you also need help parsing the response on the `/realex-response/` endpoint.

User
what is the benefit of $hostedPaymentData->merchantResponseUrl
Assistant
Great question.

Setting:

$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

### ✅ **Purpose & Benefit of `merchantResponseUrl`**

This tells **GlobalPayments HPP** (Hosted Payment Page) **where to send the transaction result** **after the customer completes the payment.**

### 🔍 Without `merchantResponseUrl`

– The customer finishes the payment on the Realex-hosted page…
– **But nothing happens — no redirection back to your site.**
– You lose the ability to:
– **Display an order confirmation screen**
– **Capture the result server-side (success/failure)**
– **Save transaction data to your DB**
– **Fire conversion tracking (e.g. Google Ads, Meta)**

### ✅ With `merchantResponseUrl`

– After payment (success or failure), **the customer is redirected to your custom endpoint**, for example:
`https://yourdomain.com/realex-response/`

– There, you can:
– ✅ Show order confirmation (thank you page)
– ✅ Validate or parse the `hppResponse` POSTed back
– ✅ Update order status
– ✅ Trigger emails, analytics, etc.

### 🔁 Bonus: It's **safer** for backend processing

Even if your JS fails or the iframe breaks, the server receives a **POST** directly from Realex with the response — making it **more reliable** than relying only on JavaScript.

### 🔒 TL;DR
| Feature | Without `merchantResponseUrl` | With `merchantResponseUrl` |
|————————————|——————————-|—————————–|
| Customer redirection post-payment | ❌ No | ✅ Yes |
| Server-side result validation | ❌ Not possible | ✅ Yes |
| Order status update | ❌ Manual | ✅ Automatic |
| Thank-you page | ❌ Can't control it | ✅ Full control |

Let me know if you want help handling the `hppResponse` in that `/realex-response/` page.

User
BUt it set in js
Assistant
You're absolutely right — in your code, the **`merchantResponseUrl`** is not set in PHP like:

$hostedPaymentData->merchantResponseUrl = 'https://yourdomain.com/realex-response/';

Instead, it's handled **in JavaScript** via this call:

RealexHpp.embedded.init("hsspp", "hspp", "realex-response", jsonFromRequestEndpoint);

Here’s how this works and how it's different:

### 🔍 What's happening in your JS?

When you call:

RealexHpp.embedded.init("hsspp", "hspp", "realex-response", jsonFromRequestEndpoint);

You're passing:

– `"realex-response"` → this is used **as the `merchantUrl`** in JS.
– `jsonFromRequestEndpoint` → the HPP JSON returned from PHP (via `$service->charge(…)`).

The `merchantUrl` is assigned as a parameter to the JS SDK, and later used as:

form.setAttribute("action", hppUrl);
form.appendChild(internal.createFormHiddenInput("MERCHANT_RESPONSE_URL", redirectUrl));

This adds this hidden input in the form:

<input type="hidden" name="MERCHANT_RESPONSE_URL" value="https://yourdomain.com/realex-response">

So yes — it **is being set**, but **in JS**, not PHP.

### ✅ Is this valid?

Yes — **Realex/GlobalPayments HPP supports setting `MERCHANT_RESPONSE_URL` either in**:

– PHP (using `$hostedPaymentData->merchantResponseUrl`) — **server-side**
– JavaScript (as `merchantUrl` param in `.init(…)`) — **client-side**

They are functionally equivalent **if**:

– The JavaScript never fails or is blocked
– The customer uses a modern browser

### 💡 Best Practice?

Set `merchantResponseUrl` **both in PHP and JavaScript** for **maximum safety**.

Why?

– If JavaScript fails or is skipped for some reason (e.g., iframe fallback, script blocker), the server-side setting ensures the HPP still knows where to respond.

**✅ Recommended:**

In PHP:

$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

In JS:

RealexHpp.embedded.init("hsspp", "hspp", "<?= site_url('/realex-response/') ?>", jsonFromRequestEndpoint);

Let me know if you want help updating the PHP or JS to include both!

User
Now I have below error for one customer
15/04/2025 11:36
RPO-538604
Paula Walsh
269 euro

The customer reported that the page just timed out, the money was captured and we do not have even a Pending order for her.

The support agent reviewing the logs seems to think that the Merchant Response URL in the payment Gateway appears to be simply www.rightpriceoil.ie or is sometimes empty.

In their terms,
They receive an IN, e.g. the payment request info
And should also receive what they call an OUT – a return URL at least.

I think we should be sending the response back to /realex-response/
possibly the file in /wp-contents/plugins/klassenergy-main/relax-response.php
Or certainly that is where the customer is supposed to go after an order is complete

Can you please investigate and see if this value is being sent correctly for Realex (Global Payments) to know where to send customers or is there a possibility it is getting left out or corrupted somehow?

I believe that we hold order info until we get a successful response form the gateway. If we don't then we store the data in the pending order table.

We don't have her in the pending orders table either.

Assistant
User is debugging a Realex (Global Payments) payment issue where a customer was charged but no order (even pending) was recorded. The concern is the `MERCHANT_RESPONSE_URL` might be empty or incorrect, and responses may not be sent back to `/realex-response/`.
Assistant
Thanks for the detailed background—here’s what’s likely happening and what you should check/fix:

### 💥 The Core Problem:
Realex/Global Payments expects a `MERCHANT_RESPONSE_URL` in the HPP (Hosted Payment Page) request. This URL is where **Realex will POST the transaction result (the “OUT”)** after the payment is complete.

**If this URL is missing or malformed**, Realex:
– Can’t notify your server of success/failure.
– Won’t trigger the customer redirect.
– And your system won't record the order in either the live or pending tables.

### 🔍 What You Need to Check:

#### ✅ 1. Is `MERCHANT_RESPONSE_URL` actually being set in PHP?

In your PHP file that creates the `$hppJson`, I didn’t see this set explicitly. You **must** add it before calling `serialize()`:

$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

Even though your JS uses this later, **Realex HPP expects it on the initial backend token generation**. It’s not enough to rely on JS.

> 🔧 **Fix: Add this line**

$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

Right before this:

$hppJson = $service->charge($product_price)

#### ✅ 2. Does your JavaScript override or reinforce the HPP URL?

You’re setting this via:

RealexHpp.setHppUrl(url); // e.g. https://pay.realexpayments.com/pay

That sets the HPP iframe URL. But **that is not the same** as the `MERCHANT_RESPONSE_URL` which tells Realex where to send the transaction result.

You can pass the merchant URL to RealexHpp’s `init()` like:

RealexHpp.embedded.init("hsspp", "hspp", "https://rightpriceoil.ie/realex-response/", hppJson);

That third parameter is interpreted as the response/return handler.

BUT: if the HPP token (`hppJson`) itself didn’t contain `MERCHANT_RESPONSE_URL`, this alone won’t help.

### 🧠 Realex (HPP) Flow Recap:
1. You send request from PHP → Realex, including `merchantResponseUrl`
2. Realex returns HPP JSON token
3. You send token to client (JS), open iframe or redirect
4. **Realex POSTS the result to `merchantResponseUrl`**
5. That endpoint (`/realex-response/`) creates the order or marks it paid

### 🧪 How to Verify It's Set

In your browser dev tools, look at the HPP token (`hppJson`) and check if it contains:

"MERCHANT_RESPONSE_URL": "https://rightpriceoil.ie/realex-response/"

If it doesn't, the backend (PHP) missed it.

### ✅ Summary Action Plan

1. **Add this line in PHP:**

$hostedPaymentData->merchantResponseUrl = site_url('/realex-response/');

2. Ensure `realex-response.php` is actually hooked in WordPress, maybe using:

add_action('init', function () {
    if (isset($_POST['hppResponse'])) {
        include_once plugin_dir_path(__FILE__) . 'relax-response.php';
        exit;
    }
});

3. Test by placing an order and confirming:
– The token contains `MERCHANT_RESPONSE_URL`
– Payment returns to `/realex-response/`
– Order gets created

Would you like help checking the contents of your `/realex-response/` file next to ensure it’s saving orders correctly?

Leave a Comment

Your email address will not be published. Required fields are marked *