PayPal Checkout Sandbox – "The payment request was refused by PayPal" on iPhone

  • Posts: 18
  • Thank you received: 1
  • Hikashop Business
1 week 2 days ago #369412

-- HikaShop version -- : 6.1.0
-- Joomla version -- : 5.4.0
-- PHP version -- : 8.3.0
-- Browser(s) name and version -- : Safari 26.0.1
-- Error-message(debug-mod must be tuned on) -- : The payment request was refused by PayPal. Please click here to return to the checkout.

Hello HikaShop Team,

I am currently testing the PayPal Checkout integration (Sandbox mode) in my Joomla / HikaShop setup.

The PayPal Checkout (Sandbox) integration works perfectly on desktop browsers (Chrome, Edge, Firefox).
However, when testing the same checkout process on iPhone / Safari (mobile) or iPhone/Chrome, I receive the following error message after confirming the payment in PayPal:

“The payment request was refused by PayPal. Please click here to return to the checkout.”

After this, PayPal redirects back to the shop, but the payment is not recorded, and the order remains “unpaid.”

Observation

It seems that the PayPal sandbox refuses the redirect back to HikaShop checkout only in mobile Safari/Chrome (possibly due to stricter cookie / session handling).

Desktop browsers are unaffected.

Question

Could you please confirm:

  1. Whether the current HikaShop PayPal Checkout plugin already includes full support for mobile Safari (iOS) in Sandbox and Live modes?
  2. Is there any workaround or setting I should adjust — e.g. Landing Page = PayPal, Redirecting after finishing = Yes or any specific Return URL that should be added manually?
  3. If this is a known issue in the Sandbox API, is there an ETA for a fix or an alternative way to test iPhone payments successfully?
  4. Could the issue be caused by the PayPal in-context popup window on mobile devices?
    → If yes, is there a way to disable the popup behavior (so that PayPal opens in a full browser redirect instead)?

Attachments:

Please Log in or Create an account to join the conversation.

  • Posts: 84727
  • Thank you received: 13792
  • MODERATOR
1 week 2 days ago #369415

Hi,

1. We are not aware of such problems with mobile devices. As far as we know, it works properly, the same way as on desktop.
2. No, no modification should be necessary.
3. It is not a known issue.
4. No, it should not be.

Normally, when an issue like this happens, it means that something unexpected happened between the JS added by the plugin on the page and PayPal. More details about the problem should be available in the console of the browser.
So, if the problem was reproducible on desktop devices, I could look into it from where I am with my computer's browser.
Since this is not possible, I see 2 possibilities:
- This page stackoverflow.com/questions/37256331/is-...ome-on-android-phone presents several solutions to be able to check the console on a mobile device: using the Kiwi browser + its mini console extension, Chrome remote debugging, adding eruda JS tool to your website, etc
- 10 days ago, we released HikaShop 6.1.1:
www.hikashop.com/support/documentation/5...ashop-changelog.html
The update via the Joomla extensions updater system is not yet activated as we're waiting for a bit more feedback from early adopters. You can manually download the latest version install package on our website and install it on yours to get the update.
If you read the changelog of this new version, you can find this fix:

We fixed a javascript error which could happen in some cases on Joomla 5 on the payment page of the PayPal Checkout payment plugin when the payment would not succeed, preventing the plugin from displaying the error message to the customer.

This should hopefully allow you to get a better error message, properly explaining what is wrong.
So, this is the first thing I would recommend trying.

The following user(s) said Thank You: pkaeller

Please Log in or Create an account to join the conversation.

  • Posts: 18
  • Thank you received: 1
  • Hikashop Business
1 week 1 day ago #369420

Hello again,
thank you for your quick reply. As recommended, I have installed the latest HikaShop version 6.1.1.
I would like to add an important technical detail that might help to diagnose the issue more precisely.

During debugging on iOS (Safari and Chrome), I monitored all outgoing JavaScript fetch() calls.
Right after returning from the PayPal popup, the PayPal Checkout plugin attempts to call the following notify URL:

https://www.fidelebrueder.de/index.php?option=com_hikashop&ctrl=checkout&task=notify&notif_payment=paypalcheckout&order_id=236&tmpl=component&lang=de&order_token=99HHF2iq&Itemid=101&createorder=1[/code
]

On iPhone (iOS) the browser reports:
[code]Fetch-Error -> Load failed

Important update:
The problem now appears only on iOS/Chrome.
Safari on iOS no longer shows the error.

This means the request is not blocked by the server and not returning an HTTP error —
iOS simply refuses to load the URL at all (network-level failure).

On desktop browsers, the same URL loads correctly and returns:
{"errorTitle":"An error has occurred","errorMessage":"","error":false,"id":"7G661998VS570111E"}

So:
  • Desktop → notify URL loads
  • iOS/Chrome → notify URL cannot be loaded via fetch (“Load failed”)
  • iOS/Safari → now working
  • This happens before HikaShop can process anything
Because the PayPal Checkout plugin relies on this fetch request to finalize the order, the whole payment flow stops on iOS/Chrome.

My follow-up questions
  1. Does the PayPal Checkout plugin always use an in-context / popup flow that depends on JavaScript fetch requests?
    If yes, this explains why the problem occurs only when the popup closes on iOS.
    Some iOS WebKit builds aggressively block fetch requests immediately after closing an in-context window.

  2. Is it possible to disable the in-context popup and use a full redirect flow instead?
    For example:
    • customer clicks PayPal
    • full-page redirect to PayPal
    • full-page redirect back to HikaShop after payment
    • server-side validation without requiring a fetch request
    This would completely avoid the iOS fetch issue

  3. Is this behavior expected for the notify URL?
    (Desktop returns JSON with error:false but errorTitle:"An error has occurred".)

  4. Do you have any suggestions on how to make the notify URL load reliably on iOS devices?
    For example:
    • disabling popup mode
    • forcing redirect mode
    • adjusting any option inside the PayPal Checkout plugin
    • or a known workaround for iOS WebKit and PayPal Sandbox

Thank you very much — I appreciate your support.
If you need more logs or debugging information, I can provide them.

Best regards,
Patrick

Last edit: 1 week 1 day ago by pkaeller.

Please Log in or Create an account to join the conversation.

  • Posts: 18
  • Thank you received: 1
  • Hikashop Business
1 week 1 day ago #369421

For anyone who might encounter similar problems with PayPal Checkout or other AJAX-based payment plugins on iOS devices, I would like to share a small debugging script that helped me identify what was happening in my case.

Since iOS Safari/Chrome does not offer a visible browser console, I added a JavaScript overlay that logs:

  • all JavaScript errors
  • unhandled promise rejections
  • all fetch() calls (URL + errors)
  • XHR requests (including HTTP errors)
  • navigation events (optional)

This made it possible to confirm that a specific fetch request triggered by the PayPal Checkout plugin was failing only on iPhone, while working normally on desktop.

Here is the script (can be added inside the Astroid “Custom JavaScript” field in Joomla):

(No <script> tags needed)
// iPhone Debug-Helfer – in Astroid > Custom JavaScript einfügen (ohne <script>-Tags)
(function () {
    var DEBUG_ACTIVE = true; // Zum Deaktivieren einfach auf false setzen

    // Nur auf iOS (iPhone/iPad/iPod) aktivieren
    var isIOS = /iP(hone|ad|od)/.test(navigator.userAgent || "");
    if (!DEBUG_ACTIVE || !isIOS) {
        return;
    }

    // Einfaches Overlay für Logs
    function createDebugOverlay() {
        if (document.getElementById("ios-debug-overlay")) return;

        var overlay = document.createElement("div");
        overlay.id = "ios-debug-overlay";
        overlay.style.position = "fixed";
        overlay.style.left = "0";
        overlay.style.right = "0";
        overlay.style.bottom = "0";
        overlay.style.maxHeight = "40%";
        overlay.style.background = "rgba(0,0,0,0.85)";
        overlay.style.color = "#0f0";
        overlay.style.fontSize = "10px";
        overlay.style.fontFamily = "monospace";
        overlay.style.zIndex = "999999";
        overlay.style.padding = "4px 6px 18px 6px";
        overlay.style.overflowY = "auto";

        var header = document.createElement("div");
        header.textContent = "iOS Debug-Konsole (Tippen zum Ein-/Ausklappen)";
        header.style.fontWeight = "bold";
        header.style.marginBottom = "2px";
        header.style.cursor = "pointer";
        header.style.color = "#fff";

        var content = document.createElement("div");
        content.id = "ios-debug-content";

        header.addEventListener("click", function () {
            if (content.style.display === "none") {
                content.style.display = "block";
            } else {
                content.style.display = "none";
            }
        });

        overlay.appendChild(header);
        overlay.appendChild(content);
        document.body.appendChild(overlay);
    }

    function logToOverlay(message) {
        try {
            createDebugOverlay();
            var content = document.getElementById("ios-debug-content");
            if (!content) return;

            var line = document.createElement("div");
            line.textContent = new Date().toISOString().slice(11, 19) + " " + message;
            content.appendChild(line);
            content.scrollTop = content.scrollHeight;
        } catch (e) {
            // falls Overlay selbst crasht, nichts tun
        }
    }

    var firstAlertShown = false;
    function notify(message) {
        logToOverlay(message);
        if (!firstAlertShown) {
            firstAlertShown = true;
            try {
                alert("iPhone JS-Fehler/Debug:\n\n" + message);
            } catch (e) { }
        }
    }

    // 1) Normale JavaScript-Fehler
    window.onerror = function (msg, url, line, col, error) {
        var text = "[Error] " + msg +
            " @ " + (url || "") +
            ":" + (line || "?") +
            (col != null ? ":" + col : "");

        if (error && error.stack) {
            text += " | Stack: " + error.stack.split("\n")[0];
        }
        notify(text);
        // return false = normal weiter behandeln
        return false;
    };

    // 2) Promise-Fehler (async/await etc.)
    window.onunhandledrejection = function (event) {
        var reason = event.reason;
        var msg = "[Promise-Rejection] ";
        if (reason && reason.message) {
            msg += reason.message;
        } else {
            msg += String(reason);
        }
        notify(msg);
    };

    // 3) fetch()-Fehler protokollieren
    if (window.fetch) {
        var originalFetch = window.fetch;
        window.fetch = function () {
            var args = arguments;
            return originalFetch.apply(this, args).catch(function (err) {
                notify("[Fetch-Error] " + (err && err.message ? err.message : err));
                throw err;
            });
        };
    }

    // 4) XHR-Fehler protokollieren (z.B. Ajax-Requests von HikaShop)
    if (window.XMLHttpRequest) {
        var originalOpen = XMLHttpRequest.prototype.open;
        var originalSend = XMLHttpRequest.prototype.send;

        XMLHttpRequest.prototype.open = function (method, url) {
            this._debugUrl = url;
            this._debugMethod = method;
            return originalOpen.apply(this, arguments);
        };

        XMLHttpRequest.prototype.send = function () {
            var xhr = this;
            xhr.addEventListener("error", function () {
                notify("[XHR-Error] " + (xhr._debugMethod || "") + " " + (xhr._debugUrl || ""));
            });
            xhr.addEventListener("load", function () {
                if (xhr.status >= 400) {
                    notify("[XHR-HTTP " + xhr.status + "] " + (xhr._debugMethod || "") + " " + (xhr._debugUrl || ""));
                }
            });
            return originalSend.apply(this, arguments);
        };
    }

    // 5) Simple Navigation-Infos (kann helfen bei Weiterleitungs-Problemen)
    (function () {
        var origPushState = history.pushState;
        if (origPushState) {
            history.pushState = function (state, title, url) {
                logToOverlay("[Navigation] pushState → " + url);
                return origPushState.apply(this, arguments);
            };
        }
        window.addEventListener("hashchange", function () {
            logToOverlay("[Navigation] hashchange → " + location.href);
        });
    })();

    logToOverlay("iOS-Debug aktiv – UserAgent: " + navigator.userAgent);
})();

What this helped me discover
On iOS, the PayPal Checkout plugin attempted a fetch() call to:
...?task=notify&notif_payment=paypalcheckout&order_id=...
This request failed with:
Fetch-Error -> Load failed



On desktop, the same URL loaded normally and returned JSON.

This confirmed that the issue was iOS-specific and happened before HikaShop processed anything.

Maybe this script can help others facing similar mobile-only issues in Joomla/HikaShop.

Attachments:
Last edit: 1 week 1 day ago by pkaeller.

Please Log in or Create an account to join the conversation.

  • Posts: 84727
  • Thank you received: 13792
  • MODERATOR
1 week 1 day ago #369424

Hi,

Thanks for all the valuable details.

1.a Yes, the PayPal Checkout plugin always use an in-context / popup flow that depends on JavaScript fetch requests.
This is how it is supposed to be done as per PayPal Checkout's API.

1.b

Some iOS WebKit builds aggressively block fetch requests immediately after closing an in-context window.

I didn't know this. That sounds strange. I would recommend activating the "debug" of the payment method. Then, check the "payment log file" of the HikaShop configuration. The payment plugin will log debug data in there on the server side while processing the fetch request. So, you can confirm whether the request reaches your website or not. It's possible that for some reason, there is some kind of error on the server side which leads to the response not being provided. You can also check the access log of your web server to confirm whether it sees the request or not.
If nothing is logged by HikaShop or the web server for the request, then it's likely that either the browser or the OS blocks the request before it can even get out of the phone.
If you get debug data for the request, then maybe there is something that can be done to fix the problem. For example, if the problem is that the request takes too long to process and the connection breaks before, something could be done to reduce the response time. However, since you said that the order is not even being confirmed, I doubt that something can be done on that end.

2. PayPal Checkout's API doesn't do that. However, their old API does. HikaShop has the PayPal payment plugin pre installed for it. This has been the workhorse of HikaShop for years in the past and it still works fine. It will redirect to PayPal at the end of the checkout, avoiding this fetch issue. So that could be an option. The plugin is marked as "legacy" as PayPal wants to sunset it in the future, but so far, no date has been decided for that.

3. Yes, that's normal. The system takes first into account the "error" parameter and the errorTitle and errorMessage are only changed and taken into account when the error parameter is set to true.

4. Removing the popup is not possible.
Following your messages, I've looked into it and found this page:
github.com/facebook/react-native/issues/35384
It says that the issue happens with POST requests and not with GET requests.
If you check the plugins/hikashoppayment/paypalcheckout/paypalcheckout_end.php file you can see :
{method: "POST"}
in several places. So the JS does a POST to fetch the request. However, the request could actually be made with a GET as it doesn't to transmit any data in the POST. We just used a POST because that was what was written in the example code provided by PayPal and it should normally work the same either way.
But maybe changing this to:
{method: "GET"}
could help circumvent the issue with iOS.
I would recommend trying this.

The following user(s) said Thank You: pkaeller

Please Log in or Create an account to join the conversation.

  • Posts: 18
  • Thank you received: 1
  • Hikashop Business
1 week 16 hours ago #369445

Hello,thank you very much for your detailed and helpful reply.
Based on your recommendations, I have carried out further tests and would like to follow up on a few points.


  1. Testing GET instead of POST
    As suggested, I modified
    plugins/hikashoppayment/paypalcheckout/paypalcheckout_end.php
    and replaced all instances of
    {method: "POST"}
    with
    {method: "GET"}.
    Unfortunately this did not change the behavior. On iOS/Chrome, the fetch request still fails instantly with “The payment request was refused by PayPal”, and the request never reaches the server.

  2. Debug mode of the payment method
    You recommended enabling the debug mode of the payment method and checking the payment log file in the HikaShop configuration.

    However, in my installation (HikaShop 6.1.1), I cannot find a “Debug mode” option:
    • not inside the PayPal Checkout payment method
    • and not in the HikaShop System → Configuration panel

    There is also no “Logs” tab available.
    Could you please clarify where the debug mode is located in HikaShop 6.x?

  3. Joomla error logs
    The Joomla error logs in administrator/logs remain empty during all PayPal tests.

Additional notes from my testing

I also want to highlight that this is not a sandbox-related issue.
The exact same behavior occurs in Live mode, with real PayPal credentials.
So the problem exists independently of the Sandbox environment.

As a temporary workaround, I have implemented device detection on my website:
For iOS/Chrome users, the PayPal payment method is automatically hidden.
This prevents affected customers from encountering the blocked fetch request.
Of course, this is only a temporary workaround — ideally, I would like to offer PayPal consistently across all devices once the underlying issue is resolved.

If there are any additional locations where logging can be enabled, or if further debugging would help you analyze the issue, I am happy to test it.

Thank you again for your support!

Best regards,
Patrick

Last edit: 1 week 16 hours ago by pkaeller.

Please Log in or Create an account to join the conversation.

  • Posts: 84727
  • Thank you received: 13792
  • MODERATOR
1 week 11 hours ago #369450

Hi,

In the payment method's setting page, you have a "debug" setting near the end of the specific settings area:
www.hikashop.com/support/documentation/4...form.html#additional
In the HikaShop configuration page, you have the "payment log file" setting where you can look at the log:
www.hikashop.com/support/documentation/5...nfig.html#main_files
You said "the request never reaches the server.". How do you know this is the case ? Did you check the access log of the web server for that ?


Regarding Safari and PayPal, I also found this page:
www.reddit.com/r/paypal/comments/1bfim2e...i_for_macos_and_ios/
But since you're saying it happens on Chrome, it's probably something else.

I've also found this page:
stackoverflow.com/questions/71280168/jav...etch-on-ios/79309390
This is quite promising.
Someone said:

Had the same problem, problems occured because of certificate and iphone took addres api as dangerous.

So it could be that the problem comes from iOS detecting your server's IP address as potentially dangerous for fetch requests. You could try to see if changing the IP address of the server solves the problem.
Someone else said:

For it was a CORS error :/ had to include the host in the destination allowlist

So maybe it is linked with CORS ? Check the HTTP headers plugin. Maybe see if deactivating helps ?
Finally, the last answer proposes adding a delay to the fetch:
stackoverflow.com/a/79668094
This is something that was recommended by someone else on a link I gave in my previous message too.
So maybe you could try adding this to the end file of the paypalcheckout plugin to see if that helps ?

Please Log in or Create an account to join the conversation.

Time to create page: 0.080 seconds
Powered by Kunena Forum