Question

J'ai récemment implémenté un IPN PayPal dans CodeIgniter2, en utilisant la PayPal Lib . J'utilise le système pour les abonnements.

J'ai une table dans ma base de données qui enregistre toutes les demandes IPN dans la base de données.

Pour une raison quelconque, après chaque inscription, les requêtes IPN ne parviennent pas correctement. J'ai tendance à obtenir un subscr_payment avec plusieurs subscr_signups, tous avec le même subscr_id. Cela cause un nombre incalculable de tracas au sein du système, pour des raisons évidentes. Ce qui ajoute à cela, c'est le fait que les demandes IPN ne viennent pas dans le bon ordre, parfois je reçois le subscr_payment avant le subscr_signup - ce qui rend impossible le suivi car il n'y a pas de subscr_id de l'inscription pour le lier à un utilisateur.

J'ai eu un Google et je ne trouve pas grand-chose à ce sujet, je semble être un peu une anomalie. Je me demande si cela a quelque chose à voir avec le PayPal Lib que j'utilise, mais je ne veux pas vraiment avoir à le faire en dehors de CodeIgniter, car je fais beaucoup de traitement. Voici le script IPN complet.

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

    }
}

Voici un exemple des appels effectués sur mon IPN pour chaque transaction (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
Était-ce utile?

La solution

Considérez ceci - PayPal est insérez des blasphèmes .Maintenant, revoyez le problème.

Il y a de fortes chances que ce ne soit pas votre faute, ni celle de CodeIgniter ni celle de la bibliothèque.PayPal est très mauvais pour fournir des données de manière uniforme et opportune, il est également lent et ne lie pas très bien les données entre elles.

Mon conseil est de tout enregistrer dans une table IPN chaque fois qu'un rappel est effectué, et même de vous envoyer un e-mail chaque fois qu'un appel IPN est effectué.Essayez ensuite de comprendre ce que PayPal vous envoie réellement, ce que vous voulez et jetez le reste.

Je pense qu'un appel IPN est effectué même si la transaction n'a rien à voir avec votre site Web.Donc, si votre grand-mère vous envoie votre argent de Noël via PayPal, il apparaîtra sur le rappel IPN.

J'espère que cela aide un peu.

Autres conseils

PayPal n'est pas vraiment facile à utiliser, mais permettez-moi de partager 3 conseils pour résoudre les problèmes que vous rencontrez.

1) Créez une table pour stocker toutes les réponses IPN de PayPal. Assurez-vous d'avoir une colonne appelée "raw" qui stocke TOUT ... faites "json_encode ($ this-> paypal_lib-> ipn_data)". Cela vous sauvera ... puisque vous pourrez plus tard écrire un script pour extraire les données de la colonne brute dans sa propre colonne plus tard. Cela facilite également le débogage.

2) Pour commencer, tirez simplement ce qui est nécessaire dans les colonnes de la table ipn afin de pouvoir interroger facilement ... voici tout ce que je juge nécessaire pour mon cas d'utilisation qui est probablement similaire au vôtre.

    $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']
    ));

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

pour comprendre tout ce qu'ils peuvent renvoyer, ce lien est en or https://cms.paypal.com/us/ cgi-bin /? cmd= _render-content & content_ID= developer / e_howto_html_IPNandPDTVariables

mais ^ soupir ^ ne vous fiez pas à ce qu'il soit mis à jour. J'ai trouvé des incohérences dans ce qu'ils disent et ce qu'ils m'ont renvoyé.

3) OK, je dois admettre que c'est une autre chose stupide que fait paypal - ils ne vous donnent pas de date IPN même s'ils ne garantissent pas l'ordre dans lequel il arrive sur votre serveur. Pour subscr_payment, ils vous donnent payment_date ... pour subscr_signup, ils vous donnent subscr_date ... donc ce que vous devez faire pour obtenir votre IPN dans le bon ordre est d'avoir une colonne appelée 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),

maintenant tout est cool, vous pouvez "commander d'ici ipn_date" et je vous assure que tout sera dans le bon ordre.

p.s. notez que mon premier exemple de code n'a pas cette colonne, mais il est censé y être. Je copie et colle juste mon code de développement pour vous donner une idée.

Ce que je fais, c'est ignorer les inscriptions et simplement traiter (créer un nouvel utilisateur, etc.) sur la transaction de paiement réelle.Et je ne prendrais pas la peine de stocker tous ces trans IPN.Demandez à votre script IPN de vous envoyer un e-mail sur chacun d'entre eux, avec un écho de tous les champs affichés.Ensuite, vous en aurez une trace.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top