You are here

public function ExpressCheckout::onNotify in Commerce PayPal 8

Processes the notification 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).

Note: This method can't throw exceptions on failure because some payment providers expect an error response to be returned in that case. Therefore, the method can log the error itself and then choose which response to return.

Parameters

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

Return value

\Symfony\Component\HttpFoundation\Response|null The response, or NULL to return an empty HTTP 200 response.

Overrides OffsitePaymentGatewayBase::onNotify

File

src/Plugin/Commerce/PaymentGateway/ExpressCheckout.php, line 368

Class

ExpressCheckout
Provides the Paypal Express Checkout payment gateway.

Namespace

Drupal\commerce_paypal\Plugin\Commerce\PaymentGateway

Code

public function onNotify(Request $request) {

  // Get IPN request data and basic processing for the IPN request.
  $ipn_data = $this->ipnHandler
    ->process($request);

  // Do not perform any processing on EC transactions here that do not have
  // transaction IDs, indicating they are non-payment IPNs such as those used
  // for subscription signup requests.
  if (empty($ipn_data['txn_id'])) {
    $this->logger
      ->alert('The IPN request does not have a transaction id. Ignored.');
    return FALSE;
  }

  // Exit when we don't get a payment status we recognize.
  if (!in_array($ipn_data['payment_status'], [
    'Voided',
    'Pending',
    'Completed',
    'Refunded',
  ])) {
    throw new BadRequestHttpException('Invalid payment status');
  }
  $payment_storage = $this->entityTypeManager
    ->getStorage('commerce_payment');
  $amount = new Price($ipn_data['mc_gross'], $ipn_data['mc_currency']);

  // If this is a prior authorization capture IPN...
  if (in_array($ipn_data['payment_status'], [
    'Voided',
    'Pending',
    'Completed',
  ]) && !empty($ipn_data['auth_id'])) {

    // Ensure we can load the existing corresponding transaction.
    $payment = $payment_storage
      ->loadByRemoteId($ipn_data['auth_id']);

    // If not, bail now because authorization transactions should be created
    // by the Express Checkout API request itself.
    if (!$payment) {
      $this->logger
        ->warning('IPN for Order @order_number ignored: authorization transaction already created.', [
        '@order_number' => $ipn_data['invoice'],
      ]);
      return FALSE;
    }
    $payment
      ->setAmount($amount);
    $payment
      ->setState($this
      ->getStatusMapping($ipn_data['payment_status']));

    // Update the remote id.
    $payment
      ->setRemoteId($ipn_data['txn_id']);
  }
  elseif ($ipn_data['payment_status'] == 'Refunded') {

    // Get the corresponding parent transaction and refund it.
    $payment = $payment_storage
      ->loadByRemoteId($ipn_data['txn_id']);
    if (!$payment) {
      $this->logger
        ->warning('IPN for Order @order_number ignored: the transaction to be refunded does not exist.', [
        '@order_number' => $ipn_data['invoice'],
      ]);
      return FALSE;
    }
    elseif ($payment
      ->getState() == 'refunded') {
      $this->logger
        ->warning('IPN for Order @order_number ignored: the transaction is already refunded.', [
        '@order_number' => $ipn_data['invoice'],
      ]);
      return FALSE;
    }
    $amount = new Price((string) $ipn_data['mc_gross'], $ipn_data['mc_currency']);

    // Check if the Refund is partial or full.
    $old_refunded_amount = $payment
      ->getRefundedAmount();
    $new_refunded_amount = $old_refunded_amount
      ->add($amount);
    if ($new_refunded_amount
      ->lessThan($payment
      ->getAmount())) {
      $payment
        ->setState('partially_refunded');
    }
    else {
      $payment
        ->setState('refunded');
    }
    $payment
      ->setRefundedAmount($new_refunded_amount);
  }
  if (isset($payment)) {
    $payment
      ->setRemoteState($ipn_data['payment_status']);
    $payment
      ->save();
  }
}