【问题标题】:Unable to use Paypal API on an SSL website无法在 SSL 网站上使用 Paypal API
【发布时间】:2016-03-30 20:54:36
【问题描述】:

我有一个使用 cURL 集成 Paypal API 的 SSL 网站。

这是我的 PHP 课程包含文件:

class paypalApi {
    public $username;
    public $password;
    public $signature;

    function post($method, $params, $mode) {
            // Method: Required
            // Parameters: An array containing the requested parameters

            // The request URL
            $url = "https://api-3t".$mode.".paypal.com/nvp";

            // Version of the API
            $version = '116.0';

            // Construct the query params
            // Set the API method, version, and API credentials.
            $credentials = array('METHOD' => $method, 'VERSION' => $version, 'USER' => $this->username, 'PWD' => $this->password, 'SIGNATURE' => $this->signature);
            $params = array_merge($credentials, $params);

            // Set the curl parameters.
            if(function_exists('curl_exec')) {
                $ch = curl_init();
                curl_setopt($ch, CURLOPT_URL, $url);
                curl_setopt($ch, CURLOPT_VERBOSE, 1);

                // Turn off the server and peer verification (TrustManager Concept).
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                curl_setopt($ch, CURLOPT_POST, 1);
                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));

                $response = curl_exec($ch);
            }

            if(empty($response)) {
                $opts = array('http' =>
                    array(
                        'protocol_version' => '1.1',
                        'method'  => 'POST',
                        'header'  => 'Content-type: application/x-www-form-urlencoded',
                        'content' => http_build_query($params)
                    )
                );
                $context = stream_context_create($opts);
                $response = file_get_contents($url, false, $context);
            }
            // Parse the response
            parse_str($response, $responseArr);

            // If the request fails
            if(empty($responseArr) || !array_key_exists('ACK', $responseArr)) {
                global $LNG;
                // Mimic a fake response
                return array('L_SHORTMESSAGE0' => $LNG['error'], 'L_LONGMESSAGE0' => $LNG['payment_error_0'], 'ACK' => 'REQUEST_FAILED');
            }
        return $responseArr;
    }
}

由于某种原因它不起作用,点击支付按钮会将用户带到 Paypal,但在完成该过程后它会将用户带回网站,url 似乎正确(例如https://example.com/pro&type=successful?token=EC-8BB04791XJ708490K&PayerID=QL54Q696KZCLA)但没有 make付款。

我也尝试了沙箱,但在 Apache 日志文件中看不到任何错误,因此我需要调试 $response,因此我尝试在解析响应之前添加 echo var_dump($response);

但是我没有太多的调试经验,而且在Chrome上我不知道在哪里查看响应。(在JS控制台上我看不到它)

如何调试此 API 响应以检查为什么 paypal 不允许为我的网站付款? (该网站已经过验证)

更新

作为身份验证,我使用 API 签名 NVP 格式和 SetExpressCheckout 方法。

这是我的回复代码:

        // Execute SetExpressCheckOut method to create the payment token and PayerID
        $paypalResponse = $paypal->post('SetExpressCheckout', $params, $PayPalMode);

        //Respond according to message we receive from Paypal
        if(strtoupper($paypalResponse["ACK"]) == "SUCCESS") {
            // Generat the PayPal payment url with the response Token
            $paypalurl = 'https://www'.$PayPalMode.'.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='.$paypalResponse["TOKEN"].'';

            // Redirect to PayPal payment page
            header('Location: '.$paypalurl);

            // Execute DoExpressCheckoutPayment to receive the payment from the user
            $paypalResponse = $paypal->post('DoExpressCheckoutPayment', $params, $PayPalMode);

            // Check if the payment was successful
            if(strtoupper($paypalResponse["ACK"]) == "SUCCESS") {


                // Verify if the payment is Completed
                if($paypalResponse["PAYMENTINFO_0_PAYMENTSTATUS"] == 'Completed') {
                    // Execute GetExpressCheckoutDetails to retrieve the transaction details
                    $params = array('TOKEN' => $token);

                    $paypalResponse = $paypal->post('GetExpressCheckoutDetails', $params, $PayPalMode);

                    // If the GetExpressCheckoutDetails was successful
                    if(strtoupper($paypalResponse["ACK"]) == "SUCCESS") {
                        $date = date("Y-m-d H:m:s", strtotime(($_SESSION['SelectedPlan'] == 1 ? "+1 year" : "+1 month")));

                        $stmt = $db->prepare(sprintf("INSERT INTO `payments`
                            (`by`, `payer_id`, `payer_first_name`, `payer_last_name`, `payer_email`, `payer_country`, `txn_id`, `amount`, `currency`, `type`, `status`, `valid`, `time`) VALUES 
                            ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s', '%s', '%s', '%s')",
                            $db->real_escape_string($feed->id), $db->real_escape_string($paypalResponse['PAYERID']), $db->real_escape_string($paypalResponse['FIRSTNAME']), $db->real_escape_string($paypalResponse['LASTNAME']), $db->real_escape_string($paypalResponse['EMAIL']), $db->real_escape_string($paypalResponse['SHIPTOCOUNTRYNAME']), $db->real_escape_string($paypalResponse['PAYMENTREQUEST_0_TRANSACTIONID']), $db->real_escape_string($paypalResponse['AMT']), $settings['currency'], $_SESSION['SelectedPlan'], 1, $date, date("Y-m-d H:m:s")));

                        // Execute the statement
                        $stmt->execute();

                        // Check the affected rows
                        $affected = $stmt->affected_rows;

                        // Close the statement
                        $stmt->close();

                        // If the pro status has been added
                        if($affected) {
                            // Set the pro account to valid
                            $proAccount = 2;
                        }
                    } else {
                        $TMPL['error'] = notificationBox('error', '<strong>'.urldecode($paypalResponse['L_SHORTMESSAGE0'].'</strong>: '.$paypalResponse['L_LONGMESSAGE0']));
                    }
                } else {
                    $TMPL['error'] = notificationBox('error', '<strong>'.urldecode($paypalResponse['L_SHORTMESSAGE0'].'</strong>: '.$paypalResponse['L_LONGMESSAGE0']));
                }
            } else {
                $TMPL['error'] = notificationBox('error', '<strong>'.urldecode($paypalResponse['L_SHORTMESSAGE0'].'</strong>: '.$paypalResponse['L_LONGMESSAGE0']));
            }
        }

编辑

这是我的 error.log 文件:

* Hostname was NOT found in DNS cache
*   Trying 173.0.82.83...
* Connected to api-3t.sandbox.paypal.com (173.0.82.83) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSL connection using ***********************
* Server certificate:
*        subject: C=US; ST=California; L=San Jose; O=PayPal, Inc.; OU=PayPal Production; CN=api-3t.sandbox.paypal.com
*        start date: 2015-09-16 00:00:00 GMT
*        expire date: 2016-10-31 23:59:59 GMT
*        issuer: C=US; O=VeriSign, Inc.; OU=VeriSign Trust Network; OU=Terms of use at https://www.verisign.com/rpa (c)10; CN=VeriSign Class 3 Secure Server CA - G3
*        SSL certificate verify ok.
> POST /nvp HTTP/1.1
Host: api-3t.sandbox.paypal.com
Accept: */*
Content-Length: 862
Content-Type: application/x-www-form-urlencoded
* upload completely sent off: 862 out of 862 bytes
< HTTP/1.1 200 OK
< Date: Wed, 30 Dec 2015 09:49:59 GMT
* Server Apache is not blacklisted
< Server: Apache
< X-PAYPAL-OPERATION-NAME: SetExpressCheckout
< X-PAYPAL-API-RC: 
< Connection: close
< Content-Length: 138
< Paypal-Debug-Id: 7d9949c818525
< Set-Cookie: X-PP-SILOVER=name%3DSANDBOX3.API.1%26silo_version%3D880%26app%3Dappdispatcher_apit%26TIME%3D3349709654; domain=.paypal.com; path=/; Secure; HttpOnly
< Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
< Content-Type: text/plain; charset=utf-8
* Closing connection 0

第二次编辑

最后我能够调试响应(存在重定向问题)

TOKEN=EC-9F8624569H0752611
BILLINGAGREEMENTACCEPTEDSTATUS=0
CHECKOUTSTATUS=PaymentActionCompleted
TIMESTAMP=2015-12-30T15:41:54Z
CORRELATIONID=19204729b140
ACK=Success
VERSION=116.0
BUILD=18308778
EMAIL=info-buyer@example.org
PAYERID=QL54Q696KZCLA
PAYERSTATUS=verified
FIRSTNAME=test
LASTNAME=buyer
COUNTRYCODE=IT
SHIPTONAME=test buyer
SHIPTOSTREET=Via Unit? d\'Italia, 5783296
SHIPTOCITY=Napoli
SHIPTOSTATE=NAPOLI
SHIPTOZIP=80127
SHIPTOCOUNTRYCODE=IT
SHIPTOCOUNTRYNAME=Italy
ADDRESSSTATUS=Unconfirmed
CURRENCYCODE=EUR
AMT=4.00
ITEMAMT=4.00
SHIPPINGAMT=0.00
HANDLINGAMT=0.00
TAXAMT=0.00
INSURANCEAMT=0.00
SHIPDISCAMT=0.00
L_NAME0=Monthly Pro Plan - example subdirectory
L_NUMBER0=cfcd208495d565ef66e7dff9f98764da
L_QTY0=1
L_TAXAMT0=0.00
L_AMT0=4.00
L_DESC0=Monthly Pro Plan - example subdirectory
L_ITEMWEIGHTVALUE0= 0.00000
L_ITEMLENGTHVALUE0= 0.00000
L_ITEMWIDTHVALUE0= 0.00000
L_ITEMHEIGHTVALUE0= 0.00000
PAYMENTREQUEST_0_CURRENCYCODE=EUR
PAYMENTREQUEST_0_AMT=4.00
PAYMENTREQUEST_0_ITEMAMT=4.00
PAYMENTREQUEST_0_SHIPPINGAMT=0.00
PAYMENTREQUEST_0_HANDLINGAMT=0.00
PAYMENTREQUEST_0_TAXAMT=0.00
PAYMENTREQUEST_0_INSURANCEAMT=0.00
PAYMENTREQUEST_0_SHIPDISCAMT=0.00
PAYMENTREQUEST_0_TRANSACTIONID=61M42051UB346361T
PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false
PAYMENTREQUEST_0_SHIPTONAME=test buyer
PAYMENTREQUEST_0_SHIPTOSTREET=Via Unit? d\'Italia, 5783296
PAYMENTREQUEST_0_SHIPTOCITY=Napoli
PAYMENTREQUEST_0_SHIPTOSTATE=NAPOLI
PAYMENTREQUEST_0_SHIPTOZIP=80127
PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE=IT
PAYMENTREQUEST_0_SHIPTOCOUNTRYNAME=Italy
PAYMENTREQUEST_0_ADDRESSSTATUS=Unconfirmed
PAYMENTREQUEST_0_ADDRESSNORMALIZATIONSTATUS=None
L_PAYMENTREQUEST_0_NAME0=Monthly Pro Plan - example subdirectory
L_PAYMENTREQUEST_0_NUMBER0=cfcd208495d565ef66e7dff9f98764da
L_PAYMENTREQUEST_0_QTY0=1
L_PAYMENTREQUEST_0_TAXAMT0=0.00
L_PAYMENTREQUEST_0_AMT0=4.00
L_PAYMENTREQUEST_0_DESC0=Monthly Pro Plan - example subdirectory
L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE0= 0.00000
L_PAYMENTREQUEST_0_ITEMLENGTHVALUE0= 0.00000
L_PAYMENTREQUEST_0_ITEMWIDTHVALUE0= 0.00000
L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE0= 0.00000
PAYMENTREQUESTINFO_0_TRANSACTIONID=61M42051UB346361T
PAYMENTREQUESTINFO_0_ERRORCODE=0

所以,在我看来,现在一切正常,但我在沙盒 Paypal 上看不到任何交易。怎么了?

【问题讨论】:

  • 这看起来不像 REST API,也不是我要求的 RAW 请求。这是生成请求的代码。需要看实际的请求。另外,作为旁注,当检查 ACK 的状态时,仅仅寻找 Success 是行不通的。您可以获得 SuccessWithWarning,这意味着它可以工作但有警告,但您的代码会将其视为失败。
  • 您的代码似乎也有点不合适。调用的正确顺序是 SetExpressCheckout、GetExpressCheckoutDetails,然后是 DoExpressCheckoutPayment。看起来你在调用 GECD 之前调用了 DECP..??
  • 好的,然后发布您收到的原始响应的示例。这就是我一直要求的。得帮我帮你,伙计。 ;)
  • 您可能要考虑的一件事是仅使用此PayPal PHP SDK 而不是您正在使用的。它已经为您准备好了一切,并且开箱即用。 Here is the sample 自带的,你会发现你可以一路无误的搞定整个流程。
  • @Simone 您是否考虑过将呼叫和响应写入您的数据库?如果没有 PayPal 的电话,在这里诊断您的问题将很困难,如果不是不可能的话。

标签: php ssl curl paypal paypal-sandbox


【解决方案1】:

您的过程似乎非常复杂。让我们分解一下

// Execute SetExpressCheckOut method to create the payment token and PayerID
$paypalResponse = $paypal->post('SetExpressCheckout', $params, $PayPalMode);

    //Respond according to message we receive from Paypal
    if(strtoupper($paypalResponse["ACK"]) == "SUCCESS") {
        // Generat the PayPal payment url with the response Token
        $paypalurl = 'https://www'.$PayPalMode.'.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='.$paypalResponse["TOKEN"].'';

        // Redirect to PayPal payment page
        header('Location: '.$paypalurl);

到目前为止一切顺利。您拨打 SEC 电话,获取您的令牌并将用户传递给 PayPal。但是接下来的部分令人困惑

// Execute DoExpressCheckoutPayment to receive the payment from the user
$paypalResponse = $paypal->post('DoExpressCheckoutPayment', $params, $PayPalMode);

// Check if the payment was successful
if(strtoupper($paypalResponse["ACK"]) == "SUCCESS") {

这没有任何意义。您刚刚使用header 将用户退回到 PayPal,我们正在调用它,看起来与我们传递给 SEC 调用的数据相同。 DoExpressCheckoutPayment 要求您传回令牌,并且用户刚刚离开站点以对其进行授权。我希望看到您的代码查找 $_GET['TOKEN'](表示从 PayPal 返回的用户),然后为此构建 new 请求。现在,当您编写代码时,它只是将所有 3 个调用链接到一个巨大的链中。

流程如下所示

  • SetExpressCheckout - 将用户退回到 PayPal。停止处理
  • GetExpressCheckoutDetails - 用户已从 PayPal 返回,因为我们在查询字符串中有一个 TOKEN。现在运行此调用可让我们确保 TOKEN 有效
  • DoExpressCheckoutPayment - 如果 TOKEN 有效,让我们完成销售。

最后但同样重要的是,您不能只寻找成功Read the docs on ACK。您还可以获得SuccessWithWarning。将你的成功条件改为

 if(stripos($paypalResponse["ACK"], "SUCCESS") !== false) {

【讨论】:

  • 我无法让它工作......正如你所建议的那样,我将GetExpressCheckoutDetails 替换为DoExpressCheckoutPayment,并且我已经替换了我所有的if(strtoupper($paypalResponse["ACK"]) == "SUCCESS") { ...现在如果我付款,它似乎有效,但是当在我网站上的成功页面上时,会出现一个引导弹出警告,里面什么都没有。并且账号没有升级到pro。 (好在付款没有完成)
  • @Simone 您是否要等到客户从 PayPal 回来后再运行 GetExpressCheckoutDetails 和其他操作?
  • 我只是将GetExpressCheckoutDetails 的位置与DoExpressCheckoutPayment 交换,保持我的代码的其他所有内容与我在上面发布的相同。此外,空警告消息仅显示在成功页面上,因此在 PayPal 页面之后
  • @Simone 那么你错过了我的重点。在用户从 PayPal 返回之后之前,您不能调用 GECD 或 DEC。您需要将这两个调用分别放在if(isset($_GET['TOKEN'])) 的块测试中。你的调用结构在这方面是错误的。
  • 我从进行 PayPal 集成的编码员那里得到了这个答案“API 调用正在按照 PayPal 的文档进行,它们没有任何问题,我认为这与您的 PayPal 真实账户有关。 PS 当用户根据 PayPal 的文档返回站点时,DEC 正在完成,否则它可能永远不会首先将用户的信息存储在数据库中。”
猜你喜欢
  • 1970-01-01
  • 2015-10-17
  • 2013-08-22
  • 2016-07-31
  • 2011-04-18
  • 2019-10-18
  • 2017-11-12
  • 2017-07-24
  • 1970-01-01
相关资源
最近更新 更多