You are here

public function AcceptJs::createPayment in Commerce Authorize.Net 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

File

src/Plugin/Commerce/PaymentGateway/AcceptJs.php, line 222

Class

AcceptJs
Provides the Accept.js payment gateway.

Namespace

Drupal\commerce_authnet\Plugin\Commerce\PaymentGateway

Code

public function createPayment(PaymentInterface $payment, $capture = TRUE) {
  $this
    ->assertPaymentState($payment, [
    'new',
  ]);
  $payment_method = $payment
    ->getPaymentMethod();
  $this
    ->assertPaymentMethod($payment_method);
  $order = $payment
    ->getOrder();
  $owner = $payment_method
    ->getOwner();

  // Transaction request.
  $transaction_request = new TransactionRequest([
    'transactionType' => $capture ? TransactionRequest::AUTH_CAPTURE : TransactionRequest::AUTH_ONLY,
    'amount' => $payment
      ->getAmount()
      ->getNumber(),
  ]);
  $tempstore_3ds = $this->privateTempStore
    ->get('commerce_authnet')
    ->get($payment_method
    ->id());
  if (!empty($tempstore_3ds)) {

    // Do not send ECI and CAVV values when reusing a payment method.
    $payment_storage = $this->entityTypeManager
      ->getStorage('commerce_payment');
    $payment_method_has_been_used = $payment_storage
      ->getQuery()
      ->condition('payment_method', $payment_method
      ->id())
      ->range(0, 1)
      ->execute();
    if (!$payment_method_has_been_used) {
      $cardholder_authentication = new CardholderAuthentication();
      $cardholder_authentication_empty = TRUE;
      if (!empty($tempstore_3ds['eci']) && $tempstore_3ds['eci'] != '07') {
        $cardholder_authentication->authenticationIndicator = $tempstore_3ds['eci'];
        $cardholder_authentication_empty = FALSE;
      }
      if (!empty($tempstore_3ds['cavv'])) {

        // This is quite undocumented, but seems that cavv needs to be
        // urlencoded.
        // @see https://community.developer.authorize.net/t5/Integration-and-Testing/Cardholder-Authentication-extraOptions-invalid-error/td-p/57955
        $cardholder_authentication->cardholderAuthenticationValue = urlencode($tempstore_3ds['cavv']);
        $cardholder_authentication_empty = FALSE;
      }
      if (!$cardholder_authentication_empty) {
        $transaction_request
          ->addDataType($cardholder_authentication);
      }
    }
    else {
      $this->privateTempStore
        ->get('commerce_authnet')
        ->delete($payment_method
        ->id());
    }
  }

  // @todo update SDK to support data type like this.
  // Initializing the profile to charge and adding it to the transaction.
  $customer_profile_id = $this
    ->getRemoteCustomerId($owner);
  if (empty($customer_profile_id)) {
    $customer_profile_id = $this
      ->getPaymentMethodCustomerId($payment_method);
  }
  $payment_profile_id = $this
    ->getRemoteProfileId($payment_method);
  $profile_to_charge = new Profile([
    'customerProfileId' => $customer_profile_id,
  ]);
  $profile_to_charge
    ->addData('paymentProfile', [
    'paymentProfileId' => $payment_profile_id,
  ]);
  $transaction_request
    ->addData('profile', $profile_to_charge
    ->toArray());
  $profiles = $order
    ->collectProfiles();
  if (isset($profiles['shipping']) && !$profiles['shipping']
    ->get('address')
    ->isEmpty()) {

    /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $shipping_address */
    $shipping_address = $profiles['shipping']
      ->get('address')
      ->first();
    $ship_data = [
      // @todo how to allow customizing this.
      'firstName' => $shipping_address
        ->getGivenName(),
      'lastName' => $shipping_address
        ->getFamilyName(),
      'address' => substr($shipping_address
        ->getAddressLine1() . ' ' . $shipping_address
        ->getAddressLine2(), 0, 60),
      'country' => $shipping_address
        ->getCountryCode(),
      'company' => $shipping_address
        ->getOrganization(),
      'city' => $shipping_address
        ->getLocality(),
      'state' => $shipping_address
        ->getAdministrativeArea(),
      'zip' => $shipping_address
        ->getPostalCode(),
    ];
    $transaction_request
      ->addDataType(new ShipTo(array_filter($ship_data)));
  }

  // Adding order information to the transaction.
  $transaction_request
    ->addOrder(new OrderDataType([
    'invoiceNumber' => $order
      ->getOrderNumber() ?: $order
      ->id(),
  ]));
  $transaction_request
    ->addData('customerIP', $order
    ->getIpAddress());

  // Adding line items.
  $line_items = $this
    ->getLineItems($order);
  foreach ($line_items as $line_item) {
    $transaction_request
      ->addLineItem($line_item);
  }

  // Adding tax information to the transaction.
  $transaction_request
    ->addData('tax', $this
    ->getTax($order)
    ->toArray());
  $transaction_request
    ->addData('shipping', $this
    ->getShipping($order)
    ->toArray());
  $request = new CreateTransactionRequest($this->authnetConfiguration, $this->httpClient);
  $request
    ->setTransactionRequest($transaction_request);
  $response = $request
    ->execute();
  if ($response
    ->getResultCode() != 'Ok') {
    $this
      ->logResponse($response);
    $message = $response
      ->getMessages()[0];
    switch ($message
      ->getCode()) {
      case 'E00040':
        $payment_method
          ->delete();
        throw new PaymentGatewayException('The provided payment method is no longer valid');
      case 'E00042':
        $payment_method
          ->delete();
        throw new PaymentGatewayException('You cannot add more than 10 payment methods.');
      default:
        throw new PaymentGatewayException($message
          ->getText());
    }
  }
  if (!empty($response
    ->getErrors())) {
    $message = $response
      ->getErrors()[0];
    throw new HardDeclineException($message
      ->getText());
  }

  // Select the next state based on fraud detection results.
  $code = $response
    ->getMessageCode();
  $expires = 0;
  $next_state = 'authorization';
  if ($code == 1 && $capture) {
    $next_state = 'completed';
  }
  elseif ($code == 252) {
    $next_state = 'unauthorized_review';
    $expires = strtotime('+5 days');
  }
  elseif ($code == 253) {
    $next_state = 'authorization_review';
    $expires = strtotime('+5 days');
  }
  $payment
    ->setExpiresTime($expires);
  $payment
    ->setState($next_state);
  $payment
    ->setRemoteId($response->transactionResponse->transId);
  $payment
    ->setAvsResponseCode($response->transactionResponse->avsResultCode);

  // @todo Find out how long an authorization is valid, set its expiration.
  $payment
    ->save();
}