You are here

commerce_square.admin.inc in Commerce Square Connect 7

Provides admin menu callbacks for Commerce Square.

File

includes/commerce_square.admin.inc
View source
<?php

/**
 * @file
 * Provides admin menu callbacks for Commerce Square.
 */
use SquareConnect\ApiException;
use SquareConnect\Configuration;
use SquareConnect\Api\OAuthApi;
use SquareConnect\Model\ObtainTokenRequest;

/**
 * Form callback: allows the user to capture a prior authorization.
 */
function commerce_square_capture_form($form, &$form_state, $order, $transaction) {
  $form_state['order'] = $order;
  $form_state['transaction'] = $transaction;

  // Load and store the payment method instance for this transaction.
  $payment_method = commerce_payment_method_instance_load($transaction->instance_id);
  $form_state['payment_method'] = $payment_method;
  $form = confirm_form($form, t('Are you sure you want to capture this transaction?'), 'admin/commerce/orders/' . $order->order_id . '/payment', '', t('Capture'), t('Cancel'), 'confirm');
  return $form;
}

/**
 * Validate handler: check the credit amount before attempting credit request.
 */
function commerce_square_capture_form_validate($form, &$form_state) {
  $transaction = $form_state['transaction'];
  if (time() - $transaction->created > 86400 * 6) {
    drupal_set_message(t('This capture has passed its 6 day limit for issuing a capture.'), 'error');
    drupal_goto('admin/commerce/orders/' . $form_state['order']->order_id . '/payment');
  }
}

/**
 * Submit handler: process the void request.
 */
function commerce_square_capture_form_submit($form, &$form_state) {
  $transaction = $form_state['transaction'];
  $client = SquareApi::createFromInstanceId($transaction->instance_id);
  try {
    $client
      ->capture($transaction);
    $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
    drupal_set_message(t('Transaction successfully captured.'));
  } catch (ApiException $e) {

    /** @var \SquareConnect\Model\VoidTransactionResponse $result */
    $result = $e
      ->getResponseObject();
    foreach ($result
      ->getErrors() as $error) {
      drupal_set_message(sprintf('%s %s %s', $error
        ->getCode(), $error
        ->getCategory(), $error
        ->getDetail()), 'error');
    }
  } catch (Exception $e) {
    drupal_set_message(t('There was an error capturing the transaction: @message', array(
      '@message' => $e
        ->getMessage(),
    )), 'error');
  }
  commerce_payment_transaction_save($transaction);
  $form_state['redirect'] = 'admin/commerce/orders/' . $form_state['order']->order_id . '/payment';
}

/**
 * Form callback: allows the user to void a transaction.
 */
function commerce_square_void_form($form, &$form_state, $order, $transaction) {
  $form_state['order'] = $order;
  $form_state['transaction'] = $transaction;

  // Load and store the payment method instance for this transaction.
  $payment_method = commerce_payment_method_instance_load($transaction->instance_id);
  $form_state['payment_method'] = $payment_method;
  $form['markup'] = array(
    '#markup' => t('Are you sure that you want to void this transaction?'),
  );
  $form = confirm_form($form, t('Are you sure that you want to void this transaction?'), 'admin/commerce/orders/' . $order->order_id . '/payment', '', t('Void'), t('Cancel'), 'confirm');
  return $form;
}

/**
 * Submit handler: process the void request.
 */
function commerce_square_void_form_submit($form, &$form_state) {
  $transaction = $form_state['transaction'];
  $client = SquareApi::createFromInstanceId($transaction->instance_id);
  try {
    $client
      ->void($transaction);
    $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
    drupal_set_message(t('Transaction successfully voided.'));
  } catch (ApiException $e) {

    /** @var \SquareConnect\Model\VoidTransactionResponse $result */
    $result = $e
      ->getResponseObject();
    foreach ($result
      ->getErrors() as $error) {
      drupal_set_message(sprintf('%s %s %s', $error
        ->getCode(), $error
        ->getCategory(), $error
        ->getDetail()), 'error');
    }
  } catch (Exception $e) {
    drupal_set_message(t('There was an error voiding the transaction: @message', array(
      '@message' => $e
        ->getMessage(),
    )), 'error');
  }
  commerce_payment_transaction_save($transaction);
  $form_state['redirect'] = 'admin/commerce/orders/' . $form_state['order']->order_id . '/payment';
}

/**
 * Form callback: allows the user to refund a transaction.
 */
function commerce_square_refund_form($form, &$form_state, $order, $transaction) {
  $form_state['order'] = $order;
  $form_state['transaction'] = $transaction;

  // Load and store the payment method instance for this transaction.
  $payment_method = commerce_payment_method_instance_load($transaction->instance_id);
  $form_state['payment_method'] = $payment_method;
  $default_amount = number_format(commerce_currency_amount_to_decimal($transaction->amount, $transaction->currency_code), 2, '.', '');
  $form['amount'] = array(
    '#type' => 'textfield',
    '#title' => t('Refund amount'),
    '#description' => t('Enter the amount to be refunded.'),
    '#default_value' => $default_amount,
    '#field_suffix' => check_plain($transaction->currency_code),
    '#size' => 16,
  );
  $form = confirm_form($form, t('What amount do you want to refund?'), 'admin/commerce/orders/' . $order->order_id . '/payment', '', t('Refund'), t('Cancel'), 'confirm');
  return $form;
}

/**
 * Validate handler: check the credit amount before attempting credit request.
 */
function commerce_square_refund_form_validate($form, &$form_state) {
  $transaction = $form_state['transaction'];
  $amount = $form_state['values']['amount'];

  // Ensure a positive numeric amount has been entered for credit.
  if (!is_numeric($amount) || $amount <= 0) {
    form_set_error('amount', t('You must specify a positive numeric amount to refund.'));
  }

  // Ensure the amount is less than or equal to the captured amount.
  if ($amount > commerce_currency_amount_to_decimal($transaction->amount, $transaction->currency_code)) {
    form_set_error('amount', t('You cannot credit more than you captured through Square.'));
  }
  if (time() - $transaction->created > 86400 * 120) {
    drupal_set_message(t('This capture has passed its 120 day limit for issuing refunds.'), 'error');
    drupal_goto('admin/commerce/orders/' . $form_state['order']->order_id . '/payment');
  }
}

/**
 * Confirm form submit callback to refund an order.
 */
function commerce_square_refund_form_submit($form, $form_state) {
  $transaction = $form_state['transaction'];
  $amount = commerce_currency_decimal_to_amount($form_state['values']['amount'], $transaction->currency_code);
  $client = SquareApi::createFromInstanceId($transaction->instance_id);
  try {
    $result = $client
      ->refund($transaction, $amount);
    $refund_transaction = commerce_payment_transaction_new('commerce_square', $transaction->order_id);
    $refund_transaction->instance_id = $transaction->instance_id;
    $refund_transaction->amount = -$amount;
    $refund_transaction->currency_code = $transaction->currency_code;
    $refund_transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
    $remote_transaction = $result
      ->getRefund();
    $tender_id = $remote_transaction
      ->getTenderId();
    $refund_transaction->remote_id = $remote_transaction
      ->getId() . '|' . $tender_id;
    $refund_transaction->order_id = $transaction->order_id;
    $refund_transaction->message = t('Manual refund');
    commerce_payment_transaction_save($refund_transaction);
    drupal_set_message(t('Amount successfully refunded'), 'status');
  } catch (ApiException $e) {

    /** @var \SquareConnect\Model\VoidTransactionResponse $result */
    $result = $e
      ->getResponseObject();
    foreach ($result
      ->getErrors() as $error) {
      drupal_set_message(sprintf('%s %s %s', $error
        ->getCode(), $error
        ->getCategory(), $error
        ->getDetail()), 'error');
    }
  } catch (Exception $e) {
    drupal_set_message(t('There was an error refunding the transaction: @message', array(
      '@message' => $e
        ->getMessage(),
    )), 'error');
  }

  // Redirect to payments page.
  drupal_goto('admin/commerce/orders/' . $transaction->order_id . '/payment');
}

/**
 * Square settings form.
 *
 * @param array $form
 *   The form.
 * @param array $form_state
 *   The form state.
 *
 * @return array
 *   The form.
 */
function commerce_square_settings_form(array $form, array &$form_state) {
  $settings = variable_get('commerce_square_settings', commerce_square_default_settings()) + commerce_square_default_settings();
  libraries_load('square');
  $successful_connection = FALSE;

  // Check current API and give warning if upgrade is needed.
  // SDK does not provide methods to getVersion or getReleaseDate,
  // so user agent string parsing is required.
  $sdk_string = explode('/', Configuration::getDefaultConfiguration()
    ->getUserAgent());
  $sdk_version = $sdk_string[1];
  $sdk_numbers = explode('.', $sdk_version);
  $sdk_date = $sdk_numbers[1];
  if ($sdk_date < 20190313) {
    drupal_set_message(t('Your Square SDK version, @version, is out of date and may stop working when deprecated methods are removed. Please upgrade to 2.20190313.0 or higher.', array(
      '@version' => $sdk_version,
    )), 'warning');
  }

  // If site is configured with Square app credentials AND we are being
  // returned to the form from Square with an application code, then
  // proceed to exchange application code for an access token.
  // Empty $form_state['input'] handles case when stale $_GET['code'] persists
  // after failed authorization attempt: input is empty when form reloads after
  // being redirected back from Square in initial authorization step.
  if (empty($form_state['input']) && !empty($_GET['code']) && !empty($settings['live_app_id']) && !empty($settings['app_secret'])) {

    // Use newer OAuth API ObtainToken endpoint with grant_type parameter,
    // available since version 20190313.
    if ($sdk_date >= 20190313 && class_exists('SquareConnect\\Api\\OAuthApi') && method_exists('SquareConnect\\Model\\ObtainTokenRequest', 'setGrantType')) {

      // If all checks out, use OAuth API to communicate with Square.
      $oauth_api = new OAuthApi();
      $request_body = new ObtainTokenRequest();
      $request_body
        ->setClientId($settings['live_app_id']);
      $request_body
        ->setClientSecret($settings['app_secret']);
      $request_body
        ->setGrantType('authorization_code');
      $request_body
        ->setCode(check_plain($_GET['code']));
      try {
        $result = $oauth_api
          ->obtainToken($request_body);
      } catch (Exception $e) {
        drupal_set_message(t('There was an error saving the OAuth token: @error', array(
          '@error' => $e
            ->getMessage(),
        )), 'error');
      }
      $access_token = !empty($result) ? $result
        ->getAccessToken() : FALSE;
      if (!empty($access_token)) {
        $settings['live_access_token'] = $access_token;
        $settings['live_access_token_expiry'] = strtotime($result
          ->getExpiresAt());
        $settings['live_access_refresh_token'] = $result
          ->getRefreshToken();
        variable_set('commerce_square_settings', $settings);
        $successful_connection = TRUE;
        drupal_set_message(t('Your Drupal Commerce store and Square have been successfully connected.'));
      }
      else {

        // Failure message, but only if something was NOT already caught as

        //error $e during obtainToken() request.
        if (!isset($e)) {
          drupal_set_message(t('There was an error saving the OAuth token: no token was returned.'), 'error');
        }
      }
    }
    else {

      // @TODO: remove this section when API/SDK versions relying on it
      // are no longer supported by Square.
      // Use the older curl-based method to communicate with Square.
      $data = array(
        'client_id' => $settings['live_app_id'],
        'client_secret' => $settings['app_secret'],
        'code' => check_plain($_GET['code']),
      );
      $data_string = json_encode($data);
      $ch = curl_init('https://connect.squareup.com/oauth2/token');
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
      curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($data_string),
      ));
      $response = curl_exec($ch);
      $response_body = drupal_json_decode($response);
      if (!empty($response_body['access_token'])) {
        $settings['live_access_token'] = $response_body['access_token'];
        $settings['live_access_token_expiry'] = strtotime($response_body['expires_at']);
        variable_set('commerce_square_settings', $settings);
        $successful_connection = TRUE;
        drupal_set_message(t('Your Drupal Commerce store and Square have been successfully connected, but consider upgrading to a newer version of the Square SDK.'));
      }
      else {
        drupal_set_message(t('There was an error saving the OAuth token. Try upgrading to a newer version of the Square SDK.'), 'error');
      }
    }
  }
  if (empty($form_state['input']) && !$successful_connection) {
    drupal_set_message(t('After clicking save you will be redirected to Square to sign in and connect your Drupal Commerce store. If the save button does not work, try loading the page in its own window and not as an overlay: /admin/commerce/config/square'), 'warning');
  }
  $form['credentials'] = array(
    '#type' => 'fieldset',
    '#collapsible' => FALSE,
    '#collapsed' => FALSE,
    '#title' => t('Application credentials'),
    '#description' => t('You can get these by selecting your app <a href="https://connect.squareup.com/apps">here</a>.'),
  );
  $form['credentials']['app_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Application Name'),
    '#default_value' => $settings['app_name'],
    '#required' => TRUE,
  );
  $form['credentials']['live_app_id'] = array(
    '#type' => 'textfield',
    '#title' => t('Application ID'),
    '#default_value' => $settings['live_app_id'],
    '#required' => TRUE,
  );
  $form['credentials']['app_secret'] = array(
    '#type' => 'textfield',
    '#title' => t('Application Secret'),
    '#description' => t('You can get this by selecting your app <a href="https://connect.squareup.com/apps">here</a> and clicking on the OAuth tab.'),
    '#default_value' => $settings['app_secret'],
    '#required' => TRUE,
  );
  $form['credentials']['redirect_url'] = array(
    '#type' => 'item',
    '#title' => t('Redirect URL'),
    '#markup' => url('admin/commerce_square/oauth/obtain', array(
      'absolute' => TRUE,
    )),
    '#description' => t('Copy this URL and use it for the redirect URL field in your app OAuth settings.'),
  );
  $form['credentials']['live_access_token'] = array(
    '#type' => 'value',
    '#default_value' => $settings['live_access_token'],
  );
  $form['credentials']['live_access_token_expiry'] = array(
    '#type' => 'value',
    '#default_value' => $settings['live_access_token_expiry'],
  );
  $form['sandbox'] = array(
    '#type' => 'fieldset',
    '#description' => t('You can get these by selecting your app <a href="https://connect.squareup.com/apps">here</a>.'),
    '#collapsible' => FALSE,
    '#collapsed' => FALSE,
    '#title' => t('Sandbox'),
  );
  $form['sandbox']['test_app_id'] = array(
    '#type' => 'textfield',
    '#title' => t('Sandbox Application ID'),
    '#default_value' => $settings['test_app_id'],
    '#required' => TRUE,
  );
  $form['sandbox']['test_access_token'] = array(
    '#type' => 'textfield',
    '#title' => t('Sandbox Access Token'),
    '#default_value' => $settings['test_access_token'],
    '#required' => TRUE,
  );
  $form['actions']['#type'] = 'actions';
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  if (form_get_errors()) {
    drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
  }
  if (!isset($form['#theme'])) {
    $form['#theme'] = 'system_settings_form';
  }
  return $form;
}

/**
 * Square settings form: validate callback.
 *
 * @param array $form
 *   The form.
 * @param array $form_state
 *   The form state.
 */
function commerce_square_settings_form_validate(array $form, array &$form_state) {
}

/**
 * Square settings form: submit callback.
 *
 * @param array $form
 *   The form.
 * @param array $form_state
 *   The form state.
 */
function commerce_square_settings_form_submit(array $form, array &$form_state) {
  form_state_values_clean($form_state);
  $values = $form_state['values'];
  unset($values['redirect_url']);
  variable_set('commerce_square_settings', $values);
  $form_state['redirect'] = url('https://connect.squareup.com/oauth2/authorize', array(
    'query' => array(
      'client_id' => $values['live_app_id'],
      'state' => drupal_get_token(),
      'scope' => implode(' ', array(
        'MERCHANT_PROFILE_READ',
        'PAYMENTS_READ',
        'PAYMENTS_WRITE',
        'CUSTOMERS_READ',
        'CUSTOMERS_WRITE',
        'ORDERS_WRITE',
      )),
    ),
  ));
}

Functions

Namesort descending Description
commerce_square_capture_form Form callback: allows the user to capture a prior authorization.
commerce_square_capture_form_submit Submit handler: process the void request.
commerce_square_capture_form_validate Validate handler: check the credit amount before attempting credit request.
commerce_square_refund_form Form callback: allows the user to refund a transaction.
commerce_square_refund_form_submit Confirm form submit callback to refund an order.
commerce_square_refund_form_validate Validate handler: check the credit amount before attempting credit request.
commerce_square_settings_form Square settings form.
commerce_square_settings_form_submit Square settings form: submit callback.
commerce_square_settings_form_validate Square settings form: validate callback.
commerce_square_void_form Form callback: allows the user to void a transaction.
commerce_square_void_form_submit Submit handler: process the void request.