Hi,
coming from here, #2035961-6: MAJOR BUG -- Upon hitting Bitpay checkout page, press back button registers as COMPLETE ORDER. (Commerce Bitpay)
(The initial problem is solved, we are on a new problem now)

This is no longer happening as described in the issue starter.
However, what does happen is that the shopping cart is emptied as soon as you visit the bitpay.com payment page.

Steps to reproduce:
Add a product to the cart.
Open two browser tabs with cart.
On one, you complete the checkout and payment.
On the other, you refresh the cart after each step to see it disappear.

I think this is not intended, or is it?
I also don't know if this is due to the payment module or something else.

The typical use case is if you go to payment and then use the browser back button.
You end up on an empty cart.

The question is, is there anything to do in the payment module?
Or is this the intended behavior?
How do other payment modules do it, and what is the intended way?

Comments

rszrama’s picture

Status: Active » Fixed

This is the intended behavior, preventing someone from updating the contents of the shopping cart after redirecting to the payment server. We're essentially saying that the order is no longer editable, and the way to do that in Commerce is to no longer treat the order as a shopping cart order. This does mean back button usage can result in a confusing user experience, but that's why most (all?) redirect payment services offer some sort of back or cancel button. Commerce offers a way for you to return the customer to a URL that will reset the order status to Checkout: Review so you can once again view / update the order if need be.

That clear it up? I'm not necessarily making recommendations here so much as describing the system. What you've encountered is the expected behavior, but if you can think of ways to clear it up for your users, feel free to propose them. : )

donquixote’s picture

A solution could be to still show the cart contents, but make them "frozen" and uneditable.
Along with a message that explains:
- the cart is in payment phase
- it is too late to change anything.
- you can either pay or leave this floating around and shop a new cart.

Interesting is also what happens if you already started a new cart, while the old one is still in payment phase.

In usability classes the main questions always were "Where am I? Where can I go? How can I get back to where I came from?"
The goal is to not feel like being in a "dead end".

All this said, I am just scratching the surface of commerce checkout designs, so my opinion on this might change..

rszrama’s picture

The two factors in the code to look at are 1. the shopping cart "refresh" process where we will recalculate product prices based on whether or not the current status of its order is a "cart" order (governed by a property of the order status itself) and 2. the checkout page info array where it indicates whether or not its coordinating order status should be considered a cart or not.

It's a hairy problem to unravel, but my general thinking on it is we should decouple whether or not an order status represents a cart from whether or not it should be refreshed and maybe from whether or not it should still be user editable. Or - we can simply stop making Checkout: Payment no longer a cart status. If payment gateways have been implemented properly and sites are using the "Checkout completed" vs. "When an order is first paid in full" events properly, there really wouldn't be an issue if someone edited their cart in a secondary tab while they were at the payment service. When the payment notification came in and was less than the total of the order, nothing should happen and an administrator would be able to intervene.

This may be one of those places where I was being over aggressive in ensuring cart data security, but this system was actually developed before "When an order is first paid in full" if I remember right.

donquixote’s picture

Hm, what about this:

Instead of just saying "Your shopping cart is empty.", we say
Your current shopping cart is empty.
However, you have some orders in "Payment" state, where the checkout or payment has not been completed.
You can view those orders and reactivate them [here].

Then a separate page (view) with a list of orders that were "lost in translation" ("Checkout: Payment" and maybe other states), and an option to reactivate them.

"Reactivate" could mean to copy the line items to the current (empty) cart, OR to reset the status of the order.

Use case: A user adds stuff to the cart, then goes to checkout and payment, but can't remember the credit cart details, has insufficient credits on Paypal, or has second thoughts about the chosen payment method. Having to start with a new cart could be a conversion killer.

Problem: What if a user "reactivates" an order in one browser tab, and then still completes the payment in another browser tab?
Solution: Don't change the order status while in Checkout: Payment on "reactivate". Instead, copy the line items to the new cart.

Problem: Don't confuse the average visitor with this stuff.
Solution: Only display this option if the user actually visits the cart while stuck in Payment.

rszrama’s picture

Hard to say what should change as the core behavior, because this really only affects redirected payment methods. If someone were to use the navigation links from the payment method to return to your website, their order would be reset properly - but we obviously can't just expect people to not use their browser's back button. Back buttons and forms don't generally play nicely unfortunately.

I'd be interested in a contrib that adds that "continue a previous order" type functionality. The alternative would be to just continue to treat these orders as shopping carts, but I think I like your proposal better.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

philipz’s picture

Issue summary: View changes
Status: Closed (fixed) » Active

If I'm missing something please correct me but this "issue" is still unsolved or rather working as designed isn't it? :)

Would you suggest for example to display a view block on cart page to show user his orders in "Payment" state?

rszrama’s picture

Status: Active » Closed (works as designed)

Sure, works as designed works for me. As a support request, it was technically fixed by explaining why things work as they do. I think a block like that could work fine if there was a way to actually recover those orders; maybe you could combine it with Rules Link to reset those orders to a shopping cart status so the customer can carry on where he or she left off. : ?

Really it's up to each individual site to determine the best thing for them. I can't guarantee at core that changing the behavior or adding additional helps will work for every payment method out there, so we have to keep it intentionally simplistic. : (

philipz’s picture

Yes I now understand also that I can make a link or a list of orders in payment state anywhere I need. All I need to do is create links in pattern like below to let client try making payment again.

<a href="checkout/[order id]/payment">Pay for this order</a>

tnfno’s picture

This is a very bad behavior and is not very customer friendly, just got a complaint from a client. I have never used a webshop where you cannot return to the site after a failed payment attempt and not preserve your cart, it is very user unfriendly and most customers will just leave your site when this happens.

I found some discussions and solutions on payment Commerce module pages (https://www.drupal.org/node/1458840), but this should be a part of the core commerce behavior, not the payment modules.

I understand the issue rszrama mentions above that a customer should not be able to change an order after you are redirected to the payment processor, but this should be solved by verifying the amount against the order sum when the payment processor returns the payment and if it does not match the order should get a partially paid status. This actually worked well in Ubercart.

If no one has a solution to this, I will make a solution when I have completed another project early September.

cameron prince’s picture

This isn't just a payment issue... I'm working on a subscription system for schools and have added additional pages to the checkout process. I ran into this issue today thinking my code was causing the cart to be cleared for some reason. It became obvious that this was in someway intentional when I happened to click "Cancel" and the cart was magically restored.

The cart is lost between custom checkout page 1 and custom checkout page 2 before the user even gets to payment. I have the shopping cart at the top of page 1. When I click "go back" on page 2, the cart is empty.

There has to be a better way to handle this.

cameron prince’s picture

Another thing I noticed with this is when you do use the "Cancel" function on page 1 you are redirected to the cart with the message "Checkout of your current order has been canceled and may be resumed when you are ready." But when you click "Checkout" again, the shopping cart contents pane is empty as if you have no items in your cart. If you click "Cancel" again, the cart page shows the items.

JieXiannn’s picture

I agree that this is bad behavior too. I myself was getting confused when my cart disappeared and I'm sure this would make my users frustrated......

Could you please maybe provide a choice for us to select what behavior we want? Or guide me on how to modify this? I'm inclined to just change the setting of the status to commerce:payment to leave it at review instead.

Please let me know what you think about it. Is it the case that it can be exploitable by adding say, 1 product to my cart and going to my payment site, then modifying my order by adding 3 more products and checking out by only paying for 1 product, hence allowing me to get 4 products by only paying for 1?

Thanks!

Jurgen8en’s picture

rszrama: "Or - we can simply stop making Checkout: Payment no longer a cart status"

function commerce_payment_commerce_checkout_page_info() {
'status_cart' => TRUE,

How to do this without hack?

rszrama’s picture

hook_commerce_checkout_page_info_alter()

neena.thakur’s picture

Following solution worked for me

function custom_module_commerce_checkout_page_info_alter(&$checkout_pages) {
$checkout_pages['payment']['status_cart'] = TRUE;
}

feyisayo’s picture

rszrama: I am following up on your thoughts about a contributed module handling this issue.

A issue related to this comes up a lot on a site I run. The site runs an event registration system. Users cannot register more than once for an event.

Here is the typical scenario: a user registers for an event and goes to pay offsite. Something goes wrong (like internet connectivity issues) and the user does not or cannot return from the payment attempt.

The user opens the site in a new tab and tries to register again but the system prevents him because of the previous pending registration.

The user has to call in and a site admin runs a routine that updates the payment status and hence the order is restored to a cart state.

I have been thinking of a way to handle this automatically and I have 2 ideas:
Idea 1) When a user is redirected offsite, the payment transaction is added to cron queue using hook and a callback is specified to process the transaction later.

If the user returns from the payment attempt then the transaction is removed from the queue.

When cron runs all transactions in the queue are processed by their respective callbacks.

Idea 2) Just like idea 1 above. But instead of the queue being processed via a cron run, it will be processed when the user logs back in to the site. However, only transactions for the user will be processed (there should only be one transaction actually)

One or both approaches could be implemented.

So here is where I need your help:
1) what do you think of the 2 approaches?
2) do you think it is best done by a contributed module or by the respective payment module itself?

John Pitcairn’s picture

@feyisayo: Cron run processing would not be ideal, we run cron as seldom as possible. The user logging in won't work for us either, we use a fully anonymous checkout flow. There is never a user account.

We are handling the situation where the user cancels while in payment redirect via a custom return path that resets the order status. @rszrama could you clarify exactly what you mean by:

Commerce offers a way for you to return the customer to a URL that will reset the order status to Checkout: Review so you can once again view / update the order if need be.

But handling the user just clicking "back" from payment redirect is a nastier problem. If they are fast enough they will get back beyond the payment redirect and wind up back in the site (not at cart, checkout or review) with their cart empty and a stuck order for admin.

For this, and the problem where the user attempts to modify the order in a new tab/window while they are in payment redirect, I think I like the general sound of the solution proposed in #4, but there will still be a few issues if the cart is not accessible when empty (as ours is, or see Commerce Kickstart). Somehow the state of the cart needs to be different from "empty" in this situation, otherwise it is still a major WTF for the user.

rszrama’s picture

Re: the event registration example, it sounds to me like whatever code is supposed to prevent duplicate registrations also just needs to check the order status and only consider a registration invalid if the relevant order has been paid for. I suppose that could result in people gaming the system where they have two separate carts registering the same user for the event or whatever, but that takes intentional effort to do whereas just changing the prevention logic would resolve the issue when it's encountered accidentally.

Witse1978’s picture

Hi,

kinda new around here and not sure if somebody actually applied one of the above solutions to their websites? We just ran into the exact same problem on our webshop.
Was just curious about what solutions people implemented and how this worked out for them?
Would making Checkout: Payment no longer a cart status" the solution?
Or should this be in combination with a view with all orders in checkout-status, suggesting people to activate one of those particular orders again, putting those in cart-status again?

Or is it either one out of these solutions?

Anyone?