Wednesday, September 20, 2006

Paypal module passes STORE_NAME instead of item

The PayPal payment module (paypal.php,v 1.39 2003/01/29) for OSCommerce does not pass a meaningful item description to Paypal as a transaction is processed. Instead, the developer of this module programmed it to send the name of your store instead:
tep_draw_hidden_field('item_name', STORE_NAME) .
The result is that when the transaction completes, Paypal sends the store owner an email notification, unhelpfully describing the item purchased as "[name of your store]".

We will fix this by changing the code in the payment module to send a better description for that field. While we're at it, we'll change the code to better support stores selling multiple items in one transaction because this Paypal module comes "out of the box" designed more for single item purchase. When multiple items are purchased, this module just gives an aggregate description of the whole transaction, without any detail of what exatly was purchased.

To fix both of these problems, use the patch file below, or edit (path to your store)/includes/modules/payment/paypal.php and jump down to this function:
function process_button() {
find the line that looks like:
$process_button_string = tep_draw_hidden_field
and insert a comment marker right before it:
/*
$process_button_string = tep_draw_hidden_field
and then go down a few more lines from there until you get to the one that has a semicolon at the end, instead of a period. After that line, insert an ending comment marker:
*/
Now, after your ending comment marker (commenting out the original code makes it not operate anymore, while preserving it for reference), insert the following:
 # Multiple item payment, P. 86 of 
# https://www.paypal.com/en_US/pdf/PP_WebsitePaymentsStandard_IntegrationGuide.pdf

$process_button_string = tep_draw_hidden_field('cmd', '_cart') .
tep_draw_hidden_field('upload', '1') .
tep_draw_hidden_field('business', MODULE_PAYMENT_PAYPAL_ID) .
tep_draw_hidden_field('handling_cart', number_format($order->info['shipping_cost'] * $currencies->get_value($my_currency), $currencies->get_decimal_places($my_currency))) .
tep_draw_hidden_field('currency_code', $my_currency) .
tep_draw_hidden_field('custom', $order->info['comments']) .
tep_draw_hidden_field('return', tep_href_link(FILENAME_CHECKOUT_PROCESS, '', 'SSL')) .
tep_draw_hidden_field('cancel_return', tep_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL'));

# add individual items and amounts to keep in PP transaction history and notices

$i=0;
foreach($order->products as $key => $arr)
{
$i++;
$process_button_string .= tep_draw_hidden_field("item_name_$i", $arr["qty"] ." ". $arr["name"]) .
tep_draw_hidden_field("amount_$i", $arr["qty"] * $arr["final_price"]);

}
Save the file, and now every payment transaction from your OS-Commerce store that is processed with this payment module will send Paypal the item name and price of each item your customer is purchasing. The "handling_cart" field adds a single shipping fee to the entire order. If you want to charge shipping amount per item, see the manual mentioned in the code comment above and use multiple "shipping_X" fields inside the foreach loop instead.

Once you make this change, the email notices that Paypal sends to the shop owner after each purchase will contain a detailed list of what was bought. Both the customer and the merchant will also have detailed records of the itemized list stored in the Paypal transaction history. This is much better than just one aggregate item with a total price and no Paypal record of what the order consisted of.

Using my patch file to make the above change.
You can skip a whole lot of manual editting if you download the patchfile included below, save it as paypal.patch in the same directory as the original paypal.php file, and then run the following shell command:
patch -b paypal.php < paypal.patch
The -b option will make a backup copy of the original file, just in case.

17 comments:

  1. Anonymous7:40 AM

    Excellent bit of information. Just had a client come back to us with this problem. Not sure what he thought the use of STORE_NAME was going to have.

    Anyway .. Good stuff man!

    ReplyDelete
  2. Anonymous12:01 AM

    Hi!
    Thanks for this info. However i implemented it and i noticed that there is a bug.
    If a client pay 3 different items in HK$ when he goes to Paypal site the same amount is there but in USD. So, there's a problem with currency conversion!
    Any idea to alter this?...
    Thank you

    ReplyDelete
  3. Anonymous11:51 AM

    Check where the value of $my_currency is coming from in the line above:

    ...
    tep_draw_hidden_field('currency_code', $my_currency) .
    ...

    Or, just replace the variable $my_currency with the literal value for "HongKong" or whatever will clue Paypal on the currency type you want to use.

    ReplyDelete
  4. I site requires size information as well, but this patch only handles item name and price/shipping. Any suggestions?

    ReplyDelete
  5. Anonymous6:04 PM

    This patch works great, but only handles item name and cost. I need to incorporate product size as well. Any suggestions?

    ReplyDelete
  6. Anonymous7:22 PM

    First you'll need to identify what variable holds your size value. You can figure thisa out by putting the following code right after the line:

    function process_button(){

    Insert this line:

    print_r($order->products); exit;

    Then place an order and you should see the entire order products variable values printed to the screen.

    Lets pretend you find from the above that products' array key 'size' held the size value the shopper sleected. (Get the correct key name from the debugging print above).

    Now delete that debugging line and go to the end of the file and change this line:

    $process_button_string .= tep_draw_hidden_field("item_name_$i", $arr["qty"] ." ". $arr["name"]) .
    tep_draw_hidden_field("amount_$i", $arr["final_price"]);

    to this:

    $process_button_string .= tep_draw_hidden_field("item_name_$i", $arr["qty"] ." ". $arr['size'] ." ". $$arr["name"]) .
    tep_draw_hidden_field("amount_$i", $arr["final_price"]);

    and that will add the size to the breakdown of what specific products were in the order and how much each cost.

    ReplyDelete
  7. Hi,

    Great code and it kind of worked for me;

    The trouble I had was if a customer selected 2 of an item only 1 of them got charged. The order would complete. Pay Pal would say:

    2xItem 1 @ 7.99

    when item 1 was 7.99 each

    it would only take 7.99 but send invoice as above. Ouch.

    Any Ideas what I did wrong!

    1 of each worked perfectly...

    Again Thanks for the Help.

    ReplyDelete
  8. The bug that Lloyd spotted should be fixed by making sure that the last line of code in the patch multiplies the final_price by the quantity.

    The patch posted above now has this added.

    Before, it just said:

    tep_draw_hidden_field("amount_$i", $arr["final_price"]);

    and now it says:

    tep_draw_hidden_field("amount_$i", $arr["qty"] * $arr["final_price"]);

    ReplyDelete
  9. We're almost there!

    I've tested that code and the correct fee is sent to paypal:

    However, say I purchased 6 socks at £1 each and 1 hat at £5.

    2 Items are submitted to paypal at the correct totals but will be listed as follows:

    6 Socks Unit Price £6. Sub Total £6.
    1 Hat Unit Price £5. Sub Total £5

    With a Grand Total of £11

    Which is the correct Grand Total but the make up of sub total could be confusing to the observant fussy customer.

    This code thus far is by far the best on offer; just one last tweek? Please

    ReplyDelete
  10. Further to my last.

    What I should have said is that the QTY is showing as 1 for all items but the real QTY is the 1st Character of the Item Name...

    e.g.

    6 Socks Qty 1 Unit Price £6

    Hope you can help.

    ReplyDelete
  11. Excelent!!!!

    My problem, is that I just want to item_name was some word fixed, but not the store_name.

    It's about confidence in selling some kind of products...

    ¿can you help me? I guess is a simple line of code, but I can't find it!!!

    Thank very much!!

    ReplyDelete
  12. But I mean is instead the Store_name, to show the store_owner, for example...

    ReplyDelete
  13. Jerry4:56 PM

    Wow, nice fix... but here's another bug I found. When adding attributes such as selecting a "Large" Tshirt, the attribute does not show. The only thing that shows in Paypal is the Tshirt part. Any fix for this? Thanks for the help!

    ReplyDelete
  14. Anonymous10:19 AM

    @Jerry:

    An answer to your question about getting sizes or other attributes could be to change this line inside the "foreach" loop:

    From this:

    $process_button_string .= tep_draw_hidden_field("item_name_$i", $arr["qty"] ." ". $arr["name"]) .

    To this:

    $process_button_string .= tep_draw_hidden_field("item_name_$i", $arr["qty"] ." ". $arr["name"] ." ". htmlspecialchars(implode(",", $arr), ENT_QUOTES)) .

    Not ideal, but will show whatever there is to know about the product.

    ReplyDelete
  15. Anonymous8:30 AM

    Hi!

    for me, this code show only the last items in the cart. The loop seems not worked correctly?

    "amount_$i" (adding this after paypal value not work? > _$i)

    Do you have this problem?

    thanks.

    ReplyDelete
  16. A better fix for the quantity problem (and in reply to Lloyds comment specifically) is to replace this:


    $process_button_string .= tep_draw_hidden_field("item_name_$i", $arr["qty"] ." ". $arr["name"]) .
    tep_draw_hidden_field("amount_$i", $arr["qty"] * $arr["final_price"]);


    with this:


    $process_button_string .= tep_draw_hidden_field("item_name_$i", $arr["name"]) .
    tep_draw_hidden_field("amount_$i", $arr["final_price"]) .
    tep_draw_hidden_field("quantity_$i", $arr["qty"]);


    The above passes the actual quantity of the item to PayPal. No manual multiplication is required and it looks better for the end user.

    ReplyDelete
  17. Anonymous10:11 AM

    I installed this code, but I need it shows up the surcharge (2%), anyone know how?
    Thank you

    ReplyDelete