Skip to content

Commit

Permalink
ITS-22: added module (#1)
Browse files Browse the repository at this point in the history
* ITS-22: added module

* ITS-22: update module

* ITS-22: update README.md

* ITS-22: modified README.md

* ITS-22: update currency in create invoice

* ITS-22: modified README.md

* ITS-22: added default uc_rbkmoney_log

* ITS-22: post review fix

* ITS-22: added if

* ITS-22: added constants

* ITS-22: added link

* ITS-22: rename module

* ITS-22: rename module in README.md

* ITS-22: update constant
  • Loading branch information
avcherkasov authored and antonlva committed May 21, 2018
1 parent c562f33 commit 2186c3d
Show file tree
Hide file tree
Showing 10 changed files with 997 additions and 1 deletion.
53 changes: 53 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm

*.DS_Store

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties

# Target folder
target
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
# rbkmoney-cms-drupal-ubercart
# Drupal uc_rbkmoney module

Модуль оплаты `rbkmoney_checkout_ubercart` необходим для интеграции с сервисом [RBKmoney](http://rbk.money/) на базе CMS Drupal и компонента [Drupal ubercart](https://www.drupal.org/project/ubercart).


### Требования к CMS Drupal:
* версия `7.x`;
* компонент `Ubercart`. Модуль тестировался на `Ubercart 7.x-3.10`


### Установка и настройка

С установкой и настройкой модуля можно ознакомиться в документации самого [модуля](https://www.drupal.org/sandbox/antonlva/rbkmoney_checkout_ubercart)


### Нашли ошибку или у вас есть предложение по улучшению модуля?

Пишите нам [email protected]
При обращении необходимо:
* Указать наименование CMS и компонента магазина, а также их версии
* Указать версию платежного модуля (доступна в списке меню `Модули`)
* Описать проблему или предложение
* Приложить снимок экрана (для большей информативности)
19 changes: 19 additions & 0 deletions rbkmoney_checkout_ubercart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Drupal uc_rbkmoney module

Платежный модуль для Ubercart, позволяющий принимать платежи через систему RBKmoney.
Модуль отправляет запрос платежа платежной системе, и обрабатывает возвращаемый ответ, меняя в соответствии с ним статус заказа.


### Установка и настройка

Модуль находится в папке `rbkmoney_checkout_ubercart`

1. Установка. Копируем папку с модулем в каталог `/sites/all/modules`
2. Включаем модуль на странице `/admin/build/modules`, раздел **Ubercart - payment**
3. Настройка осуществляется по адресу `/admin/store/settings/uc_rbkmoney`
1. Прописываем ShopId магазина (берем из личного кабинета)
2. Копируем приватный ключ (берем из личного кабинета)
3. Копируем публичный ключ для обработки уведомлений (берем из личного кабинета)
4. Указать в личном кабинете callback URL для обработки уведомлений о смене статуса инвойса: https://<your-site>/rbkmoney_checkout_ubercart/callback/result

Пожалуйста, обязательно делайте бекапы!
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php
/**
* Callback URL for RBKmoney server response
*
* @see https://rbkmoney.github.io/webhooks-events-api/
*/
function rbkmoney_checkout_ubercart_callback_result()
{
$type = 'callback_result';
$content = file_get_contents('php://input');
_rbkmoney_checkout_ubercart_logger($type, "<pre>%logs</pre>", ['%logs' => var_export(['content' => $content], TRUE)]);

if (empty($_SERVER[SIGNATURE])) {
http_response_code(HTTP_CODE_BAD_REQUEST);
_rbkmoney_checkout_ubercart_logger($type, "Webhook notification signature missing. <pre>%logs</pre>", ['%logs' => var_export(['content' => $content], TRUE)]);
echo json_encode(array('message' => 'Webhook notification signature missing'));
exit();
}

$params_signature = _rbkmoney_checkout_ubercart_get_parameters_content_signature($_SERVER[SIGNATURE]);
if (empty($params_signature[SIGNATURE_ALG])) {
_rbkmoney_checkout_ubercart_logger($type, "Missing required parameter " . SIGNATURE_ALG . " signature. <pre>%logs</pre>", ['%logs' => var_export(['content' => $content], TRUE)]);
http_response_code(HTTP_CODE_BAD_REQUEST);
echo json_encode(array('message' => 'Missing required parameter ' . SIGNATURE_ALG));
exit();
}

if (empty($params_signature[SIGNATURE_DIGEST])) {
_rbkmoney_checkout_ubercart_logger($type, "Missing required parameter " . SIGNATURE_DIGEST . " signature. <pre>%logs</pre>", ['%logs' => var_export(['content' => $content], TRUE)]);
http_response_code(HTTP_CODE_BAD_REQUEST);
echo json_encode(array('message' => 'Missing required parameter ' . SIGNATURE_DIGEST));
exit();
}

$signature = _rbkmoney_checkout_ubercart_urlsafe_b64decode($params_signature[SIGNATURE_DIGEST]);
if (!_rbkmoney_checkout_ubercart_verification_signature($content, $signature, MERCHANT_CALLBACK_PUBLIC_KEY)) {
_rbkmoney_checkout_ubercart_logger($type, "Webhook notification signature mismatch. <pre>%logs</pre>",
['%logs' => var_export([
$_SERVER[SIGNATURE],
$content,
$signature,
MERCHANT_CALLBACK_PUBLIC_KEY
], TRUE)]);
http_response_code(HTTP_CODE_BAD_REQUEST);
echo json_encode(array('message' => 'Webhook notification signature mismatch'));
exit();
}

$required_fields = [INVOICE, EVENT_TYPE];
$data = json_decode($content, TRUE);

foreach ($required_fields as $field) {
if (empty($data[$field])) {
_rbkmoney_checkout_ubercart_logger($type, " One or more required fields are missing <pre>%logs</pre>",
['%logs' => var_export([
'input_content' => $content,
'required_fields' => $required_fields
], TRUE)]);
http_response_code(HTTP_CODE_BAD_REQUEST);
echo json_encode(array('message' => 'One or more required fields are missing'));
exit();
}
}

$current_shop_id = (int)variable_get('uc_rbkmoney_shop_id');
if ($data[INVOICE][INVOICE_SHOP_ID] != $current_shop_id) {
_rbkmoney_checkout_ubercart_logger($type, ' Webhook notification ' . INVOICE_SHOP_ID . ' mismatch<pre>%logs</pre>', ['%logs' => var_export($content, TRUE)]);
http_response_code(HTTP_CODE_BAD_REQUEST);
echo json_encode(array('message' => INVOICE_SHOP_ID . ' is missing'));
exit();
}

if (empty($data[INVOICE][INVOICE_METADATA][ORDER_ID])) {
_rbkmoney_checkout_ubercart_logger($type, ORDER_ID . ' is missing <pre>%logs</pre>', ['%logs' => var_export($content, TRUE)]);
http_response_code(HTTP_CODE_BAD_REQUEST);
echo json_encode(array('message' => ORDER_ID . ' is missing'));
exit();
}

$order_id = $data[INVOICE][INVOICE_METADATA][ORDER_ID];
$order = uc_order_load($order_id);
$order_amount = _rbkmoney_checkout_ubercart_prepare_amount($order->order_total);
$invoice_amount = $data[INVOICE][INVOICE_AMOUNT];
if ($order_amount != $invoice_amount) {
_rbkmoney_checkout_ubercart_logger($type, 'Received amount vs Order amount mismatch <pre>%logs</pre>', ['%logs' => var_export($content, TRUE)]);
http_response_code(HTTP_CODE_BAD_REQUEST);
echo json_encode(array('message' => 'Received amount vs Order amount mismatch'));
exit();
}

$allowed_event_types = [EVENT_TYPE_INVOICE_PAID, EVENT_TYPE_INVOICE_CANCELLED];
if (in_array($data[EVENT_TYPE], $allowed_event_types)) {
$invoice_status = $data[INVOICE][INVOICE_STATUS];
_rbkmoney_checkout_ubercart_update_status_order($order_id, $invoice_status);
}

exit();
}

function _rbkmoney_checkout_ubercart_urlsafe_b64decode($string)
{
$data = str_replace(array('-', '_'), array('+', '/'), $string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}

function _rbkmoney_checkout_ubercart_urlsafe_b64encode($string)
{
$data = base64_encode($string);
return str_replace(array('+', '/'), array('-', '_'), $data);
}

function _rbkmoney_checkout_ubercart_get_parameters_content_signature($content_signature)
{
preg_match_all(SIGNATURE_PATTERN, $content_signature, $matches, PREG_PATTERN_ORDER);
$params = array();
$params[SIGNATURE_ALG] = !empty($matches[1][0]) ? $matches[1][0] : '';
$params[SIGNATURE_DIGEST] = !empty($matches[2][0]) ? $matches[2][0] : '';
return $params;
}

function _rbkmoney_checkout_ubercart_verification_signature($data = '', $signature = '', $public_key = '')
{
if (empty($data) || empty($signature) || empty($public_key)) {
return FALSE;
}

$public_key_id = openssl_get_publickey($public_key);
if (empty($public_key_id)) {
return FALSE;
}

$verify = openssl_verify($data, $signature, $public_key_id, OPENSSL_SIGNATURE_ALG);
return ($verify == OPENSSL_VERIFY_SIGNATURE_IS_CORRECT);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

/**
* Define payment statuses
*/
define('UC_RBKMONEY_STATUS_FAILED', 'cancelled');
define('UC_RBKMONEY_STATUS_SUCCESS', 'paid');

define('ANONYMOUS_USER_ID', 0);

/**
* Module data
*/
define('MODULE_NAME', 'rbkmoney_checkout_ubercart');
define('MODULE_BASE_PATH', base_path() . drupal_get_path('module', MODULE_NAME));

/**
* Api URL
*/
define('API_URL', 'https://api.rbk.money/v1/');

/**
* Merchant settings
*/
define('MERCHANT_PRIVATE_KEY', trim(variable_get('uc_rbkmoney_merchant_private_key', 'merchant_private_key')));
define('MERCHANT_CALLBACK_PUBLIC_KEY', trim(variable_get('uc_rbkmoney_merchant_callback_public_key', 'merchant_callback_public_key')));

/**
* Payment form
*/
define('BASE_URL', $GLOBALS['base_url']);
define('PAYMENT_FORM_URL', 'https://checkout.rbk.money/checkout.js');
define('PAYMENT_FORM_SUCCESS_URL', BASE_URL . '/'.MODULE_NAME.'/success');
define('PAYMENT_FORM_PATH_IMG_LOGO', BASE_URL . MODULE_BASE_PATH . '/images/logo_default.png');

/**
* Payment method logo
*/
define('PAYMENT_METHOD_PATH_IMG_LOGO', MODULE_BASE_PATH . '/images/logo_rbkmoney.png');

/**
* Payment form default css button
*/
define('PAYMENT_FORM_DEFAULT_CSS_BUTTON', trim(variable_get('uc_rbkmoney_payform_css_button', '
button.rbkmoney-button {
border: 1px solid #e4e4e4;
border-bottom: 1px solid #b4b4b4;
border-left-color: #d2d2d2;
border-right-color: #d2d2d2;
color: #2f2f2f;
cursor: pointer;
text-align: center;
margin-top: 2px;
margin-bottom: 1em;
margin-right: 0.6em;
padding: 4px 17px;
border-radius: 15px;
background: #eee;
box-shadow: none;
font: normal 0.929em/1.1em "Lucida Grande", "Lucida Sans Unicode", Verdana, sans-serif;
}
button.rbkmoney-button:hover {
background: #dedede;
}'
)));

/**
* Create invoice settings
*/
define('CREATE_INVOICE_TEMPLATE_DUE_DATE', 'Y-m-d\TH:i:s\Z');
define('CREATE_INVOICE_DUE_DATE', '+1 days');

/**
* Constants for Callback
*/

define('SIGNATURE', 'HTTP_CONTENT_SIGNATURE');
define('SIGNATURE_ALG', 'alg');
define('SIGNATURE_DIGEST', 'digest');
define('SIGNATURE_PATTERN', "|alg=(\S+);\sdigest=(.*)|i");


define('EVENT_TYPE', 'eventType');

// EVENT TYPE INVOICE
define('EVENT_TYPE_INVOICE_CREATED', 'InvoiceCreated');
define('EVENT_TYPE_INVOICE_PAID', 'InvoicePaid');
define('EVENT_TYPE_INVOICE_CANCELLED', 'InvoiceCancelled');
define('EVENT_TYPE_INVOICE_FULFILLED', 'InvoiceFulfilled');

// EVENT TYPE PAYMENT
define('EVENT_TYPE_PAYMENT_STARTED', 'PaymentStarted');
define('EVENT_TYPE_PAYMENT_PROCESSED', 'PaymentProcessed');
define('EVENT_TYPE_PAYMENT_CAPTURED', 'PaymentCaptured');
define('EVENT_TYPE_PAYMENT_CANCELLED', 'PaymentCancelled');
define('EVENT_TYPE_PAYMENT_FAILED', 'PaymentFailed');


define('INVOICE', 'invoice');
define('INVOICE_ID', 'id');
define('INVOICE_SHOP_ID', 'shopID');
define('INVOICE_STATUS', 'status');
define('INVOICE_DUE_DATE', 'dueDate');
define('INVOICE_AMOUNT', 'amount');
define('INVOICE_CURRENCY', 'currency');
define('INVOICE_REASON', 'reason');
define('INVOICE_METADATA', 'metadata');

define('ORDER_ID', 'order_id');

define('PAYMENT', 'payment');
define('PAYMENT_ID', 'id');
define('PAYMENT_AMOUNT', 'amount');
define('PAYMENT_CURRENCY', 'currency');
define('PAYMENT_STATUS', 'status');

/**
* Api params
*/
define('API_INVOICE_ID', 'invoice_id');

/**
* Openssl verify
*/
define('OPENSSL_VERIFY_SIGNATURE_IS_CORRECT', 1);
define('OPENSSL_VERIFY_SIGNATURE_IS_INCORRECT', 0);
define('OPENSSL_VERIFY_ERROR', -1);
define('OPENSSL_SIGNATURE_ALG', OPENSSL_ALGO_SHA256);

/**
* HTTP CODE
*/
define('HTTP_CODE_OK', 200);
define('HTTP_CODE_CREATED', 201);
define('HTTP_CODE_MOVED_PERMANENTLY', 301);
define('HTTP_CODE_BAD_REQUEST', 400);
define('HTTP_CODE_INTERNAL_SERVER_ERROR', 500);

/**
* HTTP METHOD
*/
define('HTTP_METHOD_GET', 'GET');
define('HTTP_METHOD_POST', 'POST');
8 changes: 8 additions & 0 deletions rbkmoney_checkout_ubercart/rbkmoney_checkout_ubercart.info
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name = RBKmoney
description = Provide RBKmoney payment gateway
package = "Ubercart - payment"
core = 7.x
php = 5.x
configure = admin/store/settings/rbkmoney_checkout_ubercart
dependencies[] = uc_payment
project = rbkmoney
Loading

0 comments on commit 2186c3d

Please sign in to comment.