Question

I'm working on integrating PayPal Express Checkout into an existing web application for which I've already setup Google Checkout and Amazon Payments (both SimplePay and CBA). So I'm not new to this.

Everything, including the Instant Update Callback, works fine in a setup that uses Sandbox settings. The Callback works fine both on HTTP and HTTPS in the Sandbox. But as soon as I switch to Live credentials, the PayPal UI stops hitting the CallbackURL and falls back to using the (fall back) shipping costs sent in SetExpressCheckout. Obviously, taxes aren't calculated at all.

I'm using the latest version of the PayPal PHP SDK (version 106.0). The only settings that are changed to switch the setup from Sandbox to Live are:

  • UserName
  • Password
  • Signature
  • mode (from sandbox to live)

I see a similar issue posted on PayPal's Community Help Forum, but it doesn't mention a solution. For some reason I can't seem to post in that thread ... perhaps because it's archived.

Update 9/16/2013: Seems to be an HTTPS related issue. Sandbox doesn't work on HTTPS either, but there's no SSL error when hitting callback URL from browser. Certificate is valid and perfectly acceptable to other payment vendors that we use on the site: Google Wallet and Amazon Payments.

Was it helpful?

Solution

This issue was fixed by: 1. Setting CallbackTimeout to 6 2. Using an SSL certificate that is accepted by PayPal. In our case, the cert was issued by "Go Daddy Root Secure Certificate - G2", which wasn't accepted by PayPal's system.

OTHER TIPS

I had this problem with a Godaddy UCC certificate, but did not have the problem with a non UCC certificate. So I had to write a proxy to redirect the request to the correct location.

However recently this too has failed with my Godaddy certificate so they seem to be being less permissive rather than more permissive. It even fails with an EV certificate.

It makes no fricking sense. All the callback URL does is provides shipping rates! How secure does that need process need to be. It's ridiculous! No solution right now other than changing certificate which I can't even guarantee will work.

I managed to get it working with a FREE certificate from startcom.org (heard about on Security Now Podcast).

Their website is horrible, but PayPal seems to be OK with their certificate where they aren't OK with a Godaddy certificate - both 4096 bits. Plus it's free :-) Although only good for a year.

As soon as I switched to this certificate it worked fine.

It was definitely something unacceptable to PayPal about the GoDadaddy/Starfield issued certificate.

There is also a service ngrok which I found very useful to allow me to test locally. It allows you to set up a tunneling proxy that is accessible from the outside. Even without touching firewall settings you can create an address like http://83def5f1.ngrok.io that is accessible by PayPal and redirects traffic to your local machine allowing you to set breakpoints.


Proxy page

(Completely unrelated to ngrok)

I preferred not to use this certificate for my live sites (plus I have several different sites on UCC certificates I didn't want to change), so I made a proxy page that redirects the request to the right server. I then just send the following to PayPal

"https://example.com/paypalproxy.aspx?callbackUrl=" + HttpUtility.UrlEncode(callbackUrl)

(Where callbackUrl is your regular callback URL that you would send to PayPal)

The proxy is an ASPX page - it doesn't need to be compiled just put into a .NET IIS website.

You can check to see the last request/response calling it with ?debug=Y

<%@ Page language="c#" AutoEventWireup="true" %>
<%@ Import Namespace="System.IO"%>
<Script runat="server" language="C#">

private static string _lastURL;
private static string _lastRequest = "";
private static string _lastResponse = "";
private static DateTime? _lastTime;
private static int _lastDurationMs;

    private void Page_Load(object sender, System.EventArgs e)
    {
        // no cache
        Response.Cache.SetCacheability(HttpCacheability.NoCache);

            var sw = new System.Diagnostics.Stopwatch();
            var wc = new System.Net.WebClient();
            sw.Start();

            var callbackUrl = Request.Params["callbackUrl"];
            var debugMode = Request.Params["debug"] == "Y";

            if (debugMode) 
            {
               Response.ContentType = "text/text";
               Response.Write(_lastTime + "\n");
               Response.Write("LastURL = ["+_lastURL+"]\n\n");
               Response.Write("LastDuration = [" + _lastDurationMs +"]\n\n");
               Response.Write("REQUEST: \n[\n  "+_lastRequest.Replace("&", "&\n  ")+"\n]\n\n");
               Response.Write("RESPONSE: \n[\n  "+_lastResponse.Replace("&", "&\n  ")+"\n]");
               Response.End();
           return;
            }

            _lastDurationMs = -1;
        _lastURL = Request.Params["callbackUrl"];
            _lastTime = DateTime.Now;

            if (callbackUrl.Contains("dev."))
            {
                throw new ApplicationException("Callback shouldn't be to a dev machine!");
            }
                        
            if (callbackUrl.Contains("https") == false)
            {
                throw new ApplicationException("Callback must be https");
            }
            
            var newUri = callbackUrl + "?" + Request.Form.ToString();

            var str = wc.DownloadString(newUri);

            _lastRequest = Request.Form.ToString();
            _lastResponse = str;
            _lastDurationMs = (int)sw.ElapsedMilliseconds;

            Response.Write(str);
            Response.End();

    }

</Script>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top