PayPal IPN, много регистраций, один платеж, все в неправильном порядке - PullRequest
1 голос
/ 03 февраля 2011

Я недавно внедрил IPN PayPal в CodeIgniter2, используя PayPal Lib . Я пользуюсь системой для подписок.

В моей базе данных есть таблица, в которой записываются все запросы IPN в базе данных.

Почему-то после каждой регистрации запросы IPN не проходят должным образом. Я склонен получать один subscr_payment вместе с несколькими subscr_signups, все с одним и тем же subscr_id. Это вызывает неисчислимые хлопоты в системе по очевидным причинам. Что добавляет к этому, тот факт, что запросы IPN не приходят в правильном порядке, иногда я получаю subscr_payment до subscr_signup - что делает невозможным отслеживание, так как нет подписки subscr_id от регистрации, чтобы связать его с пользователем.

У меня есть Google, и я не могу найти много по этому поводу. Кажется, я немного аномалия. Мне интересно, связано ли это с PayPal Lib, которую я использую, но я действительно не хочу делать это вне CodeIgniter, так как я делаю большую обработку. Ниже приведен полный скрипт IPN.

class Paypal extends CI_Controller { function _<em>construct() { parent::</em>_construct(); $this->load->library('paypal_lib'); }</p> <pre><code>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 } }

Ниже приведен пример вызовов на мой IPN для каждой транзакции (CSV).

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

Ответы [ 3 ]

4 голосов
/ 04 февраля 2011

Учтите это - PayPal вставить ненормативную лексику .Теперь вернитесь к проблеме.

Скорее всего, это не ваша ошибка, ни CodeIgniter, ни библиотека.PayPal очень плохо передает данные единообразно и своевременно, он также медленный и не очень хорошо связывает данные.

Мой совет вам - сохранять все в таблицу IPN всякий раз, когда выполняется обратный вызов, даже напишите себе, когда когда-либо сделан вызов IPN.Затем постарайтесь выяснить, что PayPal на самом деле отправляет вам, что вы хотите, и выбросить все остальное.

Я думаю, что IPN-вызов выполняется, даже если транзакция не имеет никакого отношения к вашему веб-сайту.Поэтому, если ваша бабушка отправит вам ваши рождественские деньги через PayPal, они появятся в обратном вызове IPN.

Надеюсь, это немного поможет.

2 голосов
/ 18 февраля 2012

PayPal не совсем прост в использовании, но позвольте мне поделиться 3 советами, чтобы решить проблемы, с которыми вы сталкиваетесь.

1) Создайте таблицу для хранения всех ответов IPN от PayPal. Убедитесь, что у вас есть столбец с именем «raw», в котором хранится ВСЕ ... do «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) Хорошо, я должен признать, что это еще одна глупость, которую делает 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. обратите внимание, что мой первый пример кода не имеет этот столбец, но он должен быть там. я просто копирую и вставляю свой код разработки, чтобы дать вам идею.

1 голос
/ 05 октября 2012

Что я делаю, так это игнорирую регистрацию и просто обрабатываю (создаю нового пользователя и т. Д.) Фактическую транзакцию оплаты.И я бы не стал хранить все эти трансляции IPN.Пусть ваш IPN-скрипт отправит вам EMail на каждый из них с эхом всех опубликованных полей.Тогда у вас будет запись о них.

...