Оплаты ЮKassa и Т-Банка в таблицу
Платёжный сервис сообщает об оплате, скрипт записывает или обновляет строку платежа на листе «Оплаты». Так в таблице всегда видно, какие заказы оплачены, без выгрузок из личного кабинета.
Что понадобится:
- таблица Coverse на тарифе «Бизнес» или выше;
- доступ к личному кабинету ЮKassa или к настройкам терминала Т-Банка;
- 15 минут.
Как это работает
- Платёжный сервис отправляет уведомление на адрес вебхука скрипта.
- Coverse проверяет адрес и, если настроено, IP-адрес отправителя, отвечает сервису
200 OKи ставит запуск скрипта в очередь. - Скрипт проверяет, что уведомление настоящее, и записывает платёж. Если строка с этим платежом уже есть, обновляет её статус.
Платёжные сервисы повторяют уведомление, пока не получат 200 OK, поэтому одно и то же уведомление может прийти несколько раз. Скрипты ниже это учитывают.
Общий шаг: адрес вебхука
- Создайте скрипт и вставьте код для своего сервиса из разделов ниже.
- На вкладке «Триггеры» добавьте триггер «По вебхуку» с функцией
onWebhook. - Скопируйте адрес вида
https://app.coverse.team/api/hooks/scripts/whs_….
Подробнее о настройках вебхука: Вебхуки.
ЮKassa
ЮKassa не подписывает уведомления. Документация ЮKassa советует две проверки: адрес отправителя и текущий статус платежа через API. Скрипт делает обе.
1. Ограничьте адреса отправителя
В настройках триггера «По вебхуку» в поле «Разрешённые IP-адреса» укажите адреса ЮKassa из её документации:
185.71.76.0/27, 185.71.77.0/27, 77.75.153.0/25, 77.75.156.11, 77.75.156.35, 77.75.154.128/25, 2a02:5180::/32
Запросы с других адресов Coverse отклонит, скрипт не запустится. Перед настройкой сверьте список с документацией ЮKassa: он может обновляться. Если список устареет, после 20 отклонённых запросов подряд Coverse выключит триггер и пришлёт владельцу письмо.
2. Сохраните ключи в секреты
На вкладке «Секреты» добавьте:
YOOKASSA_SHOP_ID— идентификатор магазина;YOOKASSA_SECRET_KEY— секретный ключ из раздела интеграции в личном кабинете ЮKassa.
3. Код скрипта
const SHEET = "Оплаты";
function onWebhook(event) {
if (!event || !event.json || !event.json.object) return;
if (String(event.json.event || "").indexOf("payment.") !== 0) return;
const paymentId = event.json.object.id;
const auth = "Basic " + Utilities.base64Encode(Secrets.get("YOOKASSA_SHOP_ID") + ":" + Secrets.get("YOOKASSA_SECRET_KEY"));
const response = Http.get("https://api.yookassa.ru/v3/payments/" + encodeURIComponent(paymentId), {
headers: { Authorization: auth },
});
if (!response.ok) throw new Error("ЮKassa ответила с кодом " + response.status + " для платежа " + paymentId);
const payment = response.json();
const metadata = payment.metadata || {};
savePayment([
payment.id,
payment.status,
Number(payment.amount.value),
payment.amount.currency,
asText(payment.description),
asText(metadata.order_id),
new Date(),
]);
}
function savePayment(values) {
const sheet = Coverse.table.getSheetByName(SHEET) || Coverse.table.insertSheet(SHEET);
if (sheet.getLastRow() === 0) {
sheet.appendRow(["ID платежа", "Статус", "Сумма", "Валюта", "Описание", "Заказ", "Обновлено"]);
}
const ids = sheet.getRange(1, 1, sheet.getLastRow(), 1).getValues().map((row) => String(row[0]));
const index = ids.indexOf(String(values[0]));
if (index === -1) {
sheet.appendRow(values);
} else {
sheet.getRange(index + 1, 1, 1, values.length).setValues([values]);
}
}
function asText(value) {
const text = String(value === undefined || value === null ? "" : value);
return text.charAt(0) === "=" ? "'" + text : text;
}
Что важно в коде:
- Скрипт не доверяет данным из уведомления, а запрашивает платёж у ЮKassa по его идентификатору и записывает то, что ответил API.
- Сумма у ЮKassa приходит текстом вида
"1500.00", скрипт превращает её в число. metadata.order_id— номер заказа, если ваш сайт передаёт его вmetadataпри создании платежа. Если поле называется иначе, поменяйте имя.- Скрипт обрабатывает только события платежей (
payment.…). Уведомления о возвратах и выплатах он пропускает. - Текст из внешнего сервиса, который начинается с
=, таблица записала бы как формулу. ФункцияasTextставит перед таким значением апостроф, и оно записывается текстом.
4. Подключите адрес в ЮKassa
- В личном кабинете ЮKassa откройте настройки HTTP-уведомлений.
- Вставьте адрес вебхука из Coverse.
- Отметьте события платежей, например
payment.succeededиpayment.canceled, и сохраните.
Т-Банк: интернет-эквайринг
Т-Банк подписывает каждое уведомление полем Token. Скрипт вычисляет токен сам и сравнивает. Для этого нужен пароль терминала.
1. Сохраните пароль терминала в секрет
На вкладке «Секреты» добавьте секрет TBANK_PASSWORD со значением пароля терминала из личного кабинета Т-Бизнеса.
2. Код скрипта
const SHEET = "Оплаты";
function onWebhook(event) {
if (!event || !event.json) return;
const data = event.json;
if (String(data.Token).toLowerCase() !== tbankToken(data, Secrets.get("TBANK_PASSWORD"))) {
console.warn("Неверный токен, уведомление пропущено");
return;
}
if (data.PaymentId === undefined || data.Amount === undefined) return;
savePayment([
String(data.PaymentId),
data.Status,
Number(data.Amount) / 100,
"RUB",
data.Success ? "" : "Ошибка " + data.ErrorCode,
asText(data.OrderId),
new Date(),
]);
}
function tbankToken(data, password) {
const pairs = Object.assign({}, data, { Password: password });
delete pairs.Token;
const text = Object.keys(pairs)
.filter((key) => pairs[key] !== null && pairs[key] !== undefined && typeof pairs[key] !== "object")
.sort()
.map((key) => String(pairs[key]))
.join("");
return Utilities.hash("sha256", text);
}
const EARLY = ["NEW", "FORM_SHOWED", "AUTHORIZING", "3DS_CHECKING", "3DS_CHECKED", "AUTHORIZED", "CONFIRMING"];
function savePayment(values) {
const sheet = Coverse.table.getSheetByName(SHEET) || Coverse.table.insertSheet(SHEET);
if (sheet.getLastRow() === 0) {
sheet.appendRow(["ID платежа", "Статус", "Сумма", "Валюта", "Комментарий", "Заказ", "Обновлено"]);
}
const rows = sheet.getRange(1, 1, sheet.getLastRow(), 2).getValues();
const index = rows.findIndex((row) => String(row[0]) === String(values[0]));
if (index === -1) {
sheet.appendRow(values);
} else if (!EARLY.includes(rows[index][1]) && EARLY.includes(values[1])) {
console.log("Устаревшее уведомление пропущено:", values[0], values[1]);
} else {
sheet.getRange(index + 1, 1, 1, values.length).setValues([values]);
}
}
function asText(value) {
const text = String(value === undefined || value === null ? "" : value);
return text.charAt(0) === "=" ? "'" + text : text;
}
Как считается токен, по документации Т-Банка:
- Берутся все поля уведомления, кроме
Tokenи вложенных объектов, напримерDataиReceipt. - Добавляется поле
Passwordс паролем терминала. - Поля сортируются по названию, значения склеиваются в одну строку.
- От строки считается SHA-256.
Сумма в уведомлении приходит в копейках, поэтому скрипт делит её на 100. Уведомления без PaymentId или суммы, например о привязке карты, скрипт пропускает.
Уведомления могут прийти повторно и не по порядку. Промежуточные статусы перечислены в EARLY: если в таблице уже записан более поздний статус, например CONFIRMED, опоздавшее уведомление AUTHORIZED его не перезапишет, а возвраты и отмены после оплаты записываются. Список статусов сверьте с документацией Т-Банка для своего способа оплаты. Там же проверьте, какую сумму присылает уведомление о частичном возврате: скрипт записывает сумму из последнего уведомления. Успешная оплата приходит со статусом CONFIRMED, при двухстадийной оплате сначала приходит AUTHORIZED.
3. Подключите адрес в Т-Банке
Укажите адрес вебхука из Coverse как адрес для уведомлений (NotificationURL) в настройках терминала или передавайте его в параметре NotificationURL при создании платежа. Т-Банк ждёт ответ 200 с телом OK, Coverse отвечает именно так.
Проверьте
- Проведите тестовый платёж в тестовом магазине ЮKassa или на тестовом терминале Т-Банка.
- Через несколько секунд на листе «Оплаты» появится строка, повторное уведомление обновит её, а не добавит новую.
- На вкладке «Журнал» скрипта видно событие и результат каждого запуска.
Если что-то не работает
| Симптом | Причина | Что сделать |
|---|---|---|
| в журнале скрипта нет запусков | неверный адрес, или IP-адрес отправителя не входит в разрешённые | проверьте адрес и список IP-адресов триггера |
| триггер выключился, пришло письмо | 20 запросов подряд отклонены, обычно из-за устаревшего списка IP-адресов | обновите список по документации ЮKassa и включите триггер |
| ЮKassa ответила с кодом 401 | неверный идентификатор магазина или секретный ключ | проверьте секреты YOOKASSA_SHOP_ID и YOOKASSA_SECRET_KEY |
| «Неверный токен, уведомление пропущено» | пароль терминала неверный или от другого терминала | сохраните пароль заново в секрет TBANK_PASSWORD |
Secrets.get вернул null | код менял не владелец скрипта | владелец должен подтвердить код, см. Секреты |
| строка не появилась, в журнале «Ошибка» | ошибка в коде после правок | текст ошибки и номер строки — в журнале |
Coverse отвечает платёжному сервису 200 OK до запуска скрипта. Если скрипт упадёт с ошибкой или запуск не состоится (в журнале статус «Не запустился», например запуск слишком долго ждал очереди или закончилась норма минут), сервис не будет повторять это уведомление. Поэтому после правок кода проверяйте журнал, а важные оплаты время от времени сверяйте с личным кабинетом. Если Coverse отклонил запрос, например закончилась норма минут скриптов, сервис получит код ошибки и повторит уведомление позже.
Частые вопросы
Можно ли записывать оплаты ЮKassa в таблицу?
Да. ЮKassa отправляет HTTP-уведомление на вебхук скрипта, скрипт проверяет платёж через API ЮKassa и записывает его статус в таблицу.
Как проверить, что уведомление Т-Банка настоящее?
Скрипт вычисляет токен уведомления по алгоритму Т-Банка с паролем терминала и сравнивает с полем Token.
Почему важно, что вебхук отвечает 200 OK?
ЮKassa и Т-Банк считают уведомление доставленным только при ответе 200, а Т-Банк ещё и с телом OK. Иначе они повторяют уведомление.
Что дальше
- Вебхуки: ограничение по IP, подпись и коды ответа.
- Заявки с сайта на Tilda в таблицу
- Выгрузка сделок из amoCRM и Битрикс24