You are here

class PayPalWebsitePaymentsPro in Ubercart 8.4

Defines the PayPal Website Payments Pro payment method.

Plugin annotation


@UbercartPaymentMethod(
  id = "paypal_wpp",
  name = @Translation("PayPal Website Payments Pro"),
)

Hierarchy

Expanded class hierarchy of PayPalWebsitePaymentsPro

File

payment/uc_paypal/src/Plugin/Ubercart/PaymentMethod/PayPalWebsitePaymentsPro.php, line 21

Namespace

Drupal\uc_paypal\Plugin\Ubercart\PaymentMethod
View source
class PayPalWebsitePaymentsPro extends CreditCardPaymentMethodBase {

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return parent::defaultConfiguration() + [
      'wps_email' => '',
      'wpp_server' => 'https://api-3t.sandbox.paypal.com/nvp',
      'api' => [
        'api_username' => '',
        'api_password' => '',
        'api_signature' => '',
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);
    $form['wps_email'] = [
      '#type' => 'email',
      '#title' => $this
        ->t('PayPal e-mail address'),
      '#description' => $this
        ->t('The e-mail address you use for the PayPal account you want to receive payments.'),
      '#default_value' => $this->configuration['wps_email'],
    ];
    $form['wpp_server'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('API server'),
      '#description' => $this
        ->t('Sign up for and use a Sandbox account for testing.'),
      '#options' => [
        'https://api-3t.sandbox.paypal.com/nvp' => $this
          ->t('Sandbox'),
        'https://api-3t.paypal.com/nvp' => $this
          ->t('Live'),
      ],
      '#default_value' => $this->configuration['wpp_server'],
    ];
    $form['api'] = [
      '#type' => 'details',
      '#title' => $this
        ->t('API credentials'),
      '#description' => $this
        ->t('@link for information on obtaining credentials. You need to acquire an API Signature. If you have already requested API credentials, you can review your settings under the API Access section of your PayPal profile.', [
        '@link' => Link::fromTextAndUrl($this
          ->t('Click here'), Url::fromUri('https://developer.paypal.com/docs/classic/api/apiCredentials/'))
          ->toString(),
      ]),
      '#open' => TRUE,
    ];
    $form['api']['api_username'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('API username'),
      '#default_value' => $this->configuration['api']['api_username'],
    ];
    $form['api']['api_password'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('API password'),
      '#default_value' => $this->configuration['api']['api_password'],
    ];
    $form['api']['api_signature'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Signature'),
      '#default_value' => $this->configuration['api']['api_signature'],
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    parent::submitConfigurationForm($form, $form_state);
    $this->configuration['wps_email'] = trim($form_state
      ->getValue('wps_email'));
    $this->configuration['wpp_server'] = $form_state
      ->getValue('wpp_server');
    $this->configuration['api']['api_username'] = $form_state
      ->getValue([
      'settings',
      'api',
      'api_username',
    ]);
    $this->configuration['api']['api_password'] = $form_state
      ->getValue([
      'settings',
      'api',
      'api_password',
    ]);
    $this->configuration['api']['api_signature'] = $form_state
      ->getValue([
      'settings',
      'api',
      'api_signature',
    ]);
  }

  /**
   * {@inheritdoc}
   */
  protected function chargeCard(OrderInterface $order, $amount, $txn_type, $reference = NULL) {
    if ($txn_type == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
      $nvp_request = [
        'METHOD' => 'DoCapture',
        'AUTHORIZATIONID' => $reference,
        'AMT' => uc_currency_format($amount, FALSE, FALSE, '.'),
        'CURRENCYCODE' => $order
          ->getCurrency(),
        'COMPLETETYPE' => 'Complete',
      ];
    }
    else {
      if (intval($order->payment_details['cc_exp_month']) < 10) {
        $expdate = '0' . $order->payment_details['cc_exp_month'] . $order->payment_details['cc_exp_year'];
      }
      else {
        $expdate = $order->payment_details['cc_exp_month'] . $order->payment_details['cc_exp_year'];
      }
      $cc_type = NULL;
      if (isset($order->payment_details['cc_type'])) {
        switch (strtolower($order->payment_details['cc_type'])) {
          case 'amex':
          case 'american express':
            $cc_type = 'Amex';
            break;
          case 'visa':
            $cc_type = 'Visa';
            break;
          case 'mastercard':
          case 'master card':
            $cc_type = 'MasterCard';
            break;
          case 'discover':
            $cc_type = 'Discover';
            break;
        }
      }
      if (is_null($cc_type)) {
        $cc_type = $this
          ->cardType($order->payment_details['cc_number']);
        if ($cc_type === FALSE) {
          $this
            ->messenger()
            ->addError($this
            ->t('The credit card type did not pass validation.'));
          \Drupal::logger('uc_paypal')
            ->error('Could not figure out cc type: @number / @type', [
            '@number' => $order->payment_details['cc_number'],
            '@type' => $order->payment_details['cc_type'],
          ]);
          return [
            'success' => FALSE,
          ];
        }
      }

      // PayPal doesn't accept IPv6 addresses.
      $ip_address = ltrim(\Drupal::request()
        ->getClientIp(), '::ffff:');
      $address = $order
        ->getAddress('billing');
      $nvp_request = [
        'METHOD' => 'DoDirectPayment',
        'PAYMENTACTION' => $txn_type == UC_CREDIT_AUTH_ONLY ? 'Authorization' : 'Sale',
        'IPADDRESS' => $ip_address,
        'AMT' => uc_currency_format($amount, FALSE, FALSE, '.'),
        'CREDITCARDTYPE' => $cc_type,
        'ACCT' => $order->payment_details['cc_number'],
        'EXPDATE' => $expdate,
        'CVV2' => $order->payment_details['cc_cvv'],
        'FIRSTNAME' => substr($address
          ->getFirstName(), 0, 25),
        'LASTNAME' => substr($address
          ->getLastName(), 0, 25),
        'STREET' => substr($address
          ->getStreet1(), 0, 100),
        'STREET2' => substr($address
          ->getStreet2(), 0, 100),
        'CITY' => substr($address
          ->getCity(), 0, 40),
        'STATE' => $address
          ->getZone(),
        'ZIP' => $address
          ->getPostalCode(),
        'COUNTRYCODE' => $address
          ->getCountry(),
        'CURRENCYCODE' => $order
          ->getCurrency(),
        'DESC' => $this
          ->t('Order @order_id at @store', [
          '@order_id' => $order
            ->id(),
          '@store' => uc_store_name(),
        ]),
        'INVNUM' => $order
          ->id() . '-' . REQUEST_TIME,
        'BUTTONSOURCE' => 'Ubercart_ShoppingCart_DP_US',
        'NOTIFYURL' => Url::fromRoute('uc_paypal.ipn', [], [
          'absolute' => TRUE,
        ])
          ->toString(),
        'EMAIL' => substr($order
          ->getEmail(), 0, 127),
        'PHONENUM' => substr($address
          ->getPhone(), 0, 20),
      ];
      if ($order
        ->isShippable()) {
        $address = $order
          ->getAddress('delivery');
        $nvp_request += [
          'SHIPTONAME' => substr($address
            ->getFirstName() . ' ' . $address
            ->getLastName(), 0, 25),
          'SHIPTOSTREET' => substr($address
            ->getStreet1(), 0, 100),
          'SHIPTOSTREET2' => substr($address
            ->getStreet2(), 0, 100),
          'SHIPTOCITY' => substr($address
            ->getCity(), 0, 40),
          'SHIPTOSTATE' => $address
            ->getZone(),
          'SHIPTOZIP' => $address
            ->getPostalCode(),
          'SHIPTOCOUNTRYCODE' => $address
            ->getCountry(),
        ];
      }
    }
    $nvp_response = $this
      ->sendNvpRequest($nvp_request);
    $types = uc_credit_transaction_types();
    switch ($nvp_response['ACK']) {
      case 'SuccessWithWarning':
        \Drupal::logger('uc_paypal')
          ->warning('<b>@type succeeded with a warning.</b>@paypal_message', [
          '@paypal_message' => $this
            ->buildErrorMessages($nvp_response),
          '@type' => $types[$txn_type],
          'link' => $order
            ->toLink($this
            ->t('view order'))
            ->toString(),
        ]);

      // Fall through.
      case 'Success':
        $message = $this
          ->t('<b>@type</b><br /><b>Success: </b>@amount @currency', [
          '@type' => $types[$txn_type],
          '@amount' => uc_currency_format($nvp_response['AMT'], FALSE),
          '@currency' => $nvp_response['CURRENCYCODE'],
        ]);
        if ($txn_type != UC_CREDIT_PRIOR_AUTH_CAPTURE) {
          $message .= '<br />' . $this
            ->t('<b>Address:</b> @avscode', [
            '@avscode' => $this
              ->avscodeMessage($nvp_response['AVSCODE']),
          ]);
          $message .= '<br />' . $this
            ->t('<b>CVV2:</b> @cvvmatch', [
            '@cvvmatch' => $this
              ->cvvmatchMessage($nvp_response['CVV2MATCH']),
          ]);
        }
        $result = [
          'success' => TRUE,
          'comment' => $this
            ->t('PayPal transaction ID: @transactionid', [
            '@transactionid' => $nvp_response['TRANSACTIONID'],
          ]),
          'message' => $message,
          'data' => [
            'module' => 'uc_paypal',
            'txn_id' => (string) SafeMarkup::checkPlain($nvp_response['TRANSACTIONID']),
          ],
          'uid' => \Drupal::currentUser()
            ->id(),
        ];

        // If this was an authorization only transaction...
        if ($txn_type == UC_CREDIT_AUTH_ONLY) {

          // Log the authorization to the order.
          uc_credit_log_authorization($order
            ->id(), $nvp_response['TRANSACTIONID'], $nvp_response['AMT']);
        }
        elseif ($txn_type == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
          uc_credit_log_prior_auth_capture($order
            ->id(), $reference);
        }

        // Log the IPN to the database.
        $this->database
          ->insert('uc_payment_paypal_ipn')
          ->fields([
          'order_id' => $order
            ->id(),
          'txn_id' => $nvp_response['TRANSACTIONID'],
          'txn_type' => 'web_accept',
          'mc_gross' => $amount,
          'status' => 'Completed',
          'payer_email' => $order
            ->getEmail(),
          'received' => REQUEST_TIME,
        ])
          ->execute();
        break;
      case 'FailureWithWarning':

      // Fall through.
      case 'Failure':
        $message = $this
          ->t('<b>@type failed.</b>', [
          '@type' => $types[$txn_type],
        ]) . $this
          ->buildErrorMessages($nvp_response);
        $result = [
          'success' => FALSE,
          'message' => $message,
          'uid' => \Drupal::currentUser()
            ->id(),
        ];
        break;
      default:
        $message = $this
          ->t('Unexpected acknowledgement status: @status', [
          '@status' => $nvp_response['ACK'],
        ]);
        $result = [
          'success' => NULL,
          'message' => $message,
          'uid' => \Drupal::currentUser()
            ->id(),
        ];
        break;
    }
    uc_order_comment_save($order
      ->id(), \Drupal::currentUser()
      ->id(), $message, 'admin');

    // Don't log this as a payment money wasn't actually captured.
    if (in_array($txn_type, [
      UC_CREDIT_AUTH_ONLY,
    ])) {
      $result['log_payment'] = FALSE;
    }
    return $result;
  }

  /**
   * Sends a request to the PayPal NVP API.
   */
  public function sendNvpRequest($params) {
    $host = $this->configuration['wpp_server'];
    $params += [
      'USER' => $this->configuration['api']['api_username'],
      'PWD' => $this->configuration['api']['api_password'],
      'SIGNATURE' => $this->configuration['api']['api_signature'],
      'VERSION' => '3.0',
    ];
    try {
      $response = \Drupal::httpClient()
        ->request('POST', $host, [
        'form_params' => $params,
      ]);
      parse_str($response
        ->getBody(), $output);
      return $output;
    } catch (TransferException $e) {
      \Drupal::logger('uc_paypal')
        ->error('NVP API request failed with HTTP error %error.', [
        '%error' => $e
          ->getMessage(),
      ]);
    }
  }

  /**
   * Builds error message(s) from PayPal failure responses.
   */
  protected function buildErrorMessages($nvp_response) {
    $code = 0;
    $message = '';
    while (array_key_exists('L_SEVERITYCODE' . $code, $nvp_response)) {
      $message .= '<br /><b>' . SafeMarkup::checkPlain($nvp_response['L_SEVERITYCODE' . $code]) . ':</b> ' . SafeMarkup::checkPlain($nvp_response['L_ERRORCODE' . $code]) . ': ' . SafeMarkup::checkPlain($nvp_response['L_LONGMESSAGE' . $code]);
      $code++;
    }
    return $message;
  }

  /**
   * Returns the PayPal approved credit card type for a card number.
   */
  protected function cardType($cc_number) {
    switch (substr(strval($cc_number), 0, 1)) {
      case '3':
        return 'Amex';
      case '4':
        return 'Visa';
      case '5':
        return 'MasterCard';
      case '6':
        return 'Discover';
    }
    return FALSE;
  }

  /**
   * Returns a human readable message for the AVS code.
   */
  protected function avscodeMessage($code) {
    if (is_numeric($code)) {
      switch ($code) {
        case '0':
          return $this
            ->t('All the address information matched.');
        case '1':
          return $this
            ->t('None of the address information matched; transaction declined.');
        case '2':
          return $this
            ->t('Part of the address information matched.');
        case '3':
          return $this
            ->t('The merchant did not provide AVS information. Not processed.');
        case '4':
          return $this
            ->t('Address not checked, or acquirer had no response. Service not available.');
        default:
          return $this
            ->t('No AVS response was obtained.');
      }
    }
    switch ($code) {
      case 'A':
      case 'B':
        return $this
          ->t('Address matched; postal code did not');
      case 'C':
      case 'N':
        return $this
          ->t('Nothing matched; transaction declined');
      case 'D':
      case 'F':
      case 'X':
      case 'Y':
        return $this
          ->t('Address and postal code matched');
      case 'E':
        return $this
          ->t('Not allowed for MOTO transactions; transaction declined');
      case 'G':
        return $this
          ->t('Global unavailable');
      case 'I':
        return $this
          ->t('International unavailable');
      case 'P':
      case 'W':
      case 'Z':
        return $this
          ->t('Postal code matched; address did not');
      case 'R':
        return $this
          ->t('Retry for validation');
      case 'S':
        return $this
          ->t('Service not supported');
      case 'U':
        return $this
          ->t('Unavailable');
      case 'Null':
        return $this
          ->t('No AVS response was obtained.');
      default:
        return $this
          ->t('An unknown error occurred.');
    }
  }

  /**
   * Returns a human readable message for the CVV2 match code.
   */
  protected function cvvmatchMessage($code) {
    if (is_numeric($code)) {
      switch ($code) {
        case '0':
          return $this
            ->t('Matched');
        case '1':
          return $this
            ->t('No match');
        case '2':
          return $this
            ->t('The merchant has not implemented CVV2 code handling.');
        case '3':
          return $this
            ->t('Merchant has indicated that CVV2 is not present on card.');
        case '4':
          return $this
            ->t('Service not available');
        default:
          return $this
            ->t('Unkown error');
      }
    }
    switch ($code) {
      case 'M':
        return $this
          ->t('Match');
      case 'N':
        return $this
          ->t('No match');
      case 'P':
        return $this
          ->t('Not processed');
      case 'S':
        return $this
          ->t('Service not supported');
      case 'U':
        return $this
          ->t('Service not available');
      case 'X':
        return $this
          ->t('No response');
      default:
        return $this
          ->t('Not checked');
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CreditCardPaymentMethodBase::cartDetails public function Returns the form or render array to be displayed at checkout. Overrides PaymentMethodPluginBase::cartDetails
CreditCardPaymentMethodBase::cartProcess public function Called when checkout is submitted with this payment method selected. Overrides PaymentMethodPluginBase::cartProcess
CreditCardPaymentMethodBase::cartReview public function Returns the payment method review details. Overrides PaymentMethodPluginBase::cartReview
CreditCardPaymentMethodBase::cartReviewTitle public function Returns the payment method title to be used on the checkout review page. Overrides PaymentMethodPluginBase::cartReviewTitle
CreditCardPaymentMethodBase::customerView public function Called when an order is being viewed by a customer. Overrides PaymentMethodPluginBase::customerView
CreditCardPaymentMethodBase::displayCardNumber protected function Returns a credit card number with appropriate masking.
CreditCardPaymentMethodBase::getDisplayLabel public function Returns the payment method label with logo. Overrides PaymentMethodPluginBase::getDisplayLabel
CreditCardPaymentMethodBase::getEnabledFields public function Returns the set of fields which are used by this payment method.
CreditCardPaymentMethodBase::getEnabledTypes public function Returns the set of card types which are used by this payment method.
CreditCardPaymentMethodBase::getTransactionTypes public function Returns the set of transaction types allowed by this payment method. 1
CreditCardPaymentMethodBase::orderEditDetails public function Called when an order is being edited with this payment method. Overrides PaymentMethodPluginBase::orderEditDetails
CreditCardPaymentMethodBase::orderLoad public function Called when an order is being loaded with this payment method. Overrides PaymentMethodPluginBase::orderLoad
CreditCardPaymentMethodBase::orderSave public function Called when an order is being saved with this payment method. Overrides PaymentMethodPluginBase::orderSave
CreditCardPaymentMethodBase::orderSubmit public function Called when an order is being submitted with this payment method. Overrides PaymentMethodPluginBase::orderSubmit
CreditCardPaymentMethodBase::orderView public function Called when an order is being viewed by an administrator. Overrides PaymentMethodPluginBase::orderView
CreditCardPaymentMethodBase::processPayment public function Process a payment through the credit card gateway.
CreditCardPaymentMethodBase::validateCardNumber protected function Validates a credit card number during checkout.
CreditCardPaymentMethodBase::validateCvv protected function Validates a CVV number during checkout.
CreditCardPaymentMethodBase::validateExpirationDate protected function Validates an expiration date on a card.
CreditCardPaymentMethodBase::validateIssueNumber protected function Validates an issue number on a card.
CreditCardPaymentMethodBase::validateStartDate protected function Validates a start date on a card.
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PaymentMethodPluginBase::$database protected property The database service.
PaymentMethodPluginBase::create public static function Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface::create
PaymentMethodPluginBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
PaymentMethodPluginBase::orderDelete public function Called when an order is being deleted. Overrides PaymentMethodPluginInterface::orderDelete 1
PaymentMethodPluginBase::orderEditProcess public function Called when an order is being submitted after being edited. Overrides PaymentMethodPluginInterface::orderEditProcess 1
PaymentMethodPluginBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration
PaymentMethodPluginBase::validateConfigurationForm public function Form validation handler. Overrides PluginFormInterface::validateConfigurationForm
PaymentMethodPluginBase::__construct public function Constructs the PaymentMethodPluginBase object. Overrides PluginBase::__construct
PayPalWebsitePaymentsPro::avscodeMessage protected function Returns a human readable message for the AVS code.
PayPalWebsitePaymentsPro::buildConfigurationForm public function Form constructor. Overrides CreditCardPaymentMethodBase::buildConfigurationForm
PayPalWebsitePaymentsPro::buildErrorMessages protected function Builds error message(s) from PayPal failure responses.
PayPalWebsitePaymentsPro::cardType protected function Returns the PayPal approved credit card type for a card number.
PayPalWebsitePaymentsPro::chargeCard protected function Called when a credit card should be processed. Overrides CreditCardPaymentMethodBase::chargeCard
PayPalWebsitePaymentsPro::cvvmatchMessage protected function Returns a human readable message for the CVV2 match code.
PayPalWebsitePaymentsPro::defaultConfiguration public function Gets default configuration for this plugin. Overrides CreditCardPaymentMethodBase::defaultConfiguration
PayPalWebsitePaymentsPro::sendNvpRequest public function Sends a request to the PayPal NVP API.
PayPalWebsitePaymentsPro::submitConfigurationForm public function Form submission handler. Overrides CreditCardPaymentMethodBase::submitConfigurationForm
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.