PPExt - PayPal extension

why do I get an error like this:

Trying to get property of non-object (/home/amirades/amiradesign.com.ua/test/protected/modules/payPal/models/PPPhpTransaction.php:103)

I’m using the example controller.

PPExt is in the sandbox by default, to go live replace


'env' => 'sandbox'

with


'env' => '' 

I need more information to give you a good answer on this. Have you modified PPPhpTransaction.php? Could you post the track stace (from the application log)?


2011/08/07 14:49:12 [info] [payPal.controllers.ipn.PPIpnAction] Successfull IPN request

Request:

cmd=_notify-validate&mc_gross=39.91&protection_eligibility=Eligible&address_status=confirmed&payer_id=T4YK2TRA2MAXY&tax=0.00&address_street=1+Main+St&payment_date=04%3A48%3A29+Aug+07%2C+2011+PDT&payment_status=Completed&charset=windows-1252&address_zip=95131&first_name=Test&mc_fee=1.46&address_country_code=US&address_name=Test+User&notify_version=3.2&custom=872&payer_status=verified&business=o.test_1312236115_biz%40gmail.com&address_country=United+States&address_city=San+Jose&quantity=1&verify_sign=AfZa-X9xQQDI-EDvbMaXq9gs045VASgw5o3D.KNIoPoLauJRvn.mzNfp&payer_email=o.tura_1312717469_per%40gmail.com&txn_id=9GU259941J0014206&payment_type=instant&btn_id=1829010&last_name=User&address_state=CA&receiver_email=o.test_1312236115_biz%40gmail.com&payment_fee=1.46&shipping_discount=0.00&insurance_amount=0.00&receiver_id=X2AFDH9NN7H6Y&txn_type=web_accept&item_name=My+button&discount=0.00&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&shipping_method=Default&handling_amount=0.00&transaction_subject=872&payment_gross=39.91&shipping=0.00&ipn_track_id=C3zzS7evgtWpzw-zCNLpVA

Response:

VERIFIED

in /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/controllers/ipn/PPIpnAction.php (128)

in /home/amirades/amiradesign.com.ua/test/protected/controllers/PayController.php (93)

in /home/amirades/amiradesign.com.ua/test/index.php (15)

2011/08/07 14:49:12 [error] [php] Trying to get property of non-object (/home/amirades/amiradesign.com.ua/test/protected/modules/payPal/models/PPPhpTransaction.php:103)

Stack trace:

#0 /home/amirades/amiradesign.com.ua/test/yii/framework/base/CModel.php(152): CInlineValidator->validate()

#1 /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/models/PPPhpTransaction.php(129): PPPhpTransaction->validate()

#2 /home/amirades/amiradesign.com.ua/test/protected/controllers/PayController.php(129): PPPhpTransaction->save()

#3 /home/amirades/amiradesign.com.ua/test/yii/framework/base/CComponent.php(568): PayController->ipnRequest()

#4 /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/controllers/ipn/PPIpnAction.php(61): PPIpnAction->raiseEvent()

#5 /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/controllers/ipn/PPIpnAction.php(129): PPIpnAction->onRequest()

#6 /home/amirades/amiradesign.com.ua/test/protected/controllers/PayController.php(93): PPIpnAction->run()

#7 /home/amirades/amiradesign.com.ua/test/yii/framework/web/actions/CInlineAction.php(50): PayController->actionIpn()

#8 /home/amirades/amiradesign.com.ua/test/yii/framework/web/CController.php(300): CInlineAction->runWithParams()

#9 /home/amirades/amiradesign.com.ua/test/yii/framework/web/CController.php(278): PayController->runAction()

#10 /home/amirades/amiradesign.com.ua/test/yii/framework/web/CController.php(257): PayController->runActionWithFilters()

#11 /home/amirades/amiradesign.com.ua/test/yii/framework/web/CWebApplication.php(328): PayController->run()

#12 /home/amirades/amiradesign.com.ua/test/yii/framework/web/CWebApplication.php(121): CWebApplication->runController()

#13 /home/amirades/amiradesign.com.ua/test/yii/framework/base/CApplication.php(155): CWebApplication->processRequest()

#14 /home/amirades/amiradesign.com.ua/test/index.php(15): CWebApplication->run()

REQUEST_URI=/pay/ipn

in /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/models/PPPhpTransaction.php (103)

in /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/models/PPPhpTransaction.php (129)

in /home/amirades/amiradesign.com.ua/test/protected/controllers/PayController.php (129)

2011/08/07 14:49:12 [trace] [system.CModule] Loading "errorHandler" application component

in /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/models/PPPhpTransaction.php (103)

in /home/amirades/amiradesign.com.ua/test/protected/modules/payPal/models/PPPhpTransaction.php (129)

in /home/amirades/amiradesign.com.ua/test/protected/controllers/PayController.php (129)






I don’t see any obvious reason for the error message. Could you try editing PPPhpTransaction.php and change the save method into this:




public function save() {

    Yii::log("Transaction saved: {$this->txnId}", 'info');

    return true;

}



If you get "Transaction saved: …" in your log you are one step further, you are able

to get the data from IPN request. The next step is to create a new model (preferably,

one that you store in a database) with your custom validation rules.

I had promised to post my working IPN controller here. I am using it as a general payment API controller to handle various gateways. I show the PayPal pieces only in this post. i check various things, load a transaction record, set a status flag in the core model (teetime), and fire off confirmation emails.

Here it is…





<?php


//include payPal libraries

Yii::import('payPal.models.*');

Yii::import('payPal.controllers.ipn.*');

Yii::import('payPal.components.*');


class ApiController extends Controller {

	

	public function actionIpn() {

                Yii::log('API:IPN:PAYPALREQUEST','info','application.controllers.api');

		$ipn = new PPIpnAction($this,"ipn");

                $ipn->onRequest = array($this, "ipnRequest");

                $ipn->onFailure = array($this, "ipnFailure");

                $ipn->onSuccess = array($this, "ipnSuccess"); 

		$ipn->run();

	}

	

    public function ipnRequest($event) {

        Yii::log("API:IPN:READY_TO_PROCESS","info","application.controllers.api");

        //Check transaction ID

        if (!isset($event->details["txn_id"])) {

		    $event->msg = "API:IPN:HAS_NO_TX";

			Yii::log($event->msg,"warning","application.controllers.api");

			$event->sender->onFailure($event);

			return;

		} else {

		    $event->msg = "API:IPN:CONFIRMED_TXN_ID";

		    Yii::log($event->msg,"info","application.controllers.api");

		} 

		

		//Check currency

		if ('USD' != $event->details['mc_currency']) {

 			$event->msg = "API:IPN:WRONG_CURRENCY";

 			Yii::log($event->msg,"warning","application.controllers.api");

 			$event->sender->onFailure($event);

 			return;

 		} else {

 		    $event->msg = "API:IPN:CONFIRMED_CURRENCY ";

 		    Yii::log($event->msg,"info","application.controllers.api");

	    }

	  

	    //Check status

		if ($event->details['payment_status'] != 'Completed') {

 			$event->msg = "API:IPN:NOT COMPLETE";

 			Yii::log($event->msg,"warning","application.controllers.api");

 			$event->sender->onFailure($event);

 			return;

 		} else {

 		    $event->msg = "API:IPN:CONFIRMED_STATUS";

 		    Yii::log($event->msg,"info","application.controllers.api");

	    }

	  

        //Add transaction to transaction logging table

        $trans = new Transaction;

		$trans->txnId = $event->details['txn_id'];

		$trans->paymentStatus = $event->details['payment_status'];

		$trans->quantity = $event->details['quantity'];

		$trans->mcCurrency = $event->details['mc_currency'];

		$trans->mcGross = $event->details['mc_gross'];

		$trans->receiverEmail = $event->details['receiver_email'];

		$trans->teetime_id = $event->details['custom'];

		

		//Set pending status to confirmed status in tee time record

		$teetime = Teetime::model()->findbyPk($event->details['custom']);

		$teetime->status = "confirmed";

		$teetime->update();

		


	    if($trans->save()) {

	        $event->msg = "API:IPN:TNX_SAVED";

	        Yii::log($event->msg,'info','application.controllers.api');

	        $event->sender->onSuccess($event,$trans,$teetime);

	    } else {

	        $event->msg = "API:IPN:TNX_NOT_SAVED";

	        Yii::log($event->msg,'info','application.controllers.api');

	        $event->sender->onFailure($event);

	    }

      

        //passes all tests and successful transaction load

        $event->msg = "API:IPN:CONFIRMED_AND_SAVED";

        Yii::log($event->msg,'info','application.controllers.api');

	    $event->sender->onSuccess($event);

	}

	

	public function ipnSuccess($event,$trans,$teetime) {

        Yii::log('API:IPN:SUCCESS_HANDLER_STARTS','info','application.controllers.api');

	  

	    //get the player record

	    $player=Player::model()->findByPk($teetime->player_id);

	  

        // Set up parameters for admin email

        $to = "email here";

        $subject = "Reservation Confirmed";

        $message = "<p>Blah Blah</p>

        <p>Blah Blah:</p>

        <p>Player: $player->name</p>

        <p>Phone: $player->phone</p>

        <p>Email: $player->email</p>

        <p>Day: $teetime->date</p>

        <p>Time: $teetime->time</p>

        ";

        $from = "email here";

        $headers = "MIME-Version: 1.0" . "\n";

        $headers .= "Content-type:text/html;charset=iso-8859-1" . "\n";

        $headers .= "From: $from" . "\n";


        // Send email

        if(mail($to,$subject,$message,$headers)) {

          Yii::log('API:IPN:ADMIN EMAIL SENT','info','application.controllers.api');


        } else {

          Yii::log('API:IPN:ADMIN EMAIL FAILED','info','application.controllers.api');

        };

      

      // Set up parameters for player email

        $to = "$player->email";

        $subject = "Your Reservation";

        $message = "<p>Hello $player->name,</p>

        <p>Blah Blah</p>

        <p>Blah Blah</p>

        <p>Day: $teetime->date at $teetime->time</p>

        <p>Our Address:</p>

        <p><a href='http://maps.google.com/mapsBlah Blah</a></p>

        <p>Questions? Check out our <a href='http://Blah Blah'>contact information.</a></p>

        ";

        $from = "email here";

        $headers = "MIME-Version: 1.0" . "\n";

        $headers .= "Content-type:text/html;charset=iso-8859-1" . "\n";

        $headers .= "From: $from" . "\n";


        // Send email

        if(mail($to,$subject,$message,$headers)) {

            Yii::log('API:IPN:USER EMAIL SENT','info','application.controllers.api');


        } else {

            Yii::log('API:IPN:USER EMAIL FAILED','info','application.controllers.api');

        };

	  

    }

	

	public function ipnFailure($event) {

	    Yii::log('API:IPN:FAILURE_HANDLER_STARTS','info','application.controllers.api');

	    //Failure handlng, emails, etc. here.

	}

}




I am noticing that I must not be adequately closing the loop with IPN. I am getting repeated TX requests to my listener class above. What should I check first? I am preventing duplicates in my records, but I need to close out the IPN validate steps.

Hello,

I am trying to integrate PayPal payments on my site and this looks like a great extension, but I am having a bit of trouble understanding how this works and what the best approach for my site would be. Here’s what I’m trying to do:

  • My site has a basic cart for virtual products.

  • After a user is done adding stuff to their cart, I just want to be able to let them pay for the cart total via PayPal.

  • How do I initiate the transaction to PayPal? Where do I supply the transaction amount, etc.?

  • I DO have a model and db table created to store the transactions, but am really having a problem understanding how the transaction flow works. Any help would be appreciated. I just want to do this:

Say order total is $5.00:

Initiate transaction to PayPal for $5.000->user sends payment via PayPal->process IPN payment via listener->log transaction

Also, where does the payment receipt confirmation page fit into this (the page that the user is returned to from PayPal)?

Thanks!

UPDATE

======

OK, I did some further in-depth reading on IPN and PDT and it looks like PDT might be the best option for me, but I would really appreciate your guys’ input. My cart consists of virtual items (classified listings) and they are stored in the session. I want to have a button to pay with PayPal (I understand the button initiates the payment process) and have the user returned to a URL which will handle the PDT transaction and display a confirmation page to the user. I was considering using IPN in conjunction with PDT, but since I only store the cart items in a session, it would be rather difficult to do this via IPN since I don’t have a transaction in the db that refers to the cart items being processed. However, if I use PDT, I can confirm receipt of payment and then create the actual order in the db from the session and clear the session afterwards.

My question is - is PDT pretty reliable? I suppose there can still be cases where the transaction might get interrupted somehow while getting back to my site and I might wind up with a payment that didn’t get processed on my site. Any suggestions on how to handle this better? Thanks!

To stop PayPal from sending the same IPN request, all you need to do is to send the same message back for verification. This is done by the PPExt’s IPN functionality. If you get VERIFIED the loop should be closed (it’s 100% sure that PayPal received the request back in the correct format). See "IPN Protocol and Architecture for details.

The easiest way (as you stated in your EDIT) is to create a button. This can be done dynamically with

the button manager. But, as you are using a shopping cart you should also consider PayPals built-in cart

functionality, see BMCreateButton (PayPal) for details about creating buttons.

  1. System creates BuyNow button for purchase (amount = $5.00) and add it to page

  2. User clicks BuyNow button and enters his / her payment info.

  3. PayPal sends IPN request to IPN handler (your site) with payment details

  4. System verifies IPN request from PayPal and stores transaction (incl. payment processing)

  5. User is returned to your site (could use PDT here, but only to show payment details, not for payment processing)

System refers to your site.

PayPal will create the transaction and return a transaction id to your site, IPN shouldn’t be a problem. To identify the user / purchase you can send an id via the BuyNow button (see earlier posts in this thread).

You should never rely on PDT for payment processing. PDT depends on the user, all payment information is sent via the users browser in a redirect. If the user clicks stop, their browser crashes, they lose their connection, etc. the payment will go unnoticed. IPN is communication directly between you and PayPal, it’s the only option.

Thank you so much for this great extension and all of your prompt responses. I think I’m headed in the right direction now. :) One last question… I know that PayPal has a lot of different button images that can be used, is there a way to specify a specific button image (for example if I select PAYMENT as the button type, can I specify a specific paypal button image?). The error codes section in the BMCreateButton API page refers to image url’s and image values, but I didn’t see the variables that would set them. Also, can the buttonManager be used to create a button of type PAYMENT (I think that’s the one I need for my application). I don’t see that listed as one of the types. Thanks.

Thank you for the positive feedback!

The API reference states that you can use L_BUTTONVARn for HTML standard button variables, these can be found at HTML Variables for websites payments standard, there you’ll see the documentation of image_url, it’s not a button image. You can use your own custom image by modifying

the HTML code returned from PayPal, similar to how you send an item id (referring to earlier posts in this thread), but I think it’s best to use PayPal’s standard buttons (then the customers know what they’re getting into).

The API reference states that you can use BUTTONTYPE=PAYMENT since version 65.2 in the page I linked in the previous post. Currently PPExt specifies version 63.0, to make it work as 65.2 you can edit PayPalModule.php and replace VERSION = "63.0" with VERSION = "65.2".

Thanks stianlik for your ext. I begin use it to my site, but i meet the fail on create button

This is my main.php


'modules'=>array(

			

		 'payPal'=>array(

			 'env'=>'sandbox',

			 'account'=>array(

				 'username'=>'bin123_1318166859_biz_api1.gmail.com',

				 'password'=>'1318166884',

				 'signature'=>'AZKdSX2UnD6t8-u4FaIYQkAFkDEoAAPuJ0U5b2ufylTICbsNAu8ulRsc',

				 'email'=>'bin123_1318166859_biz@gmail.com',

				 'identityToken'=>'PE6NERXVSGGCU',

			 ),

			 'components'=>array(

				 'buttonManager'=>array(

					//'class'=>'payPal.components.PPDbButtonManager'

					 'class'=>'payPal.components.PPPhpButtonManager',

				  ),

			 ),

		 ),

And I use code example above


$buttonManager = Yii::app()->getModule('payPal')->buttonManager;

			$nvp = array(

            'BUTTONTYPE' => 'BUYNOW', 

            'L_BUTTONVAR0' => 'currency_code=USD',

            'L_BUTTONVAR1' => 'item_name=My button',

            'L_BUTTONVAR2' => 'amount=200.00', );

			$buttonManager->createButton('My button', $nvp);

  • curl on

Please tell me any wrong :(

I can’t see anything wrong with your code, but from the error message, it seems like you got a HTTP error, i.e. you did not get a connection to PayPal. Unless you got more error messages, the request failed in PPUtil.php, line 212:




// Return false on HTTP error

if ($response['status'] === false)

	return false;



Could you try to make a HTTP request using CURL to another site to make sure it’s working (you can do this using PPUtils::httpGet(), or directly with curl-commands).

Thanks for reply, stianlik.

I make a request to google by using PPUtils::httpGet() which use curl and it give me a reponse html since I think it work. but still fail created :(

I also request to api sandbox create button manually and this work.

So, what happend ? :-s

PaypalController is my test.

And if I could create a button without hosted on PP but use IPN ?


Well, there are a problem connect https

Quote

error_msg = SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

error_no = 60

How solve ? :(

There was a similar issue with IPN, see

post #25

You can probably fix the problem by adding




// Quick fix for missing SSL certs

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);



to PPUtils::httpGet(…), after curl_init. But keep in mind that this is a quick fix, the root cause is a missing cert. See how to properly secure remote calls from php for details on how to fix this (and about security issues with the quick fix).

Yes you can, IPN is completely separate from the button manager. But using hosted buttons is the most secure alternative.

Ok I created success button. Thanks you.

I just a question. How I know exactly order id what customer paid :|. As customer buynow, a order be generated with its id. When IPN notify, I dont know order id for update status of order. Can you help me ? :(

I think a field in button with orderId when create it, but too much button will be created. maybe wrong :lol:

You can add a hidden field named "custom" to the button form, this will be returned to you in the IPN requests, see post #19 for an example.

Mean one button used for n order :rolleyes:

Perfect ! Thanks you so much :-*

Hi,

I am getting the same error, does anyone knows how to solve it?

Thanks,

Any ideas plzzzz

Thanks,

Solved that but now i have the following error

Fatal error: Call to undefined function curl_init() in C:\wamp\www\itmat\protected\modules\payPal\components\PPUtils.php on line 84

Any ideas plz?

Thanks,