You are here

function commerce_paypal_ec_do_payment in Commerce PayPal 7.2

Confirm an Express Checkout payment for an order for the specified charge amount with a DoExpressCheckoutPayment API request.

Parameters

$payment_method: The PayPal Express Checkout payment method instance whose settings should be used to submit the request.

$order: The order the payment is for.

$charge: A price field value array representing the charge amount and currency.

Return value

Boolean indicating the success or failure of the payment request.

2 calls to commerce_paypal_ec_do_payment()
commerce_paypal_ec_redirect_form_validate in modules/ec/commerce_paypal_ec.module
Payment method callback: redirect form return validation.
commerce_paypal_ec_review_pane_checkout_form_submit in modules/ec/includes/commerce_paypal_ec.checkout_pane.inc
Submit handler for the Express Checkout review and confirm page.

File

modules/ec/commerce_paypal_ec.module, line 1159
Implements PayPal Express Checkout in Drupal Commerce checkout.

Code

function commerce_paypal_ec_do_payment($payment_method, $order, $charge) {

  // Determine the currency code to use to actually process the transaction,
  // which will either be the default currency code or the currency code of the
  // charge if it's supported by PayPal if that option is enabled.
  $currency_code = $payment_method['settings']['currency_code'];
  if (!empty($payment_method['settings']['allow_supported_currencies']) && in_array($charge['currency_code'], array_keys(commerce_paypal_currencies('paypal_ec')))) {
    $currency_code = $charge['currency_code'];
  }

  // Convert the charge amount to the specified currency.
  $amount = commerce_currency_convert($charge['amount'], $charge['currency_code'], $currency_code);
  $nvp = array(
    'METHOD' => 'DoExpressCheckoutPayment',
    'TOKEN' => $order->data['commerce_paypal_ec']['token'],
    'PAYERID' => $order->data['commerce_paypal_ec']['payerid'],
    'BUTTONSOURCE' => $payment_method['buttonsource'],
    'PAYMENTREQUEST_0_AMT' => commerce_paypal_price_amount($amount, $currency_code),
    'PAYMENTREQUEST_0_CURRENCYCODE' => $currency_code,
    'PAYMENTREQUEST_0_INVNUM' => commerce_paypal_ipn_invoice($order),
    'PAYMENTREQUEST_0_PAYMENTACTION' => commerce_paypal_payment_action($payment_method['settings']['txn_type']),
    'PAYMENTREQUEST_0_NOTIFYURL' => commerce_paypal_ipn_url($payment_method['instance_id']),
  );

  // Add itemized information to the API request.
  $nvp += commerce_paypal_ec_itemize_order($order, $currency_code);

  // Submit the request to PayPal.
  $response = commerce_paypal_api_request($payment_method, $nvp, $order);

  // Prepare a transaction object to log the API response.
  $transaction = commerce_payment_transaction_new('paypal_ec', $order->order_id);
  $transaction->instance_id = $payment_method['instance_id'];
  $transaction->amount = $amount;
  $transaction->currency_code = $currency_code;
  $transaction->payload[REQUEST_TIME] = $response;

  // If available, set the remote status and transaction ID.
  $key_map = array(
    'remote_status' => 'PAYMENTINFO_0_PAYMENTSTATUS',
    'remote_id' => 'PAYMENTINFO_0_TRANSACTIONID',
  );
  foreach ($key_map as $key => $response_key) {
    if (!empty($response[$response_key])) {
      $transaction->{$key} = $response[$response_key];
    }
  }

  // Store the transaction ID as the parent transaction ID in case subsequent
  // API operations alter this transaction's remote ID.
  if (!empty($transaction->remote_id)) {
    $transaction->data['commerce_paypal_ec']['parenttransactionid'] = $transaction->remote_id;
  }

  // Check if there is an error code in the response.
  if (!empty($response['L_ERRORCODE0'])) {

    // Log the error in a payment transaction.
    $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
    $transaction->remote_status = '';
    $message = array();
    $message[] = '<strong>' . t('Payment failed') . '</strong>';
    $message[] = t('Error @code: @message', array(
      '@code' => $response['L_ERRORCODE0'],
      '@message' => $response['L_SHORTMESSAGE0'],
    ));
    $transaction->message = implode('<br />', $message);
    commerce_payment_transaction_save($transaction);

    // If the response error indicates a funding failure, redirect the customer
    // back to PayPal for another attempt.
    // @see: https://developer.paypal.com/docs/classic/express-checkout/ht_ec_fundingfailure10486/
    if ($response['L_ERRORCODE0'] === '10486') {

      // Log the error in watchdog.
      watchdog('commerce_paypal_ec', 'PayPal Express Checkout transaction funding failed for order @order_number. Redirecting the user back to PayPal.', array(
        '@order_number' => $order->order_number,
      ), WATCHDOG_NOTICE);

      // Update order.
      commerce_order_status_update($order, 'checkout_payment', FALSE, NULL, t('Customer payment transaction funding failed. Redirecting customer back to the PayPal Express Checkout page.'));

      // Redirect the user back to PayPal.
      drupal_goto(commerce_paypal_ec_checkout_url($payment_method['settings']['server'], $order->data['commerce_paypal_ec']['token']));
    }

    // Log the error in watchdog.
    watchdog('commerce_paypal_ec', 'PayPal Express Checkout transaction failed for order @order_number.', array(
      '@order_number' => $order->order_number,
    ), WATCHDOG_ERROR);
    return FALSE;
  }

  // If we received an unknown response status...
  if (!isset($response['PAYMENTINFO_0_PAYMENTSTATUS']) || !in_array($response['PAYMENTINFO_0_PAYMENTSTATUS'], array(
    'Failed',
    'Voided',
    'Pending',
    'Completed',
    'Refunded',
  ))) {

    // Display an error message and remain on the same page.
    drupal_set_message(t('We could not complete your payment with PayPal. Please try again or contact us if the problem persists.'), 'error');

    // Log the error in a payment transaction and watchdog.
    $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
    $transaction->remote_status = '';
    $transaction->message = t('Payment failed with unknown status.');
    commerce_payment_transaction_save($transaction);
    watchdog('commerce_paypal_ec', 'PayPal Express Checkout transaction failed for order @order_number.', array(
      '@order_number' => $order->order_number,
    ), WATCHDOG_ERROR);
    return FALSE;
  }

  // Build a meaningful response message.
  $message = array();

  // If we didn't get an approval response code...
  switch ($response['PAYMENTINFO_0_PAYMENTSTATUS']) {
    case 'Failed':
      $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
      $message[] = '<strong>' . t('Payment failed') . '</strong>';
      $message[] = t('Error @code: @message', array(
        '@code' => $response['PAYMENTINFO_0_ERRORCODE'],
        '@message' => $response['PAYMENTINFO_0_SHORTMESSAGE'],
      ));
      break;
    case 'Voided':
      $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
      $message[] = '<strong>' . t('Authorization voided') . '</strong>';
      break;
    case 'Pending':
      $transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
      $transaction->data['commerce_paypal_ec']['paymenttype'] = $response['PAYMENTINFO_0_PAYMENTTYPE'];
      $message[] = '<strong>' . t('Payment pending at PayPal') . '</strong>';
      $message[] = commerce_paypal_short_pending_reason($response['PAYMENTINFO_0_PENDINGREASON']);
      break;
    case 'Completed':
      $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
      $message[] = t('Payment completed successfully');
      break;
    case 'Refunded':
      $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
      $message[] = t('Refund for transaction @txn_id', array(
        '@txn_id' => $response['PAYMENTINFO_0_TRANSACTIONID'],
      ));
      break;
  }

  // Set the final message.
  $transaction->message = implode('<br />', $message);

  // Save the transaction information.
  commerce_payment_transaction_save($transaction);

  // If the payment failed, display an error and rebuild the form.
  if (!in_array($response['PAYMENTINFO_0_PAYMENTSTATUS'], array(
    'Refunded',
    'Completed',
    'Pending',
  ))) {
    drupal_set_message(t('We encountered an error processing your payment with PayPal. Please try again or contact us for assistance.'), 'error');
    return FALSE;
  }
  return TRUE;
}