Problem/Motivation

Now we have two built-in events: "Completing the checkout process" and "When an order is paid in full". They work well for actions they were designed for but not always fired on the "Checkout complete" page when a user returns back after paying for the order. The reason is that some payment providers have multi-step redirection process (e.g.- PayPal WPS) and for the time user actually returned to a drupal site both these events are already dead because they are fired exactly at the moment when he/she presses "Pay now" button. This is the problem if you need to do something on the last page of the checkout as it may happen or may not, depending on a payment method chosen by the user which makes behaviour of the site inconsistent.

Proposed resolution

My suggestion is to introduce "Checkout complete page is viewed" event and enable site builders do various actions such as displayng a message on the site, redirecting to another page, offering new products, etc.. Only be carefull if you want to log in user with this event because it has security implication.

https://drupal.org/node/1702566
https://drupal.org/node/1216386
https://drupal.org/node/1460964
https://drupal.org/node/1207878
https://drupal.org/node/1130166

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drugan’s picture

The attached patch need to be applyed inside Drupal Commerce module (inside commerce folder), but if you don`t like hacking source code or don`t know how to apply a patch there is a small custom module as a temporary solution. install it as usual.

drugan’s picture

Assigned: Unassigned » drugan
drugan’s picture

Status: Active » Needs review
Summit’s picture

Hi,
Great addition I think, when this will be committed sandbox module: https://drupal.org/sandbox/drugan/2111003 is not needed anymore, right?
Greetings, Martijn

drugan’s picture

Surely it don`t need anymore if the patch will be successfully commited by a maintainer. I`ve created the module as a temporary solution.

rszrama’s picture

Assigned: drugan » Unassigned
Status: Needs review » Closed (won't fix)

Hey drugan, thanks for sharing your patch. While it may work on a case-by-case basis, I'm generally not a fan of "when X is viewed" style Rules events as they aren't very predictable. There are many different ways for a "view" action to be triggered, and at least in the case of the checkout completion page, the page may be displayed any number of times before or after payment has actually been confirmed.

I don't think we should commit it to core, but so long as you aren't doing anything on the event personally that depends on a one-time event or payment being confirmed, it should be safe to use on your own sites.

It's worth noting that even without a core patch, you can still take action when the checkout completion page is being built by hooking into hook_commerce_checkout_router() (or just using hook_form_alter() on the checkout form and checking the page being built).

drugan’s picture

The initial problem I encountered was with login user in as one of the actions "Create a new account for an anonymous order" default rule. When you pay on PayPal WPS and then return back on your site it appears user not logged in but looking in the log messages i`ve found that actually session for the user is open! Why?

The reason is that all the actions are executed at the moment when user presses "Pay now" on the PayPal site, then user redirected (by PayPal code?) to intermediate page and that page may be two kinds depending of your PayPal account settings: static or automatic redirection. If it is static - user need click on the "Return to ExampleSite" to return and in that case action for logging user in already dead. If you set up automatic redirection you also have one more page where you asked "If you not redirected after 10 seconds click this link". And if you wait for redirection - action of logging in user does not work, but if you click the link - it does!

Obviously this behaviour is not what we need and that`s why i decided to make one more event.

rszrama’s picture

Yeah, I understand the need for the event, but I'm also not a fan of logging in a user via Rules either. If I really needed the user to be logged in after they completed the checkout process, I'd probably just hook into the router like I suggested above where I can store that logic in code.

drugan’s picture

Thank you, rszrama, for your reply.

Yes, we at any time can "hook" into something as I did in my custom module above, we can even "hack" but the main idea of the CMS is to change code through web-forms interface by clicking those wonderfull buttons and links, is not it?

drugan’s picture

The question is: when "Completing the checkout process" event fires?

As I understand it happens when our drupal code receives IPN from a payment provider or something like "Success!" message. Different providers send this notification at a different step of the payment process. Some of them do it at the moment when a user actually redirected back to our drupal site and some (as PayPal WPS does) when user clicks "Pay now" button and money transaction is over and done.

In my opinion we can`t rely on others systems code and need to abstract from it as much as we can.

rszrama’s picture

I'm not disagreeing with you, I'm just saying that the solution of adding an event to fire when a particular page of the site is viewed is insufficient as a solution. Additionally, most operations are safe to perform when the event "When an order is first paid in full" fires. The exceptions are order status changes that may interfere with other logic (I think you've already commented in the related PayPal issue) and user interface updates that are meant to be seen by the customer, not the server reporting the payment notification.

There are strategies for working around the order status changes (i.e. using a combination of "When an order is first paid in full" and "Completing the checkout process" with a condition on the order balance), and for the user interface updates I'm simply suggesting that we may already have the tools in place we need to manage those without trying to force them through Rules. Note that I've rejected adding "When a product is being viewed" for similar reasons. I just don't believe the "* is being viewed" events are reliable enough to be worth pursuing.

drugan’s picture

Sorry, a combination of "When an order is first paid in full" and "Completing the checkout process" with a condition on the order balance does not work, at least not always. But anyway, thanks for putting me on the right way because I did not undestand so far what "When an order is first paid in full" event serves for. As a matter of fact it serves exactly for what it says - for checking if the order paid in full or not. Surprisingly, is not it? :-)

I think we MUST to apply this rule on our drupal commerce site because there may be cases when a user redirected to a payment provider has not enough money on his/her account or choose to pay as a bank transfer instead of credit card or paying from a PayPal account, for example. In that case PayPal need to wait until money actually would be transfered and it flags such a transaction as "The payment is pending while it is being reviewed by PayPal for risk" which means an order is NOT paid in full and the "When an order is first paid in full" did not fire as a result. However "Completing the checkout process" event fires in any case even if only 1 (one) dollar is paid for the order say, with a cost of 1000 dollars. Don`t be misleading by a word "Completing..." in this rule as it related only with a checkout process and not a payment!

In the attached exported example rule where I don`t use "Order balance comparison" condition as it is already checked before firing "When an order is first paid in full" rule. (commerce/modules/payment/commerce_payment.module:line 455). I change the order status in this rule for "Processing" (Pending:Processing) to mark (or to flag) paid in full order.

I guess some people do not realize what those order state and statuses mean (like it was me) and I try to explain it in my own words:

  • State:Checkout Complete (Status:Checkout) - It is immediatly before firing "Completing the checkout process" rule.
  • State:Pending (Status:Pending) - It is after "Completing the checkout process" rule firing and before "When an order is first paid in full" rule. Order is registered on our drupal site, but not cleared out (paid in full).
  • State:Pending (Status:Processing) - If you apply the rule I`ve suggested order with this status is paid in full and safe for packing and delivering to a customer. (I wonder why this rule is not a default one...)
  • State:Completed (Status:Completed) - I think it is obvious, you change for this status after putting order items on their way to a customer or after receiving delivery confirmation number, depending on your firm policy. But be carefull to no mess up this status with the PayPal "The payment has completed" status because it is their inner status which is equavalent of our State:Pending (Status:Processing).
drugan’s picture

FileSize
441 bytes

I think this rule need to be a default rule.

drugan’s picture

As a conclusion for this issue I`d like to share with others what I know about the ways of accessing "Checkout Complete" page and doing something there:

  1. Use form_alter() hook putting this code into template.php of your theme
    function your_theme_name_form_alter(&$form, &$form_state, $form_id) {
    if ($form['#id'] == 'commerce-checkout-form-complete') {
      // Doo something here but only remember this is the place for changing a way
      // of displaying data, not for changing data itself.
      }
    }
    
  2. Go to admin/commerce/config/checkout/form/pane/checkout_completion_message, change text format for PHP code and do anything what you want using available tokens, variables, functions... but you need really understand what you are doing. Not recommended.
  3. Use custom module for adding "Checkout complete page is viewed" event and do all the things you can do with the power of Rules. I do NOT recommend to apply the patch above because the maintainer of Drupal Commerce has decided not to commit it into the module`s stable release.
  4. If you know more ways - share their with us here...
Anonymous’s picture

I totally agree with #13, pls commit this rule as default one. thanks.

rszrama’s picture

If we had the rule in by default, any site using PayPal Payments Standard (or similar services) would break. : )

This is one of the issues you commented in re: PayPal. If you change the order status on "When an order is first paid in full", PayPal can send an IPN while the customer is still at PayPal, which would trigger the order status update you've created so that when the customer returns from PayPal they'd get an access error trying to return to checkout/##/payment (because checkout page access is based on order status). That's why I say you have to use a combination of the rules.

Also, whether or not an order has been paid, if they're completed with the checkout process, I don't see it being a misnomer to call it "Completing the checkout process." There are many reasons why a customer might complete checkout but the order has not been paid for yet - consider the case of a pending eCheck or perhaps a credit card authorization that will be captured later during the fulfillment process.

All we can really do at a core level is provide an abstract system that can support any of these scenarios (without expecting any given scenario) and help people understand how to configure the site for their needs. It looks like you were able to do just that, so perhaps the beset result for this issue is documentation from your perspective on how you made use of the events and Rules.

drugan’s picture

May be I was misunderstood but I need to repeat that both "Completing the checkout process" and "When an order is first paid in full" events (EVENTS, not rules in which they are used) called perfectly as their names show us the exact point of time they are firing. Moreover, the default rules (RULES, which are using "Completing the checkout process" event) are extremly vital if you don`t like those mind-blowing error messages on your site.

As for the mustToBeRule I think it is also need to be a default one because it let us know if the order paid in full or not. Just that, no more. And there is no any collision with the default rule "Update the order status on checkout completion" because it is really a misnomer as this rule updates order state (STATE!) not only status as the mustToBeRule does and the default rule is triggered by the "Completing the checkout process" event which fires BEFORE "When an order is first paid in full" event and both these events are fired when a user is still on the PayPal site (or any other) and that is the right behaviour because we never can guarantee if the user will return on our site or not.

So, if the order is just claimed for paying ("Pay now" or similar button is pressed) and no matter what a scheme for paying was chosen (on-point-in-full payment, eCheck, capturing money for later, waiting for bank transfer or even some unknown tricky malicious scheme) payment provider initiates payment transaction and sends us IPN with their inner payment status and order balance. Then our drupal code assigns "Pending" state/status for the order regardless of whether it was paid in full or not and for a payment (PAYMENT, not order) "Success" or "Pending" status (not related with the order and PayPal payment state/status) based on the order balance comparison. Note that there may be many payments for one order and only one of these payments has a "Success" status. Somebody may ask why we can`t use this feature for triggering a rule using the "Completing the checkout process" event with order balance comparison condition and change the order state/status? The answer is in the name of the event - it fires only once on the completing of checkout and even if it fires when the condition is met it may cause collision with the default "Update the order status on checkout completion" rule. That`s why we need safely apply mustToBeRule triggered by the "When an order is first paid in full" event which happens only AFTER "Completing the checkout process" event not interfering with it.

To summarize: The "When an order is first paid in full" event happens not during a checkout process which is ended for the moment but in the PAYMENT process (may be a set of transactions) which is continuining until the order will be paid in full. Note that this event also happens only once and any attempts to add money for the paid in full order will be ignored as it said in the name of the event (When...is first...). There is no way back in the checkout process if a checkout was completed and also in the payment process if an order is paid in full.

About "beset result for this issue is documentation...", yes, but I don`t know exactly what place of the drupal(commerce?) it will be appropriate to put in and would be grateful if you give me right direction.

drugan’s picture

FileSize
759 bytes

For those using PayPal WPP method (on-site payment by a credit card) I need to make some update for the post above.

If a payment is making on-site in that case "When an order is first paid in full" event happens BEFORE "Completing the checkout process" event and that is right because we are still on the Checkout:Review page, are we? ...and doing payment on Checkout:Payment state/status. Above all we have a chance to go on a previous page by clicking "Back" button.

That`s why we need first of all to check if a payment transaction was successfull and only then complete checkout process. There may be a case user made a mistake typing in his/her card data and if to complete checkout on that stage there is no way to do one more attempt or to go back. With an off-site payment it is the other way round as it is a payment provider duty to check if the current user able to pay for the order. For our drupal code it is sufficient to know that "Pay now" or similar button, for which a user has access, was pressed. After that we complete checkout and register completed (Pending) order.

If you are using PayPal WPP method and want to mark paid in full orders changing their status for "Processing" you need to apply one more WPPmustToBeRule which is triggered by a "Checkout complete page is viewed" event. To have the event on your site you need to install this custom module.

Note that this rule only works if an order was paid in full. If you choose "Authorization only" option in your PayPal WPP payment method this rule will not fire. Why? Because the order balance == order total and order is not paid in full. Choose "Authorization and capture" to have this rule work. If you are using some another on-site payment method provider than PayPal WPP, please edit "Selected payment method comparison" condition in the WPPmustToBeRule.

Sorry, if somebody get into trouble follow my post above but I myself didn`t know that. The more I go - the more I know :-))

torgosPizza’s picture

Adding a related issue: #2361957: "pending" GC transactions not rolling back when customers cancel from paypal EC page

This problem manifests itself in our case when a user decides to cancel their order at the PayPal login screen and click the "go back to store" link. When they arrive back at the Checkout Review / Payment page, any Commerce GC (gift card) coupons they had will be removed from their order and stuck in Pending. This is because Commerce GC uses the checkout_complete() hook to authorize the gift card transactions.

Oddly enough it does not seem to help if we move the Rules to fire on a "paid in full" event - because the checkout_complete hook is still fired in checkout router, the only solution seems to reside either here in this issue or changing the implementation of hook_commerce_checkout_router() in PayPal EC.

Channel Islander’s picture

Just want to say thanks for this discussion.

In particular post #18 saved me a bunch of time figuring out why orders were not being marked as complete when I switched to PayPal WPP.

For those using PayPal WPP method (on-site payment by a credit card) I need to make some update for the post above.

If a payment is making on-site in that case "When an order is first paid in full" event happens BEFORE "Completing the checkout process" event

I had gotten away with not double-checking the balance on the checkout-complete rule, because it always fired before payment-first-completed, using the payment methods I had in place (WPS, EC, and cheque). I see now that I was just being lucky and you really do need to have actions for both events and check the order balance when you use the checkout-complete event.

Many thanks, @drugan

AaronBauman’s picture

For anyone still trying to get these events into rules:
#1782590-7: Create events for "Viewing cart" and "Viewing checkout pages"