Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to use Paypal API on an SSL website

I have an SSL website with Paypal API integration using cURL.

This is my class on PHP includes file:

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;
    }
}

For some reason it doesn't work, clicking on the pay button brings the user to Paypal but after finishing the process it brings back the user to the website, the url seems correct (ex. https://example.com/pro&type=successful?token=EC-8BB04791XJ708490K&PayerID=QL54Q696KZCLA) but without make the payment.

I tried the sandbox too, but I can't see any error on Apache log file so I need to debug $response, for this reason I tried adding echo var_dump($response); before parse the response.

But I don't have much experience on debugging, and on Chrome I don't know where to check the response.(on JS console I can't see it)

How could I debug this API response to check why paypal doesn't allow payments for my website? (the website is already verified)

UPDATE

As authentication I'm using the API signature NVP format with SetExpressCheckout method.

This is the code for my response:

        // 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']));
            }
        }

EDIT

This is my error.log file:

* 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

2nd EDIT

Finally I was able to debug the response (there was a redirection problem)

TOKEN=EC-9F8624569H0752611
BILLINGAGREEMENTACCEPTEDSTATUS=0
CHECKOUTSTATUS=PaymentActionCompleted
TIMESTAMP=2015-12-30T15:41:54Z
CORRELATIONID=19204729b140
ACK=Success
VERSION=116.0
BUILD=18308778
[email protected]
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

So, from my side everything works right now, but I see no transactions on sandbox Paypal. What's wrong?

like image 868
NineCattoRules Avatar asked Dec 24 '15 11:12

NineCattoRules


1 Answers

Your process seems highly convoluted. Let's break this down

// 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);

So far so good. You do your SEC call, get your token and pass the user on to PayPal. But then this next part is confusing

// 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") {

This doesn't make any sense. You just bounced the user to PayPal with header and we're calling this with what appears the be the same data we passed to the SEC call. DoExpressCheckoutPayment requires you pass back the token and the user just left the site to authorize it. I would expect to see your code look for $_GET['TOKEN'] (meaning the user returned from PayPal) and then build a new request for that. Right now, as your code is written, it's just chaining all 3 calls in one giant chain.

Here's what the process should look like

  • SetExpressCheckout - Bounce user to PayPal. Stop processing
  • GetExpressCheckoutDetails - User has returned from PayPal because we have a TOKEN in the query string. Running this call now lets us make sure the TOKEN is valid
  • DoExpressCheckoutPayment - If the TOKEN is valid, let's complete the sale.

Last but not least, you can't just look for Success. Read the docs on ACK. You can also get SuccessWithWarning. Change your success condition to

 if(stripos($paypalResponse["ACK"], "SUCCESS") !== false) {
like image 154
Machavity Avatar answered Sep 16 '22 23:09

Machavity