PayPal即时更新回调URL未在实时设置中调用


PayPal Instant Update CallbackURL not being called in live setup

我正在将PayPal Express Checkout集成到现有的web应用程序中,我已经为该应用程序设置了Google Checkout和Amazon Payments(SimplePay和CBA)。所以我对此并不陌生。

所有的东西,包括即时更新回调,在使用沙盒设置的设置中都能正常工作。回调在沙盒中的HTTP和HTTPS上都能很好地工作。但一旦我切换到Live凭据,PayPal UI就会停止点击CallbackURL,转而使用SetExpressCheckout中发送的(回退)运费。显然,税收根本没有计算在内。

我使用的是最新版本的PayPal PHP SDK(版本106.0)。将设置从Sandbox切换到Live的唯一设置是:

  • 用户名
  • 密码
  • 签名
  • 模式(从sandboxlive

我在PayPal的社区帮助论坛上看到了一个类似的问题,但它没有提到解决方案。出于某种原因,我似乎无法在那个帖子中发帖。。。也许是因为它已经存档了。

2013年9月16日更新:似乎是HTTPS相关的问题。沙盒在HTTPS上也不起作用,但从浏览器中点击回调URL时不会出现SSL错误。证书是有效的,我们在网站上使用的其他支付供应商完全可以接受:谷歌钱包和亚马逊支付。

此问题由以下人员解决:1.将CallbackTimeout设置为62.使用PayPal接受的SSL证书。在我们的案例中,证书是由"Go Daddy Root安全证书-G2"颁发的,PayPal的系统不接受该证书。

我的Godaddy UCC证书有这个问题,但非UCC证书没有问题。所以我必须编写一个代理来将请求重定向到正确的位置。

但是最近我的Godaddy证书也失败了,所以他们似乎不那么宽容,而不是更宽容。它甚至在获得电动汽车证书时失败。

这毫无意义。回调URL所做的只是提供运费!这需要多安全的流程。太荒谬了!除了更改证书之外,目前没有其他解决方案,我甚至不能保证证书会起作用。

我设法用startcom.org的免费证书让它工作(在Security Now播客上听说过)。

他们的网站很糟糕,但PayPal似乎可以接受他们的证书,而他们不可以接受Godaddy证书——都是4096位。此外,它是免费的:-)虽然只有效一年。

我一换上这个证书,效果就很好。

对于PayPal来说,GoDadaddy/Starfield颁发的证书绝对是不可接受的。

还有一个服务ngrok,我发现它非常有用,可以让我在本地进行测试。它允许您设置一个可从外部访问的隧道代理。即使不接触防火墙设置,您也可以创建一个类似http://83def5f1.ngrok.io的地址,PayPal可以访问该地址,并将流量重定向到您的本地机器,从而允许您设置断点。


代理页面

(与ngrok完全无关)

我宁愿不将此证书用于我的实时站点(另外,我在UCC证书上有几个不同的站点,我不想更改),所以我制作了一个代理页面,将请求重定向到正确的服务器。然后我只需将以下内容发送到PayPal

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

(其中callbackUrl是您将发送到PayPal的常规回调URL)

代理是一个ASPX页面-它不需要编译,只需放入.NET IIS网站即可。

您可以查看使用?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>