Repository: saleor/saleor
Pull request: Add missing OrderEvents in checkout flow
I want to merge this change because...
it adds missing OrderEvents in checkout flow.
<!-- Please mention all relevant issue numbers. -->
fixes #5535 

# Impact

* [x] New migrations
* [x] New/Updated API fields or mutations
* [ ] Deprecated API fields or mutations
* [ ] Removed API types, fields, or mutations

# Pull Request Checklist

<!-- Please keep this section. It will make maintainer's life easier. -->

* [x] Privileged queries and mutations are guarded by proper permission checks
* [ ] Database queries are optimized and the number of queries is constant
* [x] Database migration files are up to date
* [x] The changes are tested
* [x] GraphQL schema and type definitions are up to date
* [ ] Changes are mentioned in the changelog


Problem statement:
Missing payment_captured and order_fully_paid events
### What I'm trying to achieve
When placing orders using a gateway that captures the amount (without using the dashboard), the events payment_captured and order_fully_paid are not registered. These events are registered only when marking orders as paid in the dashboard.

### Steps to reproduce the problem
1. Make an Order.
2. Pay using Dummy (fully-charged) or a credit card Gateway.
3. Look into table order_orderevent.
4. Events payment_captured and order_fully_paid are missing.

### What I expected to happen
I expect these events registered when capturing a payment.

Thanks and awesome project!

Hints from issue discussion:
Thanks for reporting, it's a bug. These events should be called as well when an order is placed through checkout mutations.
Hi, this seems to be caused because order object is not set up correctly in payment object (is None when capturing a payment). Do you have any clue where in the code this can be happening (so I can debug it myself)?
@dsarmiento84 @maarcingebala 
Hi, I believe that this is because in checkout flow we create order after the payment is done.
We could create those events when we create order but then we introduce edge cases when we capture the payment and don't create an order for some reason.
Another solution would be that we create an event without order and for example checkout token and then after placing order, we update the event with it.

Commit message: Fix CHANGELOG

Patch to review:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d88b898ac4..5da0d29672a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ All notable, unreleased changes to this project will be documented in this file.
 - Fix specific product voucher in draft orders - #5727 by @fowczarek
 - Explicit country assignment in default shipping zones - #5736 by @maarcingebala
 - Drop `json_content` field from the `Menu` model - #5761 by @maarcingebala
+- Add missing OrderEvents during checkout flow - #5684 by @koradon
 
 ## 2.10.1
 
diff --git a/saleor/checkout/tests/test_checkout.py b/saleor/checkout/tests/test_checkout.py
index a32bbb2d5c1..8556cf8b99e 100644
--- a/saleor/checkout/tests/test_checkout.py
+++ b/saleor/checkout/tests/test_checkout.py
@@ -86,12 +86,11 @@ def test_last_change_update_foregin_key(checkout, shipping_method):
         assert checkout.last_change == pytz.utc.localize(frozen_datetime())
 
 
-@pytest.mark.parametrize("is_anonymous_user", (True, False))
-def test_create_order_creates_expected_events(
-    request_checkout_with_item, customer_user, shipping_method, is_anonymous_user
+def test_create_order_captured_payment_creates_expected_events(
+    request_checkout_with_item, customer_user, shipping_method, payment_txn_captured,
 ):
     checkout = request_checkout_with_item
-    checkout_user = None if is_anonymous_user else customer_user
+    checkout_user = customer_user
 
     # Ensure not events are existing prior
     assert not OrderEvent.objects.exists()
@@ -102,6 +101,7 @@ def test_create_order_creates_expected_events(
     checkout.billing_address = customer_user.default_billing_address
     checkout.shipping_address = customer_user.default_shipping_address
     checkout.shipping_method = shipping_method
+    checkout.payments.add(payment_txn_captured)
     checkout.save()
 
     # Place checkout
@@ -113,35 +113,298 @@ def test_create_order_creates_expected_events(
             tracking_code="tracking_code",
             discounts=None,
         ),
-        user=customer_user if not is_anonymous_user else AnonymousUser(),
+        user=customer_user,
         redirect_url="https://www.example.com",
     )
     flush_post_commit_hooks()
 
     # Ensure only two events were created, and retrieve them
-    placement_event, email_sent_event = order.events.all()  # type: OrderEvent
+    order_events = order.events.all()
+
+    (
+        order_placed_event,
+        payment_captured_event,
+        order_fully_paid_event,
+        payment_email_sent_event,
+        order_placed_email_sent_event,
+    ) = order_events  # type: OrderEvent
 
     # Ensure the correct order event was created
-    assert placement_event.type == OrderEvents.PLACED  # is the event the expected type
-    assert placement_event.user == checkout_user  # is the user anonymous/ the customer
-    assert placement_event.order is order  # is the associated backref order valid
+    # is the event the expected type
+    assert order_placed_event.type == OrderEvents.PLACED
+    # is the user anonymous/ the customer
+    assert order_placed_event.user == checkout_user
+    # is the associated backref order valid
+    assert order_placed_event.order is order
+    # ensure a date was set
+    assert order_placed_event.date
+    # should not have any additional parameters
+    assert not order_placed_event.parameters
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert payment_captured_event.type == OrderEvents.PAYMENT_CAPTURED
+    # is the user anonymous/ the customer
+    assert payment_captured_event.user == checkout_user
+    # is the associated backref order valid
+    assert payment_captured_event.order is order
+    # ensure a date was set
+    assert payment_captured_event.date
+    # should not have any additional parameters
+    assert "amount" in payment_captured_event.parameters.keys()
+    assert "payment_id" in payment_captured_event.parameters.keys()
+    assert "payment_gateway" in payment_captured_event.parameters.keys()
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert order_fully_paid_event.type == OrderEvents.ORDER_FULLY_PAID
+    # is the user anonymous/ the customer
+    assert order_fully_paid_event.user == checkout_user
+    # is the associated backref order valid
+    assert order_fully_paid_event.order is order
+    # ensure a date was set
+    assert order_fully_paid_event.date
+    # should not have any additional parameters
+    assert not order_fully_paid_event.parameters
+
+    # Ensure the correct email sent event was created
+    # should be email sent event
+    assert payment_email_sent_event.type == OrderEvents.EMAIL_SENT
+    # ensure the user is none or valid
+    assert payment_email_sent_event.user == checkout_user
+    # ensure the mail event is related to order
+    assert payment_email_sent_event.order is order
+    # ensure a date was set
+    assert payment_email_sent_event.date
+    # ensure the correct parameters were set
+    assert payment_email_sent_event.parameters == {
+        "email": order.get_customer_email(),
+        "email_type": OrderEventsEmails.PAYMENT,
+    }
+
+    # Ensure the correct email sent event was created
+    # should be email sent event
+    assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
+    # ensure the user is none or valid
+    assert order_placed_email_sent_event.user == checkout_user
+    # ensure the mail event is related to order
+    assert order_placed_email_sent_event.order is order
+    # ensure a date was set
+    assert order_placed_email_sent_event.date
+    # ensure the correct parameters were set
+    assert order_placed_email_sent_event.parameters == {
+        "email": order.get_customer_email(),
+        "email_type": OrderEventsEmails.ORDER,
+    }
+
+    # Ensure the correct customer event was created if the user was not anonymous
+    placement_event = customer_user.events.get()  # type: CustomerEvent
+    assert placement_event.type == CustomerEvents.PLACED_ORDER  # check the event type
+    assert placement_event.user == customer_user  # check the backref is valid
+    assert placement_event.order == order  # check the associated order is valid
     assert placement_event.date  # ensure a date was set
     assert not placement_event.parameters  # should not have any additional parameters
 
+    # mock_send_staff_order_confirmation.assert_called_once_with(order.pk)
+
+
+def test_create_order_captured_payment_creates_expected_events_anonymous_user(
+    request_checkout_with_item, customer_user, shipping_method, payment_txn_captured,
+):
+    checkout = request_checkout_with_item
+    checkout_user = None
+
+    # Ensure not events are existing prior
+    assert not OrderEvent.objects.exists()
+    assert not CustomerEvent.objects.exists()
+
+    # Prepare valid checkout
+    checkout.user = checkout_user
+    checkout.email = "test@example.com"
+    checkout.billing_address = customer_user.default_billing_address
+    checkout.shipping_address = customer_user.default_shipping_address
+    checkout.shipping_method = shipping_method
+    checkout.payments.add(payment_txn_captured)
+    checkout.save()
+
+    # Place checkout
+    order = create_order(
+        checkout=checkout,
+        order_data=prepare_order_data(
+            checkout=checkout,
+            lines=list(checkout),
+            tracking_code="tracking_code",
+            discounts=None,
+        ),
+        user=AnonymousUser(),
+        redirect_url="https://www.example.com",
+    )
+    flush_post_commit_hooks()
+
+    # Ensure only two events were created, and retrieve them
+    order_events = order.events.all()
+
+    (
+        order_placed_event,
+        payment_captured_event,
+        order_fully_paid_event,
+        payment_email_sent_event,
+        order_placed_email_sent_event,
+    ) = order_events  # type: OrderEvent
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert order_placed_event.type == OrderEvents.PLACED
+    # is the user anonymous/ the customer
+    assert order_placed_event.user == checkout_user
+    # is the associated backref order valid
+    assert order_placed_event.order is order
+    # ensure a date was set
+    assert order_placed_event.date
+    # should not have any additional parameters
+    assert not order_placed_event.parameters
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert payment_captured_event.type == OrderEvents.PAYMENT_CAPTURED
+    # is the user anonymous/ the customer
+    assert payment_captured_event.user == checkout_user
+    # is the associated backref order valid
+    assert payment_captured_event.order is order
+    # ensure a date was set
+    assert payment_captured_event.date
+    # should not have any additional parameters
+    assert "amount" in payment_captured_event.parameters.keys()
+    assert "payment_id" in payment_captured_event.parameters.keys()
+    assert "payment_gateway" in payment_captured_event.parameters.keys()
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert order_fully_paid_event.type == OrderEvents.ORDER_FULLY_PAID
+    # is the user anonymous/ the customer
+    assert order_fully_paid_event.user == checkout_user
+    # is the associated backref order valid
+    assert order_fully_paid_event.order is order
+    # ensure a date was set
+    assert order_fully_paid_event.date
+    # should not have any additional parameters
+    assert not order_fully_paid_event.parameters
+
+    # Ensure the correct email sent event was created
+    # should be email sent event
+    assert payment_email_sent_event.type == OrderEvents.EMAIL_SENT
+    # ensure the user is none or valid
+    assert payment_email_sent_event.user == checkout_user
+    # ensure the mail event is related to order
+    assert payment_email_sent_event.order is order
+    # ensure a date was set
+    assert payment_email_sent_event.date
+    # ensure the correct parameters were set
+    assert payment_email_sent_event.parameters == {
+        "email": order.get_customer_email(),
+        "email_type": OrderEventsEmails.PAYMENT,
+    }
+
     # Ensure the correct email sent event was created
-    assert email_sent_event.type == OrderEvents.EMAIL_SENT  # should be email sent event
-    assert email_sent_event.user == checkout_user  # ensure the user is none or valid
-    assert email_sent_event.order is order  # ensure the mail event is related to order
-    assert email_sent_event.date  # ensure a date was set
-    assert email_sent_event.parameters == {  # ensure the correct parameters were set
+    # should be email sent event
+    assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
+    # ensure the user is none or valid
+    assert order_placed_email_sent_event.user == checkout_user
+    # ensure the mail event is related to order
+    assert order_placed_email_sent_event.order is order
+    # ensure a date was set
+    assert order_placed_email_sent_event.date
+    # ensure the correct parameters were set
+    assert order_placed_email_sent_event.parameters == {
         "email": order.get_customer_email(),
         "email_type": OrderEventsEmails.ORDER,
     }
 
     # Check no event was created if the user was anonymous
-    if is_anonymous_user:
-        assert not CustomerEvent.objects.exists()  # should not have created any event
-        return  # we are done testing as the user is anonymous
+    assert not CustomerEvent.objects.exists()  # should not have created any event
+
+
+def test_create_order_preauth_payment_creates_expected_events(
+    request_checkout_with_item, customer_user, shipping_method, payment_txn_preauth,
+):
+    checkout = request_checkout_with_item
+    checkout_user = customer_user
+
+    # Ensure not events are existing prior
+    assert not OrderEvent.objects.exists()
+    assert not CustomerEvent.objects.exists()
+
+    # Prepare valid checkout
+    checkout.user = checkout_user
+    checkout.billing_address = customer_user.default_billing_address
+    checkout.shipping_address = customer_user.default_shipping_address
+    checkout.shipping_method = shipping_method
+    checkout.payments.add(payment_txn_preauth)
+    checkout.save()
+
+    # Place checkout
+    order = create_order(
+        checkout=checkout,
+        order_data=prepare_order_data(
+            checkout=checkout,
+            lines=list(checkout),
+            tracking_code="tracking_code",
+            discounts=None,
+        ),
+        user=customer_user,
+        redirect_url="https://www.example.com",
+    )
+    flush_post_commit_hooks()
+
+    # Ensure only two events were created, and retrieve them
+    order_events = order.events.all()
+
+    (
+        order_placed_event,
+        payment_authorized_event,
+        order_placed_email_sent_event,
+    ) = order_events  # type: OrderEvent
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert order_placed_event.type == OrderEvents.PLACED
+    # is the user anonymous/ the customer
+    assert order_placed_event.user == checkout_user
+    # is the associated backref order valid
+    assert order_placed_event.order is order
+    # ensure a date was set
+    assert order_placed_event.date
+    # should not have any additional parameters
+    assert not order_placed_event.parameters
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert payment_authorized_event.type == OrderEvents.PAYMENT_AUTHORIZED
+    # is the user anonymous/ the customer
+    assert payment_authorized_event.user == checkout_user
+    # is the associated backref order valid
+    assert payment_authorized_event.order is order
+    # ensure a date was set
+    assert payment_authorized_event.date
+    # should not have any additional parameters
+    assert "amount" in payment_authorized_event.parameters.keys()
+    assert "payment_id" in payment_authorized_event.parameters.keys()
+    assert "payment_gateway" in payment_authorized_event.parameters.keys()
+
+    # Ensure the correct email sent event was created
+    # should be email sent event
+    assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
+    # ensure the user is none or valid
+    assert order_placed_email_sent_event.user == checkout_user
+    # ensure the mail event is related to order
+    assert order_placed_email_sent_event.order is order
+    # ensure a date was set
+    assert order_placed_email_sent_event.date
+    # ensure the correct parameters were set
+    assert order_placed_email_sent_event.parameters == {
+        "email": order.get_customer_email(),
+        "email_type": OrderEventsEmails.ORDER,
+    }
 
     # Ensure the correct customer event was created if the user was not anonymous
     placement_event = customer_user.events.get()  # type: CustomerEvent
@@ -154,6 +417,93 @@ def test_create_order_creates_expected_events(
     # mock_send_staff_order_confirmation.assert_called_once_with(order.pk)
 
 
+def test_create_order_preauth_payment_creates_expected_events_anonymous_user(
+    request_checkout_with_item, customer_user, shipping_method, payment_txn_preauth,
+):
+    checkout = request_checkout_with_item
+    checkout_user = None
+
+    # Ensure not events are existing prior
+    assert not OrderEvent.objects.exists()
+    assert not CustomerEvent.objects.exists()
+
+    # Prepare valid checkout
+    checkout.user = checkout_user
+    checkout.email = "test@example.com"
+    checkout.billing_address = customer_user.default_billing_address
+    checkout.shipping_address = customer_user.default_shipping_address
+    checkout.shipping_method = shipping_method
+    checkout.payments.add(payment_txn_preauth)
+    checkout.save()
+
+    # Place checkout
+    order = create_order(
+        checkout=checkout,
+        order_data=prepare_order_data(
+            checkout=checkout,
+            lines=list(checkout),
+            tracking_code="tracking_code",
+            discounts=None,
+        ),
+        user=AnonymousUser(),
+        redirect_url="https://www.example.com",
+    )
+    flush_post_commit_hooks()
+
+    # Ensure only two events were created, and retrieve them
+    order_events = order.events.all()
+
+    (
+        order_placed_event,
+        payment_captured_event,
+        order_placed_email_sent_event,
+    ) = order_events  # type: OrderEvent
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert order_placed_event.type == OrderEvents.PLACED
+    # is the user anonymous/ the customer
+    assert order_placed_event.user == checkout_user
+    # is the associated backref order valid
+    assert order_placed_event.order is order
+    # ensure a date was set
+    assert order_placed_event.date
+    # should not have any additional parameters
+    assert not order_placed_event.parameters
+
+    # Ensure the correct order event was created
+    # is the event the expected type
+    assert payment_captured_event.type == OrderEvents.PAYMENT_AUTHORIZED
+    # is the user anonymous/ the customer
+    assert payment_captured_event.user == checkout_user
+    # is the associated backref order valid
+    assert payment_captured_event.order is order
+    # ensure a date was set
+    assert payment_captured_event.date
+    # should not have any additional parameters
+    assert "amount" in payment_captured_event.parameters.keys()
+    assert "payment_id" in payment_captured_event.parameters.keys()
+    assert "payment_gateway" in payment_captured_event.parameters.keys()
+
+    # Ensure the correct email sent event was created
+    # should be email sent event
+    assert order_placed_email_sent_event.type == OrderEvents.EMAIL_SENT
+    # ensure the user is none or valid
+    assert order_placed_email_sent_event.user == checkout_user
+    # ensure the mail event is related to order
+    assert order_placed_email_sent_event.order is order
+    # ensure a date was set
+    assert order_placed_email_sent_event.date
+    # ensure the correct parameters were set
+    assert order_placed_email_sent_event.parameters == {
+        "email": order.get_customer_email(),
+        "email_type": OrderEventsEmails.ORDER,
+    }
+
+    # Check no event was created if the user was anonymous
+    assert not CustomerEvent.objects.exists()  # should not have created any event
+
+
 def test_create_order_insufficient_stock(
     request_checkout, customer_user, product_without_shipping
 ):
diff --git a/saleor/graphql/order/mutations/orders.py b/saleor/graphql/order/mutations/orders.py
index 2c013bfb67b..c8f78376a4d 100644
--- a/saleor/graphql/order/mutations/orders.py
+++ b/saleor/graphql/order/mutations/orders.py
@@ -8,6 +8,7 @@
 from ....order.actions import (
     cancel_order,
     clean_mark_order_as_paid,
+    handle_fully_paid_order,
     mark_order_as_paid,
     order_captured,
     order_refunded,
@@ -391,8 +392,9 @@ def perform_mutation(cls, _root, info, amount, **data):
         try_payment_action(
             order, info.context.user, payment, gateway.capture, payment, amount
         )
-
         order_captured(order, info.context.user, amount, payment)
+        if order.is_fully_paid():
+            handle_fully_paid_order(order)
         return OrderCapture(order=order)
 
 
diff --git a/saleor/graphql/order/tests/test_order.py b/saleor/graphql/order/tests/test_order.py
index 6f1cef38d12..3b204368a78 100644
--- a/saleor/graphql/order/tests/test_order.py
+++ b/saleor/graphql/order/tests/test_order.py
@@ -1576,16 +1576,12 @@ def test_order_capture(
     assert data["isPaid"]
     assert data["totalCaptured"]["amount"] == float(amount)
 
-    event_order_paid = order.events.first()
-    assert event_order_paid.type == order_events.OrderEvents.ORDER_FULLY_PAID
-    assert event_order_paid.user is None
+    from pprint import pprint
+
+    pprint(order.events.all())
+
+    event_captured, event_order_fully_paid, event_email_sent = order.events.all()
 
-    event_email_sent, event_captured = list(order.events.all())[-2:]
-    assert event_email_sent.user is None
-    assert event_email_sent.parameters == {
-        "email": order.user_email,
-        "email_type": order_events.OrderEventsEmails.PAYMENT,
-    }
     assert event_captured.type == order_events.OrderEvents.PAYMENT_CAPTURED
     assert event_captured.user == staff_user
     assert event_captured.parameters == {
@@ -1594,6 +1590,15 @@ def test_order_capture(
         "payment_id": "",
     }
 
+    assert event_order_fully_paid.type == order_events.OrderEvents.ORDER_FULLY_PAID
+    assert event_order_fully_paid.user is None
+
+    assert event_email_sent.user is None
+    assert event_email_sent.parameters == {
+        "email": order.user_email,
+        "email_type": order_events.OrderEventsEmails.PAYMENT,
+    }
+
 
 def test_paid_order_mark_as_paid(
     staff_api_client, permission_manage_orders, payment_txn_preauth
diff --git a/saleor/graphql/schema.graphql b/saleor/graphql/schema.graphql
index 0654df51617..e80e1b9219a 100644
--- a/saleor/graphql/schema.graphql
+++ b/saleor/graphql/schema.graphql
@@ -2753,6 +2753,7 @@ enum OrderEventsEnum {
   ORDER_FULLY_PAID
   UPDATED_ADDRESS
   EMAIL_SENT
+  PAYMENT_AUTHORIZED
   PAYMENT_CAPTURED
   PAYMENT_REFUNDED
   PAYMENT_VOIDED
diff --git a/saleor/order/__init__.py b/saleor/order/__init__.py
index 30b7055ae25..7bc227786de 100644
--- a/saleor/order/__init__.py
+++ b/saleor/order/__init__.py
@@ -46,6 +46,7 @@ class OrderEvents:
 
     EMAIL_SENT = "email_sent"
 
+    PAYMENT_AUTHORIZED = "payment_authorized"
     PAYMENT_CAPTURED = "payment_captured"
     PAYMENT_REFUNDED = "payment_refunded"
     PAYMENT_VOIDED = "payment_voided"
@@ -72,6 +73,7 @@ class OrderEvents:
         (ORDER_FULLY_PAID, "The order was fully paid"),
         (UPDATED_ADDRESS, "The address from the placed order was updated"),
         (EMAIL_SENT, "The email was sent"),
+        (PAYMENT_AUTHORIZED, "The payment was authorized"),
         (PAYMENT_CAPTURED, "The payment was captured"),
         (PAYMENT_REFUNDED, "The payment was refunded"),
         (PAYMENT_VOIDED, "The payment was voided"),
diff --git a/saleor/order/actions.py b/saleor/order/actions.py
index cad541ae359..c6b89654776 100644
--- a/saleor/order/actions.py
+++ b/saleor/order/actions.py
@@ -33,14 +33,26 @@ def order_created(order: "Order", user: "User", from_draft: bool = False):
     events.order_created_event(order=order, user=user, from_draft=from_draft)
     manager = get_plugins_manager()
     manager.order_created(order)
+    payment = order.get_last_payment()
+    if payment:
+        if order.is_captured():
+            order_captured(
+                order=order, user=user, amount=payment.total, payment=payment
+            )
+        elif order.is_pre_authorized():
+            order_authorized(
+                order=order, user=user, amount=payment.total, payment=payment
+            )
+        if order.is_fully_paid():
+            handle_fully_paid_order(order=order, user=user)
 
 
-def handle_fully_paid_order(order: "Order"):
-    events.order_fully_paid_event(order=order)
+def handle_fully_paid_order(order: "Order", user: "User" = None):
+    events.order_fully_paid_event(order=order, user=user)
 
     if order.get_customer_email():
         events.email_sent_event(
-            order=order, user=None, email_type=events.OrderEventsEmails.PAYMENT
+            order=order, user=user, email_type=events.OrderEventsEmails.PAYMENT
         )
         send_payment_confirmation.delay(order.pk)
 
@@ -116,6 +128,15 @@ def order_shipping_updated(order: "Order"):
     get_plugins_manager().order_updated(order)
 
 
+def order_authorized(
+    order: "Order", user: "User", amount: "Decimal", payment: "Payment"
+):
+    events.payment_authorized_event(
+        order=order, user=user, amount=amount, payment=payment
+    )
+    get_plugins_manager().order_updated(order)
+
+
 def order_captured(order: "Order", user: "User", amount: "Decimal", payment: "Payment"):
     events.payment_captured_event(
         order=order, user=user, amount=amount, payment=payment
diff --git a/saleor/order/events.py b/saleor/order/events.py
index b8194577ea5..e08ba1234f9 100644
--- a/saleor/order/events.py
+++ b/saleor/order/events.py
@@ -140,8 +140,25 @@ def order_manually_marked_as_paid_event(*, order: Order, user: UserType) -> Orde
     )
 
 
-def order_fully_paid_event(*, order: Order) -> OrderEvent:
-    return OrderEvent.objects.create(order=order, type=OrderEvents.ORDER_FULLY_PAID)
+def order_fully_paid_event(*, order: Order, user: UserType) -> OrderEvent:
+    if not _user_is_valid(user):
+        user = None
+    return OrderEvent.objects.create(
+        order=order, type=OrderEvents.ORDER_FULLY_PAID, user=user
+    )
+
+
+def payment_authorized_event(
+    *, order: Order, user: UserType, amount: Decimal, payment: Payment
+) -> OrderEvent:
+    if not _user_is_valid(user):
+        user = None
+    return OrderEvent.objects.create(
+        order=order,
+        type=OrderEvents.PAYMENT_AUTHORIZED,
+        user=user,
+        **_get_payment_data(amount, payment),
+    )
 
 
 def payment_captured_event(
diff --git a/saleor/order/migrations/0084_auto_20200522_0522.py b/saleor/order/migrations/0084_auto_20200522_0522.py
new file mode 100644
index 00000000000..f756b17c10d
--- /dev/null
+++ b/saleor/order/migrations/0084_auto_20200522_0522.py
@@ -0,0 +1,44 @@
+# Generated by Django 3.0.6 on 2020-05-22 10:22
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("order", "0083_merge_20200421_0529"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="orderevent",
+            name="type",
+            field=models.CharField(
+                choices=[
+                    ("DRAFT_CREATED", "draft_created"),
+                    ("DRAFT_ADDED_PRODUCTS", "draft_added_products"),
+                    ("DRAFT_REMOVED_PRODUCTS", "draft_removed_products"),
+                    ("PLACED", "placed"),
+                    ("PLACED_FROM_DRAFT", "placed_from_draft"),
+                    ("OVERSOLD_ITEMS", "oversold_items"),
+                    ("CANCELED", "canceled"),
+                    ("ORDER_MARKED_AS_PAID", "order_marked_as_paid"),
+                    ("ORDER_FULLY_PAID", "order_fully_paid"),
+                    ("UPDATED_ADDRESS", "updated_address"),
+                    ("EMAIL_SENT", "email_sent"),
+                    ("PAYMENT_AUTHORIZED", "payment_authorized"),
+                    ("PAYMENT_CAPTURED", "payment_captured"),
+                    ("PAYMENT_REFUNDED", "payment_refunded"),
+                    ("PAYMENT_VOIDED", "payment_voided"),
+                    ("PAYMENT_FAILED", "payment_failed"),
+                    ("FULFILLMENT_CANCELED", "fulfillment_canceled"),
+                    ("FULFILLMENT_RESTOCKED_ITEMS", "fulfillment_restocked_items"),
+                    ("FULFILLMENT_FULFILLED_ITEMS", "fulfillment_fulfilled_items"),
+                    ("TRACKING_UPDATED", "tracking_updated"),
+                    ("NOTE_ADDED", "note_added"),
+                    ("OTHER", "other"),
+                ],
+                max_length=255,
+            ),
+        ),
+    ]
diff --git a/saleor/order/models.py b/saleor/order/models.py
index 0f7420c76f8..a4cefa1b21d 100644
--- a/saleor/order/models.py
+++ b/saleor/order/models.py
@@ -244,6 +244,15 @@ def is_pre_authorized(self):
             .exists()
         )
 
+    def is_captured(self):
+        return (
+            self.payments.filter(
+                is_active=True, transactions__kind=TransactionKind.CAPTURE
+            )
+            .filter(transactions__is_success=True)
+            .exists()
+        )
+
     @property
     def quantity_fulfilled(self):
         return sum([line.quantity_fulfilled for line in self])
diff --git a/saleor/payment/utils.py b/saleor/payment/utils.py
index 9f7d422afe0..0b158bc5a5f 100644
--- a/saleor/payment/utils.py
+++ b/saleor/payment/utils.py
@@ -9,7 +9,6 @@
 
 from ..account.models import User
 from ..checkout.models import Checkout
-from ..order.actions import handle_fully_paid_order
 from ..order.models import Order
 from . import ChargeStatus, GatewayError, PaymentError, TransactionKind
 from .error_codes import PaymentErrorCode
@@ -221,9 +220,6 @@ def gateway_postprocess(transaction, payment):
             payment.charge_status = ChargeStatus.FULLY_CHARGED
 
         payment.save(update_fields=["charge_status", "captured_amount", "modified"])
-        order = payment.order
-        if order and order.is_fully_paid():
-            handle_fully_paid_order(order)
 
     elif transaction_kind == TransactionKind.VOID:
         payment.is_active = False
diff --git a/saleor/tests/fixtures.py b/saleor/tests/fixtures.py
index bd5ee75a815..1cf9ffd2159 100644
--- a/saleor/tests/fixtures.py
+++ b/saleor/tests/fixtures.py
@@ -1281,7 +1281,7 @@ def order_with_lines(order, product_type, category, shipping_zone, warehouse):
     )
 
     order.shipping_address = order.billing_address.get_copy()
-    method = shipping_zone.shipping_methods.get()
+    method = shipping_zone.shipping_methods.first()
     order.shipping_method_name = method.name
     order.shipping_method = method
 


Code review comments:
diff_hunk: @@ -1576,16 +1576,12 @@ def test_order_capture(
     assert data["isPaid"]
     assert data["totalCaptured"]["amount"] == float(amount)
 
-    event_order_paid = order.events.first()
-    assert event_order_paid.type == order_events.OrderEvents.ORDER_FULLY_PAID
-    assert event_order_paid.user is None
+    from pprint import pprint
+
+    pprint(order.events.all())
path: saleor/graphql/order/tests/test_order.py
text: @user1:
Print :) 

@author:
Done