Шпаргалка. Интернет эквайринг Сбербанка на практике

Когда передо мной встала задача подключения карт сбербанка я не знал с чего начать - была пара документаций от сбербанка листов на 80 каждая из них ~30% слов, которые никогда не слышал, то есть крайне нечитабельная информация и, как вариант, можно было воспользоваться модулем с маркетплейса за 10т.р. + примерно такую же сумму попросили за доработку модуля под мои задачи. Информации в интернете было минимум или она мне просто не попалась :)

В итоге пришлось выборочно прочитать документацию сбербанка. Т.к. задачи в проекте относительно простые - пополнение счета клиента, то и кода вышло не много, именно им, как краткой инструкцией я и хочу поделиться (ну и для себя сохранить, на будущее). 

Внимание! Колхоз в чистом виде на Старте.
Я предупредил :)

В самом примитивном(!) варианте нам понадобится 2 файла - index.php, где будет форма для ввода суммы пополнения и обработка результата иpay.php с запросами в Сбербанк. 
Алгоритм, примерно, таков: 
  1. В index.php вводим сумму пополнения баланса; 
  2. AJAX запросом обращаемся к pay.php; 
  3. pay.php сохраняет данные о заказе в инфоблоках и запрашивает у Сбербанка URL для оплаты; 
  4. Скрипт из index.php получает URL для оплаты и перекидывает клиенту на эту страницу; 
  5. После оплаты пользователь попадает на index.php, где производится обновление статуса заказа. 
Итак, по порядку. 

Начнем с index.php, заполним HTML:
Пополнить баланс:
<input type="text" size="6" id="pay-amount" />
<input type="button" value="Пополнить" id="pay" />
<img src='//opt-560835.ssl.1c-bitrix-cdn.ru/images/spinner.gif' style="display: none;" id="pay-spinner" alt='' />
<div id="pay-result"></div>
Здесь у нас поле для ввода суммы пополнения баланса и кнопка "Пополнить", img для прелоадера и div для отображения результата. 

Далее на index.php размещаю/подключаю такой скрипт:
<script>
   $(document).ready(function() {
      $("#pay").on("click", function() {   // Клик по кнопке "Пополнить"
         $("#pay-spinner").show();         // Покажем прелоадер

         var arErrors = new Array();       // Массив с возможными ошибками
         arErrors["err01"] = "Введите сумму в рублях цифрами!";

         $("#pay-result").html("");        // Очищаем поле с результатом

         $.ajax({                     // Отправим AJAX запрос на наш сервер
            type: "POST",
            url: "pay.php",                // Здесь путь от корня сайта к файлу pay.php
            data: ({                       // Пересылаемые данные
               amount: $("#pay-amount").val()
            }),
            success: function(data){       // Действия при успешном AJAX запросе
               var res = jQuery.parseJSON(data);

               if(res.error) {             // Если возникли ошибки
                  if(arErrors[res.message]) {      // По коду ошибки достаем её текст из arErrors
                     $("#pay-result").html(arErrors[res.message]);
                  }
               } else if(res.urlTo) {      // Если ошибок не было то перейдем на нужный URL
                  window.location = res.urlTo;
               }

               $("#pay-spinner").hide();   // И скорем прелоадер
            },
            error: function() {            // Действия при ошибке AJAX запроса
               $("#pay-spinner").hide();   // Скрываем прелоадер и не забываем про console.log()
            }
         });
      });
   });
</script>
Если коротко, то на основе введенного значения отправляем AJAX запрос, который должен вернуть URL на страницу сбербанка с перечислением средств. 

Теперь рассмотрим pay.php, которому и идет обращение из AJAX:
<?php require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");

$amount = $_REQUEST["amount"];                  // Сохраним переданное значение суммы в $amount

if(is_numeric($amount)) {                        // Если передано число
   $amount = (float) $amount;                     // Переводим его в float
   $amount *= 100;                              // И умножаем на 100, что бы оперировать копейками
   
   // Сохраним информацию о начале тразнакции
   global $USER;
   CModule::IncludeModule("iblock");
   $seq   = new CIBlockSequence($iBlockId, $iCounterId);
   $el      = new CIBlockElement;
   $arProps = Array(
      "AMOUNT"   => $amount,
      "ORDER_ID"   => $seq->GetNext(),
      "USER"      => $USER->GetID()
   );
   
   $arElementArray = Array(
      "IBLOCK_SECTION_ID"   => false,
      "IBLOCK_ID"         => $iBlockId,
      "PROPERTY_VALUES"   => $arProps,
      "NAME"            => "Заказ",
      "ACTIVE"         => "Y",
   );
   
   if($iElementId = $el->Add($arElementArray, true, false, false)) {
      // Теперь, после того, как сохранили данные о транзакции
      // можно и подготовить данные для подключения к Сбербанку
   
      $iOrderId = $arProps["ORDER_ID"];

      $params = Array(
         'amount'      => $amount,               // Сумма пополнения
         'orderNumber'   => $iOrderId,            // Внутренний ID заказа
         'password'      => 'pwd-api',            // Здесь пароль от вашего API юзера в Сбербанке
         'userName'      => 'user-api',            // А тут его логин
         'returnUrl'      => 'http://mysite.ru/'      // URL куда вернуть пользователя после перечисления средств
      );

      $opts = Array(                           // А здесь параметры для POST запроса
         'http' => Array(
            'method'  => 'POST',
            'header'  => "Content-type: application/x-www-form-urlencoded",
            'content' => http_build_query($params),
            'timeout' => 60
         )
      );

      // И отправляем эти данные на сервер сбербанка
      $context   = stream_context_create($opts);
      // Здесь должен быть URL тестового и боевого сервера Сбербанка
      $url      = 'https://test/payment/rest/register.do';
      $result      = file_get_contents($url, false, $context);

      // Расшифруем полученные данные в массив $arSbrfResult
      $arSbrfResult = (array) json_decode($result);

      if($arSbrfResult["formUrl"]) {               // Если у нас есть URL для оплаты, то...
         $arResult = Array(                     // Готовим массив для вызывающего скрипта
            "error" => false,                  //   - без ошибок
            "message" => "",                  //   - без сообщения ошибки
            "urlTo" => $arSbrfResult["formUrl"]      //   - и с URL для оплаты
         );
         
         CIBlockElement::SetPropertyValuesEx(      // В инфоблоке с заказами добавим данные по оплате заказа
            $iElementId, $iBlockId,
            Array(
               "ORDER_ID_SBRF"   => $arSbrfResult["orderId"],
               "URL_PAY"      => $arSbrfResult["formUrl"]
            )
         );
      } else {                              //   а если URL для оплаты нет, передаем массив с ошибками
         $arResult = $arSbrfResult; /* errorCode, errorMessage */
      }
   } else { // Если не удалось сохранить заказ/транзакцию в инфоблоках, то передаем сообщение с ошибкой
      $arResult = Array(
         "error"      => true,
         "message"   => "err02" // Код ошибки: Ошибка при создании эл-та инфоблока
      );
   }
} else { //   а если пользователь ввел не число...
   $arResult = Array(
      "error" => true,
      "message" => "err01" // Код ошибки: Введите цену цифрами
   );
}

echo json_encode($arResult); // Возврат данных в скрипт, вызвавший AJAX
Этот скрипт должен получить значение суммы для оплаты, отправить запрос в Сбербанк и получить URL на оплату, затем передать его в скрипт, вызвавший AJAX. 
Важное замечание: поскольку здесь используются ваши логины и пароли к сбербанку, то нужно использовать конструкцию <?php, второе - URL для отправки данных должны быть предоставлены самим сбербанком - для тестового и боевого сервера они отличаются. 
И еще один момент, сам заказ сохраняется в инфоблоках, через элемент инфоблока можно будет отследить URL, суммы, пользователей, статус оплаты/заказа и т.п. 

И возвращаясь к index.php. После того, как на стороне Сбербанка произойдет перечисление средств пользователь вернется на index.php, где в $_REQUEST мы получим данные о заказе - они пригодятся для обновления заказа в элементах инфоблока:
<?php
if($_REQUEST["orderId"] && CModule::IncludeModule("iblock")) {
   $resElement = CIBlockElement::GetList(
      Array(),
      Array(
         "IBLOCK_ID" => 3,
         "PROPERTY_ORDER_ID_SBRF" => $_REQUEST["orderId"]
      ),
      false,
      false,
      Array(
         "IBLOCK_ID", "ID",
         "PROPERTY_ORDER_ID",
         "PROPERTY_ORDER_ID_SBRF",
         "PROPERTY_AMOUNT",
         "PROPERTY_GET"
      )
   );

   if($arElement = $resElement->GetNext()) {
      $params = array(
         'orderId'      => $_REQUEST["orderId"],
         'password'      => 'pwd-api',
         'userName'      => 'user-api'
      );

      $opts = array(
         'http' => array(
            'method'   => 'POST',
            'header'   => "Content-type: application/x-www-form-urlencoded",
            'content'   => http_build_query($params),
            'timeout'   => 60
         )
      );

      $context        = stream_context_create($opts);
      $url            = 'https://test/payment/rest/getOrderStatus.do';
      $result         = file_get_contents($url, false, $context);
      $arSbrfResult   = (array) json_decode($result);

      if($arSbrfResult["OrderStatus"]) {
         CIBlockElement::SetPropertyValuesEx(
            $arElement["ID"], $iBlockId,
            Array("STATUS" => $arSbrfResult["OrderStatus"]+1) // Some magic for prop enum id
         );
         
         LocalRedirect($APPLICATION->GetCurDir());
      }
   }
}
В этом скрипте происходит поиск заказа в инфоблоках и заброс в Сбербанк для получения статуса оплаты, на основе которого будет обновлен элемент инфоблока. 

Напоминаю - это примитивный, упрощенный код. На деле, здесь нужно дописывать все else, доработать вариант, если пользователь не вернулся на index.php после оплаты, но все оплатил (например, с интернетом проблемы) - тут нужно запускать агент на запрос статуса заказа или при очередной авторизации пользователя. 
Этого кода должно быть достаточно что бы начать работать с интернет эквайрингом от Сбербанка.