• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

C++ I need help with my IPN. It does not transfer the "coins" to the account

tommy91

Member
Joined
Oct 27, 2010
Messages
251
Reaction score
19
Location
Sweden
This is the script and it used to be points, we changed it so it was coins. Can you help me?
Lua:
<?php
    if (gethostbyaddr($_SERVER['REMOTE_ADDR']) !== 'notify.paypal.com') {
        exit();
    }

    // Require the functions to connect to database and fetch config values
    require 'config.php';
    require 'engine/database/connect.php';
    
    // Fetch and sanitize POST and GET values
    function getValue($value) {
        return (!empty($value)) ? sanitize($value) : false;
    }
    function sanitize($data) {
        return htmlentities(strip_tags(mysql_znote_escape_string($data)));
    }
    
    function VerifyPaypalIPN(array $IPN = null){
        if(empty($IPN)){
            $IPN = $_POST;
        }
        if(empty($IPN['verify_sign'])){
            return null;
        }
        $IPN['cmd'] = '_notify-validate';
        $PaypalHost = (empty($IPN['test_ipn']) ? 'www' : 'www.sandbox').'.paypal.com';
        $cURL = curl_init();
        curl_setopt($cURL, CURLOPT_SSL_VERIFYPEER, 1);
        curl_setopt($cURL, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($cURL, CURLOPT_SSLVERSION, 6);
        curl_setopt($cURL, CURLOPT_CAINFO, __DIR__ . '/engine/cert/cacert.pem');
        curl_setopt($cURL, CURLOPT_URL, "https://{$PaypalHost}/cgi-bin/webscr");
        curl_setopt($cURL, CURLOPT_ENCODING, 'gzip');
        curl_setopt($cURL, CURLOPT_BINARYTRANSFER, true);
        curl_setopt($cURL, CURLOPT_POST, true); // POST back
        curl_setopt($cURL, CURLOPT_POSTFIELDS, $IPN); // the $IPN
        curl_setopt($cURL, CURLOPT_HEADER, false);
        curl_setopt($cURL, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($cURL, CURLOPT_FORBID_REUSE, true);
        curl_setopt($cURL, CURLOPT_FRESH_CONNECT, true);
        curl_setopt($cURL, CURLOPT_CONNECTTIMEOUT, 30);
        curl_setopt($cURL, CURLOPT_TIMEOUT, 60);
        curl_setopt($cURL, CURLINFO_HEADER_OUT, true);
        curl_setopt($cURL, CURLOPT_HTTPHEADER, array(
            'Connection: close',
            'Expect: ',
        ));
        $Response = curl_exec($cURL);
        $Status = (int)curl_getinfo($cURL, CURLINFO_HTTP_CODE);
        curl_close($cURL);
        if(empty($Response) or !preg_match('~^(VERIFIED|INVALID)$~i', $Response = trim($Response)) or !$Status){
            return null;
        }
        if(intval($Status / 100) != 2){
            return false;
        }
        return !strcasecmp($Response, 'VERIFIED');
    }

    // Fetch paypal configurations
    $paypal = $config['paypal'];
    $prices = $config['paypal_prices'];
    
    // Send an empty HTTP 200 OK response to acknowledge receipt of the notification
    header('HTTP/1.1 200 OK');

    // Build the required acknowledgement message out of the notification just received
    $req = 'cmd=_notify-validate';
    foreach ($_POST as $key => $value) {
        $value = urlencode(stripslashes($value));
        $req  .= "&$key=$value";
    }
    $postdata = $req;
    
    // Assign payment notification values to local variables
    $item_name        = $_POST['item_name'];
    $item_number      = $_POST['item_number'];
    $payment_status   = $_POST['payment_status'];
    $payment_amount   = $_POST['mc_gross'];
    $payment_currency = $_POST['mc_currency'];
    $txn_id           = getValue($_POST['txn_id']);
    $receiver_email   = getValue($_POST['receiver_email']);
    $payer_email      = getValue($_POST['payer_email']);
    $custom           = (int)$_POST['custom'];

    $connectedIp = $_SERVER['REMOTE_ADDR'];
    mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', 'Connection from IP: $connectedIp', '0', '0', '0')");
    
    $status = VerifyPaypalIPN();
    if ($status) {
        // Check that the payment_status is Completed
        if ($payment_status == 'Completed') {

            
            // Check that txn_id has not been previously processed
            $txn_id_check = mysql_select_single("SELECT `txn_id` FROM `znote_paypal` WHERE `txn_id`='$txn_id'");
            if ($txn_id_check !== false) {
                // Check that receiver_email is your Primary PayPal email
                if ($receiver_email == $paypal['email']) {
                    
                    $status = true;
                    $paidMoney = 0;
                    $paidPoints = 0;

                    foreach ($prices as $priceValue => $pointsValue) {
                        if ($priceValue == $payment_amount) {
                            $paidMoney = $priceValue;
                            $paidPoints = $pointsValue;
                        }
                    }

                    if ($paidMoney == 0) $status = false; // Wrong amount of money
                    if ($payment_currency != $paypal['currency']) $status = false; // Wrong currency
                    
                    // Verify that the user havent messed around with POST data
                    if ($status) {
                        // transaction log
                        mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', '$payer_email', '$custom', '".$paidMoney."', '".$paidPoints."')");
                        
                        // Process payment
                        $data = mysql_select_single("SELECT `coins` AS `old_points` FROM `accounts` WHERE `account_id`='$custom';");

                        // Give points to user
                        $new_points = $data['old_points'] + $paidPoints;
                        mysql_update("UPDATE `accounts` SET `coins`='$new_points' WHERE `account_id`='$custom'");
                    }
                }  else {
                    $pmail = $paypal['email'];
                    mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', 'ERROR: Wrong mail. Received: $receiver_email, configured: $pmail', '0', '0', '0')");
                }
            }
        }
    } else {
        // Something is wrong
        mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', 'ERROR: Invalid data. $postdata', '0', '0', '0')");
    }
?>



Screenshot 2022-04-04 082407.png
 
This was fixed in Znote a while ago, the commit changes are here:


Remove:
PHP:
    if (gethostbyaddr($_SERVER['REMOTE_ADDR']) !== 'notify.paypal.com') {
        exit();
    }

Replace with:
PHP:
function ip_in_range( $ip, $range ) {
    if ( strpos( $range, '/' ) === false ) {
        $range .= '/32';
    }
    // $range is in IP/CIDR format eg 127.0.0.1/24
    list( $range, $netmask ) = explode( '/', $range, 2 );
    $range_decimal = ip2long( $range );
    $ip_decimal = ip2long( $ip );
    $wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1;
    $netmask_decimal = ~ $wildcard_decimal;
    return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
}

$paypal_ip_ranges = array(
    "173.0.81.65",
    "173.0.81.140",
    "64.4.240.0/21",
    "64.4.248.0/22",
    "66.211.168.0/22",
    "173.0.80.0/20",
    "91.243.72.0/23"
);

$verified = false;
for($i = 0; $i < count($paypal_ip_ranges); $i++) {
    if(ip_in_range($_SERVER["REMOTE_ADDR"], $paypal_ip_ranges[$i])) {
        $verified = true;
        break;
    }
}

if(!$verified) {
    exit();
}
 
Ill try it out, thanks!
Post automatically merged:

Well, i dont think it works. When people buy point/coins from the website it does not get added to their account, i have to do it manually. All of it shows in the znote_paypal but nothing gets over to the accounts.
 
Last edited:
It didnt work. Had a player buy some coins and it was the same thing, Payment got thru but the coins did not show up. Needed to do it manually.
 
check your sql logs, doe sit show any errors and/or payment received?

anything showing up in znote_paypal table?
 
Last edited:
The IPN verify response looks good, as it's inserting the second record.

The problem lies around the SELECT and UPDATE afterwards:
Try this:
PHP:
// Process payment
$data = mysql_select_single("SELECT `coins` FROM `accounts` WHERE `account_id`='".$custom."';");

// Give points to user
$new_points = $data['coins'] + $paidPoints;
mysql_update("UPDATE `accounts` SET `coins`='".$new_points."' WHERE `account_id`='".$custom."'");

If that doesn't work, it's pretty difficult to debug the IPN, so lets just insert some stuff just for testing:
PHP:
// Process payment
$data = mysql_select_single("SELECT `coins` FROM `accounts` WHERE `account_id`='$custom';");

// Give points to user
$old_points = $data['coins'];
mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', 'oldPoints: ".$old_points."', '$custom', '0', '0')");
 
The IPN verify response looks good, as it's inserting the second record.

The problem lies around the SELECT and UPDATE afterwards:
Try this:
PHP:
// Process payment
$data = mysql_select_single("SELECT `coins` FROM `accounts` WHERE `account_id`='".$custom."';");

// Give points to user
$new_points = $data['coins'] + $paidPoints;
mysql_update("UPDATE `accounts` SET `coins`='".$new_points."' WHERE `account_id`='".$custom."'");

If that doesn't work, it's pretty difficult to debug the IPN, so lets just insert some stuff just for testing:
PHP:
// Process payment
$data = mysql_select_single("SELECT `coins` FROM `accounts` WHERE `account_id`='$custom';");

// Give points to user
$old_points = $data['coins'];
mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', 'oldPoints: ".$old_points."', '$custom', '0', '0')");

Whaa?

you removed the query that updates the player account completely... You only have insert into znote_paypal table, which is a log table only. You need to update the players account.

It didnt work. Had a player buy some coins and it was the same thing, Payment got thru but the coins did not show up. Needed to do it manually.

Do NONE of the purcahses add points, or only some?
 
Whaa?

you removed the query that updates the player account completely... You only have insert into znote_paypal table, which is a log table only. You need to update the players account.



Do NONE of the purcahses add points, or only some?

Maybe try reading my post again. The OP has stated that there are no issues with the payment going through.

The first edit I wrote was a potential fix for updating the accounts table.

The second edit I wrote is incase the first edit doesn't fix it. It is to see if the following SELECT statement is returning the old/current points. It is for debugging only.

Look at this code as an example:
PHP:
if ($status) {
    //This is working as intended. Check the screenshot in #1. The second inserted row is there.
    mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', '$payer_email', '$custom', '".$paidMoney."', '".$paidPoints."')");

    //Therefore, the error must be here...
    $data = mysql_select_single("SELECT `coins` AS `old_points` FROM `accounts` WHERE `account_id`='$custom';");
    $new_points = $data['old_points'] + $paidPoints;
    mysql_update("UPDATE `accounts` SET `coins`='$new_points' WHERE `account_id`='$custom'");
}
 
Yeah, the payment goes through and it shows up on the znote_paypal table. But no coins to the account
Post automatically merged:

The IPN verify response looks good, as it's inserting the second record.

The problem lies around the SELECT and UPDATE afterwards:
Try this:
PHP:
// Process payment
$data = mysql_select_single("SELECT `coins` FROM `accounts` WHERE `account_id`='".$custom."';");

// Give points to user
$new_points = $data['coins'] + $paidPoints;
mysql_update("UPDATE `accounts` SET `coins`='".$new_points."' WHERE `account_id`='".$custom."'");

If that doesn't work, it's pretty difficult to debug the IPN, so lets just insert some stuff just for testing:
PHP:
// Process payment
$data = mysql_select_single("SELECT `coins` FROM `accounts` WHERE `account_id`='$custom';");

// Give points to user
$old_points = $data['coins'];
mysql_insert("INSERT INTO `znote_paypal` VALUES ('0', '$txn_id', 'oldPoints: ".$old_points."', '$custom', '0', '0')");
Ive inserted it now, lets see if it is good :D
 
Last edited:
Yeah, the payment goes through and it shows up on the znote_paypal table. But no coins to the account
Post automatically merged:


Ive inserted it now, lets see if it is good :D
Did you get this fixed?

I had an issue recently with 1 person trying to buy points and the VerifyPaypalIPN seemingly fails. Seems to only be affecting 1 person so far though and I'm a bit confused.

Wondering if yours was all good now
 
Did you get this fixed?

I had an issue recently with 1 person trying to buy points and the VerifyPaypalIPN seemingly fails. Seems to only be affecting 1 person so far though and I'm a bit confused.

Wondering if yours was all good now
I think there is a problem since i had that issue too but with 1 person so i think paypal did add some new ips that we need to add on the znote ipn
 
I think there is a problem since i had that issue too but with 1 person so i think paypal did add some new ips that we need to add on the znote ipn

I can see the connection in logs, it's coming in on a real Paypal IP address and logging that it gets the connection, but its failing on function VerifyPaypalIPN for me. Another problem is that it doesn't log this error because it tries to put the error in the email field which has a max value of 255 char. As this error dumps the post data, it exceeds this and just does nothing instead.

I made a function to write it to a txt file, but i still can't see what's wrong, because the issue seems to be inside VerifyPaypalIPN which doesn't run the data to write to file when i tried. I'm still trying to investigate further.

Seems to only be a couple people it's affected so far, but seemingly consistent. Was testing with one of the players by making a free point shop item, purcahsed multiple times but always fails at VerifyPaypalIPN()

I'm also using the latest ipn.php

My znote_paypal table just looks like this
1649674345480.png

For ones that work it should have 2 lines with the asme txn_id, one for the incoming connection (seen above) and one showing the account, amount and points purcahsed.


I also sent Znote a PM
 
I can see the connection in logs, it's coming in on a real Paypal IP address and logging that it gets the connection, but its failing on function VerifyPaypalIPN for me. Another problem is that it doesn't log this error because it tries to put the error in the email field which has a max value of 255 char. As this error dumps the post data, it exceeds this and just does nothing instead.

I made a function to write it to a txt file, but i still can't see what's wrong, because the issue seems to be inside VerifyPaypalIPN which doesn't run the data to write to file when i tried. I'm still trying to investigate further.

Seems to only be a couple people it's affected so far, but seemingly consistent. Was testing with one of the players by making a free point shop item, purcahsed multiple times but always fails at VerifyPaypalIPN()

I'm also using the latest ipn.php

My znote_paypal table just looks like this
View attachment 66939

For ones that work it should have 2 lines with the asme txn_id, one for the incoming connection (seen above) and one showing the account, amount and points purcahsed.


I also sent Znote a PM
Be wary if you're using Cloudflare. There could be some weird things going on with 503 errors or just failing the verify.

I had a client who tested disabling his cloudflare, then the paypalIPN worked flawlessly from then on. So just some cloudflare tinkering was needed to get the system working properly.
 
Yeah, i misread what you put. So it's just one individual buyer who's order isn't going through. Every other purchase works with no problems?
 
Yeah, i misread what you put. So it's just one individual buyer who's order isn't going through. Every other purchase works with no problems?

Yup.

I can't get the cUrl verbose logs to output, but it seems it receives an Invalid response.

But I can dump the POST data and it's all there, so I don't understand why it's getting an invalid response. Only happening for 1 person. They have tried from different computers.
 
It did not work, One bought coins and nothing came to his account. It all shows on znote_paypal tho..

What does it show in sql though?

When successful, I get this
1649802988148.png

2 lines with the same txn_id. This is when the points ARE applied.

When it fails, I only get 1 line like this
1649803018208.png

I found out the email has a char limit, you can alter the talbe like so
ALTER TABLE znote_paypal MODIFY email TEXT;

Then I get something like this recorded in errors.
1649803123490.png

From what I can tell, when it does the Paypal IPN Verification, it gets an "INVALID" result in this function "function VerifyPaypalIPN(array $IPN = null)"

Only seems to be affecting certain people though and I can't figure out why.


Are you seeing the same thing?

I posted an issue on znote github and tried to msg @Znote for assistance, as I'm not experianced with curl and Paypal IPN Postback.
 
Back
Top