How to verify PayPal Express Checkout details on the server? - php

I'm just now trying to get up to speed with PayPal Express Checkout (i.e. checkout.js), using the client-side REST integration described here. I see that when payment is complete, my onAuthorize function is invoked with a "payment" object.
I can't find any documentation on this object, but some poking at it reveals the following properties (at least today):
paymentToken
payerID
paymentID
intent
returnUrl
Now I need to redirect the user to the next step on my website, where I show a receipt confirming they've paid, etc. I guess I send the above data to the server, but since that step could be easily spoofed by a malicious user, I will need to verify those details in the PHP code, server side.
How do I do that?

You can make a GET call on your server side to /v1/payments/payment/PAY-XXXXXX with the paymentID and the payerID to get the payment details, and verify those details there.
https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/advanced-payments-api/show-payment-details/
See https://developer.paypal.com/docs/api/overview/#make-your-first-call for some basics on calling the REST api from your server

Assuming you are using PayPal Encrypted Buttons, you don't actually need to verify the amounts sent through server-side. Although a user could indeed manipulate the $_POST data, PayPal's got you covered, and won't allow the transaction to go through. This is because PayPal Encrypted Buttons are generated with your variables such as price built-in to the ID. If the variables don't align with those used to create the button, the transaction is denied.
Alternatively, if you are simply using your own code to make the request, you can secure the payments with PayPal's Instant Payment Notification. Again, this allows any $_POST data to be sent through for the payment. Afterwards, PayPal makes a call to your IPN page in order to validate that the parameters are correct. This is demonstrated in the following workflow:
When communicating with your IPN, if PayPal finds that the values don't match up, the order is cancelled. Assuming that the values match up, you can safely redirect them to your confirmation page.
Hope this helps! :)

Related

How to validate a sale with PayPal REST API and IPN

I currently have a fully working cart and checkout process through PayPal _xcart method, but I want to migrate it to REST API, mainly because I want to mitigate the possibility of price-jacking. Currently my IPN does check for price jack and sets the according flags so the product doesn't get downloaded (selling digital products only). Anyway more to the point, I found the PayPal documentation very confusing and I'm struggling to get the full grips of it.
This is what I have understood and worked out till now.
Using my PHP script (let's call it page A) I create the cart content, then I create a new PayPal sale and redirect the client to PayPal for authentication
Client authentication on PayPal, then it's redirected back to my site to page B (page B is defined in page A)
Page B needs to get the PaymentID (from page A) and use it to effectively complete the transaction. Once competed finalize the checkout.
Now here are my problems:
a) I had read quite a few forums and tutorials and they all mention that I should use a session to store the PaymentId from Page A and then use it in Page B to finalize the transaction. Some threads on SO suggests that PayPal should actually include the PaymentID in the call to page B, along with the token and the PayerID. Those are almost 3 years old posts and during my testing I see that PayPal now does return the PaymentID as well.
Is this a new thing, did PayPal really started to send the PaymentID as a GET variable? OK I found this on PayPal SDK documentation (not PHP of course some other language) that they return the PaymentID as well as a GET.
Is there any disadvantage on using the GET presented by PayPal compared to storing a session from page A to B? I don't really wan't sessions, so GET will be ideal for me. I guess if it is on PayPal documentation it's safe to use.
Will this work on the live page as well or only in Sandbox?
b) On Page B when I execute the Payment I get a nice JSON as response, but in the same time my IPN listener also gets called, and this is what confuses me big time. Can/Should I just trust all the data which is in the JSON response and more or less ignore the IPN listener? This will make sense for instant download, for example, much more easier to process, or should I still rely on the IPN for data validation?
If I use only the JSON returned to Page B, which are the correct fields to look for and what values? For example, there is a state field which is approved and another (Transactions -> related items -> state) which is competed. Which one do I need to check?
If I rely on the JSON do I still need to check if the paid amount matches the original amount or can I trust that the payment is equal to the amount I have requested in my call?
If I use the IPN how can I pare it with the transaction? The PaymentID doesn't show up in the variables posted to the IPN. The only way I could think of is to get the txn_id from the JSON response, but somehow that feels odd, plus how can I know if the JSON response hit the server BEFORE the IPN?
Can/Should I just trust all the data which is in the JSON response and more or less ignore the IPN listener?
Yes, and no. In that order.
In a nutshell, you can't trust the payment ID in the call to "Page B" (it could be forged, faked, repeated etc) but you can trust the response YourServer->PayPalServer as it can't be intercepted and faked by the end user.
So your process is (as you describe above)
Page A: Create a sessionID (cookie), amount, cart details etc and store in a local database/storage. You can also create a "custom" field to store your own saleID
Send off amount etc to Paypal, which returns you to ....
Page B: Grab the PayPal TransactionID and send back (server->server) to PayPal. Paypal returns the amount, state etc. Then check your database that the amount is the same and that it belongs to the sessionID. If you also use custom fields, check that too. If everything marries, you're good. If not, it's up to you how to handle.
The status should be "complete" for a simple sale at this point; but (as with IPN below) you should verify this.
Do check the amounts, just in case. They should match, but if not, the PayPal one will be what you receive and it's up to you to accept it, flag it (and phone up) or refund through the API and reject the order etc.
So why have IPN?
It is possible that the user completes the transaction on Paypal and then closes their browser before "Page B" is called. In this case, the only way you know about the order is through the IPN.
If you get an IPN notification going to your IPN handler, IPN can still be faked, but there's a slightly different way of verifying.
You actually send the IPN information back to Paypal (server to server) and Paypal confirms it's correct or wrong (https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNIntro/). Your IPN handler will then check the transaction ID (yes, that's what you use) and verify everything matches in the database (just like you do in "Page B"). If it does, mark the order as complete if the status is complete (and if not already marked as complete in "Page B").
Obviously you can't display anything to the user at this point as they are not the ones that called the page.
The docs above warns you that you can get multiple IPNs for the same transaction so you also need to check status.
(Note: you could use the APIs to verify the transactionID as you do in "Page B").
So why not reply on IPN?
Paypal warn that the IPN may not arrive. Paypal explains it best:
Although PayPal usually processes IPN messages immediately, IPN is not synchronized with actions on your website. Internet connectivity is not always 100% reliable and IPN messages can be lost or delayed. The IPN service automatically resends messages until the listener acknowledges them. The service resends messages for up to 4 days.
Because IPN is not a real-time service, your checkout flow should not wait for the IPN message before it is allowed to complete. If the checkout flow is dependent on receiving an IPN message, processing can be delayed by system load or other reasons. You should configure your checkout flow to handle a possible delay.
So back to the original question
Yes: rely on the JSON (server->server) call you make to verify the parameters to "Page B" (and in and the IPN handler if you choose)
No: Don't ignore the IPN in case Page B never gets called. But still run the verification checks here too.
Yes: Check state = complete for both "Page B" and "IPN handler"
Yes: Use the Paypal TransactionID, but mix into your own database with either custom fields or sessionID.
Yes, you can/will get both Page B and IPN notifications, I'd suggest ignoring the IPN if the payment is already marked as completed, otherwise process and handle appropriately. They should be using the same database.

PHP - Paypal payment transaction

I've a problem with Paypal IPN. The callback works very well.
I've a site and anyone can signup for free in my site. Later, the user can upgrade the account and here intervenes paypal.
How can I be sure that the user (A) paid to the transaction_id (0123) ?
Ex.
User (A) -> Click to button (item_number = (0009)) -> redirect to paypal page;
User (?) paid -> Paypal IPN sended and server received data -> [Which user has paid?]
!! This, without the user return to the site !!
Thank You
I'm sorry #Vincenzo Raco, but that is bad advice.
PDT and IPN are essentially the same exact thing except that PDT sends data to your return URL and IPN sends it to a silent listener.
There is no guarantee that users will make it back to your return URL, even if you have Auto Return enabled, so it's never a good idea to handle post-payment processing tasks on the return URL. If your IPN is getting hacked that means you have a problem with your configuration.
All IPN's are verified with PayPal's servers, and if you're using API calls, hosted buttons, encrypted buttons, etc. people can't see the original code, copy it, and make adjustments to pricing, which would be the only potential "hack" that anybody could do. Just make sure not to use basic, standard HTML buttons and that potential problem goes away.
IPN is definitely what you want to use to handle your post-payment processing. You can save your user record or whatever data you're working with in your database prior to sending the user over to PayPal. You can include the record ID of your database in the PayPal payment request (I typically use the invoice parameter for this) and then that will be returned in IPN so that you can pull that data back out of your database or add new related accordingly.

Paypal: Get Buyer ID before IPN received (Express checkout, PHP)

Payal say that IPN can take a while for orders to be relayed. I am wondering if there is a way with the other Paypal APIs to instantly fetch information about an order, the moment the user is redirected to the success URL of my site.
The problem is that only two variables seem to be relayed in the querystring to my success page: token and PayerID.
The reason I want to do this is I want to allow users to complete the checkout process without logging into my site, but then once the order is complete and they're sent to my success page, I want my site to be able to link them to their previous orders as well as this one, which would be extracted from my database.
Can this be done using token and/or PayerID? If so, how? (I'm using the PHP scripts that the Express checkout wizard provides)
IPN is generally pretty much real-time. There are times when it gets a little lagged and can be kind of slow, but it doesn't seem to happen all that much.
That said, the API calls along within your checkout will indeed return lots of good info in the actual response. Your URL only has the parameters you mentioned (token and Payer ID), however, the GetExpressCheckoutDetails response would have all the buyer info you need, and DoExpressCheckoutPayment will return the transaction ID, payment status, payer status, etc. So the DECP probably has the majority of what you're after.
You can use session variables to save GECD response data and DECP response data accordingly and then update your database, send out email notifications, etc. after calling DECP.
The thing is, the payment could end up being "pending" for various reasons. As such, IPN would still be the best way to handle this sort of thing so that you can update your DB and send out one notification for the pending payment being received, and then another update/email once that payment actually clears.

Close topic once Paypal purchase is made

I have an option on my phpBB forum to add a Paypal Buy Now button to enable users to sell and purchase items. I would like to have the ability to automatically close a topic once a user makes a purchase through paypal to avoid multiple users from purchasing the same item.
Is it possible to get the user's session data from paypal once they make a transaction? Then incorperate session data into a variable like:
$paypal = (isset($_POST['purchased'])) ? true : false;
Not sure if I need to download the Paypal SDK for this or not.
Any suggestions would be great, thanks.
Is it possible to get the user's session data from paypal once they
make a transaction?
You really want to trust the user's session data? I am sure you can read the session data, you don't want to do that, would be trivial task to alter it. Even if you can you really shouldn't read the session data for another website.
Not sure if I need to download the Paypal SDK for this or not.
This would be the correct way to do it.
When I last used it, Paypal Standard allowed you to specify a return URL (where to send the user) for failures and for successes. Dynamically generate some secret hashes to facilitate when the user is finally redirected.
Or you can use IPN.
Don't rely on the return URL. Buyers can (and will) close their browser / tab after completing a payment.
Instead, use PayPal Instant Payment Notifications to receive a server-to-server notification from PayPal which you can subsequently verify and use to update your database with the appropriate flag for a phpBB closed thread.
IPN works as follows:
You create the PayPal and incude a "notify_url". The value for this parameter will be the full URL to a script on your server, called the 'IPN script' or 'IPN handler'.
You can specify an IPN handler as follows for Website Payments Standard
<input type="hidden" name="notify_url" value="http://blah.com/ipn.php
For Express Checkout or Website Payments Pro, simply include the following in your SetExpressCheckout/DoExpressCheckoutPayment or DoDirectPayment API call respectively.
NOTIFYURL=http://blah.com/ipn.php
A buyer completes a transaction via PayPal
Once the buyer completes the transaction, he/she may close the browser, or return to your website
Once the transaction is accepted and processed by PayPal, PayPal will send out a notification to http://blah.com/ipn.php
You need to take all POST data that was sent to this script, and POST it back to https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate
If the data you send back matches the data PayPal sent you, a 'VERIFIED' response is returned.
If the response is VERIFIED, it's at this point that you would look up the matching transaction/buyer on your end, and update the phpBB thread status appropriately.
Some sample code and documentation for PayPal IPN is available at https://www.paypal.com/ipn/
In addition, some tips on making a secure IPN script are available at https://www.x.com/developers/community/blogs/ppmtsrobertg/securing-your-instant-payment-notification-ipn-script
Note: If you want to include any custom data along with the transaction which you can read out later, use 'custom'.
<input type="hidden" name="custom" value="xxxxx">
This will also be returned in the IPN POST data sent from PayPal.

PHP user signup with paypal subscriptions

I have site with a members area that I've set up and want to make it so when people register, they need to subscribe to paypal, and only then the user is created in the database.
I don't know much how paypal works. Does anyone have any pointers? How or where do I start?
Basically I need to somehow redirect the visitor to paypal after he presses the "Register" button, make the payment, and then make Paypal return to my site and tell it that the payment was processed, so the user can be created...
Paypal IPN is the Paypal deployment for this kind of requirement.
IPN (Instant Payment Notification) allows you to delegate an endpoint URL that the payment gateway will send postdata to when a payment is completed successfully.
Most of the time this requires utilizing some kind of database to save the registration state while the user is forwarded over to Paypal for the payment process, making use of their SSL encryption and payment logic. When the process is completed, your application is notified with post data containing a unique identitfier generated by your app and passed with the original redirect in order to identify the user's session that has completed payment.
You will want to generate a unique id either using PHP uniqueid() function or by hashing a timestamp. This will be passed along to paypal in your redirect, and paypal will send it along with success/fail flags when the payment is processed. When the user fills out their form and is redirected to Paypal, save a boolean value for IsPaid in your database associated with their ID (i'd recommend using both a primary key as well as this unique transaction id mentioned earlier.) Your IPN script can then listen for the post data, parse it out and change the false IsPaid boolean for that id to true.
There are numerous good tutorials available. I've only used this in MVC deployments, so if you're doing procedural, googling around will help a great deal. Paypal's documentation has the most up to date parameter guides but there are other great, if a bit old, tutorials available independent of them.
http://www.web-development-blog.com/archives/easy-payments-using-paypal-ipn/
http://net.tutsplus.com/tutorials/php/using-paypals-instant-payment-notification-with-php/
Look at the Paypal IPN API, this is used in most paid membership scripts:
https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/library_code

Categories