How does a PaymentGateway signal a completed order I'm trying to implement the Commerce Ingenico payment gateway for a Drupal 8 Commerce site. As this module is still in unfinished for Drupal 8, I'm fixing it up as I go. Most of the implementation is located in Composer packages, so it's mostly plumbing.

I'm hitting a bit of a wall now though. I'm using the off-site payment gateway, and I've configured the IPN (server to server notification). The values for the payment in the database look good*, but the Cart never turns into an Order. Somehow the trigger for signaling a completed payment to the order is missing. 

The results is that after returning from the offsite payment (and the onNotify has already been triggered, so the payment is marked as completed), I end up back at the start of the payment flow; the order summary.

How are these parts supposed to fit together?

Here's the code to the onNotify method in the PaymentGateway:

public function onNotify(Request $request) {
    parent::onNotify($request);

    // Common response processing for both redirect back and async notification.
    $payment = $this-&gt;processFeedback($request);

    // Let's also update payment state here - it's safer doing it from received
    // asynchronous notification rather than from the redirect back from the
    // off-site redirect.
    $state = $request-&gt;query-&gt;get('STATUS') == PaymentResponse::STATUS_AUTHORISED ? 'authorization' : 'completed';
    $payment-&gt;setState($state);
    $payment-&gt;setAuthorizedTime(REQUEST_TIME);
    if ($request-&gt;query-&gt;get('STATUS') != PaymentResponse::STATUS_AUTHORISED) {
      // setCapturedTime has been relegated to the mists of history
      $payment-&gt;setCompletedTime(REQUEST_TIME);
      // $payment-&gt;setCapturedTime(REQUEST_TIME);
    }
    $payment-&gt;save();
  }


And this is the resultant row in the commerce_payment table:

 payment_id
13

type
payment_default

uuid
payment_default

payment_gateway
ogone_offsite

payment_gateway_mode
test

payment_method
NULL

order_id
29

remote_id
3039111240

remote_state
9

amount__number
26.400000

amount__currency_code
EUR

refunded_amount__number
0.000000

refunded_amount__currency_code
EUR

state
completed

authorized
1534516744

expires
0

completed
1534516744

test
NULL

captured
NULL



Of course I don't know what these values are supposed to look like. I'm actually a little concerned about payment_method being NULL in this row. Is that normal for an offsite payment gateway, or should that be set to something?
A similar scenario has been discussed on #commerce Slack on Monday. In contrast to onReturn() no transition is triggered after onNotify().
@bojanz says:

it's a safeguard we'll need to add to Commerce, but won't have the resources to do so before october
once we have $order-&gt;balance and an order paid in full event, we can use it to auto-place the order
at least those two parts could land in september for 2.10


TL;DR
onReturn() must create a Payment, as suggested in a comment from @No Sssweat. In case PaymentGatewayException is thrown Commerce will redirect to the previous checkout step.
onNotify() can register info about payment but, at the current state, it won't place the order for you.