Рейтинг:2

Безопасно сохраняйте данные в заказе в onNotify()

флаг jp

Я новичок в Drupal, и мне нужно сделать внешний платежный шлюз (с Drupal Commerce 2). Все работает, но иногда нет.

Сервер поставщика удаленных платежей отправляет на сервер запросы уведомлений о статусе платежа, поэтому у меня есть оба по возвращению и onNotify в классе PaymentGateway.

С по возвращению не гарантируется вызов (клиент может закрыть браузер и т. д., и провайдер не обязательно отправляет его обратно в моем случае), но onNotify гарантированно вызывается, я создаю и сохраняю Оплата объект в onNotify, не в по возвращению, когда платеж будет завершен. (Это также то, что предлагает документация: https://docs.drupalcommerce.org/commerce2/developer-guide/payments/create-payment-gateway/off-site-gateways/handling-ipn)

Итак, мой код выглядит примерно так. (Это очень упрощенный псевдокод, проверки не включены.)

class RedirectCheckout расширяет OffsitePaymentGatewayBase, реализует SupportsNotificationsInterface {

  публичная функция onReturn () {
    $is_order_accepted = /* Проверяем, что поставщик удаленных платежей принял заказ */
    если (!$is_order_accepted) {
       бросить новое исключение NeedsRedirectException()
    }
    // Если все хорошо, ничего не делать.
  }

  публичная функция onNotify() {
    /** @var OrderInterface $заказ */
    $order = /* Загрузить заказ, о котором идет уведомление */

    $is_order_accepted = /* Проверяем, что поставщик удаленных платежей принял заказ */
    если ($ is_order_accepted) {
      $payment = $payment_storage->create();
      $платеж->сохранить();
      $order->setData('transaction_id', $transactionId);
      $ заказ-> сохранить(); // Думаю, это то, что иногда перезаписывается onReturn().
    }
  }
}

Обратите внимание, что мне нужно сохранить некоторые данные о заказе, когда заказ принят (что недоступно при создании заказа, только после успешной оплаты).
В документации Drupal Commerce говорится, что вам «не нужно (и не следует)» касаться заказа, но я должен сохранить некоторые дополнительные данные о заказе, которые ожидают другие части системы.

Это часто работает. Тем не менее, два по возвращению и onNotify запросы с удаленного сервера иногда поступают почти одновременно, что, как я полагаю, приводит к состоянию гонки.

К сожалению, хотя я ничего не делаю для заказа в по возвращению, Коммерция вроде еще спасает заказ. Я считаю, что это может когда-нибудь перезаписать данные, сохраненные в заказе, onNotify. Например:

  • по возвращению начинает работать и загружает заказ (это делает сама коммерческая библиотека, поэтому я ничего не могу с этим поделать).
  • Приходит запрос на уведомление, поэтому onNotify начинает работать, загружает и сохраняет заказ и возвращает
  • После этого по возвращению возвращает метод, возвращает управление Commerce, который снова сохраняет заказ; так как он загрузил объект заказа раньше onNotify сохранил его, он перезаписывает все onNotify писал со старыми данными

(Возможно, обратный порядок также может быть проблематичным, где onNotify может перезаписать любые данные, сохраненные в заказе Drupal Commerce за кулисами, если таковые имеются, во время по возвращению запрос.)

Есть ли хороший способ справиться с этим, например, обойти состояние гонки, чтобы иметь возможность сохранять данные заказа в onNotify?

Я использую Друпал 8.6.

Gabriel Fernandez avatar
флаг cn
Пробовали ли вы использовать события, такие как `commerce_order.place.post_transition`?
флаг jp
Интересная идея - посмотрю как это работает
флаг jp
Хорошее предложение! К сожалению, согласно моим тестам, onNotify() не размещает заказ сам по себе (поэтому это событие не срабатывает) при добавлении платежа.Таким образом, единственный способ разместить заказ в onNotify() — это «применить переход» к заказу, а затем сохранить заказ самостоятельно, но, к сожалению, мы снова возвращаемся к проблеме параллелизма, описанной выше, поскольку и onNotify(), и onReturn() сохраняет заказ. (И я думаю, мы бы тоже вернулись к этой проблеме, если бы коммерция разместила заказ сама)
Рейтинг:1
флаг cn

Вы правы насчет состояния гонки, но onReturn вообще не меняет порядок

Вот что происходит в фоновом режиме:

  • в commerce_payment.checkout.return маршрут по коммерческому платежу вызывается модуль, который сначала позволяет заказать плагин платежного шлюза создать оплату заказа
  • тогда независимо от того, что происходит в методе onReturn потока проверки заказа плагина, он будет перенаправлен на другой этап (если onReturn выдает исключение, он будет перенаправлен на предыдущий этап, иначе он будет перенаправлен на следующий этап)
  • в этой ситуации метод перенаправление на шаг в Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowBase изменит и сохранит порядок, в котором возникает проблема.

это реализация перенаправление на шаг:

/**
 * {@inheritdoc}
 */
публичная функция redirectToStep($step_id) {
  если (!$this->isStepVisible($step_id)) {
    throw new \InvalidArgumentException(sprintf('Неверный идентификатор шага "%s" передан в redirectToStep().', $step_id));
  }

  $this->order->set('checkout_step', $step_id);
  $this->onStepChange($step_id);
  $this->order->save();

  бросить новое NeedsRedirectException(Url::fromRoute('commerce_checkout.form', [
    'commerce_order' => $this->order->id(),
    'шаг' => $step_id,
  ])->toString());
}

поэтому, чтобы решить проблему в вашем методе onReturn, вы должны стоять за выполнением изменений onNotify (цикл while, который ждет, пока не будут установлены изменения в порядке)

надеется, что это поможет.

флаг jp
Спасибо за хороший ответ! Однако один вопрос: даже если я в onReturn дождусь выполнения изменений onNotify, не будет ли «$this->order» по-прежнему содержать старые значения до запуска onNotify? (поэтому redirectToStep сохранит старые значения). Если да, то есть ли способ перезагрузить "$this->order" в onReturn (после ожидания)?
флаг jp
(Хм...И не могло ли сохранение заказа в onNotify фактически вернуть изменение в «checkout_step» в заказе, сделанном redirectToStep, к его старому значению?)
Alireza Tabatabaeian avatar
флаг cn
На самом деле я не совсем уверен в этой ситуации, но я думаю, что это сработает. Хорошо, попробуйте, и если что-то пойдет не так, мы можем подумать о других подходах.

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.