xandeadx.ru Блог музицирующего веб-девелопера

Drupal → Создание собственного способа оплаты для Drupal Commerce

Опубликовано в

Каждый способ оплаты в Drupal Commerce это отдельный модуль.

Способы оплаты делятся на два вида:

  • On-site — когда для завершения оплаты не нужно покидать пределы сайта.
  • Off-site — когда для завершения оплаты нужно сделать редирект на сторонний сайт или отправить туда форму.

Процесс создания своего способа оплаты состоит из реализации хука hook_commerce_payment_method_info() и необходимых функций обратного вызова:

1. Реализуем хук hook_commerce_payment_method_info(), в котором возвращаем базовую информацию о способе оплаты:

/**
 * Implements hook_commerce_payment_method_info().
 */
function mypaymentmethod_commerce_payment_method_info() {
  return array(
    // Системное название способа оплаты.
    'mypaymentmethod' => array(
      // Название способа оплаты для отображения в админке.
      'title' => t('My Payment Method'),
      // Название способа оплаты для отображения в форме чекаута. Может содержать html.
      // Опционально.
      'display_title' => t('My Payment Method'),
      // Описание способа оплаты. Опционально.
      'description' => t('Example of Payment Method'),
      // Состояние способа оплаты при включении модуля: TRUE — включён, FALSE — выключен
      // (по умолчанию). Опционально.
      'active' => TRUE,
      // Сможет ли администратор самостоятельно добавить этот способ оплаты для
      // завершённого заказа. Опционально. По умолчанию TRUE.
      'terminal' => TRUE,
      // Нужно ли покупателю покидать пределы сайта при оплате заказа этим способом.
      // Опционально. По умолчанию FALSE.
      'offsite' => TRUE,
      // Автоматический редирект на сторонний сайт при оплате заказа этим способом.
      // Опционально. По умолчанию FALSE.
      'offsite_autoredirect' => TRUE,
    ),
  );
}

2. Если способ оплаты имеет настройки, то реализуем callback PAYMENTNAME_settings_form():

/**
 * Payment method callback: settings form.
 */
function mypaymentmethod_settings_form($settings = NULL) {
  $form = array();
  $settings = (array)$settings + array(
    'merchant_id' => '',
    'information' => '',
  );
 
  // Пример настройки — идентификатор магазина
  $form['merchant_id'] = array(
    '#type' => 'textarea',
    '#title' => t('Merchant ID'),
    '#default_value' => $settings['merchant_id'],
  );
 
  // Пример настройки — информация о способе оплаты
  $form['information'] = array(
    '#type' => 'textarea',
    '#title' => t('Information'),
    '#description' => t('Information you would like to be shown to users when they select this payment method.'),
    '#default_value' => $settings['information'],
  );
 
  return $form;
}

3. Если при выборе способа оплаты нужно показывать какую-нибудь информацию или для оплаты от покупателя требуются дополнительные данные, то реализуем callback PAYMENTNAME_submit_form():

/**
 * Payment method callback: submit form.
 */
function mypaymentmethod_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
  $form = array();
  $pane_values += array('name' => '');
 
  // Выводим информацию о способе оплаты
  if (!empty($payment_method['settings']['information'])) {
    $form['mypaymentmethod_information'] = array(
      '#markup' => $payment_method['settings']['information']
    );
  }
 
  // Дополнительные данные — поле для ввода имени
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#description' => t('Your full name.'),
    '#default_value' => $pane_values['name'],
    '#required' => TRUE,
  );
 
  return $form;
}

4. Если в прошлом пункте возвращается форма и нужно её проверять, то делаем это в PAYMENTNAME_submit_form_validate():

/**
 * Payment method callback: submit form validation.
 */
function mypaymentmethod_submit_form_validate($payment_method, $pane_form, $pane_values, $order, $form_parents = array()) {
  // Делаем проверку на длину имени
  if (drupal_strlen($pane_values['name']) < 2) {
    form_set_error(implode('][', array_merge($form_parents, array('name'))), t('You must enter a name two or more characters long.'));
    return FALSE;
  }
}

5. Если пользователя не нужно отсылать на сайт платёжной системы, то завершаем оплату в PAYMENTNAME_submit_form_submit():

/**
 * Payment method callback: submit form submission.
 */
function mypamentmethod_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
  $order->data['mypaymentmethod'] = $pane_values;
 
  // Создаём транзакцию
  $transaction = commerce_payment_transaction_new('mypamentmethod', $order->order_id);
  $transaction->instance_id = $payment_method['instance_id'];
  $transaction->amount = $charge['amount'];
  $transaction->currency_code = $charge['currency_code'];
  $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
  $transaction->message = t('The payment has completed.');
  commerce_payment_transaction_save($transaction);
}

Иначе в PAYMENTNAME_redirect_form() формируем форму с данными, которые будут отправлены на сайт платёжной системы:

/**
 * Payment method callback: payment redirect form.
 */
function mypaymentmethod_redirect_form($form, &$form_state, $order, $payment_method) {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
 
  // Адрес платёжного шлюза
  $form['#action'] = 'https://example.com/payment';
 
  // Пример данных — идентификатор магазина
  $form['merchant_id'] = array(
    '#type' => 'hidden',
    '#value' => $payment_method['settings']['merchant_id'],
  );
 
  // Пример данных — сумма заказа
  $form['amount'] = array(
    '#type' => 'hidden',
    '#value' => $order_wrapper->commerce_order_total->amount->value(),
  );
 
  // Пример данных — адрес для редиректа после успешной оплаты
  $form['redirect_uri'] = array(
    '#type' => 'hidden',
    '#value' => url('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key'], array('absolute' => TRUE)),
  );
 
  // Пример данных — адрес для редиректа в случаем если оплата не состоялась
  $form['error_uri'] = array(
    '#type' => 'hidden',
    '#value' => url('checkout/' . $order->order_id . '/payment/back/' . $order->data['payment_redirect_key'], array('absolute' => TRUE)),
  );
 
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Pay'),
  );
 
  return $form;
}

После того как покупатель оплатит заказ на сайте платёжной системы и возвратится на страницу checkout/[order_id]/payment/return/[key], сначала будет вызван callback PAYMENTNAME_redirect_form_validate(), в котором можно проверить факт оплаты, а потом PAYMENTNAME_redirect_form_submit(), в котором нужно завершить оплату:

/**
 * Payment method callback: redirect form return validation.
 */
function mypaymentmethod_redirect_form_validate($order, $payment_method) {
  if (/* ... */) {
    return TRUE; // будет вызван callback PAYMENTNAME_redirect_form_submit()
  }
  else {
    return FALSE; // покупатель возвратится на шаг назад в форме чекаута
  }
}
 
/**
 * Payment method callback: redirect form submission.
 */
function mypaymentmethod_redirect_form_submit($order, $payment_method) {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
 
  // Создаём транзакцию
  $transaction = commerce_payment_transaction_new('mypamentmethod', $order->order_id);
  $transaction->instance_id = $payment_method['instance_id'];
  $transaction->amount = $order_wrapper->commerce_order_total->amount->value();
  $transaction->currency_code = $order_wrapper->commerce_order_total->currency_code->value();
  $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
  $transaction->message = t('The payment has completed.');
  commerce_payment_transaction_save($transaction);
}
Написанное актуально для Drupal Commerce 1.x
Похожие записи

Комментарии RSS

Это самый лучший блог о Друпале, на который я когда либо подписывался.

Комментарии в коде лучше на одном языке. А тов Doc на английском, а внутри на русском

Здесь комментарии на русском для тех кто нуб в английском, а не для копипаста в рабочий код)

Расскажите поподробней что хранится в $pane_values? Я правильно понимаю, что там хранятся данные, которые относятся только к текущему заказу и в дополнении к мим можно в процессе оплаты добавлять еще данные (как у вас в примере с добавлением поля для ввода имени)? И верно ли, что добавленные таким образом данные могут быть сохранены в заказе ($order->data['mypaymentmethod'] = $pane_values;) и будут доступны в дальнейшем? Если так, то как их позднее можно вызвать? Примерно так: $order->data['mypaymentmethod']['name'] (если брать опять же ваш пример)?

в $pane_values хранятся данные из PAYMENTNAME_submit_form.
свои данные добавлять можно.

А вызвать как эти данные потом можно?

$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$tiket = $order_wrapper->...а дальше?

И еще вопрос: Если в PAYMENTNAME_submit_form в $pane_values небыли внесены данные, то я могу в ф-ии mypamentmethod_submit_form_submit туда что-то внести?

$order->data[...]

И еще вопрос: Если в PAYMENTNAME_submit_form в $pane_values небыли внесены данные, то я могу в ф-ии mypamentmethod_submit_form_submit туда что-то внести?

можете, только не понятно зачем

можете, только не понятно зачем

Удаленный сервер присылает мне уникальный номер платежной операции, который надо использовать в дургих функциях. Поэтому я хочу понять как мне этот номер занести в $order

$order->data['paymentname']['unique_number'] = $unique_number;
commerce_order_save($order);

но возможно нужно хранить эти данные в транзакции, а не в заказе

но возможно нужно хранить эти данные в транзакции, а не в заказе

я в paymentname_redirect_form хочу передать unique_number, а туда данные из транзакции не попадают (вроде бы).

Кстати, если я в paymentname_redirect_form пишу следующее:
$unique_number = $order->data['paymentname']['unique_number'];
То получаю ошибку — Notice: Undefined variable: unique_number.

Вот еще момент:
в ф-ии mypamentmethod_submit_form_submit я отправляю на удаленный сервер (через curl) адрес страницы, на которую перенаправляется пользователь после оплаты:

$back_url = url('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key'], array('absolute' => TRUE));

И получаю ошибку: Notice: Undefined index: payment_redirect_key

payment_redirect_key доступен только в PAYMENTNAME_redirect_form

Кстати, если я в paymentname_redirect_form пишу следующее:
$unique_number = $order->data['paymentname']['unique_number'];
То получаю ошибку — Notice: Undefined variable: unique_number.

все в порядке. это я опечатался в коде. Спасибо за разъяснения!

Подскажите, пожалуйста, как обновить статус транзакции?

Сам спросил сам отвечаю:

function mypaymentmethod_transaction($order, $status_code) {
 
	$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
 
	// If this is a prior authorization for which we've already
	// created a transaction...
	$transactions = commerce_payment_transaction_load_multiple(array(), array('order_id' => $order->order_id));
	if (!empty($transactions)) {
		// Load the prior transaction and update that with the capture values.
		$transaction = reset($transactions);
	}
	else {
		// Create a new payment transaction for the order.
		$transaction = commerce_payment_transaction_new('mypaymentmethod', $order->order_id);
		$transaction->instance_id = $order->data['payment_method'];
	}

Здравствуйте. А если мне нужно запросить некоторые данные от пользователя и сохранить их в заказ, чтобы просмотреть на странице заказов admin/commerce/orders/, как мне сохранить $pane_values?

Спасибо.

Спасибо

А вот такой вопрос возник. Сделал свой способ оплаты через кредитные карты, все отлично работает, за исключением одного.

Если после оплаты человек не нажал "Вернуться в магазин" ( ну или еще как). Оплата проходит, но на сайте заказ весит в "Корзине".

Сама платежка отправляет $_POST['status'] где лучше принять эти переменные и обновить статус заказа?

Спасибо. Надеюсь понятно написал.

Необходимо создать/обновить транзакцию. У меня это сделано так:

/**
 * Create a transaction and associate it with an order.
 */
 
function modulename_transaction($order, $status_code) {
 
	$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
 
	// If this is authorization for which we've already
	// created a transaction...
	$transactions = commerce_payment_transaction_load_multiple(array(), array('order_id' => $order->order_id));
	if (!empty($transactions)) {
		// Load transaction and update that with the capture values.
		$transaction = reset($transactions);
	}
	else {
		// Create a new payment transaction for the order.
		$transaction = commerce_payment_transaction_new('commerce_avangard', $order->order_id);
		$transaction->instance_id = $order->data['payment_method'];
	}
	$transaction->amount = $order_wrapper->commerce_order_total->amount->value();
	$transaction->currency_code = $order_wrapper->commerce_order_total->currency_code->value();
 
	// Set a status for the payment - one of COMMERCE_PAYMENT_STATUS_SUCCESS, COMMERCE_PAYMENT_STATUS_PENDING or COMMERCE_PAYMENT_STATUS_FAILURE.
	switch ($status_code) {
		case '1':
			$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
			$transaction->message = t('The payment has pending.');
//			commerce_order_status_update($order, 'checkout_payment');
			break;
		case '3':
			$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
			$transaction->message = t('The payment has pending.');
			commerce_order_status_update($order, 'checkout_complete');
			commerce_checkout_complete($order);			
			break;
		case '2':
			$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
			$transaction->message = t('The payment has canceled.');
//			commerce_order_status_update($order, 'canceled');
			break;
	}
	commerce_payment_transaction_save($transaction);
}

А собственно $_POST['status'] от платежгой системы можно отлавливать через hook_menu_item

Гарик, собственно так и пришлось сделать, другого в голову не пришло, просто думал как то более кошерно что ли можно сделать:)

Видимо, я что-то делаю не так, ибо гугление ничего не дало. Вопрос в следующем - как сделать так, чтобы вновь созданные способы оплаты стали доступны вьюхе Payments (admin/content/payment и user/%/payment). После добавления нового способа оплаты в это представление по-прежнему попадают платежи только методов Collect on delivery и No payment required. Заранее спасибо!

сбросить кэш?

Гарик, к сожалению - нет )

Гарик, к сожалению - нет )

Никто не знает, как достать информацию из billing_info? Добавил туда дополнительное поле - не знаю как достать. И еще надо бы достать описание товара. Тоже не знаю откуда.

Как в методе mypaymentmethod_commerce_payment_method_info получить настройки платежного метода? Хотелось бы выводить название, которое админ введет в админ панели, а не из кода.

подскажите, пожалуйста. я создал свои методы модулем Custom Offline Payments, но описание к ним не выводится. можно же через хук его задать? если да, то как

не совсем пойму, зачем в 5-м пункте мы сохраняем данные в ордер
$order->data['mypaymentmethod'] = $pane_values;

мы их потом нигде не видим в админке в заказе

в примере платежного метода, данные сохраняются в транзакцию
$transaction->message = 'Number: @numberExpiration: @month/@year';
и это сообщение можно будет посмотреть в payment в заказе admin/commerce/orders/NUMBER/payment

загрузить транзакцию можно так
$payments = commerce_payment_transaction_load_multiple(NULL, array('order_id' => $order->order_number));

но если мы все же сохранили данные сюда $order->data['mypaymentmethod']
то как их выводить на странице user/*/order/* ?

вертел hook_page_alter(&$page)
$page['content']['system_main']['commerce_order']
но что-то ничего не вышло

зачем в 5-м пункте мы сохраняем данные в ордер

где ещё их сохранять?

где ещё их сохранять?

в commerce_payment_example данные сохраняются в транзакцию.
у себя добавил поле markup ордеру и пишу туда инфу оплаты, это позволяет показать юзеру данные оплаты в его заказе на странице user/*/order/*

все зависит от задачи видимо, но когда в data данные пишутся, то они доступны только программно, так ведь?

так

Очень полезная статья...Огромное спасибо!!!

Оставить комментарий

Содержимое этого поля является приватным и не будет отображаться публично. Если у вас есть аккаунт в Gravatar, привязанный к этому e-mail адресу, то он будет использован для отображения аватара.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступные HTML теги: <a> <i> <b> <strong> <code> <ul> <ol> <li> <blockquote> <em> <s>
  • Строки и параграфы переносятся автоматически.
  • Подсветка кода осуществляется с помощью тегов: <code>, <css>, <html>, <ini>, <javascript>, <sql>, <php>. Поддерживаемые стили выделения кода: <foo>, [foo].

Подробнее о форматировании