<input type="hidden" name="_csrf" value="<?=Yii::$app->request->getCsrfToken()?>" />
public function beforeAction($action)
{
$this->enableCsrfValidation = false;
return parent::beforeAction($action);
}
<?php
namespace frontend\models;
use Yii;
use yii\base\Model;
use yii\helpers\Url;
use common\models\Orders;
/**
* Модель валидации приема платежей с яндекс денег
*
* @param string $action Тип операции.
* @param datetime $requestDatetime Дата платежа.
* @param string(32) $md5 Хэш полей для валидации.
* @param float $orderSumAmount Сумма денег указаная в форме.
* @param float $orderSumCurrencyPaycash Код валюты для суммы заказа.
* @param float $orderSumBankPaycash Код процессингового центра в Яндекс.Деньгах для суммы заказа.
* @param string $shopId Идентификатор магазина, выдается при подключении к сервису Яндекс.Денег.
* @param integer $invoiceId Уникальный номер транзакции в Яндекс.Деньгах.
* @param integer $customerNumber Идентификатор пользователя на сайте.
* @param integer $orderNumber Идентификатор заказа на сайте.
*
* @param string logString Переменная для записи лога.
*
*/
class YandexKassa extends Model {
const ACTION_CHECK = 'checkOrder';
const ACTION_AVISO = 'paymentAviso';
public $action;
public $requestDatetime;
public $md5;
public $orderSumAmount;
public $orderSumCurrencyPaycash;
public $orderSumBankPaycash;
public $shopId;
public $invoiceId;
public $customerNumber;
public $orderNumber;
public static $logFilename;
private $logString = "";
/**
* Инициализация класса, задаем файл для логов
**/
public function init()
{
self::$logFilename = Yii::getAlias('@frontend/paymentsLogs.txt');
}
/**
* @return array the validation rules.
*/
public function rules()
{
return [
[['action', 'requestDatetime', 'md5', 'orderSumAmount', 'orderSumCurrencyPaycash', 'orderSumBankPaycash', 'shopId', 'invoiceId', 'customerNumber'], 'required'],
['action', 'in', 'range' => [self::ACTION_AVISO, self::ACTION_CHECK]],
['requestDatetime', 'string', 'min' => 10, 'max' => 50],
['orderSumAmount', 'number'],
[['customerNumber', 'orderNumber'], 'integer'],
['md5', function($att, $par){$this->$att = strtoupper($this->$att);}],
['orderNumber', 'exist', 'targetClass' => Orders::className(), 'targetAttribute' => 'id', 'filter' => ['users_id' => $this->customerNumber], 'message' => 'Заказ этого пользователя с указанным индентификатором не найден.', 'on' => 'checkOrder'],
['md5', function($att, $par){
if ($this->$att !== $this->generateMd5())
$this->addError('md5', 'Контрольная сумма не совпала.');
}],
];
}
//Обработка и вывод
public function process()
{
$result = $this->save();
return $this->xmlResponse($result);
}
/**
* Функция сохранения результатов
* @return boolean
**/
public function save()
{
$this->addLog('Дата: '.date("Y.m.d H:i", time())."\r\n
Детали платежа: \r\n
action: {$this->action} \r\n
orderSumAmount: {$this->orderSumAmount} \r\n
customerNumber: {$this->customerNumber} \r\n
orderNumber: {$this->orderNumber}");
//Валидируем эту бражку
if (!$this->validate()){
$this->addLog("Найдены ошибки: ".print_r($this->getErrors(), true));
$this->saveLog();
return false;
}
//action
if ($this->action == self::ACTION_CHECK) {
$result = $this->checkOrder();
}
else if ($this->action == self::ACTION_AVISO) {
$result = $this->paymentAviso();
}
//save log
$this->saveLog();
return $result;
}
/**
* Берем первую ошибку первого атрибута модели
* @return string
**/
public function getOneError()
{
$array = array_keys($this->getErrors());
return $this->getFirstError(array_shift($array));
}
/**
* Проверка наличия заказа
* @return boolean
**/
private function checkOrder()
{
$model = Orders::findOne(['id' => $this->orderNumber, 'users_id' => $this->customerNumber, 'paid' => '0', 'price' => intval($this->orderSumAmount)]);
if ($model === NULL){
$this->addLog("Заказ №{$this->orderNumber}, пользователя: {$this->customerNumber} не найден.");
$this->addError('action', 'Не найден не заказ.');
return false;
}
$this->addLog("Заказ найден.");
return true;
}
/**
* Оплата заказа
* @return boolean
**/
private function paymentAviso()
{
//Начинаем транзакцию
$connection = \Yii::$app->db;
$transaction = $connection->beginTransaction();
$model = Orders::find()
->where([
'orders.id' => $this->orderNumber,
'users_id' => $this->customerNumber,
'paid' => [0, 1],
'price' => intval($this->orderSumAmount)
])
->joinWith('users')->one();
if ($model === NULL) {
$this->addLog("Заказ №{$this->orderNumber}, пользователя: {$this->customerNumber} не найден.");
$this->addError('action', 'Не найден не заказ.');
return false;
}
$status = true;
//add amount
if ($model->paid == 0){
$model->users->money += $model->money;
$status = $model->users->save(false);
}
$model->paid = '1';
if ($status && $model->save(false)) {
$transaction->commit();
$this->addLog("Заказ №{$this->orderNumber} успешно оплачен.");
return true;
} else {
$transaction->rollBack();
$this->addError('action', 'Ошибка записи в базу.');
$this->addLog("Заказ №{$this->orderNumber} не оплачен, ошибка записи в базу.");
return false;
}
}
/**
* Возвращаем ответ яндексу
* @return xml
*/
public function xmlResponse($result)
{
if ($result) {
if ($this->action == self::ACTION_CHECK) {
echo '<checkOrderResponse performedDatetime="'.$this->requestDatetime.'" code="0" invoiceId="'.$this->invoiceId.'" shopId="'.$this->shopId.'"/>';
} else if ($this->action == self::ACTION_AVISO) {
echo '<paymentAvisoResponse performedDatetime="'.$this->requestDatetime.'" code="0" invoiceId="'.$this->invoiceId.'" shopId="'.$this->shopId.'"/>';
}
} else {
$error = $this->getOneError();
if ($this->action == self::ACTION_CHECK) {
echo '<checkOrderResponse performedDatetime="'.$this->requestDatetime.'" code="1" invoiceId="'.$this->invoiceId.'" shopId="'.$this->shopId.'" message="'.$error.'" techMessage="'.$error.'"/>';
} else if ($this->action == self::ACTION_AVISO) {
echo '<paymentAvisoResponse performedDatetime="'.$this->requestDatetime.'" code="1" invoiceId="'.$this->invoiceId.'" shopId="'.$this->shopId.'" message="'.$error.'" techMessage="'.$error.'"/>';
}
}
}
/**
* Генерируем md5 хэш
* @return string - md5 hash
**/
private function generateMd5()
{
return strtoupper(md5(implode(';', [$this->action, $this->orderSumAmount, $this->orderSumCurrencyPaycash, $this->orderSumBankPaycash, $this->shopId, $this->invoiceId, $this->customerNumber, Yii::$app->params['shopPassword']])));
}
/**
* Добавляем текст в переменную логов
**/
private function addLog($text)
{
$this->logString .= $text."\r\n";
}
/**
* Сохраняем лог
**/
private function saveLog()
{
return file_put_contents(self::$logFilename, $this->logString."##############################################", FILE_APPEND);
}
}