You are here

commerce_paypal.module in Commerce PayPal 8

Same filename and directory in other branches
  1. 7.2 commerce_paypal.module
  2. 7 commerce_paypal.module

Implements PayPal payment services for use with Drupal Commerce.

File

commerce_paypal.module
View source
<?php

/**
 * @file
 * Implements PayPal payment services for use with Drupal Commerce.
 */
use Drupal\commerce_checkout\Entity\CheckoutFlowInterface;
use Drupal\commerce_payment\Entity\PaymentGateway;
use Drupal\commerce_paypal\Plugin\Commerce\PaymentGateway\CheckoutInterface;
use Drupal\commerce_price\Calculator;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Implements hook_theme().
 */
function commerce_paypal_theme() {
  $theme = [
    'commerce_paypal_checkout_custom_card_fields' => [
      'variables' => [],
    ],
  ];
  return $theme;
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function commerce_paypal_form_views_form_commerce_cart_form_default_alter(&$form, FormStateInterface $form_state, $form_id) {

  /** @var \Drupal\views\ViewExecutable $view */
  $view = reset($form_state
    ->getBuildInfo()['args']);

  // Only add the smart payment buttons if the cart form view has order items.
  if (empty($view->result)) {
    return;
  }
  $entity_type_manager = \Drupal::entityTypeManager();
  $order_id = $view->args[0];

  /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
  $order = $entity_type_manager
    ->getStorage('commerce_order')
    ->load($order_id);

  // Skip injecting the smart payment buttons if the order total is zero or
  // negative.
  if (!$order
    ->getTotalPrice() || !$order
    ->getTotalPrice()
    ->isPositive()) {
    return;
  }

  /** @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $payment_gateway_storage */
  $payment_gateway_storage = $entity_type_manager
    ->getStorage('commerce_payment_gateway');

  // Load the payment gateways. This fires an event for filtering the
  // available gateways, and then evaluates conditions on all remaining ones.
  $payment_gateways = $payment_gateway_storage
    ->loadMultipleForOrder($order);

  // Can't proceed without any payment gateways.
  if (empty($payment_gateways)) {
    return;
  }
  foreach ($payment_gateways as $payment_gateway) {
    $payment_gateway_plugin = $payment_gateway
      ->getPlugin();
    if (!$payment_gateway_plugin instanceof CheckoutInterface) {
      continue;
    }
    $config = $payment_gateway_plugin
      ->getConfiguration();

    // We only inject the Smart payment buttons on the cart page if the
    // configured payment solution is "smart_payment_buttons" and if the
    // "enable_on_cart" setting is TRUE.
    if ($payment_gateway_plugin
      ->getPaymentSolution() !== 'smart_payment_buttons' || !$config['enable_on_cart']) {
      continue;
    }

    /** @var \Drupal\commerce_paypal\SmartPaymentButtonsBuilderInterface $builder */
    $builder = \Drupal::service('commerce_paypal.smart_payment_buttons_builder');
    $form['paypal_smart_payment_buttons'] = $builder
      ->build($order, $payment_gateway, FALSE);
    break;
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter() for commerce_checkout_flow.
 */
function commerce_paypal_form_commerce_checkout_flow_alter(&$form, FormStateInterface $form_state) {

  /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
  $order = \Drupal::routeMatch()
    ->getParameter('commerce_order');

  // Loop over the payment methods to remove potentially duplicate PayPal
  // options (See http://www.drupal.org/project/commerce_paypal/issues/3154770).
  if (isset($form['payment_information']['payment_method'], $form['payment_information']['#payment_options'])) {

    /** @var \Drupal\commerce_payment\PaymentOption $payment_option */
    $paypal_checkout_options_count = 0;
    foreach ($form['payment_information']['#payment_options'] as $key => $payment_option) {

      /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */
      $payment_gateway = PaymentGateway::load($payment_option
        ->getPaymentGatewayId());
      $plugin = $payment_gateway
        ->getPlugin();
      if ($plugin instanceof CheckoutInterface && $plugin
        ->getPaymentSolution() === 'smart_payment_buttons') {
        $paypal_checkout_options_count++;

        // This will ensure we only keep the first paypal checkout option found.
        if ($paypal_checkout_options_count > 1 && isset($form['payment_information']['payment_method']['#options'][$key])) {
          unset($form['payment_information']['payment_method']['#options'][$key]);
        }
      }
    }
  }

  // Inject the Smart payment buttons on the review page.
  if ($form['#step_id'] !== 'review') {
    return;
  }
  if ($order
    ->get('payment_gateway')
    ->isEmpty() || !$order
    ->get('payment_gateway')->entity || $order
    ->get('checkout_flow')->target_id === 'paypal_checkout') {
    return;
  }

  // Skip injecting the smart payment buttons if the order total is zero or
  // negative.
  if (!$order
    ->getTotalPrice() || !$order
    ->getTotalPrice()
    ->isPositive()) {
    return;
  }

  /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */
  $payment_gateway = $order
    ->get('payment_gateway')->entity;
  $payment_gateway_plugin = $payment_gateway
    ->getPlugin();
  if (!$payment_gateway_plugin instanceof CheckoutInterface || $payment_gateway_plugin
    ->getPaymentSolution() !== 'smart_payment_buttons') {
    return;
  }

  /** @var \Drupal\commerce_paypal\SmartPaymentButtonsBuilderInterface $builder */
  $builder = \Drupal::service('commerce_paypal.smart_payment_buttons_builder');
  $form['paypal_smart_payment_buttons'] = $builder
    ->build($order, $payment_gateway, TRUE);
  $form['actions']['#access'] = FALSE;

  // Put back the "go back" link.
  if (isset($form['actions']['next']['#suffix'])) {
    $form['paypal_smart_payment_buttons']['#suffix'] = $form['actions']['next']['#suffix'];
  }
}

/**
 * Implements hook_ENTITY_TYPE_access().
 *
 * Forbids the "paypal_checkout" checkout flow from being deletable.
 */
function commerce_paypal_commerce_checkout_flow_access(CheckoutFlowInterface $checkout_flow, $operation, AccountInterface $account) {
  if ($checkout_flow
    ->id() === 'paypal_checkout' && $operation === 'delete') {
    return AccessResult::forbidden();
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_library_info_build().
 */
function commerce_paypal_library_info_build() {

  // Only build the PayPal Credit messaging JS if a PayPal Client ID was set on
  // the PayPal Credit messaging settings form.
  $client_id = \Drupal::config('commerce_paypal.credit_messaging_settings')
    ->get('client_id');
  if (!$client_id) {
    return [];
  }
  $url = sprintf('https://www.paypal.com/sdk/js?client-id=%s&components=messages', $client_id);
  $libraries['credit_messaging'] = [
    'header' => TRUE,
    'js' => [
      $url => [
        'type' => 'external',
        'attributes' => [
          'data-partner-attribution-id' => 'CommerceGuys_Cart_SPB',
        ],
      ],
    ],
  ];
  return $libraries;
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function commerce_paypal_form_commerce_order_item_add_to_cart_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Check to see if PayPal Credit messaging is enabled on Add to Cart forms.
  $enable_messaging = \Drupal::config('commerce_paypal.credit_messaging_settings')
    ->get('add_to_cart');

  /** @var \Drupal\commerce_order\Entity\OrderItemInterface $order_item */
  $order_item = $form_state
    ->getFormObject()
    ->getEntity();
  if (!$enable_messaging || !$order_item
    ->getUnitPrice()) {
    return;
  }

  // Add Credit Messaging JS to the form.
  // @todo ensure messaging reapplies after an Ajax refresh.
  $form['#attached']['library'][] = 'commerce_paypal/credit_messaging';
  $form['paypal_credit_messaging_product'] = [
    '#type' => 'html_tag',
    '#tag' => 'div',
    '#attributes' => [
      'data-pp-message' => '',
      'data-pp-placement' => 'product',
      'data-pp-amount' => Calculator::trim($order_item
        ->getUnitPrice()
        ->getNumber()),
    ],
    '#weight' => 1,
  ];
}

/**
 * Implements hook_js_alter().
 */
function commerce_paypal_js_alter(&$javascript, \Drupal\Core\Asset\AttachedAssetsInterface $assets) {
  $client_id = \Drupal::config('commerce_paypal.credit_messaging_settings')
    ->get('client_id');
  if (!$client_id) {
    return;
  }
  $paypal_checkout_js = drupal_get_path('module', 'commerce_paypal') . '/js/paypal-checkout.js';

  // The paypal-checkout JS file isn't present, no need to do anything.
  if (!isset($javascript[$paypal_checkout_js])) {
    return;
  }

  // Remove the extra JS SDK added for credit messaging library if present.
  foreach ($javascript as $key => $js) {
    if (strpos($key, 'https://www.paypal.com/sdk/js') === 0) {
      unset($javascript[$key]);
      break;
    }
  }
}