You are here

public function Checkout::onReturn in Commerce PayPal 8

Processes the "return" request.

This method should only be concerned with creating/completing payments, the parent order does not need to be touched. The order state is updated automatically when the order is paid in full, or manually by the merchant (via the admin UI).

Parameters

\Drupal\commerce_order\Entity\OrderInterface $order: The order.

\Symfony\Component\HttpFoundation\Request $request: The request.

Throws

\Drupal\commerce_payment\Exception\PaymentGatewayException Thrown when the request is invalid or the payment failed.

Overrides OffsitePaymentGatewayBase::onReturn

File

src/Plugin/Commerce/PaymentGateway/Checkout.php, line 612

Class

Checkout
Provides the PayPal Checkout payment gateway.

Namespace

Drupal\commerce_paypal\Plugin\Commerce\PaymentGateway

Code

public function onReturn(OrderInterface $order, Request $request) {
  try {
    $sdk = $this->checkoutSdkFactory
      ->get($this->configuration);
    $paypal_request = $sdk
      ->getOrder($order
      ->getData('paypal_order_id'));
    $paypal_order = Json::decode($paypal_request
      ->getBody());
  } catch (BadResponseException $exception) {
    throw new PaymentGatewayException('Could not load the order from PayPal.');
  }
  $paypal_amount = $paypal_order['purchase_units'][0]['amount'];
  $paypal_total = Price::fromArray([
    'number' => $paypal_amount['value'],
    'currency_code' => $paypal_amount['currency_code'],
  ]);

  // Make sure the order total matches the total we get from PayPal.
  if (!$paypal_total
    ->equals($order
    ->getTotalPrice())) {
    throw new PaymentGatewayException('The PayPal order total does not match the order total.');
  }
  if (!in_array($paypal_order['status'], [
    'APPROVED',
    'SAVED',
  ])) {
    throw new PaymentGatewayException(sprintf('Unexpected PayPal order status %s.', $paypal_order['status']));
  }
  $flow = $order
    ->getData('paypal_checkout_flow');
  $order
    ->setData('commerce_paypal_checkout', [
    'remote_id' => $paypal_order['id'],
    'flow' => $flow,
    'intent' => strtolower($paypal_order['intent']),
  ]);
  if (empty($order
    ->getEmail())) {
    $order
      ->setEmail($paypal_order['payer']['email_address']);
  }
  if ($this->configuration['update_billing_profile']) {
    $this
      ->updateProfile($order, 'billing', $paypal_order);
  }
  if (!empty($this->configuration['update_shipping_profile']) && $order
    ->hasField('shipments')) {
    $this
      ->updateProfile($order, 'shipping', $paypal_order);
  }
  $payment_method = NULL;

  // If a payment method is already referenced by the order, no need to create
  // a new one.
  if (!$order
    ->get('payment_method')
    ->isEmpty()) {

    /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */
    $payment_method = $order
      ->get('payment_method')->entity;
  }

  // If the order doesn't reference a payment method yet, or if the payment
  // method doesn't reference the right gateway, create a new one.
  if (!$payment_method || $payment_method
    ->getPaymentGatewayId() !== $this->parentEntity
    ->id()) {

    // Create a payment method.
    $payment_method_storage = $this->entityTypeManager
      ->getStorage('commerce_payment_method');
    assert($payment_method_storage instanceof PaymentMethodStorageInterface);
    $payment_method = $payment_method_storage
      ->createForCustomer('paypal_checkout', $this->parentEntity
      ->id(), $order
      ->getCustomerId(), $order
      ->getBillingProfile());
  }
  $payment_method
    ->setRemoteId($paypal_order['id']);
  $payment_method
    ->setReusable(FALSE);
  $payment_method
    ->save();
  $order
    ->set('payment_method', $payment_method);
  if ($flow === 'shortcut' && $order
    ->hasField('checkout_flow')) {

    // Force the checkout flow to PayPal checkout which is the flow the module
    // defines for the "shortcut" flow.
    $order
      ->set('checkout_flow', 'paypal_checkout');
    $order
      ->set('checkout_step', NULL);
  }

  // For the "mark" flow, create the payment right away (if not configured
  // to be skipped).
  if ($flow === 'mark' && !$request->query
    ->has('skip_payment_creation')) {
    $payment_storage = $this->entityTypeManager
      ->getStorage('commerce_payment');

    /** @var \Drupal\commerce_payment\Entity\PaymentInterface $payment */
    $payment = $payment_storage
      ->create([
      'state' => 'new',
      'amount' => $order
        ->getBalance(),
      'payment_gateway' => $this->parentEntity
        ->id(),
      'payment_method' => $payment_method
        ->id(),
      'order_id' => $order
        ->id(),
    ]);
    $this
      ->createPayment($payment);
  }
}