Управление платежами в Приват24 из Google-таблиц

  • Tutorial

Работать с интернет-банкингом большого количества предприятий может оказаться достаточно рутинной задачей. Для создания платежей и их контроля необходимо каждый раз переключаться между кабинетами. А если таких организаций больше 50, то можно с легкостью сбиться и наделать ошибок, не говоря уже о портаченном времени. Давайте посмотрим, как можно облегчить жизнь бухгалтера на примере API ПриватБанка для бизнеса.

Для работы со счетом необходимо создать Автоклиент и получить данные для авторизации. Эти данные, а так же платежную информацию будем хранить в Google-таблице.





Инструменты / Редактор скриптов. Здесь будем писать бекенд на Google Apps Script, в основе которого лежит Java Script. Наша задача — создать http-запрос, указав необходимые реквизиты.

Скрипт будет реагировать на изменения в последнем столбце. Для этого добавим триггер при изменении (onEdit) — выполнять функцию onEditt(). И саму функцию:

var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
var lastCol = sheet.getLastColumn();


function onEditt(e) {
  
  var range = e.range;
  var col = range.getColumn();
  var row = range.getRow();

  if (row > 1) {
    
    if (col == lastCol) {
      
      var value = range.getValue();
      
      if (value == 'Отправить') {
        createPayment(range.getRow());
      }
      
      else if (value == 'Проверить') {
        checkPayment(range.getRow(), sheet);
      }        
      
      range.clearContent();    
    } 
}

Создание платежек


Для создания платежек используется POST-запрос. В процессе генерируется номер документа, который пригодится чуть позже. В результате будут видны статусы выполнения по каждому запросу — или успех или ошибка.

var url = "https://acp.privatbank.ua/api/proxy/payment/create_pred";
var day = getDay();

//передать номер строки в таблице
function createPayment(i) {
  
  var iRange = sheet.getRange(i, 1, 1, lastCol);
  var values = iRange.getDisplayValues();
  var docNumRange = sheet.getRange(i, 3);
  var stateRange = sheet.getRange(i, 10);
  var state = stateRange.getValue();  
  
  if (state == 'Создано') {
    iRange.setBackground('#66ff66');
    return;
  } else if (state == 'Оплачено') {
    iRange.setBackground('#ebebe0');
    return;
  }
  
  docNumRange.clearContent();
  
  var name = values[0][0];
  var userData = getUserData(name);
  
  if (!userData) {
    iRange.setBackground('#ffff80');
    stateRange.setValue('Нет данных пользователя');
    return;
  }
  
  var inn = userData[1];
  var id = userData[2];
  var token = userData[3];
  var payer_account = userData[4];
  var doc_type = values[0][1];
  var document_number = "" + day + (i + 10) + Math.floor(Math.random() * 90 + 10);
  var payment_amount = values[0][3];
  var payment_naming = values[0][4];    
  var recipient_account = values[0][5];
  var recipient_nceo = values[0][6]; 
  var recipient_ifi = values[0][7];  
  var payment_destination = values[0][8];
  
  if (!inn || !id || !token || !payer_account) 
  {
    iRange.setBackground('#ffff80');
    stateRange.setValue('Нет данных пользователя');
    return;
  }
    
  if (!document_number || ! recipient_account || !recipient_nceo || 
      !payment_naming || !recipient_ifi || !payment_amount || !payment_destination) 
  {
    iRange.setBackground('#ffff80');
    stateRange.setValue('Ошибка заполнения');
    return;
  }
    
  var headers = {
    'User-Agent' : 'GooApps',
    'id' : id,
    'token' : token,      
    'Connection' : 'close'
  }
  
  var payload = {
    'document_number' : document_number,
    'payer_account' : payer_account,
    'recipient_account' : recipient_account,
    'recipient_nceo' : recipient_nceo,
    'payment_naming' : payment_naming,
    'recipient_ifi' : recipient_ifi,
    'payment_amount' : payment_amount,
    'payment_destination' : payment_destination    
  };
  
  var options = {
    'method' : 'post',
    'contentType' : 'application/json;charset=utf8',
    'headers' : headers,
    'payload' : JSON.stringify(payload),
    'muteHttpExceptions' : true
  };    

  var response = UrlFetchApp.fetch(url, options);  
  var respCode = response.getResponseCode();
  
  if (respCode == 201) {
    docNumRange.setValue(document_number);      
    iRange.setBackground('#66ff66');  
    stateRange.setValue('Создано');
  } else {    
    iRange.setBackground('#ff9980');   
    var resp = JSON.parse(response.getContentText());
    var error = resp.code;

    if (!error) {
      error = resp.error_code;
    }

    stateRange.setValue(error);    
  }    
}


//создать все платежки
function createPayments() {

  var range = sheet.getRange(2, 1, lastRow, lastCol);
  range.clearFormat();  
    
  for (var i = 2; i <= lastRow; i++) {
    createPayment(i);
  }
}


function getUserData(name) {        
  var uSheet = ss.getSheetByName('Users');
  var uRange = usersSheet.getRange(2, 1, uSheet.getLastRow(), uSheet.getLastColumn());
  var values = uRange.getDisplayValues();
  
  for (var i = 0; i < values.length; i++) {
    if (name == values[i][0]) {
      return values[i];
    }
  }  
}


function getDay() {   
  var now = new Date();
  var start = new Date('January 1, 2018 00:00:00 +0200');
  var number = Math.round((now.getTime() - start.getTime()) / (1000 * 3600 * 24));    
  return number;
}

Контроль оплат


Для контроля оплат будем получать все выписки за последние 30 дней и искать в них номер документа, статус и сумму оплаты. Если все совпадает — платежка оплачена. В этом случае используется GET-запрос.

var urlGet = 'https://acp.privatbank.ua/api/proxy/transactions';
var stDate = Utilities.formatDate(substMonth() , '+0200', 'dd-MM-yyyy');
var endDate = Utilities.formatDate(new Date(), '+0200', 'dd-MM-yyyy');

//передать номер строки в таблице
function checkPayment(i) {
  
  var iRange = sheet.getRange(i, 1, 1, lastCol);
  var values = iRange.getDisplayValues();  
  var stateRange = sheet.getRange(i, 10);
  var state = stateRange.getValue();
  
  if (state != 'Создано') {
    return;
  }  
  
  var name = values[0][0];
  var userData = getUserData(name);
  
  if (!userData) {
    iRange.setBackground('#ffe066');
    stateRange.setValue('Нет данных пользователя');
    return;
  }
  
  var id = userData[2];
  var token = userData[3];
  var payer_account = userData[4];
  var document_number = values[0][2];
  var payment_amount = values[0][3];  
 
  if (!id || !token || !payer_account) {
    iRange.setBackground('#ffe066');
    stateRange.setValue('Нет данных пользователя');
    return;
  }

  if (!document_number || !payment_amount) {
    iRange.setBackground('#ffe066');
    stateRange.setValue('Ошибка заполнения');  
    return;
  }
  
  var headers = {
    'User-Agent' : 'GooApps',
    'id' : id,
    'token' : token,      
    'Connection' : 'close'
  }
  
  var options = {
    'method' : 'get',
    'contentType' : 'application/json;charset=utf8',
    'headers' : headers,
    'muteHttpExceptions' : true
  };      
  
  var url = Utilities.formatString('%s?acc=%s&startDate=%s&endDate=%s', urlGet, payer_account, stDate, endDate);  
  var response = UrlFetchApp.fetch(url, options);  
  var resp = JSON.parse(response);
  var statements = resp.StatementsResponse.statements;
  
  for (var j = 0; j < statements.length; j++) {    
    for (var name in statements[j]) {
      var bill = statements[j][name];
      
      if (document_number == bill.BPL_NUM_DOC) {
        var pay = payment_amount.toString().replace(',','.');
        if (pay == bill.BPL_SUM) {
          if ('r' == bill.BPL_PR_PR) {
            iRange.setBackground('#ebebe0');
            stateRange.setValue('Оплачено');
          } else {
            iRange.setBackground('#df80ff');
            stateRange.setValue('Не оплачено');
          }
        } else {
          Logger.log(pay + ' ' + bill.BPL_SUM)
          iRange.setBackground('#df80ff');
          stateRange.setValue('Сумма не совпадает');
        }      
      }        
    }
  }    
}


function substMonth() {
  var now = new Date();
  var start = new Date(now.getTime() - 30 * (1000 * 3600 * 24));
  return start;
}

Проверку можно поставить на триггер. Останется только наблюдать, как выполняются оплаты.



P.S. Попробуйте, как это работает на тестовой таблице (ссылка обновлена на улучшенную версию). Скопируйте ее на свой диск. Для автоматизации проверок необходимо добавить триггер по времени на функцию checkPayments.
Реклама
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее

Комментарии 15

    0

    Post платежка формируется бухгалтером по предприятиям, которые он ведёт. А сами предприниматели оплачивают сформированные платежки.
    С помощью выписок, они же логи оплаты, бухгалтер контролирует выполнение оплат.

      0
      Все же управление ожидаемо сводится к внутреннему документообороту и чтению логов из банков.
      т.е. POST платёжки все ещё направляется бухгалтеру, или в лучшем случае – секретарше?
        0

        Post платежка формируется бухгалтером по предприятиям, которые он ведёт. А сами предприниматели оплачивают сформированные платежки.
        С помощью выписок, они же логи оплаты, бухгалтер контролирует выполнение оплат.

        0
        Подходящая статья, что бы сверить часы: в Беларуси та же ситуация. слать токены в банк безполезно, декларировать можно/нужно, а учитывать приходится в любом случае.
          0
          данный сервис бесплатный? Просто насколько я помню за G Suite Business гугл просил денег
            0
            Сервис бесплатный
            0
            Несколько вопросов для тех, кто не в теме:
            1) Как происходит авторизация в банкинге? Вы эмулируете браузер, или это можно делать из коробки Google Apis, или есть готовые решения от Приватбанка? Или Приватбанк сам даёт публичный API?
            2) В России большинство интернет-банков для юр.лиц используют usb-токены для генерации усиленной ЭЦП. Поэтому довольно непонятно как вы подписываете платежи, или Google-таблицы тоже это умеют делать?
            3) Ну и последний вопрос: а не боитесь хранить пароли от 50 компаний со всеми деньгами на счетах, и паролями от любых операций (отправить деньги, ага) в онлайновой таблице google? Злоумышленнику не нужно даже взламывать ваш комп, ему нужен только ваш пароль от аккаунта google, где хранятся все доки…
              +1
              Взаимодействие происходит с помощью публичного API ПриватБанка. Создаётся Автоклиент — подобие пользователя с ограниченными правами. Зная его id и токен можно получить доступ к тому счету или группе счетов, на который настроен Автоклиент. Его права ограничены созданием платежек и просмотром выписок. Подпись платежек электронным ключом и их проведение осуществляется самим предпринимателем. Т.е. автоклиент не влияет на движение денежных средств, а лишь создаёт черновик документа. В самой худшей ситуации, злоумышленник получит доступ к уже оплаченным выпискам.
              Остаётся только создать http-запрос, что apps script умеет делать из коробки.
                0
                Интересно, спасибо.
              0

              ...

                0
                Пожалуйста поделитесь тестовой таблицей.
                  0
                  Добавил в конец статьи.
                • НЛО прилетело и опубликовало эту надпись здесь
                    0
                    Думал добавить эл. ящики пользователей и делать рассылки по событиям. Про смс-сервис не знал. Спасибо за подсказку.
                    • НЛО прилетело и опубликовало эту надпись здесь

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое