I have a problem that i seems can't figure out properly.
I got a website where you can pick some offer and buy it.
The steps for this procedure is:
1) Customer pick offer (index.php) he like and proceed to (buy.php) page
2) At (buy.php) page he selects quantity and payment method
3) When customer presses buy he is redirected to (payment.php) where all data is verified again and doing some database recording. Payment.php page processes user to selected payment gateway (out of my website).
So the problem is that i can swap $_POST data from buy.php page to payment.php and payment page would think that data is correct.
Before i was simply checking if price that comes to payment page is one of allowed prices in my $array and i had no problems with this. But now i offer a discounts and i can not tell if amount coming in is indeed correct.
How usually all this is processed? I'm new to working with payments.
Thanks.
The usual way payment providers do this, is the following.
You have a form that will result in a POST array.
add one field to this array with a hash. Make this hash from the string as follows:
Define a secret string (some sort of a 'salt', but different)
Sort all your POST keys alphabetically.
Make a string like this:
key=value.secretString.key2=value2.secretString ... etc
hash the string and send it in the post.
(do NOT send, show or reveal your secret string, obviously)
Now when you receive the POST, you can use your secret string to recreate the hash that should accompany it, and compare it to the hash you got (also in the post ofcourse, obviously don't hash that too). If it is equal, the values where not tampered with. If it isn't you should reject the payment.
Because you also include a date, a user/orderID etc in your post, it cannot be changed for the post of another order. Changing one of the values in the post would also mean the HASH has to be changed, but as the user can't create it, this will not do.
Try a db table of product id's with prices and a table of discounts with the amount (or percent). When the users submits an order, send the ids of products with their respective quantities along with the ids of any discounts to the payment script. Let it handle the final price calculation.
Related
In addition to our regular processing, we have integrated the option for customers of our site to check out with their shopping cart through PayPal, using a combination of PayPal IPN & PDT to record the transaction. We've had the code completed for almost a year, and it works reliably and as expected. However, we have been extremely hesitant to implement it until recently for this reason: We cannot determine any reasonable way to return or retrieve item option values post-checkout from PayPal.
We have to have these item options. For example, we have a slightly complicated system that when a user adds an item, let's say a t-shirt, there are two item option IDs indicating the color and size of the t-shirt. These are stripped out from PayPal before being returned to us, meaning we only get the general item, and not specifically what the customer ordered.
Here is our dilemma:
1) While the documentation clearly states that you can send PayPal on0_ and os0_ variables with the item option information, we cannot determine any way to have it return these back to you. Below is a stripped down sample of the item return values.
This is sent to PayPal:
<input type="hidden" name="item_number_1" value="10272">
<input type="hidden" name="amount_1" value="19.95">
<input type="hidden" name="shipping_1" value="3.55">
<input type="hidden" name="quantity_1" value="1">
<input type="hidden" name="on0_1" value="15001">
<input type="hidden" name="on1_1" value="14000">
This is returned from PayPal (stripped to only have relevant item info):
Post Vars: Array
(
[item_number1] => 10272
[num_cart_items] => 1
[mc_handling1] => 3.55
[mc_shipping1] => 3.55
[item_name1] => [Complete Item Description...]
[mc_gross_1] => 25.00
[transaction_subject] => Shopping Cart[complete item description]
[payment_gross] => 25.00
[ipn_track_id] => 4f3054cc843ba
)
2) From what I understand, there IS a custom field. A single custom field. However, our customers can have 10 or more items in their shopping carts, and most items would have one to three item options. So we'd have to create a parseable pseudo-array of item IDs, options and numbers as a string of text to place in the custom field to simply have our item's options returned to us. This hacky workaround appears to be the "Best chance" option we've got, however it is also not feasible because there is a 256 character limit, so we could not rely on the information returned.
3) So, if I'm correct, we cannot receive or retrieve returns of Item Option variables. But, if PayPal simply returned customers to our website after checking out, then we could still retrieve the session variables, which DO contain full shopping cart information. So when PayPal redirects users, we CAN get information. Except when users check out, they are NOT automatically redirected to PayPal, and instead the choice to redirect is optional and in a small, easy to miss link post-checkout -- meaning not technically reliable by any means.
Ultimately, we went ahead and began accepting PayPal, having to manually go retrieve item options for every customer on PayPal.com, and simply had code in the redirect page to correctly populate orders from users that DID choose to redirect, populating which orders we reasonably could. We had about a hundred orders, and few of them actually redirected back to our site.
4) We tried passing custom variables in hidden input fields, however, in line with PayPal's documentation, these seem to be stripped out before being returned.
5) It is not a realistic or feasible for us to create a custom database record for every user that clicks "PayPal" to store the information for retrieval if or when the IPN comes back. This is not a solution.
UPDATE:
6) As per a user suggestion, we tried a call to PayPal's GetTransactionDetails() through the rather unhelpful use of some documentation here. This did not return item options, instead it returns an array similar to what's returned in the PDT in this format:
[40] => L_NUMBER0=10100
We may have to shut the PayPal option off again if we cannot find an acceptable fix to this problem. We simply cannot determine any actual way to return item option values, yet using PayPal's cart system, we absolutely have to let the user leave our site to finish checking out, and complete cart data is not returned to us, so it appears we cannot record complete customer transactions unless we want to store transaction data of every single person who clicks the "PayPal" button. Is this correct?
Is there any way to accomplish this that we're missing? If PayPal's cart solution is the best way to check out, why does PayPal not return the cart's data?
Here's what I do.
1) Create an order identifier and store all the items that the user is buying into a table. In an ideal system you would have a SKU (stock keeping unit) for every possible combination of item you sell, e.g:
Green tee with Alien print
Green tee with Fish print
Red tee with Alien print
So against the order ID you would store these SKU IDs, the quantity, any additional info, i.e. if there's a discount applied maybe.
2) Pass the invoice ID to PayPal and then you can recall it on the IPN.
3) For user's who don't go all the way through the payment process you can create a cron job to clear out unwanted data. Run it every so often against the time the order was initialised, if that order is older than a set amount of time (because PayPal can get delayed), then remove it from the table.
diggersworld has the "Correct"/best answer; put the cart info in a table (or other store) as a pending order so that you can retrieve it (via invoice ID) and complete it when you get the IPN if there was no return that provides PDT information.
There are many, many other ways to skin this cat, though. A few others:
the GetTransactionDetails docs say it returns item option information; call this when you get the IPN messages to get the particular PayPal fields you do not get in the raw IPN
use a bit of javascript to copy/jam your option selection information into your item descriptions (and then parse it back out when handling IPNs)
switch to using Express Checkout so that the customer is always redirected (pre-payment) and you can use your session variables to create the order exactly as you do with other payment methods after you call DoEC
But as diggersworld says, in general asking your payments provider to handle (even pass through) arbitrarily-complex entities is not the best path. Visa doesn't care about yout T-shirt sizes and colors, and PayPal shouldn't either. If your fulfillment processes require that, you are not on the firmest ground.
I have a custom plugin that calls an API and adds some additional things to my orders at Woocommerce platform.
I had three scenarios before:
first time purchase, uses data submitted from the checkout form fields is used, e.g., $_POST['account_phone'], and everything went fine
subsequent purchases used the same principle
renewals used the order data from the initial orders, and acted like a single purchase is made
Now, the API has changed a bit and several new methods are available, which changes the scenario options above:
first time purchases go into two categories, new users at the API site, and existing users at the API site but new to this platform
remaining is the same
I have issue with the purchases for existing users of this site. First time purchase sends a request to the API, using the POST parameters. However, if a user is already registered, those parameters, like his cell phone number, should be read from the billing data he/she already entered. Thus, I need a way to tell the WooCommerce is this new user trying to buy something and registering, or that this is an already existing user that has his or her data entered. I can get this data from the initial order, or user's billing info, but can't figure out how to check what type of order is it and where WooCommerce should get those values from.
Thanks!
Managed to solve this via another way. The $fields['account']['account_phone'] was shown only during the registration, and simple change of that to $fields['billing']['account_phone'] makes that field "belong" to billing info, which is always shown, which is very convenient and makes no difference later when I check for input there.
Imagine a site like istockphoto or envato where the user can only purchase digital credits...
How would one implement this in the cleanest, easiest way using paypal's API's? The docs there are a bit confusing to navigate...
Aside from that general "best-practices" question, my guess was to start with the Integration Wizard at https://devtools-paypal.com/integrationwizard/ and select "Express Checkout - Digital goods"
After setting up some test accounts on the sandbox, I've kinda sorta got it working- but I'm confused how I track dynamic data across the session.
I.e. where would I securely store the "number of credits" and "userid" values so that it gets added to this user (who may not have an email address, nevermind one matching their paypal account) upon successful payment. Is a database/callback system necessary?
Assuming so for the time being, my guess is to maybe store the data in a database where the primary key is the TOKEN received at SetExpressCheckoutDG(), and prepopulated fields are the data I want to keep... and then the final confirm.php page will check the database at that token and implement that data... but something just seems strange about that, i.e. relying on a callback (could be timeouts, etc.) and I'm not sure if it's really the most secure way of doing it.
If using examples, please stick with PHP (not curl or other langs). Thanks!
You could just bounce this along with your calls. I'll use NVP for my examples
You set your digital goods in SetExpressCheckout. Inside, you pass how many credits the user is buying and set the PAYMENTREQUEST_0_CUSTOM with their user ID.
In GetExpressCheckoutDetails that data would be passed back to you so you could store it in some fashion (you would get their items and quantity, plus the user ID). Then, you finish with DoExpressCheckoutPayment.
If that still doesn't help let me know.
I have a custom store where I'm selling a single product (as a gift), I need to be able to store the recipients address (different from paypal users), a message for the gift and the name they want their gift to be from (if any).
I'd really like to capture the data on this page:
http://sendvalentinesflowers.co.uk/responsive-buy-rose.html
It appears that the standard buttons don't allow this much data to be passed/stored along side a transaction. I'm just wondering how this HAS to be done with IPN? I'm looking for the simplest way to do it.
I would save all of the information in your local database as "pending" prior to sending the user over to PayPal for payment. You can include the invoice parameter in your PayPal code and set the value to the record ID of your local record.
This invoice value will be returned in IPN so you can pull the data back out and process it as necessary, and also update the existing record's payment status according to the current IPN.
I have this small PHP/MySQL cart system that users add products to, checkout, and pay.
These products all have IDs on them so that when the user checks out,
I can get that product's attributes (price, weight, the vendor's account id, etc).
Right now it would be really easy for someone to open up Firebug,
guess another product ID, change it, and checkout.
What would be the best way to prevent this?
The store and the checkout system are on two different domains if that matters.
I could use something like a unique token
but how would that work if multiple customers could be using the cart at the same time?
EDIT: Wow, typed this too fast, left out some important details. The cart is currently represented as JSON that is being stored in a PHP session. All products have an account_id that associates them with a vendor's account.
The problem would occur if a user changed the product id and happened to get a product under another vendor's account (essentially purchasing another company's product from a different company's store) which would be undesirable. Thank you for the answers so far.
Use a server side session to store the cart details.
Every session gets a unique ID, stored in a cookie. All details (selected items, amount, etc) are tied to this sessionId.
By definition, you do not want different customers to use the same cart. Instead, every custommer uses their own separate copy of the cart.
If you need to 'share' the sessionId with some external service, instead calculate a separate unique key and share this key with the third party service (=checkout service in your case).
This ensures that you can uniquely identify your customer in communications with the thirds party, without the third party knowing anything about how you identify or communicate with your customer on your side of the fence. (the important thing to remember is, a sessionId is a shared secret, nobody else should ever know about it).
If you have access to the cart system, the proper way would be to have it duplicate the ID lookup and cost calculations before running the payment. That way, if someone DOES change from a $1.99 box of candy to a $1999.99 HDTV, they will get charged for the tv.
If you don't have access to your cart system, or you can't tell it what the products are and their cost. Get a new cart system.
As a side note: You should NEVER trust data that has come from the user. There should be no need to have to build in trusting the user. Just accept the IDs and run all the numbers on the server.
One way to do this is to use a hash. When they select a product and the form is rendered, take a hash of the Product Id and store it in a hidden field alongside the Product Id. When the 'checkout' post occurs, take a hash of the Product Id that is posted and compare it to the one that was sent out in the form. If they don't match, then the Product Id has been tampered with. I'm not familiar with PHP so can't provide a code sample but I've used this approach in ASP.NET and it works a charm.
Hope that makes some sense!
And what the threat is? If the client-side has only product IDs, then if they change the ID, they'd be buying different product, and that's all. Or your products are not available for all? If they aren't, you need to use random IDs so that they couldn't be easily guessed.
I think what you have to do is encrypt/decrypt your product id and use it.
you can use base64_encode() and base64_decode()
Hope this will help