简体   繁体   中英

Paypal Subscription IPN Returning Invalid

As the title states, I am having an issue with Paypal and the IPN returning verified for a subscription method of payment. For every subscribtion IPN that I have done so far, it has returned INVALID with data. The thing is, for the same exact code, changing of the button from _xclick-subscriptions to _donations for example gives me a VERIFIED result.

I have been looking for a bit of time now for a solution to my issue but I have been unable to find one. Here is what is happening - I have created the subscription button that leads the user to paypal(in this case the sandbox) where they then pay and after they pay they have an option to return to the website. Here is the code for the button.

<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
<input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but20.gif" border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
<input type="hidden" name="cmd" value="_xclick-subscriptions">
<input type="hidden" name="business" value="mail@url.com"> 
<input type="hidden" name="item_name" value="Monthly Sub">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="a3" value="10.00"> 
<input type="hidden" name="p3" value="1"> 
<input type="hidden" name="t3" value="M"> 
<input type="hidden" name="src" value="1"> 
<input type="hidden" name="sra" value="1">
<input type="hidden" name="rm" value="2">
<input type="hidden" name="return" value="http://url.com/thanks.php">
</form>

Since this has been a decent issue for me I have broken the button down into having pretty much barebones required values until I am able to actually get it to return verified instead of invalid. The following is the code that I have as my IPN listener.

define("DEBUG", 1);
define("USE_SANDBOX", 1);
define("LOG_FILE", "./ipn.log");
// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
    $keyval = explode ('=', $keyval);
    if (count($keyval) == 2)
        $myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
    $get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
    if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
        $value = urlencode(stripslashes($value));
    } else {
        $value = urlencode($value);
    }
    $req .= "&$key=$value";
}
// Post IPN data back to PayPal to validate the IPN data is genuine
// Without this step anyone can fake IPN data
if(USE_SANDBOX == true) {
    $paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
} else {
    $paypal_url = "https://www.paypal.com/cgi-bin/webscr";
}
$ch = curl_init($paypal_url);
if ($ch == FALSE) {
    return FALSE;
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
if(DEBUG == true) {
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
}
// Set TCP timeout to 30 seconds
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
$res = curl_exec($ch);
if (curl_errno($ch) != 0) // cURL error
    {
    if(DEBUG == true) { 
        error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE);
    }
    curl_close($ch);
    exit;
} else {
        // Log the entire HTTP response if debug is switched on.
        if(DEBUG == true) {
            error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE);
            error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE);
            // Split response headers and payload
            list($headers, $res) = explode("\r\n\r\n", $res, 2);
        }
        curl_close($ch);
}
// Inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
    if(DEBUG == true) {
        error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE);
    }
} else if (strcmp ($res, "INVALID") == 0) {
    if(DEBUG == true) {
        error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE);
    }
}

As I said earlier, a cmd value of _donations returns VERIFIED but _xclick-subscriptions returns INVALID with the data. I have an example of the data that is returned here.

cmd=_notify-validate&txn_type=subscr_signup&subscr_id=I-SFYTVAKSSYGK&last_name=lastname&residence_country=US&mc_currency=USD&item_name=Monthly+Sub&business=mail%40url.com&amount3=10.00&recurring=1&address_street=1+Main+St&payer_status=verified&payer_email=name%40email.com&address_status=confirmed&first_name=firstname&receiver_email=mail%40url.com&address_country_code=US&payer_id=SYPW3V3BVWTJW&address_city=San+Jose&reattempt=1&address_state=CA&subscr_date=07%3A33%3A13+Aug+27%2C+2014+PDT&address_zip=95131&charset=windows-1252&period3=1+M&address_country=United+States&mc_amount3=10.00&address_name=firstname+lastname&auth=AwIHoldf-BK2GZqtPhPo0O2g3go74cV9ZOLRYhHTJdKDM5EP0YHuqHLo23RYPfQs-3YDnvhjVf.J3AtydGfvDfA&form_charset=UTF-8

I honestly have no clue as to where to go from here on out so any and all help is greatly appreciated!

After another entire day of testing I ended up figuring out what was causing my specific issue. It turns out that Paypal likes to use multiple IP's to post the information from sandbox to your server. I am using htaccess to block all but my ip address from viewing the website(have a server but I am really close to going live and this was the last thing barring me from opening the website up). After grabbing what I thought was all of the Paypal sandbox IP's I opened those up in htaccess. Turns out that Paypal uses a different IP to post the data from a subscription method as compared to a one time payment method. Once I found that out and added in the IP to htaccess my problem went away.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM