Yesterday we had a customer complaining that when they logged on to Opencart to place an order, their previous order was still showing in their cart.
On investigation, this occurs where a customer makes a Paypal payment, but then does not return to Opencart after completing the Paypal transaction. In fact most of our customers seem to do this – why bother returning, I suppose. However, it’s the checkout/success page which clears the customer’s cart. In the absence of this, the cart remains stored against the customer’s record, and hence appears next time they log on.
Now, this has never been mentioned before, and this particular Opencart installation has been live for over 18 months, so it’s obviously not a massive issue, but it’s still not quite what you’d want. We’re still running a (heavily customised) OC 1.4.9.5, but in fact the problem remains on OC 1.5.4, and presumably, although I haven’t tested, with any other payment gateway where the payment is handled offsite.
It seemed to me that it would be better to clear the cart when the IPN message is returned from Paypal (this is what sets the order status and updates stock etc). The important cases are where a payment comes through as Completed, or as Pending (complete but waiting Paypal confirmation), so I edited catalog/controller/payment/pp_standard.php
, adding the code in red:
switch($data['payment_status']){
case 'Completed':
if ($this->order_info['customer_id']){ $this->model_checkout_order->clearCustomerCart($this->order_info['customer_id']);
}
if ($verified) {
if ($this->order_info['order_status_id'] == '0') {
$this->model_checkout_order->confirm($order_id, $this->config->get('pp_standard_order_status_id'), 'Thank you for your Paypal payment');
} elseif (isset($data['payment_type']) && $data['payment_type'] == 'echeck') {
$this->model_checkout_order->update($order_id, $this->config->get('pp_standard_order_status_id'), $data['payment_status'], TRUE);
} elseif ($this->order_info['order_status_id'] != $this->config->get('pp_standard_order_status_id')) {
$this->model_checkout_order->update($order_id, $this->config->get('pp_standard_order_status_id'), $data['payment_status'], FALSE);
}
and similarly, a little further down:
case 'Pending':
if ($this->order_info['customer_id']){
$this->model_checkout_order->clearCustomerCart($this->order_info['customer_id']);
}
if ($this->order_info['order_status_id'] == '0') {
$this->model_checkout_order->confirm($order_id, $this->config->get('pp_standard_order_status_id_pending'), 'This payment is subject to a review by Paypal');
} else {
$this->model_checkout_order->update($order_id, $this->config->get('pp_standard_order_status_id_pending'), $comment, TRUE);
}
break;
In turn, this calls the following code appended to catalog/model/checkout/order.php
:
public function clearCustomerCart($customer_id){
$this->db->query("UPDATE `" . DB_PREFIX . "customer` SET cart = '' WHERE customer_id = '" . (int)$customer_id . "'");
$this->session->data['cart'] = array();
}
I’ve shown here direct edits in OC1.4.9.5, but you could just as easily apply this via vQmod. This fix has now been working for several weeks with no problem.
vQmod for OC1.5.x
This code works with minor adjustments in OC1.5.x. Here’s a vQmod, tested in OC1.5.6.1:
<modification>
<id>vQmods for Opencart mobile theme</id>
<version>For OC 1.5.6</version>
<vqmver>1.0</vqmver>
<author>Simon Battersby</author>
<file name="catalog/controller/payment/pp_standard.php">
<operation>
<search position="after"><![CDATA[$order_status_id = $this->config->get('pp_standard_completed_status_id');]]></search>
<add><![CDATA[
$this->clearCustomerCart($order_info['customer_id']);
]]></add>
</operation>
<operation>
<search position="after"><![CDATA[case 'Pending':]]></search>
<add><![CDATA[
$this->clearCustomerCart($order_info['customer_id']);
]]></add>
</operation>
<operation>
<search position="after" offset="2"><![CDATA[curl_close($curl);]]></search>
<add><![CDATA[
public function clearCustomerCart($customer_id){
if($customer_id){
$this->db->query("UPDATE `" . DB_PREFIX . "customer` SET cart = '' WHERE customer_id = '" . (int)$customer_id . "'");
$this->session->data['cart'] = array();
}
}
]]></add>
</operation>
</file>
</modification>
Linked to this, I also noticed that the default session expiry, set via php.ini
is 138 days:
session.gc_maxlifetime = 12000000;
This seems excessive….I’ve set mine to the more reasonable 24 hours.