Version: current

      1 <?php
      2 // Способ оплаты через Сбербанк
      3 class SberbankPayment extends PaymentMethod {
      4     static $db = array(
      5         'TestMode' => 'Boolean',
      6         'BaseURL' => 'Varchar(150)',
      7
      8         'Login' => 'Varchar(150)',
      9         'Password' => 'Varchar(150)',
     10
     11         'TestLogin' => 'Varchar(150)',
     12         'TestPassword' => 'Varchar(150)',
     13
     14         'TaxSystem' => "Enum('0, 1, 2, 3, 4, 5')", //Система налогообложения магазина (СНО)
     15     );
     16
     17     static $defaults = array(
     18         'BaseURL' => 'https://3dsec.sberbank.ru/payment/rest/',
     19     );
     20
     21     static $paymentMethodIcons = array(
     22         'cart_payment/img/SberbankPayment.png',
     23     );
     24
     25     /*
     26      * Возвращаем созданный способ оплаты через Я.Кассу
     27      *
     28      * @return DataObject
     29      */
     30     static function getPaymentMethod() {
     31         return DataObject::get_one('SberbankPayment', 'Active = 1');
     32     }
     33
     34     static $errorCodes = array(
     35         1 => 'Заказ с таким номером уже зарегистрирован в системе',
     36         3 => 'Неизвестная (запрещенная) валюта',
     37         4 => 'Отсутствует обязательный параметр запроса',
     38         5 => 'Ошибка значение параметра запроса',
     39         7 => 'Системная ошибка',
     40     );
     41
     42     static $orderStatuses = array(
     43         0 => 'Заказ зарегистрирован, но не оплачен',
     44         1 => 'Предавторизованная сумма захолдирована',
     45         2 => 'Проведена полная авторизация суммы заказа',
     46         3 => 'Авторизация отменена',
     47         4 => 'По транзакции была проведена операция возврата',
     48         5 => 'Инициирована авторизация через ACS банка-эмитента',
     49         6 => 'Авторизация отклонена',
     50     );
     51
     52     function onBeforeWrite() {
     53         parent::onBeforeWrite();
     54     }
     55
     56     function getTaxSystems() {
     57         $rs = array();
     58         foreach($this->dbObject('TaxSystem')->enumValues() as $type) {
     59             $rs[$type] = _t('SberbankPayment.TaxSystem_' . $type, $type);
     60         }
     61         return $rs;
     62     }
     63
     64     function getPaymentHandler() {
     65         return 'SberbankPayment_Handler';
     66     }
     67
     68     function getCMSFields() {
     69         $fields = parent::getCMSFields();
     70         $fields->findOrMakeTab('Root.Settings', _t('PaymentMethod.tab_Settings','Settings'));
     71
     72         $fields->addFieldToTab('Root.Settings', new DropdownField('TaxSystem', $this->fieldLabel('TaxSystem'), $this->getTaxSystems()));
     73
     74         $fields->addFieldToTab('Root.Settings', new HeaderField('WorkSettings', _t('PaymentMethod.WorkSettings', 'Work Settings')));
     75         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('BaseURL'));
     76
     77         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('Login'));
     78         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('Password'));
     79
     80         $fields->addFieldToTab('Root.Settings', new HeaderField('TestSettings', _t('PaymentMethod.TestSettings', 'Test Settings')));
     81
     82         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('TestLogin'));
     83         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('TestPassword'));
     84
     85         return $fields;
     86     }
     87
     88     /* Пока комментим - как-то неудобно !!
     89     public function validate() {
     90         if ($this->Active) {
     91             //if (!$this->Login)
     92                 //return new ValidationResult(false, _t('YaMoneyPayment.WrongLogin', 'Wrong Login'));
     93             if (!$this->Password)
     94                 return new ValidationResult(false, _t('YaMoneyPayment.WrongPassword', 'Wrong Password'));
     95             if (!$this->ShopID)
     96                 return new ValidationResult(false, _t('YaMoneyPayment.WrongShopID', 'Wrong ShopID'));
     97             if (!$this->SCID)
     98                 return new ValidationResult(false, _t('YaMoneyPayment.WrongSCID', 'Wrong SCID'));
     99             if (!$this->BaseURL)
    100                 return new ValidationResult(false, _t('YaMoneyPayment.WrongBaseURL', 'Wrong BaseURL'));
    101         }
    102         if ($this->TestMode) {
    103             //if (!$this->TestLogin)
    104                 //return new ValidationResult(false, _t('YaMoneyPayment.WrongTestLogin', 'Wrong Test Login'));
    105             if (!$this->TestPassword)
    106                 return new ValidationResult(false, _t('YaMoneyPayment.WrongTestPassword', 'Wrong Test Password'));
    107             if (!$this->TestShopID)
    108                 return new ValidationResult(false, _t('YaMoneyPayment.WrongTestShopID', 'Wrong Test ShopID'));
    109             if (!$this->TestSCID)
    110                 return new ValidationResult(false, _t('YaMoneyPayment.WrongSCID', 'Wrong SCID'));
    111             if (!$this->TestBaseURL)
    112                 return new ValidationResult(false, _t('YaMoneyPayment.WrongTestBaseURL', 'Wrong Test BaseURL'));
    113         }
    114         return new ValidationResult();
    115     }
    116     */
    117
    118     function Login() {
    119         return ($this->TestMode) ? $this->TestLogin : $this->Login;
    120     }
    121
    122     function Password() {
    123         return ($this->TestMode) ? urlencode($this->TestPassword) : urlencode($this->Password);
    124     }
    125
    126     // можно вызвать повторно, у уже сформированного платежа (отправить пользователю на почту)
    127     function processPayment($payment) {
    128         return new Payment_Processing($this->paymentLink($payment->ID));
    129         // Регистрируем платеж в Сбере только при переходе по ссылке на оплату
    130         /*
    131         if ($payment->Status != 'Failure' && $payment->Status != 'Success') {
    132             return $this->createSberbankPayment($payment);
    133         } else {
    134             return new Payment_Failure($payment->ExceptionError);
    135         }
    136         */
    137     }
    138
    139     function getOrderBundleData($payment) {
    140         $order = $payment->PaidObject();
    141         if (!$order) {
    142             return false;
    143         }
    144         $bundleData = array();
    145         $bundleData['orderCreationDate'] = strtotime($order->Created);
    146
    147         // Блок с атрибутами данных о покупателе
    148         if ($order->Email || $order->Phone) {
    149             $customerDetails = array();
    150             if ($order->Email) {
    151                 $customerDetails['email'] = $order->Email;
    152             }
    153             if ($order->Phone) {
    154                 $customerDetails['phone'] = str_replace('+7', '8', $order->Phone);
    155             }
    156             if ($order->ClientName) {
    157                 $customerDetails['contact'] = $order->ClientName;
    158             }
    159             if ($order->ShippingMethodID && ($method = $order->ShippingMethod())) {
    160                 if ($order->Country && $order->City && $order->Address) {
    161                     $deliveryInfo = array();
    162                     $deliveryInfo['deliveryType'] = $method->Name;
    163                     $deliveryInfo['country'] = $order->Country;
    164                     $deliveryInfo['city'] = $order->City;
    165                     $deliveryInfo['postAddress'] = $order->Address;
    166                     $customerDetails['deliveryInfo'] = $deliveryInfo;
    167                 }
    168             }
    169             $bundleData['customerDetails'] = $customerDetails;
    170         }
    171
    172         // Блок с атрибутами товарных позиции Корзины.
    173         $items = array();
    174         $i = 1;
    175         foreach($order->Items() as $item) {
    176             $itm = array();
    177             $itm['positionId'] = $i;
    178             $itm['name'] = $item->Title;
    179
    180             // itemDetails - Дополнительный блок с параметрами описания товарной позиции.
    181             // напр. [{ "value": "Metzeler ", "name": "brand" }, { "value": "17inch", "name": "radius" }]
    182             // пока не добавляем - необязательно
    183
    184
    185             $itm['quantity'] = array(
    186                 'value' => $item->Quantity,
    187                 'measure' => _t('SberbankPayment.OrderItemMeasure'),
    188             );
    189
    190             $itm['itemPrice'] = $item->ItemPrice * 100;
    191             $itm['itemAmount'] = $item->getItemsPrice() * 100;
    192             $itm['itemCode'] = $item->LinkedID;
    193
    194             // discount - Дополнительный блок с атрибутами описания скидки для товарной позиции (необязат)
    195             // напр. "discount": { "discountType": "percent", "discountValue": "5" }
    196             // непонятно, откуда всзять скидку
    197
    198             if (singleton('Product')->hasMethod('getVAT')) {
    199                 $itm['tax'] = array();
    200                 $vat = $item->getProduct()->getVAT();
    201                 $itm['tax']['taxType'] = $vat->YaKassaCode;
    202             }
    203             $items[] = $itm;
    204             $i++;
    205         }
    206         $bundleData['cartItems'] = array('items' => $items);
    207         return $bundleData;
    208     }
    209
    210     /*
    211      * Создаем платеж в Сбербанке
    212      *
    213      *
    214      */
    215     function createSberbankPayment($payment) {
    216         $amount = $payment->Amount * 100;
    217         $createLink = $this->BaseURL . "register.do?userName={$this->Login()}&password={$this->Password()}&orderNumber={$payment->ID}&amount={$amount}&returnUrl=" . Director::absoluteURL(SberbankPayment_Handler::success_link()) . "&failUrl=" . Director::absoluteURL(SberbankPayment_Handler::error_link());
    218         if ($orderBundle = $this->getOrderBundleData($payment)) {
    219             $createLink .= "&taxSystem={$this->TaxSystem}&orderBundle=" . urlencode(json_encode($orderBundle));
    220         }
    221         echo '**'.$createLink.'**';
    222
    223         $answer = file_get_contents($createLink);
    224         $rs = json_decode($answer, true);
    225         echo '<pre>';
    226         print_r($rs);
    227         echo '</pre>';
    228         die;
    229         if ($rs && is_array($rs)) {
    230             if (isset($rs['errorCode']) && ($rs['errorCode'] != 0)) {
    231                 $payment->Status = 'Failure';
    232                 $payment->ExceptionError = $rs['errorMessage'] . '___' . $createLink;
    233                 $payment->write();
    234                 return new Payment_Failure($payment->ExceptionError);
    235             } else {
    236                 $payment->Status = 'Pending';
    237                 $payment->SberbankPaymentID = $rs['orderId'];
    238                 $payment->TransactionID = $rs['orderId'];
    239                 $payment->SberbankBackURL = $rs['formUrl'];
    240                 $payment->write();
    241                 return new Payment_Processing($payment->SberbankBackURL);
    242             }
    243         } else {
    244             $payment->Status = 'Failure';
    245             $payment->ExceptionError = $answer;
    246             $payment->write();
    247             return new Payment_Failure($payment->ExceptionError);
    248         }
    249     }
    250
    251     /*
    252      * Проверяем результаты платежа
    253      *
    254      *
    255      */
    256     function checkSberbankPayment($payment) {
    257         $answer = file_get_contents($this->BaseURL . "getOrderStatus.do?userName={$this->Login()}&password={$this->Password()}&orderId={$payment->SberbankPaymentID}");
    258         $payment->PaymentResponse = $answer;
    259         $payment->write();
    260         $rs = json_decode($answer, true);
    261         if ($rs && is_array($rs)) {
    262             if (isset($rs['OrderStatus'])) {
    263                 if ($rs['OrderStatus'] == 2) {
    264                     $this->completePayment($payment);
    265                     return new Payment_Success($payment->Message);
    266                 } else {
    267                     $payment->Status = 'Pending';
    268                     $payment->Message = self::$orderStatuses[$rs['OrderStatus']];
    269                     $payment->write();
    270                     return new Payment_Failure($payment->Message);
    271                 }
    272             } else {
    273                 $payment->Status = 'Failure';
    274                 $payment->ExceptionError = (isset($rs['ErrorMessage']) ? $rs['ErrorMessage'] : _t('SberbankPayment.NoOrder','No Order'));
    275                 $payment->write();
    276                 return new Payment_Failure($payment->ExceptionError);
    277             }
    278         } else {
    279             $payment->Status = 'Failure';
    280             $payment->ExceptionError = _t('SberbankPayment.ProcessingError','Processing Error');
    281             $payment->write();
    282             return new Payment_Failure($payment->ExceptionError);
    283         }
    284     }
    285
    286     // "чистая" ссылка (URL) на оплату
    287     function getClearPaymentLink($payment) {
    288         return Director::absoluteURL(SberbankPayment_Handler::create_sber_payment_link($payment->HashLink));
    289     }
    290
    291     /*
    292      * Проверка результатов совершения платежа
    293      *
    294      */
    295     function completePayment($payment) {
    296         if ($payment->Status != 'Success') {
    297             $payment->Status = 'Success';
    298             $payment->Message = _t('SberbankPayment.DONE', 'Заказ оплачен');
    299             $payment->write();
    300         }
    301     }
    302 }
    303
    304 /**
    305  * Handler for responses from the YaMoney
    306  */
    307 class SberbankPayment_Handler extends Payment_Handler {
    308
    309     static $URLSegment = 'sberbank';
    310
    311     static function success_link() {
    312         return self::$URLSegment . '/success';
    313     }
    314
    315     static function error_link() {
    316         return self::$URLSegment . '/error';
    317     }
    318
    319     static function create_sber_payment_link($paymentHash) {
    320         return self::$URLSegment . "/create_sber_payment/{$paymentHash}";
    321     }
    322
    323     // создаем платеж сбера и редиректим по его URL
    324     function create_sber_payment($request) {
    325         if (($orderHash = Convert::raw2sql($request->param('ID'))) && ($payment = DataObject::get_one('Payment', "HashLink = '{$orderHash}'"))) {
    326             if ($order = $payment->PaidObject()) {
    327                 if (($order->Status == 'Unpaid') || ($order->Status == 'Query')) {
    328                     if (!$payment->isNew()) {
    329                         $newPayment = $payment->PaymentType()->createPayment($order);
    330                         unset($payment);
    331                         $payment = $newPayment;
    332                     }
    333                     // регистрируем платеж в Сбере и переходим по SberbankBackURL
    334                     $rs = $payment->PaymentType()->createSberbankPayment($payment);
    335                     if ($rs->isProcessing()) {
    336                         $this->redirect($rs->getValue());
    337                     } else {
    338                         echo $rs->getValue();
    339                     }
    340                 } else {
    341                     $link = CheckoutPage::find_link('order/' . $order->HashLink);
    342                     return $this->customise(array(
    343                         'Result' => _t('Order.CantPayOrder', 'Can not Pay Order'),
    344                         'Order' => $order,
    345                         'OrderLink' => $link,
    346                     ))->renderWith(array('SberbankPayment_error', 'Page' ));
    347                 }
    348             }
    349         }
    350     }
    351
    352     function callback() {
    353         if (isset($_GET['mdOrder']) && isset($_GET['orderNumber']) && isset($_GET['operation']) && isset($_GET['status'])) {
    354             $sbankOrderID = $_GET['mdOrder'];
    355             $orderID = (int)$_GET['orderNumber'];
    356             $operation = $_GET['operation'];
    357             $status = (int)$_GET['status'];
    358             if ($status && ($payment = DataObject::get_by_id('Payment', $orderID))) {
    359                 if ($payment->SberbankPaymentID == $sbankOrderID) {
    360                     $paymentType = $payment->PaymentType();
    361                     if ($operation == 'deposited') {
    362                         $paymentType->completePayment($payment);
    363                     }
    364                     // !! Делаем что-то при возврате
    365                     if ($operation == 'refunded') {
    366                     }
    367                 }
    368             }
    369         }
    370     }
    371
    372     /**
    373      * Успешная оплата
    374      */
    375     function success() {
    376         if (isset($_GET['orderId'])) {
    377             $orderID = Convert::raw2sql($_GET['orderId']);
    378             if ($payment = DataObject::get_one('Payment', "SberbankPaymentID = '{$orderID}'")) {
    379                 $paymentType = $payment->PaymentType();
    380                 $rs = $paymentType->checkSberbankPayment($payment);
    381                 if ($payment->PaidObject()) {
    382                     $link = CheckoutPage::find_link('order/' . $payment->PaidObject()->HashLink);
    383                     return singleton('Page_Controller')->customise(array(
    384                         'Result' => $rs->getValue(),
    385                         'Order' => $payment->PaidObject(),
    386                         'OrderLink' => $link,
    387                     ))->renderWith(array('SberbankPayment_success', 'Page' ));
    388                 }
    389             }
    390         }
    391     }
    392
    393     /**
    394      * НЕУспешная оплата
    395      */
    396     function error() {
    397         if (isset($_GET['orderId'])) {
    398             $orderID = Convert::raw2sql($_GET['orderId']);
    399             if ($payment = DataObject::get_one('Payment', "SberbankPaymentID = '{$orderID}'")) {
    400                 $paymentType = $payment->PaymentType();
    401                 $rs = $paymentType->checkSberbankPayment($payment);
    402                 if ($payment->PaidObject()) {
    403                     $link = CheckoutPage::find_link('order/' . $payment->PaidObject()->HashLink);
    404                     return singleton('Page_Controller')->customise(array(
    405                         'Result' => $rs->getValue(),
    406                         'Order' => $payment->PaidObject(),
    407                         'OrderLink' => $link,
    408                     ))->renderWith(array('SberbankPayment_error', 'Page' ));
    409                 }
    410             }
    411         }
    412     }
    413 }
    414

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -