Checkout Flow Customization
April 17, 2025 · AI Conversations
<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>
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.
/*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)
—
### 💡 **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.
$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/`.
—
### ✅ 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).
// 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
### 🔍 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.
—
### ✅ **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.
<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"
—
## ✅ 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.
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.
$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!
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.
—
### 💥 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?