I have implemented PayPal payments using PDT. I realize a combination of PDT and IPN is recommended, however for my purposes PDT is sufficient.
I am able to direct user to PayPal, collect payment and then conduct an action (updating account credits for the user in the database) when the user returns to my site. The URL shows an address like:
http://www.domain.com/process_pdt?tx=45138128VH922173V&st=Completed&amt=1.99&cc=USD&cm=&item_number=
So far so good. The problem I have is refreshing the browser with this URL causes the user's account to get credited again without redirecting the user to PayPal to collect payment a second time.
I have read that I should add the transaction id (tx in URL above) to the user table and check to ensure this tx id has not already been used. If it has, I should display an error message. Is this the only way to go about this, i.e. by storing each tx id in the user table and then checking for the id's existence each time the process_pdt URL above is called by the user (legitimately or fraudulently)?
Ah, yes: this is indeed the only way to go. As a simple sanity check you may want to do a redirect from the process page to a URL that does not include the txn_id (that way a casual refresh won't cause the problem, but if they use their back button it will still happen) but you must verify that the txn_id is only used once yourself.
Related
I maintain a PayPal integration which uses PayPal HTML Buttons and confirms payment using IPNs. This system has been in production for more than 5 years.
The system sets the cancel_return variable to a unique URL for each order. If this URL gets hit (i.e. if the customer cancels at paypal and gets sent to this URL), it first checks the session, to ensure the user hitting the URL is the same one who initiated the order, then cancels the order.
I noticed that several recent transactions have been cancelled even though the transactions at PayPal were successful.
The logs showed that customers are hitting the cancel_return URL - in some cases just after the IPN has been received, and in one case just before the IPN was received.
I found at least one way to reproduce this behaviour:
initiate the order on my site
complete the payment at paypal
return to merchant
use the back button to return to paypal
I then get a message that says "Your payment was completed. To continue shopping, please return to the merchant." One button is provided: "Return to merchant name".
Clicking this button takes the user to the cancel_return URL!!
PayPal's documentation for cancel_return says
A URL to which PayPal redirects the buyers' browsers if they cancel checkout before completing their payments. For example, specify a URL on your website that displays a "Payment Canceled" page.
Default — PayPal redirects the browser to a PayPal webpage.
My question is whether this is an intentional change by PayPal -- if so I expect it will break many sites -- or if this is a bug. I couldn't find a bug report at PayPal.
Yes, they changed the meaning of cancel_url, though they are not acknowledging this as a bug or an intentional change. This is definitely a bug in my opinion (as documented in the question above).
In addition to the steps above (reproducing using the back button), I found a customer click-trail in my server logs where the customer was sent to the cancel_url, without using the back button, soon after PayPal sent the IPN confirming their successful payment. I confirmed that the customer did not want to cancel and did nothing to intentionally cancel their order. Unfortunately I could not find a way to reproduce.
I registered at www.paypal-techsupport.com and submitted a ticket about this issue. After several exchanges, the end result I got was:
This is the information that I received from our engineer.
Its an expected behavior when the customer click on the back button, it will bring back to the cancel_url.
We would suggest you to do some modification so that the order is not being cancel when the customer go to the cancel_url.
With poor customer support like this, I will not be recommending PayPal to any of my clients in the future.
You can post it here and the PayPal folks can pick it up.....
https://www.paypal-community.com/t5/Merchant-Technical-Support/ct-p/mts
With PayPal billing, with Auto Return and PDT enabled, when a user is returned to one's Return URL after a successful charge event, several parameters are appended by PayPal to the URL (e.g. "tx" for transaction id), which can then be used to identify the user.
However, if the user has just joined subscription with a free trial, no such information is appended and so the user is not identifiable at that point in time.
Since IPN is asynchronous, the IPN notification may arrive too late so one cannot rely on this.
Is there any way of identifying a user via the Return URL after they have entered a subscription with a free trial?
Similar Que: Implementation of free trial period with PayPal doesn't return any custom parameter
"If you are using PDT, then nothing will be sent back as no payment is taking place. Information will only be sent back if you are making a payment. I would recommend using IPN. Both the rm variable and PDT are dependent on the buyer returning back to your site. If the buyer does not return, such as their browser window unexpectedly crashes and closes, or they simply shut it no information will be sent back and it will be lost. This is why the rm variable and PDT are used more for creating dynamic receipts. IPN is more used for updating your system as it does not rely on the buyer returning back to your site. "
"Also with IPN you can resend the data to your system if you need to, and you can also view your IPN history from within your account. IPN usually takes place right after the payment is completed."
I've setup a PayPal site which uses IPN and I was having trouble getting PayPal to send the GET variables to the return URL that I had specified. It was sending the user's browser to the return URL, but nothing was being passed via GET or POST.
I changed one setting in the PayPal business account: "Payment Data Transfer (optional)" to On which generated an "Identity Token" on the PayPal website.
I also got an automated email from PayPal saying:
---------- Forwarded message ----------
From: service#paypal.com <service#paypal.com>
Subject: Payment Data Transfer (PDT) Has Been Enabled
This email is to inform you that you have successfully enabled Payment Data Transfer.
PDT's primary function is to display payment transaction details to buyers when they are redirected back to your site upon payment completion. However, there are cases, such as with pending transactions, where you won't receive notification of all transactions. For this reason, PayPal strongly recommends that you also enable Instant Payment Notification (IPN).
To learn more about enabling and setting up IPN:
https://www.paypal.com/us/cgi-bin/?cmd=p/xcl/rec/ipn-intro-outside
To learn more about Payment Data Transfer, including setup instructions and a complete list of variables:
https://www.paypal.com/us/cgi-bin/?cmd=p/xcl/rec/pdt-intro-outside
Sincerely,
PayPal
Clicking on the second link and clicking on "Technical Overview" (https://www.paypal.com/us/cgi-bin/webscr?cmd=p/xcl/rec/pdt-techview-outside) shows:
Your POST should be sent to
https://www.paypal.com/cgi-bin/webscr.
You must post the transaction token
using the variable "tx" and the value
of the transaction token previously
received (e.g.
"tx=transaction_token"), and the
special identity token using the
variable at and the value of your PDT
identity token (e.g.
"at=identity_token"). You will also
need to append a variable named "cmd"
with the value "_notify-synch", for
example "cmd=_notify-synch", to the
POST string.
However, I am NOT passing the Identity Token at all, yet everything is working fine!
(a) Is this a problem?
(b) Why is it working if the documentation implies that it shouldn't?
(c) Is this a consequence of specifying an outdated API version (58.0)? What is the value I should be using?
In my opinion the identity token should be a required param since it is the only way Paypal can verify that the request you're making is valid. Otherwise, other people can simply guess a transaction id (even though it is not intended for their accounts) and get details for that transaction from Paypal.
I'm guessing this is a bug you're experiencing. Are you testing in the Paypal sandbox or in a live environment?
Realizing that the OP probably no longer needs an answer after 9 years, but others still might:
The POST of the transaction ID and identity token is purely for the purpose of verifying that the original transaction notification (relayed via the GET method to the merchant's Return URL) actually came from PayPal.
It is as if to say to PayPal, "My website just got this supposed confirmation that a customer paid. Here is the transaction ID and my seller ID again. Is this a legitimate match?"
In fact, at https://developer.paypal.com/docs/api-basics/notifications/payment-data-transfer/, when talking about setting up for testing, it only talks about getting your script ready to receive, parse and display the GET data. It doesn't mention the POSTing back to PayPal (though that is mentioned elsewhere). So, yes, the PDT function should work without doing the POST back to PayPal afterward and waiting for that response of SUCCESS or FAIL, but...
Anyone who knew what they were doing could go to a seller's URL and append a query string with the right combination of variables to fake the same kind of GET request that the PayPal PDT system would initially send, whether or not the transaction ID were a real one.
I'm implementing a simple Buy Now button and I'm using IPN (not PDT) to verify the transaction.
A thing that bugs me is that I don't understand how come only after I set the notify_url field to the button I also get the transaction variables to the success URL, as GET params. It would seem normal to be the other way around.
Since the user could or could not choose to "return to the merchant's website" there's not way I should rely on that data. It does however seem to be identical to the one sent to the notify URL.
Some clarification would help. Thanks!
I know what you mean, after the user has finished paying and chooses to return to the sellers website (button in paypal page) they are redirected to your thankyou page.
I don't think it used to do this back in 2009 but now paypal will issue the redirect with all sorts of parameters in the url query string.
I'm just ignoring this information and relying on the backend IPN post which is verifiable by paypal
In my previous experience with IPN, I defined the location of the notify URL in the control panel. Once the payment is processed, PayPal sends a POST notification to this URL independent of any user behavior.
In any case, even if you define the notify_url parameter in the button, I believe PayPal will still send the payment notification seperate from the user behavior - they spawn a new process to send the data. As to why they are using a GET rather than a POST, that is odd behavior. However, in your IPN script you should still have logic that verifies the call with PayPal before you continue processing.
I am working an e-commerce site which will allow pdfs to be downloaded once payment is done.
So, user will put them in a cart, click buy using paypal and from there will come back to see download link on a success page.
I have implemented PDT.
Not sure which is the best option ?
Please help. I want to take care of any connection lost scenario too, like user completes payment at PAYPAL and while returning back to our site's success page, connection is lost and for some n days, user couldnt access internet as well.
I would just use the IPN.
Keep a record in your database that has all of the order items.
e.g. Order and Order Items (in 2 tables)
Then send that primary key across as the "custom" variable.
Then your ipn handler page will validate the payment and then email the customer their digital goods.