Current setup :
Commerce recurring with commerce Authorize.net Accept Js
AcceptJs.php is throwing PaymentGatewayException exceptions that are not being catched by DeclineException causing handleDecline not to fire.
I looked around some other payment methods module and noticed more that one exception class used

Proposed solution :
Change it to PaymentGatewayException since all exceptions classes appear to extend this one.

Also maybe document exception that should be thrown in https://docs.drupalcommerce.org/commerce2/developer-guide/payments/creat...
Maybe this is documented somewhere else but i couldn’t find it.

Comments

josebc created an issue. See original summary.

josebc’s picture

Status: Active » Needs review
StatusFileSize
new1.59 KB
josebc’s picture

StatusFileSize
new2.64 KB

forgot handleDecline method oops

Status: Needs review » Needs work

The last submitted patch, 3: commerce_recurring-replace_declineexception-2984454-3.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

josebc’s picture

Status: Needs work » Needs review
StatusFileSize
new2.62 KB

Fix error

joachim’s picture

Status: Needs review » Needs work
+++ b/src/Plugin/AdvancedQueue/JobType/RecurringOrderClose.php
@@ -99,7 +99,7 @@ class RecurringOrderClose extends JobTypeBase implements ContainerFactoryPluginI
-    catch (DeclineException $exception) {
+    catch (PaymentGatewayException $exception) {

Decline exceptions need to be handled specifically, to tell the job to retry.

What your patch should be doing instead is handling the more general PaymentGatewayException as a second catch() block, which then does something else.

bojanz credited heddn.

bojanz’s picture

Title: Exception not catched in process RecurringOrderClose.php » RecurringOrderClose doesn't catch all exceptions

heddn had the right fix for this in #2988470: Don't initiate payment of an already paid recurring order:

+    catch (\Exception $exception) {
+      // Payment gateways can fail for other reasons than decline, let's handle
+      // these by reporting correctly.
+      return JobResult::failure($exception->getMessage());
+    }

EDIT: Actually, advancedqueue already does this for us. In Processor.php we have:

    try {
      $job_type = $this->jobTypeManager->createInstance($job->getType());
      $result = $job_type->process($job);
    }
    catch (\Exception $e) {
      $job_type = NULL;
      $result = JobResult::failure($e->getMessage());
      watchdog_exception('cron', $e);
    }

So, sounds like we don't need to do anything?

bojanz’s picture

@josebc
You said:

AcceptJs.php is throwing PaymentGatewayException exceptions that are not being catched by DeclineException causing handleDecline not to fire.

What's in that exception? Perhaps it's AcceptJs that is buggy?

joachim’s picture

> EDIT: Actually, advancedqueue already does this for us. In Processor.php we have:
> So, sounds like we don't need to do anything?

Not quite.

If we let advancedqueue catch this, then the job is treated as failed, and won't be retried, which is fine.

But we are left with:

- a recurring order in state 'needs_payment', rather than the more accurate 'failed'
- a subscription that is in state 'active', rather than move to the unpaidSubscriptionState

What we need is the part of handleDecline() that deals with the final attempt at dunning to clean up when an \Exception is caught.

joachim’s picture

Status: Needs work » Needs review
StatusFileSize
new2.5 KB

Here's a patch.

I'm not too keen on the need to save the order outside of the new markOrderAsFailed() method, but I assume that handleDecline() saves only at the end in case the RecurringEvents::PAYMENT_DECLINED has subscribers that change the order.

There is an inconsistency that we should maybe think about:

When dunning fails, we treat the job as having succeeded:

      $result = JobResult::success('Dunning complete, recurring order not paid.');

When there's an \Exception, it seems logical to me to treat the job as having failed:

// code from my patch:
    catch (\Exception $exception) {
      // If something more general goes wrong, we assume it's not possible
      // or desirable to retry.
      $this->markOrderAsFailed($order);

      // Rethrow the exception so that Advanced Queue can log the job's failure.
      throw $exception;
    }

But that means that the same result -- an order that's marked as failed -- can be counted as a successful job, or as a failed job.

joachim’s picture

Updated patch, with tests.

(Though branch tests are failing, so they're not going to get run here...)

jsacksick’s picture

Status: Needs review » Fixed
StatusFileSize
new8.51 KB
new2.32 KB

Committed a slightly different patch (Added a $save_order parameter to handleFailedOrder and updated the dependencies in RetryTest).

  • jsacksick committed 143cbf4 on 8.x-1.x authored by joachim
    Issue #2984454 by josebc, joachim, jsacksick, bojanz, heddn:...

Status: Fixed » Closed (fixed)

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