WhatsApp Business API для подтверждения успешной оплаты
Сценарий отправляет клиенту персонализированный шаблон WhatsApp при успешной оплате.
Обзор сценария
Сценарий отправляет клиенту персонализированный шаблон WhatsApp при успешной оплате. Сообщение включает имя клиента, уплаченную сумму и дату оплаты. Статическая кнопка URL открывает страницу квитанции или историю оплат.
Пример шаблона
Здравствуйте {{1}}, мы получили вашу оплату на сумму {{2}} от {{3}}. Спасибо, что пользуетесь нашим сервисом.
- {{1}}имя клиента
- {{2}}сумма оплаты
- {{3}}дата оплаты
- “Чек”кнопка — исправлена в шаблоне Meta

Когда его использовать
Используйте этот сценарий, когда оплата проходит успешно и клиенту нужно сразу получить подтверждение на канале, который он действительно просматривает, — а не квитанцию по электронной почте, которая остаётся непрочитанной. Это подходит для команд по выставлению счетов и подпискам, подтверждающим оплату по счёту, для коммерческих и сервисных компаний, подтверждающих оформление заказа или банковские переводы, а также для агентств, подключающих платёжные шлюзы или ERP-системы к автоматизированным подтверждениям в WhatsApp.
Как это работает
- Триггер
Биллинг или платёжный шлюз подтверждает, что транзакция завершилась успешно.
event·triggered - Захват события
Номер телефона клиента, оплаченная сумма и дата оплаты получаются из биллинговой системы.
phone:"+…" - Собрать и отправить
Персонализированное сообщение шаблона построено с тремя переменными в теле и кнопкой со статической URL-ссылкой на страницу квитанции.
POST/sendTemplate - Доставлено
Клиент получает подтверждение в WhatsApp и может открыть квитанцию по кнопке.
delivered - Отслеживание статуса
Результат доставки фиксируется для учёта, выставления счёта или поддержки.
status:"read"

Техническая реализация
Предварительные условия
- Ключ API 1MSG · Как получить API-ключ
- Аккаунт WhatsApp Business · Как подключить WABA
- Шаблон WhatsApp · Как одобрить шаблон WABA
- Согласие клиента · Как управлять согласием клиентов
Примеры кода
#!/usr/bin/env bash
set -euo pipefail
# === Configuration (replace "___" placeholders) ===
API_BASE_URL="https://api.1msg.io" # production 1MSG API base URL
CHANNEL_ID="___" # channel ID from 1MSG dashboard
API_TOKEN="___" # channel JWT token (Bearer)
TEMPLATE_NAME="___" # approved template name
TEMPLATE_NAMESPACE="___" # template namespace (required — send fails without it)
TEMPLATE_LANGUAGE="___" # template language code, e.g. "en"
# === Test data ===
TEST_PHONE="___" # client phone in international format
TEST_CUSTOMERNAME="___" # {{1}} customer name
TEST_PAYMENTAMOUNT="___" # {{2}} payment amount
TEST_DATE="___" # {{3}} date or time
PHONE_NORM="$(printf '%s' "$TEST_PHONE" | tr -cd '0-9')"
for pair in "CHANNEL_ID=$CHANNEL_ID" "API_TOKEN=$API_TOKEN" \
"TEMPLATE_NAME=$TEMPLATE_NAME" "TEMPLATE_NAMESPACE=$TEMPLATE_NAMESPACE" \
"TEMPLATE_LANGUAGE=$TEMPLATE_LANGUAGE" "TEST_PHONE=$TEST_PHONE" \
"TEST_CUSTOMERNAME=$TEST_CUSTOMERNAME" \
"TEST_PAYMENTAMOUNT=$TEST_PAYMENTAMOUNT" \
"TEST_DATE=$TEST_DATE"; do
val="${pair#*=}"
if [ -z "$val" ] || [ "$val" = "___" ]; then
echo "Missing configuration value: ${pair%%=*}" >&2
exit 1
fi
done
if [ -z "$PHONE_NORM" ]; then
echo "Error: phone number has no digits after normalization" >&2
exit 1
fi
URL="${API_BASE_URL%/}/${CHANNEL_ID}/sendTemplate"
# params carries body ONLY. Button text is fixed in the Meta template — no button param.
# {{1}} customer name → ${TEST_CUSTOMERNAME}
# {{2}} payment amount → ${TEST_PAYMENTAMOUNT}
# {{3}} date or time → ${TEST_DATE}
read -r -d '' PAYLOAD <<JSON || true
{
"phone": "${PHONE_NORM}",
"template": "${TEMPLATE_NAME}",
"namespace": "${TEMPLATE_NAMESPACE}",
"language": { "policy": "deterministic", "code": "${TEMPLATE_LANGUAGE}" },
"params": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "${TEST_CUSTOMERNAME}" },
{ "type": "text", "text": "${TEST_PAYMENTAMOUNT}" },
{ "type": "text", "text": "${TEST_DATE}" }
]
}
]
}
JSON
RESPONSE="$(curl -s -w '\n%{http_code}' -X POST "$URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_TOKEN}" \
-d "$PAYLOAD")"
HTTP_CODE="$(printf '%s' "$RESPONSE" | tail -n1)"
BODY="$(printf '%s' "$RESPONSE" | sed '$d')"
case "$BODY" in
*'"sent":true'*) ok=1 ;;
*) ok=0 ;;
esac
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ] && [ "$ok" -eq 1 ]; then
echo "Message sent to client."
echo "API response: $BODY"
else
echo "Send failed. HTTP status: $HTTP_CODE" >&2
echo "$BODY" >&2
exit 1
fi
Статус ответа и доставки
HTTP 2xx и JSON "sent": true означают, что 1MSG приняла сообщение для отправки — это не значит, что оно уже достигло телефона клиента. Сохраните id поле (похоже на wamid.…) для сопоставления с обратными вызовами о доставке.
{
"sent": true,
"id": "wamid.HBgLMzgwNjM5...",
"message": "Message accepted for delivery"
}sentПринято для отправки — ещё не на устройстве клиента
idХраните это; доставка callback и
hookInfoзавязаны на этом
Доставка поступает позже, в отдельном обратном вызове. Зарегистрируйте вебхук POST …/webhook, и 1MSG отправит обновления статуса на ваш HTTPS-эндпоинт в основном hooks[]теле.
{
"hooks": [
{
"id": "gBGGeSaGViBfAgnlzOSHEwK9O6F",
"type": "message",
"status": "sent",
"timestamp": "1654864094",
"recipient_id": "556123122026"
}
]
}statussent,delivered,read— или статус сбоя, если применимоidСоотносит обратный вызов с
id, возвращённым вызовом sendtimestampКоличество секунд Unix в виде строки
Если вы предпочитаете не получать обратные вызовы, вместо этого используйте опрос состояния. На практике доставка часто завершается за считанные секунды — GET {base}/{channel}/hookInfo?messageId=<id>но в контракте API это не гарантируется, так что никогда не блокируйте процесс в ожидании этого.
Распространённые ошибки
| Статус | Ответ | Причина |
|---|---|---|
| 200 | Message was not sent: template is not defined | namespace, template or language missing from the request body. |
| 200 | template name (…) does not exist in <language> | The template is approved in a different language than the one requested. |
| 200 | Message was not sent: provide chatId, phone, bsuid, or username | No recipient the channel could resolve. |
| 403 | access denied | The token is wrong, or belongs to a different channel than the URL. |
| 429 | too many requests. please try later | The channel is over its send rate. |
