You are here

public function Checkout::createPayment in Commerce PayPal 8

Creates a payment.

Parameters

\Drupal\commerce_payment\Entity\PaymentInterface $payment: The payment.

bool $capture: Whether the created payment should be captured (VS authorized only). Allowed to be FALSE only if the plugin supports authorizations.

Throws

\InvalidArgumentException If $capture is FALSE but the plugin does not support authorizations.

\Drupal\commerce_payment\Exception\PaymentGatewayException Thrown when the transaction fails for any reason.

Overrides SupportsStoredPaymentMethodsInterface::createPayment

1 call to Checkout::createPayment()
Checkout::onReturn in src/Plugin/Commerce/PaymentGateway/Checkout.php
Processes the "return" request.

File

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

Class

Checkout
Provides the PayPal Checkout payment gateway.

Namespace

Drupal\commerce_paypal\Plugin\Commerce\PaymentGateway

Code

public function createPayment(PaymentInterface $payment, $capture = TRUE) {
  $payment_method = $payment
    ->getPaymentMethod();
  if (!$payment_method || empty($payment_method
    ->getRemoteId())) {
    throw new PaymentGatewayException('Cannot create the payment without the PayPal order ID.');
  }
  $sdk = $this->checkoutSdkFactory
    ->get($this->configuration);
  $order = $payment
    ->getOrder();
  $checkout_data = $order
    ->getData('commerce_paypal_checkout', [
    'flow' => '',
  ]);
  $remote_id = $payment_method
    ->getRemoteId();
  try {

    // Ensure the PayPal order is up to date and in sync with Drupal.
    $sdk
      ->updateOrder($remote_id, $order);
    $request = $sdk
      ->getOrder($remote_id);
    $paypal_order = Json::decode($request
      ->getBody());
  } catch (BadResponseException $exception) {
    throw new PaymentGatewayException($exception
      ->getMessage());
  }

  // When in the "shortcut" flow, the PayPal order status is expected to be
  // "approved".
  if ($checkout_data['flow'] === 'shortcut' && !in_array($paypal_order['status'], [
    'APPROVED',
    'SAVED',
  ])) {
    throw new PaymentGatewayException(sprintf('Wrong remote order status. Expected: "approved"|"saved", Actual: %s.', $paypal_order['status']));
  }
  $intent = $checkout_data['intent'] ?? $this->configuration['intent'];
  try {
    if ($intent == 'capture') {
      $response = $sdk
        ->captureOrder($remote_id);
      $paypal_order = Json::decode($response
        ->getBody()
        ->getContents());
      $remote_payment = $paypal_order['purchase_units'][0]['payments']['captures'][0];
      $payment
        ->setRemoteId($remote_payment['id']);
    }
    else {
      $response = $sdk
        ->authorizeOrder($remote_id);
      $paypal_order = Json::decode($response
        ->getBody()
        ->getContents());
      $remote_payment = $paypal_order['purchase_units'][0]['payments']['authorizations'][0];
      if (isset($remote_payment['expiration_time'])) {
        $expiration = new \DateTime($remote_payment['expiration_time']);
        $payment
          ->setExpiresTime($expiration
          ->getTimestamp());
      }
    }
  } catch (BadResponseException $exception) {
    throw new PaymentGatewayException($exception
      ->getMessage());
  }
  $remote_state = strtolower($remote_payment['status']);
  if (in_array($remote_state, [
    'denied',
    'expired',
    'declined',
  ])) {
    throw new HardDeclineException(sprintf('Could not %s the payment for order %s. Remote payment state: %s', $intent, $order
      ->id(), $remote_state));
  }
  $state = $this
    ->mapPaymentState($intent, $remote_state);

  // If we couldn't find a state to map to, stop here.
  if (!$state) {
    $this->logger
      ->debug('PayPal remote payment debug: <pre>@remote_payment</pre>', [
      '@remote_payment' => print_r($remote_payment, TRUE),
    ]);
    throw new PaymentGatewayException(sprintf('The PayPal payment is in a state we cannot handle. Remote state: %s.', $remote_state));
  }

  // Special handling of the "pending" state, if the order is "pending review"
  // we allow the order to go "through" to give a chance to the merchant
  // to accept the payment, in case manual review is needed.
  if ($state === 'pending' && $remote_state === 'pending') {
    $reason = $remote_payment['status_details']['reason'];
    if ($reason === 'PENDING_REVIEW') {
      $state = 'authorization';
    }
    else {
      throw new PaymentGatewayException(sprintf('The PayPal payment is pending. Reason: %s.', $reason));
    }
  }
  $payment_amount = Price::fromArray([
    'number' => $remote_payment['amount']['value'],
    'currency_code' => $remote_payment['amount']['currency_code'],
  ]);
  $payment
    ->setAmount($payment_amount);
  $payment
    ->setState($state);
  $payment
    ->setRemoteId($remote_payment['id']);
  $payment
    ->setRemoteState($remote_state);
  $payment
    ->save();
}