commerce_cop.module in Commerce Custom Offline Payments 7
Custom offline payment methods for Drupal Commerce.
Allows store administrators to easily define custom offline payment methods as Drupal Commerce payment methods.
File
commerce_cop.moduleView source
<?php
/**
* @file
* Custom offline payment methods for Drupal Commerce.
*
* Allows store administrators to easily define custom offline payment methods
* as Drupal Commerce payment methods.
*/
/**
* Implements hook_menu().
*/
function commerce_cop_menu() {
$items = array();
$items['admin/commerce/config/custom-offline-payments'] = array(
'title' => 'Custom Offline Payments',
'description' => 'Manage custom offline payment methods',
'access arguments' => array(
'administer custom offline payments',
),
'file' => 'commerce_cop.admin.inc',
'page callback' => 'commerce_cop_admin_overview',
'weight' => 3,
);
$items['admin/commerce/config/custom-offline-payments/add'] = array(
'title' => 'Add payment method',
'access arguments' => array(
'administer custom offline payments',
),
'file' => 'commerce_cop.admin.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_cop_edit_payment_form',
),
'type' => MENU_LOCAL_ACTION,
);
$items['admin/commerce/config/custom-offline-payments/%custom_offline_payment'] = array(
'title' => 'Edit payment method',
'access arguments' => array(
'administer custom offline payments',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_cop_edit_payment_form',
4,
),
'file' => 'commerce_cop.admin.inc',
);
$items['admin/commerce/config/custom-offline-payments/%custom_offline_payment/edit'] = array(
'title' => 'Edit',
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
'weight' => 0,
);
$items['admin/commerce/config/custom-offline-payments/%custom_offline_payment/delete'] = array(
'title' => 'Delete',
'access arguments' => array(
'administer custom offline payments',
),
'file' => 'commerce_cop.admin.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_cop_delete_payment_form',
4,
),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 10,
);
// Add a menu item for capturing authorizations.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/offline-payment-confirm'] = array(
'title' => 'Confirm payment',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_cop_confirm_payment_form',
3,
5,
),
'access callback' => 'commerce_cop_payment_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
);
return $items;
}
/**
* Implements hook_permission().
*/
function commerce_cop_permission() {
return array(
'administer custom offline payments' => array(
'title' => t('Administer custom offline payment methods'),
'description' => t('Create, update, edit the custom offline payment methods.'),
),
);
}
/**
* User access callback for cashing cheque
*/
function commerce_cop_payment_access($order, $transaction) {
if (custom_offline_payment_load($transaction->payment_method) && $transaction->status != COMMERCE_PAYMENT_STATUS_SUCCESS) {
// Allow access if the user can update payments on this order.
return commerce_payment_transaction_access('update', $transaction);
}
// Return FALSE if the transaction is not with a Custom offline Payment and is not pending.
return FALSE;
}
/**
* Form to Receive the Payment
*/
function commerce_cop_confirm_payment_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;
$balance = commerce_payment_order_balance($order);
if ($balance['amount'] > 0 && $balance['amount'] < $transaction->amount) {
$default_amount = $balance['amount'];
}
else {
$default_amount = $transaction->amount;
}
// Convert the price amount to a user friendly decimal value.
$default_amount = commerce_currency_amount_to_decimal($default_amount, $transaction->currency_code);
$description = implode('<br />', array(
t('Order balance: @balance', array(
'@balance' => commerce_currency_format($balance['amount'], $balance['currency_code']),
)),
));
$payment = custom_offline_payment_load($transaction->payment_method);
$form['amount'] = array(
'#type' => 'textfield',
'#title' => t('Confirm %payment amount', array(
'%payment' => $payment['title'],
)),
'#description' => $description,
'#default_value' => $default_amount,
'#field_suffix' => check_plain($transaction->currency_code),
'#size' => 16,
);
// Fieldable payment transactions.
if (module_exists('commerce_payment_fields') && !empty($payment_method['fieldable'])) {
$instances = field_info_instances('commerce_payment_transaction', $transaction->payment_method);
if (!empty($instances)) {
// Add the field widgets for the profile.
field_attach_form('commerce_payment_transaction', $transaction, $form, $form_state);
}
}
$form = confirm_form($form, t('What amount do you want to confirm?'), 'admin/commerce/orders/' . $order->order_id . '/payment', '', t('Confirm'), t('Cancel'), 'confirm');
return $form;
}
/**
* Validate handler: ensure a valid amount is given.
*/
function commerce_cop_confirm_payment_form_validate($form, &$form_state) {
$transaction = $form_state['transaction'];
$amount = $form_state['values']['amount'];
// Ensure a positive numeric amount has been entered for capture.
if (!is_numeric($amount) || $amount <= 0) {
form_set_error('amount', t('You must specify a positive numeric amount to capture.'));
}
// Ensure the amount is less than or equal to the transaction amount.
if ($amount > commerce_currency_amount_to_decimal($transaction->amount, $transaction->currency_code)) {
form_set_error('amount', t('You cannot confirm more than transaction amount.'));
}
// Fieldable payment transactions.
if (!empty($form_state['field'])) {
// Notify field widgets to validate their data.
field_attach_form_validate('commerce_payment_transaction', $transaction, $form, $form_state);
}
}
/**
* Submit handler: confirm the "offline" payment
*/
function commerce_cop_confirm_payment_form_submit($form, &$form_state) {
$transaction = $form_state['transaction'];
$amount = $form_state['values']['amount'];
// Update the transaction amount to the actual confirmed amount.
$transaction->amount = commerce_currency_decimal_to_amount($amount, $transaction->currency_code);
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
// Append a confirmed indication to the result message.
$transaction->message .= '<br />' . 'Confirmed: @date';
$transaction->message_variables['@date'] = format_date(REQUEST_TIME, 'short');
// Fieldable payment transactions.
if (!empty($form_state['field'])) {
// Notify field widgets.
field_attach_submit('commerce_payment_transaction', $transaction, $form, $form_state);
}
commerce_payment_transaction_save($transaction);
drupal_set_message(t('Payment confirmed successfully.'));
$form_state['redirect'] = 'admin/commerce/orders/' . $form_state['order']->order_id . '/payment';
}
/**
* Implements hook_commerce_payment_method_info().
*/
function commerce_cop_commerce_payment_method_info() {
$payment_methods = array();
foreach (commerce_cop_get_payments() as $method_id => $payment) {
$payment_methods[$method_id] = array(
'base' => 'commerce_cop',
'title' => $payment['title'],
'description' => $payment['description'],
'active' => $payment['status'],
'checkout' => $payment['checkout'],
'terminal' => $payment['terminal'],
'fieldable' => $payment['fieldable'],
);
}
return $payment_methods;
}
/**
* Payment method callback: settings form.
*/
function commerce_cop_settings_form($settings = NULL) {
$settings = (array) $settings + array(
'information' => array(
'value' => '',
'format' => 'plain_text',
),
);
$form = array();
$form['information'] = array(
'#type' => 'text_format',
'#title' => t('Information'),
'#description' => t('Information you would like to be shown to users when they select this payment method, such as delivery payment details.'),
'#default_value' => $settings['information']['value'],
'#format' => $settings['information']['format'],
);
if (module_exists('token')) {
$form['token_tree'] = array(
'#theme' => 'token_tree',
'#token_types' => array(
'site',
'commerce_order',
'commerce-order',
),
'#dialog' => TRUE,
);
}
return $form;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function commerce_cop_form_rules_ui_edit_element_alter(&$form, &$form_state) {
// Use the custom offline payment definition information for the payment action,
// if the action payment information was not set yet.
if (!empty($form['parameter']['payment_method'])) {
$custom_offline_payment = custom_offline_payment_load($form['parameter']['payment_method']['settings']['payment_method']['method_id']['#value']);
if ($custom_offline_payment && empty($form['parameter']['payment_method']['settings']['payment_method']['settings']['information']['#default_value'])) {
$form['parameter']['payment_method']['settings']['payment_method']['settings']['information']['#default_value'] = $custom_offline_payment['information'];
$form['parameter']['payment_method']['settings']['payment_method']['settings']['information']['#format'] = $custom_offline_payment['format'];
}
}
}
/**
* Payment method callback: checkout form.
*/
function commerce_cop_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
$form = array();
$payment_info = NULL;
if (!empty($payment_method['settings']['information'])) {
$payment_info = check_markup($payment_method['settings']['information']['value'], $payment_method['settings']['information']['format']);
}
else {
$payment = custom_offline_payment_load($payment_method['method_id']);
if ($payment && !empty($payment['information'])) {
$payment_info = $payment['information'];
}
}
if ($payment_info) {
// Run token replacement.
$payment_info = token_replace($payment_info, array(
'commerce_order' => $order,
), array(
'language' => $GLOBALS['language'],
'clear' => TRUE,
));
$form['commerce_cop_info'] = array(
'#markup' => $payment_info,
);
}
// Need to create a dummy value to solve http://drupal.org/node/1230666
// Probably an issue in the main commerce module
$form['transaction'] = array(
'#type' => 'hidden',
'#value' => 'dummy_value',
);
return $form;
}
/**
* Payment method callback: checkout form validation.
*/
function commerce_cop_submit_form_validate($payment_method, $pane_form, $pane_values, $order, $form_parents = array()) {
// empty
}
/**
* Payment method callback: checkout form submission.
*/
function commerce_cop_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
$order->data['commerce_cop'] = $pane_values;
// Drupal Commerce Payment Transaction Fields integration.
// If installed and payment fieldable, the payment transaction object
// with the fields data will be available here in the pane values.
// @see https://www.drupal.org/node/2272735
// @see https://www.drupal.org/node/2293025
if (is_object($pane_values['transaction'])) {
$transaction = $pane_values['transaction'];
}
else {
$transaction = commerce_payment_transaction_new($payment_method['method_id'], $order->order_id);
}
commerce_cop_transaction($transaction, $payment_method, $order, $charge);
}
/**
* Creates a cheque payment transaction for the specified charge amount.
*
* @param $transaction
* The transaction instance object.
* @param $payment_method
* The payment method instance object used to charge this payment.
* @param $order
* The order object the payment applies to.
* @param $charge
* An array indicating the amount and currency code to charge.
*/
function commerce_cop_transaction($transaction, $payment_method, $order, $charge) {
$transaction->instance_id = $payment_method['instance_id'];
$transaction->amount = $charge['amount'];
$transaction->currency_code = $charge['currency_code'];
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->message = $payment_method['description'];
commerce_payment_transaction_save($transaction);
}
/**
* Function to get custom payments.
*/
function commerce_cop_get_payments() {
static $payments = array();
if (empty($payments)) {
$q = db_select('commerce_custom_offline_payment', 'cop');
$q
->fields('cop');
$q
->orderBy('cop.title');
$q = $q
->execute();
while ($payment = $q
->fetchAssoc()) {
$payments[$payment['id']] = $payment;
}
}
return $payments;
}
/**
* Load a custom offline payment by ID.
* @param string $id
*/
function custom_offline_payment_load($id) {
static $custom_offline_payments = array();
if (!isset($custom_offline_payments[$id])) {
// Get custom payment properties from own table.
$payment = db_select('commerce_custom_offline_payment', 'cop');
$payment
->fields('cop');
$payment
->condition('id', $id);
$payment = $payment
->execute();
$payment = $payment
->fetchAssoc();
// Put payment to static variable.
$custom_offline_payments[$id] = $payment;
}
return $custom_offline_payments[$id];
}
/**
* Delete a checkout payment.
* @param array $payment
*/
function commerce_cop_payment_save($payment, $skip_reset = FALSE) {
if (!isset($payment['id'])) {
$payment['id'] = $payment['id'];
}
$counter = db_query('SELECT 1 FROM {commerce_custom_offline_payment} WHERE id = :id', array(
':id' => $payment['id'],
));
$counter = $counter
->fetchColumn();
if ($counter) {
$payment_record = drupal_write_record('commerce_custom_offline_payment', $payment, array(
'id',
));
}
else {
$payment_record = drupal_write_record('commerce_custom_offline_payment', $payment);
}
// Clear the necessary caches.
if (!$skip_reset) {
entity_defaults_rebuild();
rules_clear_cache(TRUE);
}
return $payment_record;
}
/**
* Delete a checkout payment.
* @param array $payment
*/
function commerce_cop_payment_delete($payment, $skip_reset = FALSE) {
$delete = db_delete('commerce_custom_offline_payment');
$delete
->condition('id', $payment['id']);
$delete
->execute();
// Clear the necessary caches.
if (!$skip_reset) {
entity_defaults_rebuild();
rules_clear_cache(TRUE);
}
}
/**
* Implements hook_features_api().
*/
function commerce_cop_features_api() {
return array(
'commerce_custom_offline_payment' => array(
'name' => t('Commerce Custom Offline Payments'),
'default_hook' => 'commerce_custom_offline_payments',
'feature_source' => TRUE,
'file' => drupal_get_path('module', 'commerce_cop') . '/commerce_custom_offline_payment.features.inc',
),
);
}
/**
* Implements hook_token_info_alter().
*/
function commerce_cop_token_info_alter(&$info) {
$info['tokens']['commerce-order']['payment-method-information'] = array(
'name' => t('Payment method information'),
'description' => t('Data or instructions to make payment.'),
);
}
/**
* Implements hook_tokens().
*/
function commerce_cop_tokens($type, $tokens, array $data = array(), array $options = array()) {
static $token_replacement_running;
$replacements = array();
if ($type == 'commerce-order' && !empty($data['commerce-order'])) {
$order = $data['commerce-order'];
// Get the order payment method if it is set.
if (isset($order->data['payment_method'])) {
$payment_method = commerce_payment_method_instance_load($order->data['payment_method']);
// Use the payment method information settings if exists.
if (!empty($payment_method['settings']['information'])) {
$payment_info = check_markup($payment_method['settings']['information']['value'], $payment_method['settings']['information']['format']);
}
else {
$payment = custom_offline_payment_load($payment_method['method_id']);
if ($payment && !empty($payment['information'])) {
$payment_info = $payment['information'];
// Run token replacement.
if (!isset($token_replacement_running)) {
// We need to avoid recursion since commerce_cop_tokens() calls this
// function too. So if a token replacement is running don't trigger another
// token replacement.
$token_replacement_running = TRUE;
$payment_info = token_replace($payment_info, array(
'commerce_order' => $order,
), array(
'language' => $GLOBALS['language'],
'clear' => TRUE,
));
unset($token_replacement_running);
}
}
}
if (!empty($payment_info)) {
foreach ($tokens as $name => $original) {
switch ($name) {
case 'payment-method-information':
$replacements[$original] = $payment_info;
break;
}
}
}
}
}
return $replacements;
}