ITS-28: uploaded module

This commit is contained in:
avcherkasov 2017-04-07 18:29:13 +03:00
parent 7eb93018d7
commit 8a35706973
6 changed files with 746 additions and 1 deletions

53
.gitignore vendored Normal file
View File

@ -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

View File

@ -1 +1,19 @@
# rbkmoney-cms-wp-e-commerce
# rbkmoney-cms-wp-e-commerce
Платежный плагин RBKmoney для Wordpress + WP e-Commerce
Проверено на Wordpress 4.3.2, WP e-Commerce 3.12.0
### Установка и настройка модуля
#### Установка
- Поместите файлы в директорию "wp-content/plugins" в соответствии с их архитектурой
- Включите плагин в разделе Settings -> Store -> Payments (wp-admin/options-general.php?page=wpsc-settings&tab=gateway).
#### Для начала приема платежей на Вашем сайте необходимо:
- Настройте плагин в соответствии с данными из [личного кабинета RBKmoney](https://dashboard.rbk.money).
- Скопируйте notification Url и укажите его в настройках магазина в личном кабинете RBKmoney
- Сохраните изменения и проведите тестовый платеж

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,289 @@
<?php
/**
* Common settings
*/
$nzshpcrt_gateways[$num]['name'] = __('RBKmoney', 'wp-e-commerce');
$nzshpcrt_gateways[$num]['internalname'] = 'rbkmoney';
$nzshpcrt_gateways[$num]['function'] = 'gateway_rbkmoney_payment';
$nzshpcrt_gateways[$num]['form'] = "form_rbkmoney_payment";
$nzshpcrt_gateways[$num]['submit_function'] = "submit_rbkmoney_payment";
$nzshpcrt_gateways[$num]['payment_type'] = "rbkmoney";
$nzshpcrt_gateways[$num]['display_name'] = __('RBKmoney', 'wp-e-commerce');
$nzshpcrt_gateways[$num]['image'] = WPSC_URL . '/images/rbkmoney_payment.png';
require 'rbkmoney-payment/RBKmoneyPayment.php';
require 'rbkmoney-payment/RBKmoneyPaymentVerification.php';
/**
* Output payment button
*
* @param $separator
* @param $sessionid
*/
function gateway_rbkmoney_payment($separator, $sessionid)
{
global $wpdb;
$purchase_log_sql = $wpdb->prepare("SELECT * FROM `" . WPSC_TABLE_PURCHASE_LOGS . "` WHERE `sessionid`= %s LIMIT 1", $sessionid);
$purchase_log = $wpdb->get_results($purchase_log_sql, ARRAY_A);
// e.g. RUB
$currency = WPSC_Countries::get_currency_code(get_option('currency_type'));
$form_company_name = '';
if(!empty(trim(get_option('rbkmoney_payment_form_company_name')))) {
$form_company_name = 'data-name="' . trim(get_option('rbkmoney_payment_form_company_name')).'"';
}
$form_path_logo = '';
if(!empty(trim(get_option('rbkmoney_payment_form_company_name')))) {
$form_path_logo = 'data-logo="' . trim(get_option('rbkmoney_payment_form_path_logo')).'"';
}
$amount = number_format($purchase_log[0]['totalprice'], 2, '.', '');
$params = array(
'shop_id' => trim(get_option('rbkmoney_payment_shop_id')),
'currency' => $currency,
'product' => $purchase_log[0]['id'],
'description' => 'Order ID ' . $purchase_log[0]['id'],
'amount' => $amount,
'order_id' => $purchase_log[0]['id'],
'session_id' => $sessionid,
'merchant_private_key' => trim(get_option('rbkmoney_payment_private_key')),
);
try {
$rbk_api = new RBKmoneyPayment($params);
$invoice_id = $rbk_api->create_invoice();
$invoice_access_token = $rbk_api->create_access_token($invoice_id);
} catch (Exception $ex) {
echo $ex->getMessage();
exit();
}
$output = '<html><body><script src="https://checkout.rbk.money/payframe/payframe.js" class="rbkmoney-checkout"
data-invoice-id="' . $invoice_id . '"
data-invoice-access-token="' . $invoice_access_token . '"
data-endpoint-success="' . home_url('/?rbkmoney_payment_results') . '"
data-endpoint-failed="' . home_url('/?rbkmoney_payment_results') . '"
data-amount="' . $amount . '"
data-currency="' . $currency . '"
' . $form_company_name . '
' . $form_path_logo . '
>
</script></body></html>';
echo $output;
exit();
}
/**
* e.g. http{s}://{your-site}/?rbkmoney_payment_callback
*/
function nzshpcrt_rbkmoney_payment_callback()
{
global $wpdb;
if (isset($_GET['rbkmoney_payment_callback'])) {
if(empty($_SERVER[RBKmoneyPayment::SIGNATURE])) {
http_response_code(RBKmoneyPayment::HTTP_CODE_BAD_REQUEST);
exit();
}
$content = file_get_contents('php://input');
$required_fields = ['invoice_id', 'payment_id', 'amount', 'currency', 'created_at', 'metadata', 'status', 'session_id'];
$data = json_decode($content, TRUE);
foreach ($required_fields as $field) {
if (empty($data[$field])) {
http_response_code(RBKmoneyPayment::HTTP_CODE_BAD_REQUEST);
exit();
}
}
if (empty($data[RBKmoneyPayment::METADATA][RBKmoneyPayment::ORDER_ID])) {
http_response_code(RBKmoneyPayment::HTTP_CODE_BAD_REQUEST);
exit();
}
if (!$signature = base64_decode($_SERVER[RBKmoneyPayment::SIGNATURE], TRUE)) {
http_response_code(RBKmoneyPayment::HTTP_CODE_BAD_REQUEST);
exit();
}
if (!RBKmoneyPaymentVerification::verification_signature($content, $signature, trim(get_option('rbkmoney_payment_callback_public_key')))) {
http_response_code(RBKmoneyPayment::HTTP_CODE_BAD_REQUEST);
exit();
}
$purchase_log_sql = $wpdb->prepare( "SELECT * FROM `".WPSC_TABLE_PURCHASE_LOGS."` WHERE `id`= %s AND `sessionid`= %s LIMIT 1",
$data[RBKmoneyPayment::METADATA][RBKmoneyPayment::ORDER_ID],
$data[RBKmoneyPayment::METADATA][RBKmoneyPayment::SESSION_ID]
);
$purchase_log = $wpdb->get_results($purchase_log_sql,ARRAY_A);
if(empty($purchase_log)) {
http_response_code(RBKmoneyPayment::HTTP_CODE_BAD_REQUEST);
exit();
}
$all_statuses = array(
'1' => 'pending',
'2' => 'completed',
'3' => 'ok',
'4' => 'processed',
'5' => 'closed',
'6' => 'rejected',
);
if($data[RBKmoneyPayment::STATUS] == 'paid') {
$details = array(
'processed' => array_search('ok', $all_statuses),
'transactid' => $data[RBKmoneyPayment::INVOICE_ID],
'date' => time(),
);
$session_id = $data[RBKmoneyPayment::METADATA][RBKmoneyPayment::SESSION_ID];
wpsc_update_purchase_log_details( $session_id, $details, 'sessionid' );
transaction_results($session_id, false, $data[RBKmoneyPayment::INVOICE_ID]);
}
}
}
/**
* e.g. http{s}://{your-site}/?rbkmoney_payment_results
*/
function nzshpcrt_rbkmoney_payment_results()
{
if (isset($_GET['rbkmoney_payment_results'])) {
header('Location: ' . get_option('transact_url'), true, RBKmoneyPayment::HTTP_CODE_MOVED_PERMANENTLY);
exit;
}
}
/**
* To confirm that you have changed the fields in the admin panel
*
* @return bool
*/
function submit_rbkmoney_payment()
{
$text_fields = array(
'rbkmoney_payment_shop_id',
'rbkmoney_payment_form_path_logo',
'rbkmoney_payment_form_company_name',
'rbkmoney_payment_logs',
);
_rbkmoney_payment_update_options($text_fields, 'text');
$textarea_fields = array(
'rbkmoney_payment_private_key',
'rbkmoney_payment_callback_public_key',
);
_rbkmoney_payment_update_options($textarea_fields, 'textarea');
return true;
}
/**
* Helper function for updating fields
*
* @param $fields array
* @param $type string
*/
function _rbkmoney_payment_update_options($fields, $type)
{
foreach ($fields as $field) {
if (isset($_POST[$field])) {
if ($type == 'textarea') {
update_option($field, sanitize_textarea_field(trim($_POST[$field])));
} else if ($type == 'text') {
update_option($field, sanitize_text_field(trim($_POST[$field])));
}
}
}
}
/**
* Output fields in the admin panel
*
* @return string
*/
function form_rbkmoney_payment()
{
$output = "
<tr>
<td>" . __('Shop ID', 'wp-e-commerce') . "</td>
<td>
<input type='text' size='60' value='" . trim(get_option('rbkmoney_payment_shop_id')) . "' name='rbkmoney_payment_shop_id' />
<p class='description'>
" . __('Number of the merchant\'s shop system RBKmoney', 'wp-e-commerce') . "
</p>
</td>
</tr>
<tr>
<td>" . __('Logo in payment form', 'wp-e-commerce') . "</td>
<td>
<input type='text' size='60' value='" . trim(get_option('rbkmoney_payment_form_path_logo')) . "' name='rbkmoney_payment_form_path_logo' />
<p class='description'>
" . __('Your logo for payment form', 'wp-e-commerce') . "
</p>
</tr>
<tr>
<td>" . __('Company name in payment form', 'wp-e-commerce') . "</td>
<td>
<input type='text' size='60' value='" . trim(get_option('rbkmoney_payment_form_company_name')) . "' name='rbkmoney_payment_form_company_name' />
<p class='description'>
" . __('Your company name for payment form', 'wp-e-commerce') . "
</p>
</tr>
<tr>
<td>" . __('Private key', 'wp-e-commerce') . "</td>
<td>
<textarea rows='10' cols='45' name='rbkmoney_payment_private_key'>" . trim(get_option('rbkmoney_payment_private_key')) . "</textarea>
<p class='description'>
" . __('The private key in the system RBKmoney', 'wp-e-commerce') . "
</p>
</tr>
<tr>
<td>" . __('Callback public key', 'wp-e-commerce') . "</td>
<td>
<textarea rows='10' cols='45' name='rbkmoney_payment_callback_public_key'>" . trim(get_option('rbkmoney_payment_callback_public_key')) . "</textarea>
<p class='description'>
" . __('Callback public key', 'wp-e-commerce') . "
</p>
</tr>
<tr>
<td>" . __('Notification URL', 'wp-e-commerce') . "</td>
<td>
<input type='text' size='60' value='" . home_url('/?rbkmoney_payment_callback') . "' name='rbkmoney_payment_notification_url' readonly />
<p class='description'>
" . __('This address is to be inserted in a private office RBKmoney', 'wp-e-commerce') . "
</p>
</tr>";
$selected = (get_option('rbkmoney_payment_logs') == 'on') ? 'checked="checked"' : '';
$output .= "<tr>
<td>" . __('Enable logs:', 'wp-e-commerce') . "</td>
<td>
<input type='hidden' name='rbkmoney_payment_logs' value='off' />
<label for='rbkmoney_payment_logs'>
<input type='checkbox' name='rbkmoney_payment_logs' id='rbkmoney_payment_logs' value='on' " . $selected . "' />
</label>
<p class='description'>
" . __('Enable logs?', 'wp-e-commerce') . "
</p>
</tr>
</tr>
<tr>
<td colspan='2'>
" . sprintf(__('For more help configuring RBKmoney, read our documentation <a href="%s">here</a>', 'wp-e-commerce'), esc_url('https://rbkmoney.github.io/docs/')) . "
</td>
</tr>";
return $output;
}
add_action('init', 'nzshpcrt_rbkmoney_payment_callback');
add_action('init', 'nzshpcrt_rbkmoney_payment_results');
?>

View File

@ -0,0 +1,360 @@
<?php
class RBKmoneyPayment
{
/**
* HTTP METHOD
*/
const HTTP_METHOD_POST = 'POST';
const HTTP_METHOD_GET = 'GET';
/**
* HTTP CODE
*/
const HTTP_CODE_OK = 200;
const HTTP_CODE_CREATED = 201;
const HTTP_CODE_MOVED_PERMANENTLY = 301;
const HTTP_CODE_BAD_REQUEST = 400;
const HTTP_CODE_INTERNAL_SERVER_ERROR = 500;
/**
* Create invoice settings
*/
const CREATE_INVOICE_TEMPLATE_DUE_DATE = 'Y-m-d\TH:i:s\Z';
const CREATE_INVOICE_DUE_DATE = '+1 days';
/**
* Constants for Callback
*/
const INVOICE_ID = 'invoice_id';
const PAYMENT_ID = 'payment_id';
const AMOUNT = 'amount';
const CURRENCY = 'currency';
const CREATED_AT = 'created_at';
const METADATA = 'metadata';
const STATUS = 'status';
const SIGNATURE = 'HTTP_X_SIGNATURE';
const ORDER_ID = 'order_id';
const SESSION_ID = 'session_id';
private $api_url = 'https://api.rbk.money/v1/';
private $merchant_private_key = '';
private $shop_id = '';
private $currency = '';
private $product = '';
private $description = '';
private $amount = 0;
private $order_id;
private $session_id;
protected $errors = array();
protected $requiredFields = array();
public function getApiUrl()
{
return $this->api_url;
}
public function setApiUrl($api_url)
{
if (filter_var($api_url, FILTER_VALIDATE_URL) === false) {
$this->setErrors($api_url . ' is not a valid URL');
}
$this->api_url = $api_url;
}
public function getMerchantPrivateKey()
{
return $this->merchant_private_key;
}
public function setMerchantPrivateKey($merchant_private_key)
{
$this->merchant_private_key = $merchant_private_key;
}
public function getShopId()
{
return $this->shop_id;
}
public function setShopId($shop_id)
{
$this->shop_id = $shop_id;
}
public function getCurrency()
{
return $this->currency;
}
public function setCurrency($currency)
{
$this->currency = $currency;
}
public function getProduct()
{
return $this->product;
}
public function setProduct($product)
{
$this->product = $product;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getAmount()
{
return $this->amount;
}
public function setAmount($amount)
{
if (!is_numeric($amount)) {
throw new Exception($amount . ' no a numeric');
}
$this->amount = $amount;
}
public function getOrderId()
{
return $this->order_id;
}
public function setOrderId($order_id)
{
$this->order_id = $order_id;
}
public function getSessionId()
{
return $this->session_id;
}
public function setSessionId($session_id)
{
$this->session_id = $session_id;
}
private function getErrors()
{
return $this->errors;
}
private function setErrors($errors)
{
$this->errors[] = $errors;
}
private function clearErrors()
{
$this->errors = array();
}
public function getRequiredFields()
{
return $this->requiredFields;
}
public function setRequiredFields($requiredFields)
{
if (!is_array($requiredFields) || empty($requiredFields)) {
$this->setErrors('Отсутствуют обязательные поля');
}
$this->requiredFields = $requiredFields;
}
public function __construct(array $params = array())
{
if (!empty($params)) {
$this->bind($params);
}
}
private function toUpper($pockets)
{
return ucfirst(str_replace(['_', '-'], '', $pockets[1]));
}
private function getMethodName($name, $prefix = 'get')
{
$key = preg_replace_callback('{([_|-]\w)}s', array(__CLASS__, 'toUpper'), $name);
return $prefix . ucfirst($key);
}
private function bind(array $params)
{
foreach ($params as $name => $value) {
$method = $this->getMethodName($name, 'set');
if (!empty($value) && method_exists($this, $method)) {
$this->$method($value);
}
}
}
private function checkRequiredFields()
{
$required_fields = $this->getRequiredFields();
foreach ($required_fields as $field) {
$method = $this->getMethodName($field);
if (method_exists($this, $method)) {
$value = $this->$method();
if (empty($value)) $this->setErrors('<b>' . $field . '</b> is required');
} else {
$this->setErrors($field . ' method not found');
}
}
}
private function prepare_due_date()
{
date_default_timezone_set('UTC');
return date(static::CREATE_INVOICE_TEMPLATE_DUE_DATE, strtotime(static::CREATE_INVOICE_DUE_DATE));
}
private function prepare_metadata($order_id, $session_id)
{
return [
'cms' => 'wordpress',
'module' => 'wp-e-commerce',
'plugin' => 'rbkmoney_payment',
'order_id' => $order_id,
'session_id' => $session_id,
];
}
/**
* Prepare amount (e.g. 124.24 -> 12424)
*
* @param $amount int
* @return int
*/
private function prepare_amount($amount)
{
return $amount * 100;
}
private function prepare_api_url($path = '', $query_params = [])
{
$url = rtrim($this->api_url, '/') . '/' . $path;
if (!empty($query_params)) {
$url .= '?' . http_build_query($query_params);
}
return $url;
}
public function create_invoice()
{
$this->setRequiredFields([
'merchant_private_key',
'shop_id',
'amount',
'order_id',
'session_id',
'currency',
'product',
'description',
]);
$headers = [];
$headers[] = 'X-Request-ID: ' . uniqid();
$headers[] = 'Authorization: Bearer ' . $this->merchant_private_key;
$headers[] = 'Content-type: application/json; charset=utf-8';
$headers[] = 'Accept: application/json';
$data = [
'shopID' => (int)$this->shop_id,
'amount' => $this->prepare_amount($this->amount),
'metadata' => $this->prepare_metadata($this->order_id, $this->session_id),
'dueDate' => $this->prepare_due_date(),
'currency' => $this->currency,
'product' => $this->product,
'description' => $this->description,
];
$this->validate();
$url = $this->prepare_api_url('processing/invoices');
$response = $this->send($url, static::HTTP_METHOD_POST, $headers, json_encode($data, true));
$response_decode = json_decode($response['body'], true);
$invoice_id = !empty($response_decode['id']) ? $response_decode['id'] : '';
return $invoice_id;
}
public function create_access_token($invoice_id)
{
if (empty($invoice_id)) {
throw new Exception('Не передан обязательный параметр invoice_id');
}
$headers = [];
$headers[] = 'X-Request-ID: ' . uniqid();
$headers[] = 'Authorization: Bearer ' . $this->merchant_private_key;
$headers[] = 'Content-type: application/json; charset=utf-8';
$headers[] = 'Accept: application/json';
$url = $this->prepare_api_url('processing/invoices/' . $invoice_id . '/access_tokens');
$response = $this->send($url, static::HTTP_METHOD_POST, $headers);
if ($response['http_code'] != static::HTTP_CODE_CREATED) {
throw new Exception('Возникла ошибка при создании токена для инвойса');
}
$response_decode = json_decode($response['body'], true);
$access_token = !empty($response_decode['payload']) ? $response_decode['payload'] : '';
return $access_token;
}
private function send($url, $method, $headers = [], $data = '')
{
if (empty($url)) {
throw new Exception('Не передан обязательный параметр url');
}
$allowed_methods = [static::HTTP_METHOD_POST, static::HTTP_METHOD_GET];
if (!in_array($method, $allowed_methods)) {
throw new Exception('Unsupported method ' . $method);
}
$curl = curl_init($url);
if ($method == static::HTTP_METHOD_POST) {
curl_setopt($curl, CURLOPT_POST, TRUE);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$body = curl_exec($curl);
$info = curl_getinfo($curl);
$curl_errno = curl_errno($curl);
$response['http_code'] = $info['http_code'];
$response['body'] = $body;
$response['error'] = $curl_errno;
curl_close($curl);
return $response;
}
private function validate()
{
$this->checkRequiredFields();
if (count($this->getErrors()) > 0) {
$errors = 'Errors found: ' . implode(', ', $this->getErrors());
throw new Exception($errors);
}
$this->clearErrors();
}
}

View File

@ -0,0 +1,25 @@
<?php
class RBKmoneyPaymentVerification
{
/**
* Openssl verify
*/
const OPENSSL_VERIFY_SIGNATURE_IS_CORRECT = 1;
const OPENSSL_VERIFY_SIGNATURE_IS_INCORRECT = 0;
const OPENSSL_VERIFY_ERROR = -1;
public static function 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_ALGO_SHA256);
return ($verify == static::OPENSSL_VERIFY_SIGNATURE_IS_CORRECT);
}
}