Problem/Motivation

The Authorize only, and Authorize and capture transaction mode settings are global in the payment process within the default checkout flow in Drupal 8.

Site builders should be able to utilize different transaction modes according to the payment gateway's conditional criteria. Some examples could be:

  1. Large transactions that the site owner would like to verify (orders over $10,000 etc)
  2. International purchases that might be high risk, or that might turn out to be unable to be fulfilled
  3. Specific products that could have a tendency to be delayed in fulfillment
  4. Billing/shipping mismatch verification

Proposed resolution

If we move the transaction mode settings into the applicable gateways, we can then easily leverage the gateway conditions for the payments methods in determining when to Auth only, and when to Auth and capture.

@mglaman also mentioned in slack chat including an override feature at the checkout flow for folks who want global settings instead.

CommentFileSizeAuthor
#5 3178226-5.patch2.91 KBtonytheferg
#5 Screenshot (526).png16.9 KBtonytheferg

Comments

ToneLoc created an issue. See original summary.

mglaman’s picture

I agree. We should add the setting to payment gateways. A lot of times I forget, even, we customize it on checkout.

This issue helps us realign with the most common use case. Someone has one payment gateway (maybe two) and one checkout flow.

The special use case should be overriding the transaction type per-checkout flow. Such as a digital checkout. Most users probably have a mixed checkout flow that would use the default.

So we need to add a capture setting to the payment gateway configuration schema

  mapping:
    capture:
      type: boolean
      label: 'Default transaction mode'

The form can say some diddy about how payments create with this gateway will use that mode. But it can be overridden per-checkout flow instance.

In the PaymentProcess pane we need another option for capture. Basically "Use default transaction mode". Some kind of checkbox which hides the radios.

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);
    $form['capture'] = [
      '#type' => 'radios',
      '#title' => $this->t('Transaction mode'),
      '#description' => $this->t('This setting is only respected if the chosen payment gateway supports authorizations.'),
      '#options' => [
        TRUE => $this->t('Authorize and capture'),
        FALSE => $this->t('Authorize only (requires manual capture after checkout)'),
      ],
      '#default_value' => (int) $this->configuration['capture'],
    ];

    return $form;
  }

Then anywhere we check for the capture value we'll need to check if default and pull from the payment gateway

     try {
        $payment->payment_method = $this->order->get('payment_method')->entity;
        $payment_gateway_plugin->createPayment($payment, $this->configuration['capture']);
        $this->checkoutFlow->redirectToStep($next_step_id);
      }

We could initialize a $capture variable below these lines

    $payment = $this->createPayment($payment_gateway);
    $next_step_id = $this->checkoutFlow->getNextStepId($this->getStepId());

Something like

$capture = $this->configuration['capture'] ?? $payment_gateway_plugin->getTransactionMode()
lisastreeter’s picture

It would be helpful if there was also a way to control the transaction mode apart from the gateways. A third-party fraud detection service could be used during checkout to pre-authorize the payment transaction. If the responses returned by the detection service include "pending" or "under review" in addition to strict approve/deny results, we'd want to set the transaction mode to "authorize only" instead of the default "authorize and capture" for pre-approved transactions.

Perhaps instead of:

$capture = $this->configuration['capture'] ?? $payment_gateway_plugin->getTransactionMode()

Create a new method for the checkout pane with default implementation that could be overridden by extending the class:

protected function getTransactionCapture(PaymentGatewayInterface $payment_gateway_plugin) {
  if ($payment_gateway_plugin instanceof SupportsConditionalPaymentCapture) {
    return $payment_gateway_plugin->getTransactionMode($this->order);
  }
  else {
    return $this->configuration['capture'];
  }
}

And then this:

$capture = $this->configuration['capture'] ?? $this->getTransactionCapture($payment_gateway_plugin);

tonytheferg’s picture

We should leave the checkout flow setting in place as a global setting, and then allow it to be overridden on the gateway level. Then there won't be any loss of functionality nor backwards compatibility issues.

tonytheferg’s picture

StatusFileSize
new16.9 KB
new2.91 KB

Tossing a patch that just adds the schema, and form stuff to authnet.js, and adding a picture for thoughts on UX.
picture

My thought is that the capture setting is only respected when override_capture is set to TRUE.

The patch doesn't add any logic to check the override_capture setting just yet.
Wanted to see if this might be an acceptable approach first.

tonytheferg’s picture

Status: Active » Needs review

NR just to see if the UX might work as a general approach.

Noticed this is wrong from my copying from accept.js.

+          'input[name="configuration[authorizenet_acceptjs][override_capture]"]' => ['checked' => TRUE],
mglaman’s picture

Dumping some thoughts. I saw this Slack convo and it rolled in my brain yesterday afternoon

When we first added the auth or auth+capture setting, in hindsight I wish we kept it on the payment gateway and then one of the following:

  1. You used conditions to control which payment gateway (auth or auth+capture) was used on checkout flows
  2. The checkout flow pane was an override behavior

For backwards compatibility, #2 is the best. Whereas #1 provides better visibility (possibly, we don't show conditions on the gateway collection list.)

So the gateway should dictate default behavior. Then the checkout flow can have:

- Use default behavior
- Only authorize, overriding defaults
- Always capture, overriding defaults