1

最近、 PayPal Libを使用して、PayPal IPN を CodeIgniter2 に実装しました。定期購読システムを利用しています。

データベース内のすべての IPN 要求を記録するテーブルがデータベース内にあります。

なんらかの理由で、サインアップするたびに IPN リクエストが正しく送信されません。私は、すべて同じ subscr_id を持つ複数の subscr_signups と一緒に 1 つの subscr_payment を取得する傾向があります。明らかな理由から、システム内で計り知れないほどの面倒を引き起こしています。これに加えて、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

    }
}

以下は、各トランザクション (CSV) に対して私の IPN に対して行われた呼び出しの例です。

paypal_id,txn_id,subscr_id,txn_type,created
1,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:19:43
2,9XM95194MM564230E,I-FMUK0B5KJWKA,subscr_payment,2011-02-03 16:19:45
3,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:19:57
4,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:20:19
6,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:21:03
7,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:22:25
8,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:25:08
10,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:30:33
12,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:41:16
14,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 17:02:42
16,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 17:45:26
4

3 に答える 3

4

これを考慮してください - PayPal は冒とく的な表現です。ここで問題を再検討してください。

おそらく、これはあなたのせいではなく、CodeIgniter やライブラリのせいでもありません。PayPal は、均一でタイムリーな方法でデータを提供するのが非常に苦手です。処理も遅く、データをうまくリンクできません。

あなたへの私のアドバイスは、コールバックが行われるたびにすべてをIPNテーブルに保存し、IPNコールが行われるたびに自分自身に電子メールを送ることです. それから、PayPal が実際にあなたに何を送っているのか、あなたが何を望んでいるのかを理解しようとし、残りを捨ててください。

取引があなたのウェブサイトとは関係なくても、IPN 呼び出しが行われると思います。そのため、おばあちゃんが PayPal 経由でクリスマスのお金を送ってきた場合、IPN コールバックに表示されます。

それが少し役立つことを願っています。

于 2011-02-04T01:35:10.133 に答える
2

Paypal は使いやすいとは言えませんが、直面している問題に対処するための 3 つのヒントを紹介します。

1) PayPal からのすべての IPN 応答を格納するテーブルを作成します。すべてを保存する「生」という列があることを確認してください...「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

しかし^ため息^更新されるとは信じていません。彼らの文書の内容と彼らが私に送り返してきた内容に矛盾があることを発見しました。

3) OK、これはペイパルが行うもう 1 つの愚かなことであることを認めなければなりません。サーバーに到着する順序を保証しないにもかかわらず、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 で注文」できます。すべてが正しい順序で行われることを保証します。

ps 私の最初のサンプルコードにはこの列がありませんが、そこにあることが意図されていることに注意してください。アイデアを提供するために、開発コードをコピーして貼り付けているだけです。

于 2012-02-18T14:02:34.300 に答える
1

私がしていることは、サインアップのものを無視し、実際の支払いトランザクションで処理 (新しいユーザーの作成など) を行うことです。そして、私はそれらすべての IPN トランザクションを保存することを気にしません。ただし、IPNスクリプトで、投稿されたすべてのフィールドのエコーとともに、それらのすべてに電子メールを送信してください. 次に、それらの記録を取得します。

于 2012-10-05T16:25:12.053 に答える