This is related to a similar scenario is Commerce Registration, https://drupal.org/node/1979900

User registers, goes to payment, but does not complete it (invalid card, accidentally closes window, power failure, etc.).

User tries to register again, but is denied, because registration already exists. Order is stuck in payment state.

We're using Cybersource HOP, which doesn't provide a cancel button on their hosted payment page, which makes things worse; if there is an error in submitting the card number, there is no graceful way to get back to the site. Perhaps if there was a cancel button required as part of the payment API and a rules event to capture it, the cart could automatically be put back in the correct state.

My feeling is that a cart abandoned during payment, for whatever reason should never be left in the payment state.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

pglatz’s picture

Issue summary: View changes
tauno’s picture

If it's not an anonymous user, you could show them their pending registrations for the event so they can pick up where they left off. A link to add an existing pending registration to your cart should also be provided.

If they are an anonymous users - that's trickier. I think we'd have to rely on the pending order itself being deleted before we could remove the pending registration.

gcb’s picture

I agree with Tauno here: the issue here appears to be with your abandoned cart situation, not with registration commerce's default handling. The best we could do would be to make our "is this registration in another cart" check much more complex, having it check the other cart to see if it's been abandoned (and how to determine this? Updated date and state?), and then remove the line item from that cart if so. Sounds like a lot of code to put in place to work around an issue that isn't really ours... unless you can convince me that it's really this module's responsibility to handle abandoned carts.

gcb’s picture

Category: Bug report » Feature request
Status: Active » Postponed (maintainer needs more info)
lightsurge’s picture

Even an anonymous user would still have access to their cart, right, unless they break the session? So if checkout doesn't complete, the line items would remain in the shopping cart, and the user would still be able to pay for them.

In that case, you'd be able to expire abandoned carts (https://drupal.org/project/commerce_cart_expiration) and configure a hook_cron to delete registrations that have been in the pending state for the same time >= cart expiry. You'd need to expire the carts, otherwise you'll have line items in unexpired carts referring to registrations that no longer exist.

This would also handle expiring carts for an anonymous user, who for whatever reason, no longer have access to carts they opened (clears their cookies, etc.)., but obviously not immediately. Seems a bit of an edge-case, though, and I'd agree that if this module is currently just about putting a registration into a commerce cart, it's a step at least for a new version to handle.

lightsurge’s picture

Title: Exit during checkout: Registration remains pending » Provide rules action to delete abandoned cart registrations
Status: Postponed (maintainer needs more info) » Needs review
FileSize
1.42 KB

Patch attached which adds rules action "delete all registrations on an order".

This might be as far as this module needs to go. Works well with commerce_cart_expirations which invokes a "before deleting an expired cart" rules event:

http://drupalcode.org/project/commerce_cart_expiration.git/blob/refs/hea...

  • gcb committed 67017ec on 7.x-1.x authored by lightsurge
    #2160217: add action to delete all registrations in a cart.
    
gcb’s picture

Status: Needs review » Fixed

Thanks @lightsurge. Very handy.

Status: Fixed » Closed (fixed)

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

MrPaulDriver’s picture

Ref #7 - Big thanks for this @lightsurge

MrPaulDriver’s picture

I have this rule working nicely for deleting orders when a cart has expired. But I too am finding problems with registrations being left in a pending state when a transaction fails during checkout or more accurately, where a registrant has second thoughts and backs out of payment, as by this point the registrations have been removed from the cart, but the registration has been created.

In #5 @lightsurge makes the suggestion "delete registrations that have been in the pending state for the same time >= cart expiry."

I am struggling to phrase the conditions for such a rule, although I am guessing that it would be based on a data comparison.

Can somebody point me in the right direction please?

MrPaulDriver’s picture

Status: Closed (fixed) » Active

Temporarily setting to active for visibility.

gcb’s picture

Status: Active » Closed (fixed)

@MrPaulDriver:

I'm not exactly sure what workflow you want here. It sounds like you are saying you have cart expiration working, but it doesn't work when there's a failed transaction on the cart?

You need an event to trigger your rule off of that will provide access to these line items: that's going to be a order/cart trigger of some kind.

I would probably just try to configure the cart expiry tool in some way that it deals with these carts and use the trigger rule presented above.

The job of identifying an expired cart is not part of this module: providing a useful action to execute IS, which is what @lightsurge has provided. If you have a cart in a state that you need to deal with, look to modules and features around commerce_order: we are just dealing with line items here!

MrPaulDriver’s picture

@gcb Thank you for your suggestions

You are right to say that I have cart expiration working, but it doesn't work when there's a failed transaction on the cart. So yes, this is not an abandoned cart problem but one of an abandoned payment. In practical terms this is the same problem as a client would see it.

The relevance to this issue is that the "delete all registrations on an order" action posted by @lightsurge does not seem to work here. It is as if the Registration and Shopping Cart Order have somehow become detached from each other. I was finding that failed registrations of this kind, are left in limbo, uncompleted and consuming slots. Granted this may be an edge case which does not present itself very often, but I have come across so frequently in testing that I expect it will be a problem in production sooner rather than laters.

I was trying to come up with a housekeeping Rule which would periodically seek and delete such registrations and orders. I ended up creating scheduled Rule to fire a rule set which would delete registration entities of a certain state at a certain time after they are created. Similarly I also needed another rule to delete uncompleted shopping cart orders which are flying under the expired cart radar.

gcb’s picture

Huh. I'm not sure why you would see things getting "detached": have you turned on Rules Debugging to get a sense of where the rule you expected to solve this issue is failing?

lightsurge’s picture

@MrPaulDriver

I'm not sure how you're ending up with registrations that aren't attached to an order, as if a user changes their mind prior to payment, and removes an item from the cart, it should remove the registration (at least, that's what happens for me, and I can't remember doing anything special to set that up- but I might be remembering wrong).

I would guess this is about cart expiry rule not acting on the carts in question, rather than about this feature not "acting" on the cart being expired. I would explore that and perhaps post in:

https://drupal.org/project/commerce_cart_expiration

Possibly if your orders have some kind of failed payment attached to them, they're not falling within scope of expiry.

MrPaulDriver’s picture

@lightsurge @gcb Please disregard the 'detached' expression, I guess this was more a metaphor than a true description was is actually happening. My mistake.

1. Registration is created and added to the cart.
2. Registrant completes checkout is sent to off-site payment service.
3. Registrant changes mind and backs out of payment - uses back button.
4. Cart block no longer contains an order because it's order status has changed.
5. Order still exists, with status Checkout: Payment
5. Abandoned registration remains in its 'held pending payment' state, consuming slots.

The recently added rule 'Delete registration and order with line item' does not fire because is was never triggered. The cart order wasn't removed from the cart, but it's order status was changed to 'Checkout: Payment' when it was passed to the payment service - put another way the order left the shopping cart block.

Like I said it may be a edge case, but it does pose a housekeeping problem and the potential exists for abandoned registrations consuming slots. I was looking for suggestions how how to phrase a condition to delete such abandoned registrations and perhaps the associated order as well.

Maybe it's just something peculiar about this payment service provider. For example it's callbacks are pretty buggy and I am relaying on the checkout completion trigger to change the state of a held registration to paid. I would be happier using 'when an order is first paid in full' - but that's another story.

gcb’s picture

Commerce_cart_expiration should have your back here. It may be that it needs to have one of it's rules tweaked slightly, but your problem is triggering something related to the cart, not getting the action to work once triggered. This is really, really, NOT a registration_commerce issue. This is a CART workflow issue. I suggest you take it to commerce_cart_expiration's queue to figure out how to expire those carts that get stuck with no payment.

MrPaulDriver’s picture

Thank you @gcb & @lightsurge

bkat’s picture

What I do for this is

Active states = Pending & Complete
Held States = Abandoned & Held (I don't actually use Held)
States that allow payment = Pending and Abandoned
Hold Expiration Hours: 1
Hold Expiration State: Abandoned

So when someone saves their registration, it is in Pending state. If they don't complete registration within an hour, registration_cron() will change the status to Abandoned. Please note, this function had bugs that prevented it from working. I submitted a patch for that in Registration module.

This frees up the slots. However, if the person comes back later to complete checkout their cart is empty and attempting to add to cart in the Registration fails because its already in an order. This happens because the order is in status "Payment: Checkout" which isn't treated like a cart.

For now, my players email me and I change their order to cart and then they continue quite happily. Sometimes I find that they have gone in and created a new registration. But that's a different story.