Jump to content
  • Checkout
  • Login
  • Get in touch

osCommerce

The e-commerce.

Digital Download Content with PayPal IPN?


CarolineBogart

Recommended Posts

Hi all,

 

The PayPal IPN help says it fixes the problem of oscommerce needing to go back to the store from PayPal. I have this exact problem with PayPal IPN. If the customer does not click PayPal's "complete the order," the product is stuck in "pending" limbo.

 

I think the instructions are saying that this is OK, the store owner has to go through and compare payments from PayPal to "Pending" orders and update the Pending orders to Canceled or Paid accordingly.

 

The problem is, this site is selling instant digital downloads. If the customer doesn't return to the site, PayPal never calls the script that marks the order "paid," and the customer *has* paid but cannot have their download until a human intervenes.

 

I can't just hide the download link if the order is pending. Then customers who really did pay but who did not return to the site would not be given access to their product.

 

I've setup PayPal on other software where the URL is asynchronous. PayPal calls the IPN script without user interaction. Against direct instruction I tried setting PayPal's IPN profile value to the store's checkout_process.php and the store's paypal_ipn.php settings. I didn't remove the notify_url in the, maybe I should have, but both of these attempts to fix the problem failed.

 

Is there any way to force paypal to return to the store, or force paypal to call the "yes he really paid" code if the user doesn't return to the store?

 

Thank you very much for your input.

 

Caroline

Edited by CarolineBogart
Link to comment
Share on other sites

The most recent PayPal IPN contribution (osCommerce_PayPal_IPN_v2.3.3) is a major fix to osCommerce's PayPal IPN design. Prior to this fix, IPN -- which stands for Instant Payment Notification -- wasn't really instant.

 

Instead, if the user returned to the website after payment, the order was marked paid. If the user didn't return, it was not marked paid.

 

This led to two problems:

(1) Orders that were paid at PayPal but unpaid at osCommerce

(2) Orders that were unpaid at PayPal, but if the user manually navigated to checkout_process.php, they were marked paid at osCommerce.

 

Only users who paid at PayPal and clicked that final PayPal navigation back to osCommerce were in the right place. Their orders were paid in both places.

 

osCommerce_PayPal_IPN_v2.3.3 makes Instant Payment Notification an instant process. Even if the user doesn't return from PayPal to osCommerce, the two sites are in sync. Unpaid orders are unpaid at PayPal and "Preparing" on osCommerce. Paid orders are paid at PayPal and "Completed" or "Delivered" at osCommerce.

 

I was hired to fix a PayPal IPN that used osCommerce_PayPal_IPN_v2.3.3 to pay for digital downloads. The paid orders were not being marked as paid on osCommerce.

 

The documents are Digital Rights Managed. They are encrypted and available on a foreign URL.

 

Like any digital download, though, the files were not to be accessible until paid for.

 

In my store (the code as I inherited it), the order went through to PayPal IPN but the IPN didn't trigger the user's access to their purchased document. They paid but the system would not give them their license to view the document.

 

At first, no order would get set to paid.

 

Once that was fixed, the next bug revealed was that every PayPal order became two orders. The first was a zombie order waiting for PayPal processing; the second was a copy of that order, set to Paid/Completed. This meant a minimum of 50% of all PayPal orders in this store would be "abandonded." Worse, every order generated two orders in the customer's account. Confusing customers can be bad for business.

 

One of the fixes for PayPal at osCommerce contributions is a downloads manager. I'm not sure how that solves the problem, because hiding the document from an unpaid user is fine, but hiding it from a paid user is contrary to the store's reason for being.

 

I have to believe other people have run into this. It seems that I found all the leftover old design from before IPN became really instant. This is my shot at finishing that contribution. The fixes below only mark an order as paid when it's really paid, and only allow the system to create one order number per order.

 

I want to thank the hard-working osCommerce authors for giving me such a strong base to work on. The IPN contribution is a huge move in the right direction for osCommerce.

 

This is how PayPal IPN works:

 

1) Customer adds item to cart

2) osCommerce displays shopping_cart.php

3) Customer clicks checkout button.

4) osCommerce displays checkout_payment.php

5) Customer selects credit/debit PayPal

6) Customer clicks continue

7) osCommerce displays checkout_confirmation.php

 

checkout_confirmation uses includes/modules/payment/paypal_ipn.php to build a PayPal form.

 

Among the hidden fields in the form is the order number, sent over as "invoice."

 

Another field sent from osCommerce to PayPal is "notify_url." This is the script PayPal calls when the customer's money is successfully transferred to your account. In a sense, the notify_url is "PayPal IPN." It's the official, verified call from PayPal to oscommerce that the payment is good. It looks something like this:

https://www.example.com/store/ext/modules/p...anguage=english

 

The call from PayPal to the oscommerce IPN script is made outside of the customer's experience. The customer does not see anything having to do with ipn.php. PayPal might receive the payment instantly, or it might take it up to a few days to send a message along that the order is paid.

 

When the payment is successfully transferred from the customer's to your store's account, PayPal calls the notify_url to allow osCommerce to finally mark the order as "paid." osCommerce can then release the order to the customer.

 

8) Customer clicks Confirm

 

9) The PayPal form takes the user to PayPal

10) Customer logs into PayPal

11) Customer clicks Pay Now

 

After the user gives authorization to pay the money, PayPal displays a final page. Its button says something like: "Click to Finish Confirmation" (that text is controlled by the osCommerce file includes/languages/english/modules/payment/paypal_ipn.php constant CONFIRMATION_BUTTON_TEXT).

 

Clicking this button is optional. If the customer clicks the button, the browser calls https://www.example.com/store/checkout_process.php, and checkout_process.php calls checkout_success.php.

 

checkout_process.php is only called if the user takes the option to return to the site. ipn.php is only called when PayPal calls it. No one but PayPal has control over when PayPal calls ipn.php.

 

checkout_process.php/checkout_success.php is for thanking the user and giving instructions.

 

ipn.php is for marking the order paid, and then doing whatever comes from that status (such as granting access to the now-paid-for document).

 

 

=====================================

Filenames

=====================================

Since I always find this part difficult, here's a little road map for the IPN files.

 

They *do* have unique file names, though sometimes it seems we're calling the same file names in different directories. We're not. The file name does uniquely identify each file without having to qualify the names with their directories to make them unique.

 

paypal_ipn.php is used in checkout confirmation to make the form to go to PayPal.

 

checkout_confirmation.php uses paypal_ipn.php to create the confirmation page with the button that takes the user to PayPal.

 

ipn.php is called by PayPal to mark the order paid. It is called asynchronously, meaning we cannot know what time it will be called. It's totally up to PayPal. Work based on order status should be in (or be accessible to) this file.

 

checkout_process.php / checkout_success.php are where PayPal returns the user IF the user clicks that last confirmation button. There is no way to force the user to click the PayPal last button that will return them to the site, so work based on order status should never be in these files.

 

 

 

=====================================

paypal_ipn.php

checkout_confirmation.php

=====================================

includes/modules/payment/paypal_ipn.php

$Id: paypal_ipn.php,v 2.3.3.0 11/17/2007 11:15:28 alexstudio Exp $

 

./checkout_confirmation.php

$Id: checkout_confirmation.php 1739 2007-12-20 00:52:16Z hpdl $

=====================================

 

checkout_confirmation.php, the very last step before going to PayPal, creates the order's order number. It also creates the form that takes the user to PayPal. checkout_confirmation.php uses paypal_ipn.php to create the PayPal form.

 

The first time the user lands on checkout_confirmation.php, the script creates an order number. It stores this and something called the Cart ID in the $_SESSION.

 

If the customer lands on the confirmation page again, the script should recognize it already has an order number and not create another one.

 

In my store, the checkout_confirmation.php script never knew it had an a Cart ID/Order number. It created one and stored it in Session, but was not coded correctly to ever retrieve that value.

 

The PayPal form always sent over an empty order number. ("Order Number" is "Invoice" in PayPal-speak.)

 

$_SESSION is a global. Globals are off on some servers. You can determine if globals are on by going to:

Admin / Tools / Server Info /

and search for for register_globals.

 

In my case, register_globals is on. But my script could never see the incoming $_SESSION value referred to as a global named $cart_PayPal_IPN_ID.

 

Not trusting that globals was really on, I added a php.ini to includes/modules/payments/ with the contents:

 

register_globals = On

 

But I still could not see the incoming order number even if the script had previously created it.

 

This is because the script was looking in the wrong place for the previously-created order number.

 

The Cart ID/Order Number is stored in a variable named cart_PayPal_IPN_ID.

 

$cart_PayPal_IPN_ID is a global, but it is never set to anything.

 

$_SESSION['cart_PayPal_IPN_ID'] has the Cart ID/Order Number. $cart_PayPal_IPN_ID has nothing. $cart_PayPal_IPN_ID is always empty.

 

 

Because $cart_PayPal_IPN_ID has no value, PayPal is never sent the order's number. When PayPal doesn't receive the order's number, the IPN script is called without an order number.

 

On the other side, coming back from PayPal to the IPN script, the IPN will never know about the order number either.

 

The order number is not sent to PayPal and it's not sent from PayPal to ipn.php.

 

When PayPal does the instant notification call to ipn.php, that script creates a whole new order number, completely forgetting about the original order number. The symptom this created in my store was that every order became two orders; the customer's account had two orders for every order they created. The first was preparing for PayPal, the second was created when PayPal called ipn.php.

 

This would be confusing to the customer. If they ordered 5 times they'd have 10 orders in their account.

 

The order number wasn't sent to PayPal because checkout_process.php / paypal_ipn.php couldn't read it. The order number wasn't send from PayPal to ipn.php because PayPal never received it to send back to the store upon receipt of payment.

 

The first fix is: Before leaving for PayPal, retrieve the order number correctly, and put it in the data being sent to PayPal.

 

Change the includes/modules/payment/paypal_ipn.php as follows:

Line 93 becomes $order_id = substr($_SESSION['cart_PayPal_IPN_ID'], strpos($_SESSION['cart_PayPal_IPN_ID'], '-')+1);

Line 270 becomes $_SESSION['cart_PayPal_IPN_ID'] = $cartID . '-' . $insert_id;

Line 510 becomes $parameters['invoice'] = substr($_SESSION['cart_PayPal_IPN_ID'], strpos($_SESSION['cart_PayPal_IPN_ID'], '-')+1);

 

 

** An aside: **

Later on down the road we will be massively chopping up checkout_process.php. Even if you don't want to do that, or you disagree with how I resolved this, do comment line 608 of paypal_ipn.php.

 

To understand why, look at line 72 of checkout_process.php.

It invokes$payment_modules->before_process();

 

Inside one of these before_process() calls, line 608 of paypal_ipn.php redirects to checkout_success.php, thereby abruptly stopping any processing that would have occurred below line 72 of checkout_process.php. In other words, the entire "old" way of re-creating the order when the customer returns from PayPal to osCommerce is started but never finished. Hey, maybe this was on purpose. If it was, it was sneaky and hard to tell that was the intent. Usually one doesn't call before_process() without intending to have a process and then calling after_process(). Here, before_process() goes off to the before_process() function in ipn.php, which redirects to checkout_success.php, and never returns to checkout_process.php.

 

Comment checkout_process.php line 608 as follows:

// tep_redirect(tep_href_link(FILENAME_CHECKOUT_SUCCESS, '', 'SSL'));

 

 

 

=====================================

ipn.php

=====================================

ext/modules/payment/paypal_ipn/ipn.php

paypal_ipn.php,v 2.3.0.0 10/09/2007 11:58:21 alexstudio Exp $

=====================================

 

ipn.php is the script that PayPal calls to mark the order "paid." PayPal calls this script whenever it feels like it. The call might be instant, it might not. We cannot force PayPal to call this script. The user never sees the script in her browser. The one the user might see is the "return" URL - usually oscommerce's "checkout_process.php."

 

When ipn.php is called, it sends a message back to PayPal: "Hey, did you just call me with a message about an order? Because someone did, and if it wasn't you, it was a hacker, or some dufus programmer loading the page to see if it has any syntax errors in it."

 

PayPal will immediately respond to this "callback" with a "yes" or "no." A yes is seen as $result == 'VERIFIED'.

 

The rest of this explanation assumes ipn.php receives a status of 'Completed'...

 

PayPal invokes ipn.php on PayPal's own timing schedule. ipn.php updates the order's status to "paid." I like to set the status to Delivered in the Admin PayPal IPN setup area. This makes sense for digital downloads. If the order is paid we are about to release the content. By the time the script finishes, the order really will be "delivered."

 

To set this new status, ipn.php creates a new orders_status_history record with a status of "Delivered."

 

 

 

In my store, the ipn.php was not receiving the order number (invoice) back from PayPal. With the fix to paypal_ipn.php / checkout_process.php above, the store started sending PayPal the order number. PayPal therefore sent the order number back to ipn.php.

 

Once ipn.php started to receive the order number, a whole bunch of things started going right. The paid-for order was set to "Delivered." The "Delivered" order was therefore safe to give to the customer. Orders which before were never getting to the user were now being delivered.

 

For the very geeky among us: When PayPal didn't send the order number, ipn.php skipped executing the code lines 93 to 318, and lines 333 to 353.

 

Now that ipn.php had an order number, the code inside these lines executed...

 

 

This is what executes now that ipn.php is receiving an order number:

- the order status history is updated to Completed (PayPal term) or Delivered (osCommerce term);

- the customer email acknowledging payment is sent;

- the admin and extra admin emails showing an order are sent;

- stock is decremented if appropriate;

- product count purchased is incremented if appropriate;

- $order->content_type is set to virtual, physical or mixed as required;

- downloads are released;

- and basket is emptied.

 

 

Changes to ipn.php:

 

ipn.php needs to know about the incoming order number and customer ID.

 

- on line 92, after

if (isset($_POST['invoice']) && is_numeric($_POST['invoice']) && ($_POST['invoice'] > 0)) {

add

$orders_id = $_POST['invoice'];

$customers_id = $_POST['custom'];

 

 

 

Now use these variables wherever the script calls for order ID or customer ID

 

- immediate after the addition above, change

$order_query = tep_db_query("select currency, currency_value from " . TABLE_ORDERS . " where orders_id = '" . $_POST['invoice'] . "' and customers_id = '" . (int)$_POST['custom'] . "'");

to

$order_query = tep_db_query("select currency, currency_value from " . TABLE_ORDERS . " where orders_id = '" . $orders_id . "' and customers_id = '" . $customers_id . "'");

 

 

change line 98 from

$order = new order($_POST['invoice']);

to

$order = new order($orders_id);

 

 

 

Change line 101 from

$total_query = tep_db_query("select value from " . TABLE_ORDERS_TOTAL . " where orders_id = '" . $_POST['invoice'] . "' and class = 'ot_total' limit 1");

to

$total_query = tep_db_query("select value from " . TABLE_ORDERS_TOTAL . " where orders_id = '" . $orders_id . "' and class = 'ot_total' limit 1");

 

 

Change lines 138 - 144 from

tep_db_query("update " . TABLE_ORDERS . " set orders_status = '" . $order_status_id . "', last_modified = now() where orders_id = '" . $_POST['invoice'] . "'");

 

$sql_data_array = array('orders_id' => $_POST['invoice'],

'orders_status_id' => $order_status_id,

'date_added' => 'now()',

'customer_notified' => $customer_notified,

'comments' => 'PayPal IPN Verified [' . $comment_status . ']');

to

tep_db_query("update " . TABLE_ORDERS . " set orders_status = '" . $order_status_id . "', last_modified = now() where orders_id = '" . $orders_id . "'");

 

$sql_data_array = array('orders_id' => $orders_id,

'orders_status_id' => $order_status_id,

'date_added' => 'now()',

'customer_notified' => $customer_notified,

'comments' => 'PayPal IPN Verified [' . $comment_status . ']');

 

 

Change line 225 from

$comment_query = tep_db_query("select comments from " . TABLE_ORDERS_STATUS_HISTORY . " where orders_id = '" . $_POST['invoice'] . "'");

 

to

$comment_query = tep_db_query("select comments from " . TABLE_ORDERS_STATUS_HISTORY . " where orders_id = '" . $orders_id . "'");

 

 

 

Change line 231 from

$content_query = tep_db_query("select * from " . TABLE_ORDERS_PRODUCTS_DOWNLOAD . " where orders_id = '" . (int)$_POST['invoice'] . "'");

to

$content_query = tep_db_query("select * from " . TABLE_ORDERS_PRODUCTS_DOWNLOAD . " where orders_id = '" . (int)$orders_id . "'");

 

 

Change lines 247 - 251 from

$email_order = STORE_NAME . "\n" .

EMAIL_SEPARATOR . "\n" .

EMAIL_TEXT_ORDER_NUMBER . ' ' . $_POST['invoice'] . "\n" .

EMAIL_TEXT_INVOICE_URL . ' ' . tep_href_link(FILENAME_ACCOUNT_HISTORY_INFO, 'order_id=' . $_POST['invoice'], 'SSL', false) . "\n" .

EMAIL_TEXT_DATE_ORDERED . ' ' . strftime(DATE_FORMAT_LONG) . "\n\n";

 

to

$email_order = STORE_NAME . "\n" .

EMAIL_SEPARATOR . "\n" .

EMAIL_TEXT_ORDER_NUMBER . ' ' . $orders_id . "\n" .

EMAIL_TEXT_INVOICE_URL . ' ' . tep_href_link(FILENAME_ACCOUNT_HISTORY_INFO, 'order_id=' . $orders_id, 'SSL', false) . "\n" .

EMAIL_TEXT_DATE_ORDERED . ' ' . strftime(DATE_FORMAT_LONG) . "\n\n";

 

 

Change lines 298-304 from

$email_order = STORE_NAME . "\n" .

EMAIL_SEPARATOR . "\n" .

EMAIL_TEXT_ORDER_NUMBER . ' ' . $_POST['invoice'] . "\n" .

EMAIL_TEXT_INVOICE_URL . ' ' . tep_href_link(FILENAME_ACCOUNT_HISTORY_INFO, 'order_id=' . $_POST['invoice'], 'SSL', false) . "\n" .

EMAIL_TEXT_DATE_ORDERED . ' ' . strftime(DATE_FORMAT_LONG) . "\n\n" .

EMAIL_SEPARATOR . "\n" .

EMAIL_PAYPAL_PENDING_NOTICE . "\n\n";

 

 

to

$email_order = STORE_NAME . "\n" .

EMAIL_SEPARATOR . "\n" .

EMAIL_TEXT_ORDER_NUMBER . ' ' . $orders_id . "\n" .

EMAIL_TEXT_INVOICE_URL . ' ' . tep_href_link(FILENAME_ACCOUNT_HISTORY_INFO, 'order_id=' . $orders_id, 'SSL', false) . "\n" .

EMAIL_TEXT_DATE_ORDERED . ' ' . strftime(DATE_FORMAT_LONG) . "\n\n" .

EMAIL_SEPARATOR . "\n" .

EMAIL_PAYPAL_PENDING_NOTICE . "\n\n";

 

 

Change lines 314-315 from

tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET . " where customers_id = '" . (int)$_POST['custom'] . "'");

tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET_ATTRIBUTES . " where customers_id = '" . (int)$_POST['custom'] . "'");

to

tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET . " where customers_id = '" . $customers_id . "'");

tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET_ATTRIBUTES . " where customers_id = '" . $customers_id . "'");

 

 

 

Change line 333 from

$check_query = tep_db_query("select orders_id from " . TABLE_ORDERS . " where orders_id = '" . $_POST['invoice'] . "' and customers_id = '" . (int)$_POST['custom'] . "'");

 

to

$check_query = tep_db_query("select orders_id from " . TABLE_ORDERS . " where orders_id = '" . $orders_id . "' and customers_id = '" . $customers_id . "'");

 

 

I actually have

$orders_id = $_POST['invoice'];

and then the check_query line. I think that's unnecessary but if I'm wrong add it back.

 

 

Change lines 343-349 from

tep_db_query("update " . TABLE_ORDERS . " set orders_status = '" . ((MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID > 0) ? MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID : DEFAULT_ORDERS_STATUS_ID) . "', last_modified = now() where orders_id = '" . $_POST['invoice'] . "'");

 

$sql_data_array = array('orders_id' => $_POST['invoice'],

'orders_status_id' => (MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID > 0) ? MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID : DEFAULT_ORDERS_STATUS_ID,

'date_added' => 'now()',

'customer_notified' => '0',

'comments' => 'PayPal IPN Invalid [' . $comment_status . ']');

 

to

tep_db_query("update " . TABLE_ORDERS . " set orders_status = '" . ((MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID > 0) ? MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID : DEFAULT_ORDERS_STATUS_ID) . "', last_modified = now() where orders_id = '" . $orders_id . "'");

 

$sql_data_array = array('orders_id' => $orders_id,

'orders_status_id' => (MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID > 0) ? MODULE_PAYMENT_PAYPAL_IPN_ORDER_STATUS_ID : DEFAULT_ORDERS_STATUS_ID,

'date_added' => 'now()',

'customer_notified' => '0',

'comments' => 'PayPal IPN Invalid [' . $comment_status . ']');

 

 

 

 

 

 

 

=====================================

checkout_process.php

=====================================

 

./catalog/checkout_process.php

checkout_process.php 1750 2007-12-21 05:20:28Z hpdl $

=====================================

 

After the customer uses PayPal to transfer money from their account to your store's, PayPal presents a PayPal.com page with a button.

 

The button's text instructs the customer to click the button if she wants to return to oscommerce.

 

IF the customer clicks the button, the browser is sent to the "return" URL specified back at the checkout_conformation.php / paypal_ipn.php stage:

http://example.com/store/checkout_process.php.

 

 

And, then, when checkout_process.php successfully completes, it calls checkout_success.php.

 

The user actually never sees checkout_process.php. It does its stuff and silently calls checkout_success.php, which is the first HTML the user sees upon returning from PayPal.

 

Returning to oscommerce after the purchase is OPTIONAL. The user can shut down the browser or browse elsewhere. Your code must assume that the user might never return to the store. Nothing very important can happen in return/checkout_process.php and checkout_success.php. It might never get called.

 

The only "processing" we want to do upon this optional return to oscommerce is to empty the basket. Then checkout_success.php can show some static instructions and some "thank yous."

 

 

In my store, a return to checkout_process.php created a brand new order.

 

I definitely didn't want two orders for every order the customer created.

 

 

Only checkout_confirmation.php should create a new order.

 

There should only ever be one order number.

 

Neither ipn.php nor checkout_process.php should be creating a new order.

 

Using the changes above, the order number created in checkout_confirmation before going to PayPal number is now properly sent to PayPal; and it is properly sent from PayPal to ipn.php. The order number created before leaving for PayPal is the same one being used upon PayPal's call to ipn.php.

 

Therefore most of the checkout_process.php can be removed.

 

My fixed version references nothing to do with orders, payments, shipping, order status, a form of payment called BuySAFE (it belongs somewhere but not here), stock, customers, order totals, email customer notifications, vendor shipping, downloads, products sold, product attributes, weight, tax, cost or admin emails.

 

The only thing checkout_process.php should do is reset the cart on the chance that the order is paid. We don't even know if the order is paid. The right way to handle this is to receive the order number from PayPal in checkout_process.php. If it's paid, reset the cart, if it's not, don't reset the cart as a convenience to the customer. The order number is received in $_POST['invoice']. That's be a nice fix for someone to add.

 

This is the resulting checkout_process.php:

 

<?php

/*

$Id: checkout_process.php 1750 2007-12-21 05:20:28Z hpdl $

*/

include('includes/application_top.php');

 

// if the customer is not logged on, redirect them to the login page

if (!tep_session_is_registered('customer_id')) {

$navigation->set_snapshot(array('mode' => 'SSL', 'page' => FILENAME_CHECKOUT_PAYMENT));

tep_redirect(tep_href_link(FILENAME_LOGIN, '', 'SSL'));

}

// if there is nothing in the customers cart, redirect them to the shopping cart page

if ($cart->count_contents() < 1) {

tep_redirect(tep_href_link(FILENAME_SHOPPING_CART));

}

// if no shipping method has been selected, redirect the customer to the shipping method selection page

if (!tep_session_is_registered('shipping') || !tep_session_is_registered('sendto')) {

tep_redirect(tep_href_link(FILENAME_CHECKOUT_SHIPPING, '', 'SSL'));

}

if ( (tep_not_null(MODULE_PAYMENT_INSTALLED)) && (!tep_session_is_registered('payment')) ) {

tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL'));

}

// avoid hack attempts during the checkout procedure by checking the internal cartID

if (isset($cart->cartID) && tep_session_is_registered('cartID')) {

if ($cart->cartID != $cartID) {

tep_redirect(tep_href_link(FILENAME_CHECKOUT_SHIPPING, '', 'SSL'));

}

}

include(DIR_WS_LANGUAGES . $language . '/' . FILENAME_CHECKOUT_PROCESS);

 

$cart->reset(true);

 

// unregister session variables used during checkout

tep_session_unregister('sendto');

tep_session_unregister('billto');

tep_session_unregister('shipping');

tep_session_unregister('shipping_quotes');

tep_session_unregister('payment');

tep_session_unregister('comments');

// {{ buySAFE Module

tep_session_unregister('WantsBond');

// }}

 

tep_redirect(tep_href_link(FILENAME_CHECKOUT_SUCCESS, '', 'SSL'));

 

require(DIR_WS_INCLUDES . 'application_bottom.php');

?>

 

 

 

 

=====================================

checkout_success.php

=====================================

./checkout_success.php

checkout_success.php 1749 2007-12-21 04:23:36Z hpdl $

=====================================

 

Like checkout_process.php which precedes it, checkout_success.php should not have any code dependent upon the user returning to osCommerce after paying at PayPal. It should not make any assumptions (without checking the database) that the order is paid.

 

In my checkout_success.php code a previous coder had added code dependent on payment verification. The logic is: if the customer returning to osCommerce is new, create them in a foreign, off-site database for access to secure documents. I moved this code to a central place that any returning payment method (PayPal IPN, credit card...) could access. It's one thing to give the user messages about how to use such products. It's another thing to give the user access to the product ONLY if they use an optional button to return to your store.

 

If you have any behavior dependent upon payment in your checkout_process.php or checkout_success.php, move that code to a place accessible to ipn.php and perhaps other payment providers.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...