Drupal → Создание собственного способа оплаты для Drupal Commerce 1
Каждый способ оплаты в 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); }
Комментарии
Это самый лучший блог о Друпале, на который я когда либо подписывался.
Комментарии в коде лучше на одном языке. А тов 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[...]
можете, только не понятно зачем
Удаленный сервер присылает мне уникальный номер платежной операции, который надо использовать в дургих функциях. Поэтому я хочу понять как мне этот номер занести в $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) адрес страницы, на которую перенаправляется пользователь после оплаты:
И получаю ошибку: Notice: Undefined index: payment_redirect_key
payment_redirect_key доступен только в PAYMENTNAME_redirect_form
все в порядке. это я опечатался в коде. Спасибо за разъяснения!
Подскажите, пожалуйста, как обновить статус транзакции?
Сам спросил сам отвечаю:
Здравствуйте. А если мне нужно запросить некоторые данные от пользователя и сохранить их в заказ, чтобы просмотреть на странице заказов admin/commerce/orders/, как мне сохранить $pane_values?
Спасибо.
Спасибо
А вот такой вопрос возник. Сделал свой способ оплаты через кредитные карты, все отлично работает, за исключением одного.
Если после оплаты человек не нажал "Вернуться в магазин" ( ну или еще как). Оплата проходит, но на сайте заказ весит в "Корзине".
Сама платежка отправляет $_POST['status'] где лучше принять эти переменные и обновить статус заказа?
Спасибо. Надеюсь понятно написал.
Необходимо создать/обновить транзакцию. У меня это сделано так:
А собственно $_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']
но что-то ничего не вышло
где ещё их сохранять?
в commerce_payment_example данные сохраняются в транзакцию.
у себя добавил поле markup ордеру и пишу туда инфу оплаты, это позволяет показать юзеру данные оплаты в его заказе на странице user/*/order/*
все зависит от задачи видимо, но когда в data данные пишутся, то они доступны только программно, так ведь?
так
Очень полезная статья...Огромное спасибо!!!
Подскажите, пожалуйста, по какой причине для одного из блоков edit-panes-payment-payment-method-cod-wrapper может задаваться параметр style=display: none??? На сайте пропал пункт Оплата наличными, хотя в коде страницы он есть, но скрыт. Заранее спасибо!
Отличная статья, спасибо!
Только возник вопрос, как при написании своего on-site способа оплаты, отобразить какие-то дополнительные данные клиенту после оплаты (например, чек с информацией от платежного шлюза или дополнительные кнопки)? Т.е. как в mypamentmethod_submit_form_submit можно что-то дописать на итоговую форму? Есть ли стандартный способ кастомизации completion message (или того, что отображается под ним)?
hook_form_commerce_checkout_form_complete_alter
Уважаемые, подскажите пожалуйста, почему не обрабатывается - checkout/[order_id]/payment/return/[key]?
Платежный шлюз возвращается на этот путь checkout/[order_id]/payment/return/[key] и вместо успешного завершения заказа, я вижу информацию о том, что такого пути не существует.
PAYMENTNAME_redirect_form_validate() и PAYMENTNAME_redirect_form_submit() создал, но видимо до них управление не доходит.
Ну вот, стоило пожаловаться и решение нашлось само по себе. Отыскал пример у какого-то индуса, дай Бог ему здоровья.
https://github.com/payu-india/PayU-Integration-Kit-Drupal-Commerce/blob/....
Похоже у меня была где-то опечатка в функции создания PAYMENTNAME_redirect_form(), поэтому при возврате от платежного шлюза не происходила валидация.
Перепечатав код по примеру, заработало. Но до конца так и не понял где я ошибся.
Почему в $settings задается 'cancel_return' и 'return', но платежный шлюз просит передавать ему адрес для возврата в параметре с именем 'result_url_1'?
Получается 'cancel_return' и 'return' это для Друпала, но почему тогда в примере у Автора заданы 'redirect_uri' и 'error_uri' и так работает?
Оставить комментарий