PayPal IPN,大量注册,一次付款,所有订单都是错误的

时间:2011-02-03 15:59:36

标签: php codeigniter paypal paypal-ipn codeigniter-2

我最近使用PayPal Lib在CodeIgniter2中实现了PayPal IPN。我正在使用该系统进行订阅。

我的数据库中有一个表,用于记录数据库中的所有IPN请求。

出于某种原因,每次注册后,IPN请求都没有正确通过。我倾向于获得一个subscr_payment以及几个subscr_signups,它们都具有相同的subscr_id。由于显而易见的原因,它在系统内造成了无数的麻烦。更重要的是,IPN请求的顺序不正确,有时我会在subscr_signup之前得到subscr_payment - 这使得无法跟踪,因为注册时没有subscr_id将其链接到用户。

我有一个谷歌,在这方面找不到多少,我似乎有点异常。我想知道它是否与我正在使用的PayPal Lib有关,但我真的不想在CodeIgniter之外进行,因为我正在进行大量处理。以下是完整的IPN脚本。

class Paypal extends CI_Controller { function _construct() { parent::_construct(); $this->load->library('paypal_lib'); }

function ipn()
{

    $this->output->enable_profiler(TRUE);

    $this->load->model('payments_model');
    $this->load->model('paypal_model');
    $this->load->model('users_model');

    ob_start();

    if ($this->paypal_lib->validate_ipn()) 
    {


            $paypal_id = $this->paypal_model->add_paypal_ipn($this->paypal_lib->ipn_data);
            // Split the 'custom' field up, containing ID of temp user, ID of package and coupon
            $custom = explode(';', $this->paypal_lib->ipn_data['custom']);

            ###
            # subscription sign up
            ###
            if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_signup') {
                // Activate user/move from temp > live
                $this->users_model->move_temp($custom[0], $this->paypal_lib->ipn_data['subscr_id']);
            } # end subscr_signup


            ###
            # subscription payment
            ###
            if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_payment') {
                // Grab the coupon info, if we have one
                $discount = 1;
                if(!empty($custom[2])){
                    $this->load->model('coupons_model');
                    $couponinfo = $this->coupons_model->get_coupon($custom[2]);
                    $discount = $couponinfo->discount;
                }                    
                // Grab the package info
                $package = $this->packages_model->get_package($custom[1]);
                $price = $package->monthly * $discount; // Calculate discount, 0.8 = 20% off

                // Does the price calculated match the gross price?  If not something fishy is going on, block it
                if($price != $this->paypal_lib->ipn_data['mc_gross']){
                    mail(CONTACT_EMAIL, SITE_NAME.' failed payment attempt, possible hack', 'Price paid doesnt match price computed... paid: '.$this->paypal_lib->ipn_data['mc_gross'].' - price worked out: '.$price."\n\n".print_r($this->paypal_lib->ipn_data, true));
                    exit;
                }

                // Grab the user's details based on the subscr_id
                $user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']);

                // Add payment to the payments table
                $data = array(
                    'user_id' => $user->user_id,
                    'subscr_id' => $user->subscr_id,
                    'txn_id' => $this->paypal_lib->ipn_data['txn_id'],
                    'amount' => $this->paypal_lib->ipn_data['mc_gross'],
                    'package_id' => $custom[1],
                    'coupon' => (empty($custom[2]) ? '' : $custom[2])
                );
                $this->payments_model->add_payment($data);

                // Set (forced) user as active, and update their current active package
                $data1 = array(
                    'package_id' => $custom[1],
                    'active' => 1
                );
                $this->users_model->update_user($data1, $user->user_id);
            } # end subscr_payment


            ###
            # subscription failed/cancelled
            ###
            if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_cancel' || $this->paypal_lib->ipn_data['txn_type'] == 'subscr_failed') {
                // Grab user
                $user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']);

                // Make user inactive
                $data = array('active' => 0);
                $this->users_model->update_user($data, $user->user_id);
            } # end subscr_cancel|subscr_failed





            ###
            # subscription modified/payment changed
            ###
            if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_modify') {
                // Grab the coupon info, if we have one
                $discount = 1;
                if(!empty($custom[2])){
                    $this->load->model('coupons_model');
                    $couponinfo = $this->coupons_model->get_coupon($custom[2]);
                    $discount = $couponinfo->discount;
                }                    
                // Grab the package info
                $package = $this->packages_model->get_package($custom[1]);
                $price = $package->monthly * $discount; // Calculate discount, 0.8 = 20% off

                // Does the price calculated match the gross price?  If not something fishy is going on, block it
                if($price != $this->paypal_lib->ipn_data['mc_gross']){
                    mail(CONTACT_EMAIL, SITE_NAME.' failed payment attempt, possible hack', 'Price paid doesnt match price computed... paid: '.$this->paypal_lib->ipn_data['mc_gross'].' - price worked out: '.$price."\n\n".print_r($this->paypal_lib->ipn_data, true));
                    exit;
                }

                // Grab the user's details based on the subscr_id
                $user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']);

                // Add payment to the payments table
                $data = array(
                    'user_id' => $user->user_id,
                    'subscr_id' => $user->subscr_id,
                    'txn_id' => $this->paypal_lib->ipn_data['txn_id'],
                    'amount' => $this->paypal_lib->ipn_data['mc_gross'],
                    'package_id' => $custom[1],
                    'coupon' => (empty($custom[2]) ? '' : $custom[2])
                );
                $this->payments_model->add_payment($data);

                // Set (forced) user as active, and update their current active package
                $data1 = array(
                    'package_id' => $custom[1],
                    'active' => 1
                );
                $this->users_model->update_user($data1, $user->user_id);
            } # end subscr_modify

    }
}

function ipn() { $this->output->enable_profiler(TRUE); $this->load->model('payments_model'); $this->load->model('paypal_model'); $this->load->model('users_model'); ob_start(); if ($this->paypal_lib->validate_ipn()) { $paypal_id = $this->paypal_model->add_paypal_ipn($this->paypal_lib->ipn_data); // Split the 'custom' field up, containing ID of temp user, ID of package and coupon $custom = explode(';', $this->paypal_lib->ipn_data['custom']); ### # subscription sign up ### if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_signup') { // Activate user/move from temp > live $this->users_model->move_temp($custom[0], $this->paypal_lib->ipn_data['subscr_id']); } # end subscr_signup ### # subscription payment ### if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_payment') { // Grab the coupon info, if we have one $discount = 1; if(!empty($custom[2])){ $this->load->model('coupons_model'); $couponinfo = $this->coupons_model->get_coupon($custom[2]); $discount = $couponinfo->discount; } // Grab the package info $package = $this->packages_model->get_package($custom[1]); $price = $package->monthly * $discount; // Calculate discount, 0.8 = 20% off // Does the price calculated match the gross price? If not something fishy is going on, block it if($price != $this->paypal_lib->ipn_data['mc_gross']){ mail(CONTACT_EMAIL, SITE_NAME.' failed payment attempt, possible hack', 'Price paid doesnt match price computed... paid: '.$this->paypal_lib->ipn_data['mc_gross'].' - price worked out: '.$price."\n\n".print_r($this->paypal_lib->ipn_data, true)); exit; } // Grab the user's details based on the subscr_id $user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']); // Add payment to the payments table $data = array( 'user_id' => $user->user_id, 'subscr_id' => $user->subscr_id, 'txn_id' => $this->paypal_lib->ipn_data['txn_id'], 'amount' => $this->paypal_lib->ipn_data['mc_gross'], 'package_id' => $custom[1], 'coupon' => (empty($custom[2]) ? '' : $custom[2]) ); $this->payments_model->add_payment($data); // Set (forced) user as active, and update their current active package $data1 = array( 'package_id' => $custom[1], 'active' => 1 ); $this->users_model->update_user($data1, $user->user_id); } # end subscr_payment ### # subscription failed/cancelled ### if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_cancel' || $this->paypal_lib->ipn_data['txn_type'] == 'subscr_failed') { // Grab user $user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']); // Make user inactive $data = array('active' => 0); $this->users_model->update_user($data, $user->user_id); } # end subscr_cancel|subscr_failed ### # subscription modified/payment changed ### if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_modify') { // Grab the coupon info, if we have one $discount = 1; if(!empty($custom[2])){ $this->load->model('coupons_model'); $couponinfo = $this->coupons_model->get_coupon($custom[2]); $discount = $couponinfo->discount; } // Grab the package info $package = $this->packages_model->get_package($custom[1]); $price = $package->monthly * $discount; // Calculate discount, 0.8 = 20% off // Does the price calculated match the gross price? If not something fishy is going on, block it if($price != $this->paypal_lib->ipn_data['mc_gross']){ mail(CONTACT_EMAIL, SITE_NAME.' failed payment attempt, possible hack', 'Price paid doesnt match price computed... paid: '.$this->paypal_lib->ipn_data['mc_gross'].' - price worked out: '.$price."\n\n".print_r($this->paypal_lib->ipn_data, true)); exit; } // Grab the user's details based on the subscr_id $user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']); // Add payment to the payments table $data = array( 'user_id' => $user->user_id, 'subscr_id' => $user->subscr_id, 'txn_id' => $this->paypal_lib->ipn_data['txn_id'], 'amount' => $this->paypal_lib->ipn_data['mc_gross'], 'package_id' => $custom[1], 'coupon' => (empty($custom[2]) ? '' : $custom[2]) ); $this->payments_model->add_payment($data); // Set (forced) user as active, and update their current active package $data1 = array( 'package_id' => $custom[1], 'active' => 1 ); $this->users_model->update_user($data1, $user->user_id); } # end subscr_modify } }

以下是针对每笔交易(CSV)对我的IPN进行的调用的示例。

3 个答案:

答案 0 :(得分:4)

考虑一下 - 贝宝是插入亵渎性。现在重新审视这个问题。

这可能不是你的错,或CodeIgniter或图书馆的错。 PayPal在以统一和及时的方式提供数据方面非常糟糕,它也很慢并且不能很好地将数据链接在一起。

我建议您在进行回调时将所有内容保存到IPN表中,甚至在进行IPN呼叫时通过电子邮件发送给自己。然后努力弄清楚PayPal实际上发送给你的是什么,你想要什么,然后扔掉其余部分。

我认为即使交易与您的网站无关,也会进行IPN通话。因此,如果您的奶奶通过PayPal向您发送圣诞节资金,它将出现在IPN回拨中。

希望有所帮助。

答案 1 :(得分:2)

paypal不是很容易使用,但让我分享3个提示,解决您遇到的问题。

1)创建一个表来存储来自PayPal的所有IPN响应。确保你有一个名为“raw”的列存储一切......执行“json_encode($ this-> paypal_lib-> ipn_data)”。这样可以节省您的时间......因为您可以稍后编写一个脚本,将原始列中的数据提取到其自己的列中。这也有助于调试。

2)首先,只需将必要的内容拉出到ipn表的列中,这样您就可以轻松查询...这里是我认为可能与我的用例类似的所有内容。

    $this->payment_model->create_ipn(array(
        'invoice' => $this->paypal_lib->ipn_data['invoice'],
        'txn_type' => $this->paypal_lib->ipn_data['txn_id'],
        'parent_txn_id' => $this->paypal_lib->ipn_data['parent_txn_id'],
        'txn_type' => $this->paypal_lib->ipn_data['txn_type'],
        'item_name' => $this->paypal_lib->ipn_data['item_name'],
        'item_number' => $this->paypal_lib->ipn_data['item_number'],
        'quantity' => $this->paypal_lib->ipn_data['quantity'],
        'exchange_rate' => $this->paypal_lib->ipn_data['exchange_rate'],
        'settle_amount' => $this->paypal_lib->ipn_data['settle_currency'],
        'settle_amount' => $this->paypal_lib->ipn_data['settle_amount'],
        'mc_currency' => $this->paypal_lib->ipn_data['mc_currency'],
        'mc_fee' => $this->paypal_lib->ipn_data['mc_fee'],
        'mc_gross' => $this->paypal_lib->ipn_data['mc_gross'],
        'payment_date' => $this->paypal_lib->ipn_data['payment_date'],
        'payment_status' => $this->paypal_lib->ipn_data['payment_status'],
        'payment_type' => $this->paypal_lib->ipn_data['payment_type'],
        'pending_reason' => $this->paypal_lib->ipn_data['pending_reason'],
        'reason_code' => $this->paypal_lib->ipn_data['reason_code'],
        'subscr_id' => $this->paypal_lib->ipn_data['subscr_id'],
        'subscr_date' => $this->paypal_lib->ipn_data['subscr_date'] ? mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['subscr_date'])) : NULL,
        'subscr_effective' => $this->paypal_lib->ipn_data['subscr_effective'] ? mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['subscr_effective'])) : NULL,
        'period1' => $this->paypal_lib->ipn_data['period1'],
        'period2' => $this->paypal_lib->ipn_data['period2'],
        'period3' => $this->paypal_lib->ipn_data['period3'],
        'amount1' => $this->paypal_lib->ipn_data['amount1'],
        'amount2' => $this->paypal_lib->ipn_data['amount2'],
        'amount3' => $this->paypal_lib->ipn_data['amount3'],
        'mc_amount1' => $this->paypal_lib->ipn_data['mc_amount1'],
        'mc_amount2' => $this->paypal_lib->ipn_data['mc_amount2'],
        'mc_amount3' => $this->paypal_lib->ipn_data['mc_amount3'],
        'recurring' => $this->paypal_lib->ipn_data['recurring'],
        'reattempt' => $this->paypal_lib->ipn_data['reattempt'],
        'retry_at' => $this->paypal_lib->ipn_data['retry_at'] ? mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['retry_at'])) : NULL,
        'recur_times' => $this->paypal_lib->ipn_data['recur_times'],
        'payer_id' => $this->paypal_lib->ipn_data['payer_id'],
        'payer_email' => $this->paypal_lib->ipn_data['payer_email'],
        'payer_status' => $this->paypal_lib->ipn_data['payer_status'],
        'payer_business_name' => $this->paypal_lib->ipn_data['payer_business_name'],
        'ipn_track_id' => $this->paypal_lib->ipn_data['ipn_track_id'],
        'raw' => json_encode($this->paypal_lib->ipn_data_arr),
        'test_ipn' => $this->paypal_lib->ipn_data['test_ipn']
    ));

不要复制我上面的代码,因为它只是为了给你一些粗略的想法......如果你调整了我的代码,还要确保ipn_data函数是这样的(否则你会得到大量的错误)

function ipn_data($key)
{
    return isset($this->fields[$key]) ? $this->fields[$key] : NULL;
}

了解他们可以发回的所有可能的东西这个链接是黄金 https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables

但是^ sigh ^不信任它要更新。我发现他们所说的内容和他们寄回给我的内容不一致。

3)好吧,我必须承认Paypal的另一个愚蠢的事情 - 即使他们不保证它到达你的服务器的顺序,他们也不会给你一个IPN日期。对于subscr_payment,他们给你payment_date ...对于subscr_signup他们给你subscr_date ...所以你需要做的就是让你的IPN按正确的顺序排列,这就是有一个名为ipn_date的列。

'ipn_date' => isset($this->paypal_lib->ipn_data['payment_date']) ? 
    mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['payment_date'])) : 
        (isset($this->paypal_lib->ipn_data['subscr_date']) ? 
            mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['subscr_date'])) : NULL),

现在一切都很酷,你可以“通过ipn_date订购”,我保证一切都会按照正确的顺序。

P.S。请注意我的第一个示例代码没有此列,但它意味着存在。我只是复制并粘贴我的开发代码,以便给你一个想法。

答案 2 :(得分:1)

我所做的是忽略注册,只是在实际支付交易上处理(创建新用户等)。而且我不打扰存储所有那些IPN trans。让你的IPN脚本给你发送每个人的电子邮件,并回复所有发布的字段。然后你就会有他们的记录。