I have been developing an online retail shop.
It is written in php and uses paypal and IPN to handle payments.
I have written my own shopping cart. When the user wants to checkout they click on the checkout button which has the standard cart upload function wrapped around the button and the user is taken to paypal to complete payment. Paypal then sends me an IPN to notify me of the payment.
My question is, at what point should I store the order and when should the stock levels be reduced?
The standard process flow is that I have right now is as follows:
User adds item to cart.
If item is now sold out or the quantity added is more than available, the cart is updated to reflect this.
However, stock added to the cart does not reduce stock levels.
User clicks checkout.
The cart is loaded into an order record in the db AND the stock levels are reduced.
User is taken to paypal to complete payment.
(a) User completes payment.
(b) User does not complete payment by either returning to website or going somewhere else / closing browser.
(Optional) User clicks return to website.
User sees a 'thank you, order complete' complete page.
Nothing is processed relating to the order table since paypal will send IPN anyway.
Paypal sends IPN
Update order with the transaction status
As you may see, there are some issues with this process. If the customer leaves the paypal page without completing payment I will have a 'dangling' order and since stock levels are also reduced this stock will not be available to other customers! A solution to this is to manually 'clean' the database every so often.
Alternatives?
option I) Do not store the order in the database until a 'completed transaction' IPN is received, then use the cart info stored in the session to create an order and reduce stock levels. However, sessions can expire and paypal payments might take days depending on payment.
option II) Store the order as is now but do not reduce stock levels until completed transaction IPN is received. This still has the issue of dangling orders but at least no stock will have to be re added again when cleaning up, I'll just have to remove the orders. Another problem with this though is that if multiple people order at similar times and thus collectively their orders contain quantities that exceed stock. This could be quite chaotic when the system receives completed IPNS and then reduces stock levels to negative quantities!
I have looked everywhere on the internet for some sort of help, but it is not mentioned anywhere! Everyone just skips to how IPN should be handled. I just don't understand how other people could not have had this problem?!
Please help!
You're dealing with the same problem that airline reservation systems have for ages. Use the same solution ie
1.Upon user clicking checkout (while being redirected to Paypal)
reduce inventory count
place a timestamp in the database along with a state that this order is temporary
2a. Once you receive an IPN ie know the billing has been successful
change state of the order to permanent
2b. Have a cron job that runs every few minutes to track temporary orders. If temporary orders are from a time greater than what you allow eg 20 mins then:
remove the temporary order from database
undo the change in inventory count
As both a merchant and a developer, I prefer to adjust inventory stock with the IPN, even if that IPN is pending (eg. an eCheck). The chance of 2 customers going through checkout at the same time with your last remaining stock is typically low. If you do have high enough volume and low stock levels (why would you?) then you may want to do something in the cart to place locks on the items for the duration of the session timeout.
Make sure your order processing code returns the inventory to stock if the order is canceled or returned.
Related
I'm selling some stuff on my website using paypal IPN + PDT. I always check how many items are in the stock before payment. After customer makes his order, he is redirected into paypal for make payment. When payment is completed I'm updating database about selling stuff, until this moment I had no problem becouse I was selling unlimited items. I'm not sure what should I do in case where I have for example 10 items for sell, for example in this scenario:
There is last item for sell. Customer makes his order and he is going to pay on the paypal but he didn't payed yet (website still showing that there is one item for sell ), meanwhile customer B see that there is one item left in stock so he is going to buy it too. in this scenario both customers will pay but only one (faster one) get his product.
What I must do to dont make this happen ?
I can't give you a code answer without seeing yours first, but here's how it could be done on paper :
When customer A clicks the "Pay" button, the item quantity is updated in the database
If the payment goes through, do nothing, if the payment doesn't go through (cancelled, incorrect payment informations, etc) then revert the quantity back to the original
This should work fine as long as you do a final "quantity check" when the user clicks the checkout button. Let's say that both customer A and customer B have an item X in the cart, and there's only one left. They can't both pay for the item, since your system would check the quantity before redirecting to Paypal, and as soon as one of the customers do click on the button, the quantity is updated to reflect the purchase.
Drown's answer is right, and I think it's better to reserve the items (reduce them from the stock) for a period of time, for example for 20 minutes.
You need a cron job or something to give back the unpaid items into the stock.
And if you are worried about crawlers you can set another cron job to check those users who have for example 3 unpaid orders with 20 minutes interval among their payments.
You probably can check this out through the orders table in your database. Maybe you need to block these kinds of users for a while or make a better decision about them.
I am having a problem on an e-comm site i am trying to build where it's possible for a user to add additional cart items when checking out with the SecurePay payment gateway.
When the user clicks 'pay now' button, they are redirected off site to the SecurePay payment page where they enter their credit card information. It's here where its possible for the user to open another page and add/remove cart items after the payment information has already been sent to the payment gateway without me being able to detect it. The shopping cart is a database shopping cart with a cookie as a unique identifier to remember the user.
The problem is that there is no input fields to for any product data in the SecurePay form that's sent to the payment gateway, you can only specify limited information about the order, ie, the total $ amount of the order including shipping(can't even send shipping total separately), a reference number(invoice number) and the currency type(AUD), surcharge percentage etc. So i have to process everything based on the the users shopping cart after the order is successful and they are redirect back to my site. If the user add/removes cart items while they are paying offsite, it is then reflected when i generate the invoice and do the stock take on the order completion page.
Im just not sure how to combat this.
My possible scenario:
I lock the cart when the user hits the checkout page, then unlock it if they hit any other page than the checkout page. Though then there is no possibility of any up selling without taking the user completely out of the checkout process and i'm also worried about any possible scenarios where if the user returns to the site their cart may still be locked. Plus maybe it seems a little messy/hackish of a solution?
I did test a couple random e-comm sites, and it was also possible to add additional cart items from another page during the 'pay now' stage of the checkout process without it detecting the additional cart items unless you actually manually refresh the page. Though i did not verify by actually paying for the items, but i can't see how it would be any different to my problem.
Does anyone have any suggestions?
I initially thought this kind of behaviour was a bug until I learned about upselling. So my solution to this (if I ever needed one), is to make sure every checkout process is a new order, and not a update. And add a hook, to let the user know about the new order, and give an option to discard (clear out) items currently in the cart.
If you can send a reference number, why not use that to generate the order? Whenever the user goes to pay, create an order with the items in their basket and send that number to SecurePay. When the payment is complete mark that order as paid for. If they change the contents of the basket then the old reference number will still refer to the products originally in the basket and if they go to check out again a new order will be created with the correct items in it.
There could be an option - that when the user is redirected back to your site - that secure pay could pass back some information including the amount charged. Then you could check and confirm that figure against whatever is in the cart.
Or - I'm looking at the secure pay developers page and they have two different methods where the customer would never leave your website.
https://www.securepay.com.au/developers/products-and-services/accepting-e-commerce-payments/
Are you in Australia - if yes you could also look at Stripe, it might be easier https://stripe.com/au/pricing
I'm currently implementing Paypal Express Checkout within a Zend Framework Application, I wrote my own cart model which consist of two simple class Cart & CartItem, and they are able to se quantity, get total price, etc.
I'm not much aware on payment system, and I only use Paypal (it's my first time), but I though since I'm not able to control what's happing there (at Paypal) it is a good idea to "lock" the Cart at the beginning of the process (when redirecting to Paypal) to avoid inconsistencies over the current Order processed by paypal and the actual cart on the website.
It can happen for example if the user open a new tab and try to add products while he is a payment process with paypal.
If your cart is locked the use is not able to add or remove anything from its cart until he finished to process the payment thorough paypal or cancel its order.
My question is do you think it is a good approach?
Edit: The reason I don't store anything in the database but only the shopping cart in session is all order are stored with Paypal, so I don't want to store it twice, it is a really really simple system.
I've done it a different way.
When the user browses the site, the cart is stored using a session, a cookie or whatever.
Once he clicks the "pay" button, the content of the cart goes to a orders datable table with a unique ID.
You delete the session or the cookie to prevent him to add new stuff to the order.
You use the unique ID to give it to paypal. Once the payment is done, Paypal comes back to you with IPN, and gives you back the unique ID. From that you know it's paid and can put a boolean or something in your orders table.
Sometimes the user won't pay, and you'll have some never paid orders. You should have a cron that check orders older than one day and remove them from the table.
I think your approach is fine. But make sure you inform the users why they can't add items to cart and offer an easy option to unlock the cart.
Use case:
fill cart
start payment
ah, I forgot the socks
close payment tab
shop opened in other tab
add socks
"you can't add, it's locked"
I can't return to payment to cancel it and can't add anything to cart => i leave
I'm creating a simple online shop with PHP integrated with PayPal that sells unique items. What I'm wondering is how other shops deal with multiple people attempting to go through the payment process with the same item.
This is my current draft strategy:
Items have three status types: available, on hold & sold.
As a user moves to the PayPal payment page it checks the status of all the items in the cart to ensure they're available. It also sets the item's status to "on hold" until they either come back after payment is confirmed or it times out (10 minutes? Not sure what this should be).
Is this standard practice or is there a more practical way I should be going about this?
Thanks in advance!
Have a look at Dell's UK outlet. When someone adds a system to their shopping basket it is held and not available to other customers. If it isn't purchased, the item is removed from the basket after 15mins of inactivity and is then available to other customers.
I would say the first part of your strategy is correct - as you move to the payment page, flag all the products as 'on hold'
When the user has finished the payment, you will get a postback from Paypal which lets you know if the authorisation was successful or not (and possibly also allows you to check the CSC/CVV2 result), and at that point you have the option of either accepting the payment, or rejecting it.
On receipt of the postback you should also check whether the items are still on hold. If they have timed out you can reject the payment and display a message 'sorry - timeout exceeded' or somesuch.
This method also allows you to work out an ideal timeout period if you keep track of how often customers run into the timeout, so you can extend the timeout from (eg) 5 to 10 minutes if too many are timing out, or shorten it if none are timing out.
This is a fairly common issue with fixed inventory systems such as venue, transport/airline tix etc.
I like the airline model where once you get the itinery you want and click select, you get a page with passenger info with a message saying, seats on hold and you now have xx(10/15) minutes to complete the purchase. Everything becomes explicit at that time. For other unique/one of a kind item, I'd think a message on any page, that the user clicks on, saying you have xx(mins) remaining to complete the purchase would be a big motivator for "on the edge" buyers !
woot.com is notorious for this problem but their solution works well. After payment information is verified the user is brought to a page with a small amount of text saying something like "your order is in, we are verifying inventory".
It looks like the paypal API has a "RefundTransaction" message so something like that might not be possible. But the user experience could be awkward if your going to the paypal website then coming back to your site.
This is much like booking theatre tickets or similar online and yes the way you describe is generally the way it works. At some point the item is "reserved" in the system and either the customer completes the transaction or the item is released after some time for others to buy.
Of course at what point you reserve the item (when it's added to the cart, at the point you send them off to pay etc) is up to you. I would expect putting it in the cart would be the best choice as it makes it less likely someone will build up a basket of stuff only to find half of it is no longer available at the checkout.
I have finally got my paypal system working using IPN and a reservation system to ensure that two people do not buy the same product.
The problem is, the reservations last for 10 mins on my application. If a user reserves the product the 'timer' starts, he clicks pay and ends up on paypals website. He then leaves the computer for 10 mins and comes back. He then buys it. By now though the reservation is up and it is possible that someone else could have already bought it.
Is it possible to send a time limit with the paypal IPN request to stop this happening?
As far as I know, you cannot specify that a transaction has to close within a certain amount of time with paypal.
However, I see two options you can use:
Offer a full refund if they purchase and the product is not available. You would check during the ipn notification of the purchase and if it is not available, then refund. You might be penalized by paypal if this happens a lot, but I don't have a lot of experience with offering refunds.
You can specify that the button needs to check inventory see "Step 2 – Tracking Inventory With Your Buy Now Button" in the standard payments guide. Once the product becomes sold out, you use the api to notify paypal the inventory is zero (if there is an api, you might have to use the website for this), and they should be able to notify the user there is zero inventory. I don't have experience with this either, but it seems more complicated than #1, and I would probably go with #1 if this didn't happen a lot. I think in the end you still end up having to issue a refund if the user goes through with the transaction even after seeing there is zero inventory, but I'm not sure about the wording in the docs.