Вебхуки
Для работы с вебхуками должно быть выдано разрешение ManageWebhookData.
Вебхук — это POST-запрос, который Точка отправляет на указанный вами URL, когда происходит важное событие по вашим счетам: например, поступил или ушёл платёж. Так вам не нужно постоянно опрашивать API — банк сам уведомляет ваш сервис.
В теле вебхука приходит JWT-токен в виде строки, подписанный по алгоритму RS256. Его нужно расшифровать на своей стороне.
Проверка подлинности вебхука
Прежде чем доверять данным из вебхука, проверьте его подпись публичным ключом Точки. Если подпись не сходится — это значит, что запрос пришёл не от Точка Банка, и обрабатывать его нельзя.
Вебхуки отправляются только об успешных операциях. Если вебхук не пришёл, получите информацию о платеже:
- методом Get Payment Operation Info, если оплата проходила по платёжной ссылке.
- методом Get Qr Codes Payment Status, если оплата проходила по QR-коду.
- методомGet Statement, если оплата проходила по реквизитам. Не забудьте создать выписку методом Init Statement.
Виды событий
Сейчас доступно пять видов событий. По каждому — отдельная страница с описанием, кому оно подходит, какие поля приходят и примером тела:
incomingPayment— входящие платежи на счёт.outgoingPayment— исходящие платежи со счёта.incomingSbpPayment— входящие платежи через СБП.incomingSbpB2BPayment— входящие платежи через B2B QR-код по СБП.acquiringInternetPayment— входящие платежи по платёжным ссылкам.
Несколько особенностей в работе вебхуков
- Если на отправленный вебхук мы не получили ответ с кодом HTTP
200, а получили любой другой, то будем повторять отправку 30 раз с интервалом 10 секунд. - При создании или изменении вебхука мы проверяем доступность вашего хоста — отправляем по одному тестовому вебхуку на каждое событие, на которое вы подписаны. Если в ответ не придёт код HTTP
200, вебхук не будет создан или изменён. - Подключить или изменить вебхук можно только по протоколу HTTPS на порт 443. Другой порт или протокол использовать нельзя.
- Вебхук отправляется с заголовком
Content-Type: text/plain— в теле приходит «голая» строка JWT, а не JSON.
Примеры обработки вебхуков
Примеры кода, который принимает вебхук и проверяет его подпись.
В примерах ниже публичный ключ указан прямо в коде только для наглядности. В рабочей интеграции лучше получать ключ по ссылке, чтобы он не устарел.
Python
from aiohttp import web
import jwt
from jwt import exceptions
import json
// Публичный ключ Точка Банка. Может быть получен из https://enter.tochka.com/doc/openapi/static/keys/public
key_json = '{"kty":"RSA","e":"AQAB","n":"rwm77av7GIttq-JF1itEgLCGEZW_zz16RlUQVYlLbJtyRSu61fCec_rroP6PxjXU2uLzUOaGaLgAPeUZAJrGuVp9nryKgbZceHckdHDYgJd9TsdJ1MYUsXaOb9joN9vmsCscBx1lwSlFQyNQsHUsrjuDk-opf6RCuazRQ9gkoDCX70HV8WBMFoVm-YWQKJHZEaIQxg_DU4gMFyKRkDGKsYKA0POL-UgWA1qkg6nHY5BOMKaqxbc5ky87muWB5nNk4mfmsckyFv9j1gBiXLKekA_y4UwG2o1pbOLpJS3bP_c95rm4M9ZBmGXqfOQhbjz8z-s9C11i-jmOQ2ByohS-ST3E5sqBzIsxxrxyQDTw--bZNhzpbciyYW4GfkkqyeYoOPd_84jPTBDKQXssvj8ZOj2XboS77tvEO1n1WlwUzh8HPCJod5_fEgSXuozpJtOggXBv0C2ps7yXlDZf-7Jar0UYc_NJEHJF-xShlqd6Q3sVL02PhSCM-ibn9DN9BKmD"}'
key = json.loads(key_json)
jwk_key = jwt.jwk_from_dict(key)
async def handle(request: web.Request):
payload = await request.text()
try:
# тело вебхука
webhook_jwt = jwt.JWT().decode(
message=payload,
key=jwk_key,
)
except exceptions.JWTDecodeError:
# Неверная подпись, вебхук не от Точка Банка или с ним что-то не так
pass
return web.Response(status=200)
app = web.Application()
app.router.add_route('POST', '/', handle)
if __name__ == '__main__':
web.run_app(app, port=80)
PHP
<?php
require __DIR__ . "/vendor/autoload.php";
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
$entityBody = file_get_contents("php://input");
// Публичный ключ Точка Банка. Может быть получен из https://enter.tochka.com/doc/openapi/static/keys/public
$json_key = '{"kty":"RSA","e":"AQAB","n":"rwm77av7GIttq-JF1itEgLCGEZW_zz16RlUQVYlLbJtyRSu61fCec_rroP6PxjXU2uLzUOaGaLgAPeUZAJrGuVp9nryKgbZceHckdHDYgJd9TsdJ1MYUsXaOb9joN9vmsCscBx1lwSlFQyNQsHUsrjuDk-opf6RCuazRQ9gkoDCX70HV8WBMFoVm-YWQKJHZEaIQxg_DU4gMFyKRkDGKsYKA0POL-UgWA1qkg6nHY5BOMKaqxbc5ky87muWB5nNk4mfmsckyFv9j1gBiXLKekA_y4UwG2o1pbOLpJS3bP_c95rm4M9ZBmGXqfOQhbjz8z-s9C11i-jmOQ2ByohS-ST3E5sqBzIsxxrxyQDTw--bZNhzpbciyYW4GfkkqyeYoOPd_84jPTBDKQXssvj8ZOj2XboS77tvEO1n1WlwUzh8HPCJod5_fEgSXuozpJtOggXBv0C2ps7yXlDZf-7Jar0UYc_NJEHJF-xShlqd6Q3sVL02PhSCM-ibn9DN9BKmD"}';
$jwks = json_decode($json_key, true, 512, JSON_THROW_ON_ERROR);
try {
$decoded = JWT::decode($entityBody, JWK::parseKey($jwks,"RS256"));
} catch (\UnexpectedValueException $e) {
// Неверная подпись, вебхук не от Точка Банка или с ним что-то не так
echo "Invalid webhook";
}
// Тело вебхука
$decoded_array = (array) $decoded;
http_response_code(200);
Java
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jwt.SignedJWT;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.ParseException;
@RestController
@RequestMapping("/")
public class WebhookResource {
@PostMapping(value = "/")
public void tochkaWebhook(@RequestBody String webhookData) throws ParseException, JOSEException {
// Публичный ключ Точка Банка. Может быть получен из https://enter.tochka.com/doc/openapi/static/keys/public
var jsonKey = "{\"kty\":\"RSA\",\"e\":\"AQAB\",\"n\":\"raJQJyBXIgS1YzYFkmQGq5XtadLVvMcx5u-guR2r5ZgSb-HGUG7HF5NM-NJeL9YrVtjjGf8VNLpwGbeejsS9LRniPfKkCYaVqV1DSGOZ6RTOtqN3jKW1W86cVb-LffrQo3eFhPX5V464uduPu9RouFplQ7wprY5ewke0Yj0FCOr6Ebxlpql-aJp_wk8JSzzFN17IC5tfUXgGDjEmnMjxag_CntnJtKWmw69ivhrq5sTPspclL3Ij8K_Qk0MwAZFCci25WxIuKQe7Mk4dvay6CUfrCbAgEtqMcWUSqoG7pdBig59lo-kIMWvVQIAWjo2JhI7VlI_ssvFtiJg5T9myE914aESFZ8jEheQv-4kZ81F0qk02k2mJ4C7AasGhbzC4F8YQ7nbr49v1n_j8udNZZXA8vI2hacG517A66-uvEHIxXRUo_gIcubR-vdbJbaK_k8JRLJNmdf4B9HchJ6VD9aGjMT0GYfhQ8jf16E1L_U4G4XLB5cnb0h88PD2MaMGP\"}";
var jwkKey = JWK.parse(jsonKey);
var jwtData = SignedJWT.parse(webhookData);
var verifier = new RSASSAVerifier(jwkKey.toRSAKey().toRSAPublicKey());
if (!jwtData.verify(verifier)) {
// Неверная подпись, вебхук не от Точка Банка или с ним что-то не так
System.out.println("Invalid webhook");
return;
}
// Тело вебхука
var webhookParsedData = jwtData.getJWTClaimsSet().getClaims();
return;
}
}
Вебхуками можно управлять с помощью методов
- Get Webhooks — получить список всех вебхуков, настроенных на приложение.
- Create Webhook — создать новый вебхук.
- Edit Webhook — изменить URL или тип существующего вебхука.
- Delete Webhook — удалить вебхук.
- Send Webhook — отправить тестовый вебхук.