Version: current

      1 <?php
      2
      3 // Способ оплаты через Uniteller
      4 class UnitellerPayment extends PaymentMethod {
      5     static $db = array(
      6         'TestMode' => 'Boolean',
      7
      8         'Login' => 'Varchar',
      9         'Password' => 'Varchar(150)',
     10         'ShopID' => 'Varchar',
     11         'BaseURL' => 'Varchar(100)',
     12
     13         'TestLogin' => 'Varchar',
     14         'TestPassword' => 'Varchar(150)',
     15         'TestShopID' => 'Varchar',
     16         'TestBaseURL' => 'Varchar(100)',
     17
     18         'MeanType' => 'Varchar',
     19         'EMoneyType' => 'Varchar',
     20         'LifeTime' => 'Varchar',
     21
     22     );
     23
     24     static $defaults = array(
     25         'BaseURL' => 'https://wpay.uniteller.ru/',
     26         'TestBaseURL' => 'https://test.wpay.uniteller.ru/',
     27         'MeanType' => '',
     28         'EMoneyType' => '',
     29         'LifeTime' => ''
     30     );
     31
     32     static $paymentMethodIcons = array(
     33         'cart_payment/img/Visa.png',
     34     );
     35
     36     static $meanTypes = array(
     37         '1' => 'VISA',
     38         '2' => 'MasterCard',
     39         '3' => 'DinersClub',
     40         '4' => 'JCB',
     41         //'5' => 'AMEX', //пока не поддерживается
     42     );
     43
     44     static function getMeanTypes() {
     45         $rs = array();
     46         $rs[''] = _t('Payment.Any','Any');
     47         foreach(self::$meanTypes as $key=>$type)
     48             $rs[$key] = $type;
     49
     50         return $rs;
     51
     52     }
     53
     54     static $eMoneyTypes = array(
     55         '1' => 'YandexMoney',
     56         '2' => 'RBKMoney',
     57         '3' => 'MoneyMail',
     58         '4' => 'WebCreds',
     59         '5' => 'EasyPay',
     60         '6' => 'PlatezhRu',
     61         '7' => 'DengiMail',
     62         '8' => 'Megafon',
     63         '9' => 'MTS',
     64         '10' => 'Beeline',
     65         '11' => 'PayPal',
     66         '12' => 'VK',
     67         '13' => 'Euroset',
     68         '14' => 'YotaMoney',
     69         '15' => 'QIWI',
     70         '16' => 'PlatPhone',
     71         '17' => 'MoneyBookers',
     72         '29' => 'WebmoneyWMR'
     73     );
     74
     75     static function getEMoneyTypes() {
     76         $rs = array();
     77         $rs[''] = _t('Payment.Any','Any');
     78         foreach(self::$eMoneyTypes as $key=>$type)
     79             $rs[$key] = _t('UnitellerPayment.EMoneyType_' . $type, $type);
     80         return $rs;
     81
     82     }
     83
     84     function getPaymentHandler() {
     85         return 'UnitellerPayment_Handler';
     86     }
     87
     88     function getCMSFields() {
     89         $fields = parent::getCMSFields();
     90         $fields->findOrMakeTab('Root.Settings', _t('PaymentMethod.tab_Settings','Settings'));
     91
     92         $fields->addFieldToTab('Root.Settings', new DropdownField('MeanType', $this->fieldLabel('MeanType'), self::getMeanTypes(), $this->MeanType));
     93         $fields->addFieldToTab('Root.Settings', new DropdownField('EMoneyType', $this->fieldLabel('EMoneyType'), self::getEMoneyTypes(), $this->EMoneyType));
     94         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('LifeTime'));
     95
     96         $fields->addFieldToTab('Root.Settings', new HeaderField('TestSettings', _t('PaymentMethod.WorkSettings', 'Work Settings')));
     97         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('Login'));
     98         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('Password'));
     99         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('ShopID'));
    100         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('BaseURL'));
    101
    102         $fields->addFieldToTab('Root.Settings', new HeaderField('TestSettings', _t('PaymentMethod.TestSettings', 'Test Settings')));
    103         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('TestLogin'));
    104         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('TestPassword'));
    105         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('TestShopID'));
    106         $fields->addFieldToTab('Root.Settings', $fields->dataFieldByName('TestBaseURL'));
    107
    108         return $fields;
    109     }
    110
    111     public function validate() {
    112         if ($this->Active) {
    113             if (!$this->Login)
    114                 return new ValidationResult(false, _t('UnitellerPayment.WrongLogin', 'Wrong Login'));
    115             if (!$this->Password)
    116                 return new ValidationResult(false, _t('UnitellerPayment.WrongPassword', 'Wrong Password'));
    117             if (!$this->ShopID)
    118                 return new ValidationResult(false, _t('UnitellerPayment.WrongShopID', 'Wrong ShopID'));
    119             if (!$this->BaseURL)
    120                 return new ValidationResult(false, _t('UnitellerPayment.WrongBaseURL', 'Wrong BaseURL'));
    121         }
    122         if ($this->TestMode) {
    123             if (!$this->TestLogin)
    124                 return new ValidationResult(false, _t('UnitellerPayment.WrongTestLogin', 'Wrong Test Login'));
    125             if (!$this->TestPassword)
    126                 return new ValidationResult(false, _t('UnitellerPayment.WrongTestPassword', 'Wrong Test Password'));
    127             if (!$this->TestShopID)
    128                 return new ValidationResult(false, _t('UnitellerPayment.WrongTestShopID', 'Wrong Test ShopID'));
    129             if (!$this->TestBaseURL)
    130                 return new ValidationResult(false, _t('UnitellerPayment.WrongTestBaseURL', 'Wrong Test BaseURL'));
    131         }
    132         return new ValidationResult();
    133     }
    134
    135
    136     function Password() {
    137         return ($this->TestMode) ? $this->TestPassword : $this->Password;
    138     }
    139
    140     function Login() {
    141         return ($this->TestMode) ? $this->TestLogin : $this->Login;
    142     }
    143
    144     function ShopID() {
    145         return ($this->TestMode) ? $this->TestShopID : $this->ShopID;
    146     }
    147
    148     function BaseURL() {
    149         return ($this->TestMode) ? $this->TestBaseURL : $this->BaseURL;
    150     }
    151
    152     function MeanType() {
    153         return ($this->MeanType) ? $this->MeanType : '';
    154     }
    155
    156     function EMoneyType() {
    157         return ($this->EMoneyType) ? $this->EMoneyType : '';
    158     }
    159
    160     function LifeTime() {
    161         return ($this->LifeTime) ? $this->LifeTime : '';
    162     }
    163
    164     // можно вызвать повторно, у уже сформированного платежа (отправть пользователю на почту)
    165     function processPayment($payment) {
    166         if ($payment->Status != 'Failure' && $payment->Status != 'Success') {
    167             $payment->Status = 'Pending';
    168             $payment->write();
    169             $link = $this->paymentLink($payment->ID);
    170             return new Payment_Processing($link);
    171         } else {
    172             // ??? как-то ругаемся, что нельзя обработать уже обработанный платеж (успешно или с ошибкой)
    173         }
    174     }
    175
    176     // "чистая" ссылка (URL) на оплату
    177     function getClearPaymentLink($payment) {
    178         return Director::absoluteURL(UnitellerPayment_Handler::form_link($payment->ID));
    179     }
    180
    181     /*
    182      * Форма оплаты Uniteller
    183      *
    184      * @return Form
    185      */
    186     function getForm($payment) {
    187         $orderCost = $payment->PaidObject()->getTotalPrice();
    188         $responseURL = Director::absoluteURL(UnitellerPayment_Handler::return_link());
    189         $url = $this->BaseURL() . 'pay/';
    190         $shopID = $this->ShopID();
    191         $signature = $this->getSignature($payment->ID, $orderCost);
    192         $html = '<form id="PaymentForm" action="'.$url.'" method="post" enctype="application/x-www-form-urlencoded">';
    193         $html .= '<input type="hidden" name="Shop_IDP" value="'.$shopID.'" />';
    194         $html .= '<input type="hidden" name="Order_IDP" value="'.$payment->ID.'" />';
    195         $html .= '<input type="hidden" name="Subtotal_P" value="'.$orderCost.'" />';
    196         $html .= '<input type="hidden" name="Signature" value="'.$signature.'" />';
    197         $html .= '<input type="hidden" name="URL_RETURN" value="'.$responseURL.'" />';
    198         $html .= '<input type="submit" value="Оплатить" />';
    199         $html .= '</form>';
    200         return $html;
    201     }
    202
    203     //Сигнатура уведомления о изменеии статуса заказа
    204     function getRequestSignature($orderID, $status) {
    205         $realSignature = strtoupper(md5($orderID . $status . $this->Password()));
    206         return $realSignature;
    207     }
    208
    209     //Сигнатура для подписи формы
    210     function getSignature($orderID, $orderSum) {
    211         return strtoupper(md5(
    212             md5($this->ShopID()) .
    213             '&' . md5($orderID) .
    214             '&' . md5($orderSum) .
    215             '&' . md5($this->MeanType()) .
    216             '&' . md5($this->EMoneyType()) .
    217             '&' . md5($this->LifeTime()) .
    218             '&' . md5('') .
    219             '&' . md5('') .
    220             '&' . md5('') .
    221             '&' . md5('') .
    222             '&' . md5($this->Password())
    223         ));
    224     }
    225
    226     /*
    227      * Проверка результатов совершения платежа
    228      *
    229      */
    230     function checkPaymentResult($payment) {
    231         if ($payment->Status != 'Success') {
    232             $data = $this->getAuthResult($payment->ID);
    233             $payment->PaymentResponse = serialize($data);
    234             if (isset($data['error'])) {
    235                 $payment->Status = 'Failure';
    236                 $payment->Message = _t('UnitellerPayment.FAILURE', 'Заказ не оплачен');
    237             }
    238             else {
    239                 if ($data['Status'] == 'Authorized' || $data['Status'] == 'Paid') {
    240                     $payment->Status = 'Success';
    241                     $payment->Message = _t('UnitellerPayment.DONE', 'Заказ оплачен');
    242                     $payment->TransactionID = $data['BillNumber'];
    243                 }
    244                 else {
    245                     $payment->Status = 'Failure';
    246                     $payment->Message = _t('UnitellerPayment.FAILURE', 'Заказ не оплачен');
    247                 }
    248             }
    249             $payment->write();
    250         }
    251     }
    252
    253
    254     // Запрос результата авторизации (состояния платежа)
    255     function getAuthResult($orderID) {
    256         $sPostFields = array(
    257             'Shop_ID' => $this->ShopID(),
    258             'Login' => $this->Login(),
    259             'Password' => $this->Password(),
    260             'Format' => 1,
    261             'ShopOrderNumber' => $orderID,
    262             'S_FIELDS' => 'Status;Error_Code;Error_Comment;ApprovalCode;BillNumber;Total;Response_Code;Recommendation;CardNumber;Date;CardHolder'
    263         );
    264         $ch = curl_init();
    265         curl_setopt($ch, CURLOPT_URL, $this->BaseURL() . "results/");
    266         curl_setopt($ch, CURLOPT_HEADER, 0);
    267         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    268         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    269         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    270         curl_setopt($ch, CURLOPT_VERBOSE, 0);
    271         curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    272         curl_setopt($ch, CURLOPT_POST, 1);
    273         curl_setopt($ch, CURLOPT_POSTFIELDS, $sPostFields);
    274         curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
    275         curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
    276         $curl_response = curl_exec($ch);
    277         $curl_error = curl_error($ch);
    278
    279         $data = array(); // результат для возврата
    280         if ($curl_error) {
    281             // обработка ошибки обращения за статусом платежа
    282             $data['error'] = $curl_error;
    283         } else {
    284             // данные получены // обработка данных из переменной $curl_response
    285             $arr = explode( ";", $curl_response );
    286             if ( count($arr) >= 11 ) {
    287                 $data = array(
    288                     "Status" => $arr[0] ,
    289                     "Error_Code" => $arr[1] ,
    290                     "Error_Comment" => $arr[2],
    291                     "ApprovalCode" => $arr[3],
    292                     "BillNumber" => $arr[4],
    293                     "Total" => $arr[5],
    294                     "ResponseCode" => $arr[6],
    295                     "Recommendation" => $arr[7],
    296                     "CardNumber" => $arr[8],
    297                     "Date" => $arr[9],
    298                     "CardHolder" => $arr[10]
    299                 );
    300             } else {
    301                 // что-то не так, обработчик полученного ответа ...
    302                 if ($curl_response) {
    303                     $data['error'] = $curl_response;
    304                 } else {
    305                     $data['error'] = 'Платеж не совершен!';
    306                 }
    307                 //debug::show($curl_response);
    308             }
    309         }
    310         return $data;
    311     }
    312 }
    313
    314
    315 /**
    316  * Handler for responses from the PayPal site
    317  */
    318 class UnitellerPayment_Handler extends Payment_Handler {
    319
    320     static $URLSegment = 'uniteller';
    321
    322     static function form_link($paymentID) {
    323         return self::$URLSegment . '/show_form?paym_id=' . $paymentID;
    324     }
    325
    326     static function return_link() {
    327         return self::$URLSegment . '/process';
    328     }
    329
    330     function show_form() {
    331         if (isset($_GET['paym_id'])) {
    332             $paymentID = (int)$_GET['paym_id'];
    333             if ($payment = DataObject::get_by_id('Payment', $paymentID)) {
    334                 return $this->customise(array(
    335                     'Form' => $payment->PaymentType()->getForm($payment)
    336                 ))->renderWith(array('UnitellerPayment_form'));
    337             }
    338         }
    339         return false;
    340     }
    341
    342     /**
    343      * Manages the Uniteller replies
    344      */
    345     function process() {
    346         if (isset($_GET['Order_ID'])) {
    347             $orderID = (int)$_GET['Order_ID'];
    348             if ($payment = DataObject::get_by_id('Payment', $orderID)) {
    349                 $payment->PaymentType()->checkPaymentResult($payment);
    350                 Director::redirect(CheckoutPage::find_link('order/' . $payment->PaidObject()->HashLink));
    351             }
    352         }
    353     }
    354
    355     //Получение уведомления о статусе оплаты (ссылку на этот action нужно указать в личном кабинете)
    356     function changeOrderStatus() {
    357         if ( count($_POST) && isset($_POST["Order_ID"]) && isset($_POST["Status"]) && isset($_POST["Signature"]) ) {
    358             $orderID = (int)$_POST["Order_ID"];
    359             if ($payment = DataObject::get_by_id('Payment', $orderID)) {
    360                 $realSignature = $payment->PaymentType()->getRequestSignature($_POST["Order_ID"], $_POST["Status"]);
    361                 if (trim((string)$_POST["Signature"]) == $realSignature) {
    362                     $payment->PaymentType()->checkPaymentResult($payment);
    363                 }
    364             }
    365         }
    366     }
    367 }
    368

[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. -