Привет, Хабр. В этой статье расскажем, как настроить автоматические напоминания о предстоящих и просроченных платежах и мотивировать клиентов оплачивать счета заранее или хотя бы вовремя.
Чтобы менеджеры и операторы не тратили время на подобный контроль, а добросовестные клиенты получали поощрения, мы построили простую систему напоминаний с игровой механикой. Это рассылка сообщения через SMS API от МТС Exolve, при этом данные о клиентах и сделках храним в CRM-системе Битрикс24.
Игрофикация подразумевает вознаграждение за раннюю оплату и штрафы — списание бонусных баллов за задержку оплаты. Для этого
Каждый день проверяем базу и находим тех, у кого сделка ещё не оплачена
Клиенту, с которым создана новая сделка, раз в сутки присылаем сообщение за 5, 4, 3, 2, 1 день и в последний — с утра
При оплате за 5 дней и ранее даём бонус 10%, далее каждый день минус 2%, в последний день — 0%
Тем, кто не оплатил, рассылаем SMS спустя 1, 3 и 5 дней после даты оплаты
Кто так и не перевёл деньги, уведомляем о сгорании бонусов и разрыве договора на 5-й день просрочки платежа
Отправляем такие сообщения
К оплате N ₽. При оплате сегодня +10% бонусов. Бонусный счёт: M ₽.
Пришлите в ответ сумму списания бонусов или не отвечайте для их накопления.
Оплатите счёт до [дедлайн], получайте бонусы за раннюю оплату! 1 день = 2% бонусов!
Теперь пошагово настроим и закодим решение.
Настройка переадресации
Такая система не учитывает жизненных обстоятельств клиентов, и лучше предусмотреть комфортный способ решения вопроса, чтобы компания не казалась «бездушным коллектором». Все подобные ситуации будем решать простым звонком менеджеру. Платёж можно отложить, выбрать срок рассрочки или получить индивидуальные условия.
Интуитивный способ — позвонить на номер, с которого приходит SMS. Он привязан к приложению в личном кабинете МТС Exolve. Чтобы не строить целую виртуальную АТС, мы сделаем переадресацию с привязанного к приложению виртуального номера на менеджера или многоканальный телефон с операторами, хотя в будущем можно настроить приём вызовов напрямую на этот номер.
Вот как это сделать:
Вход в МТС Exolve. Авторизуйтесь в личном кабинете МТС Exolve и подключите номер, который будет использоваться для переадресации.
Если регистрируетесь, то на этапе тестирования рекомендуем работать без подписания договора. Подтвердите личный номер телефона, создайте приложение. Как новый пользователь получите бонусные баллы и на них арендуйте виртуальный номер телефона для отправки SMS в неограниченном количестве.
Когда будете готовы к коммерческой эксплуатации, подпишите договор и отправляйте SMS на любые номера телефонов.Настройка переадресации. Перейдите в ваше приложение, в раздел «Номера», выберите номер, с которого нужно настроить переадресацию, включите её и укажите номер, куда будут перенаправляться звонки.
Номер можно ввести в международном формате (например, +79161234567).

Взаимодействие с SMS API
Сообщения отправляются через API. Для этого получите API-ключ вашего приложения в МТС Exolve. Советуем взять код функции отправки SMS и инструкцию о получении ключа в нашей предыдущей статье.
Чтобы получать уведомления о доставке SMS и использовать их текст, нужно настроить исходящий вебхук. Для этого развернём свой сервис. Из приятного — это можно сделать и бесплатно, а использование докера и фреймворка Flask максимально упростит процесс. Инструкцию по настройке вебхука и развёртыванию сервера вы найдёте в той же статью.
Если хочется получить полный код — мы разместили его в нашем публичном репозитории.
Пропишем все нужные константы и инициализируем переменные, затем запускаем приложение:
from fast_bitrix24 import Bitrix
from datetime import datetime
import os
from flask import Flask, request
BITRIX_CODE = os.environ['CRM_PHONE']
BITRIX_URL = r'https://b24-57wnll.bitrix24.ru/rest/1/' + BITRIX_CODE
CRM_PHONE = os.environ['CRM_PHONE']
SMS_API = os.environ['SMS_API']
endpoint = Bitrix(BITRIX_URL)
app = Flask(__name__)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Следующая функция обрабатывает вебхук от платформы МТС Exolve, оповещающий о приходе SMS. Здесь мы проверяем тип события — пришло ли сообщение или хватает ли у пользователя бонусов, а также что он прислал именно сумму, а не что-то другое:
@app.route('/receive_sms', methods=['POST'])
def receive_sms():
SMS_data = request.get_json()
print(SMS_data)
if SMS_data.get('event_id') == 'DIRECTION_OUTGOING':
print('SMS not received')
return 'Not processed', 200
try:
bonus_to_pay = float(SMS_data.get('text'))
except:
return 'Invalid SMS', 200
contact = get_contacts(SMS_data.get('sender'))[0]
bonus_to_pay = min(bonus_to_pay, float(contact[FIELD_NAME]))
update_bonus(contact['ID'], bonus_to_pay, bonus_to_pay=True)
return 'OK', 200
Для ответа клиенту зададим строки, которые далее будем форматировать (подставлять конкретные цифры и дату) перед отправкой:
FIVE_DAYS_STRING = '''К оплате {} руб. При оплате сегодня +10% бонусов.
Бонусный счет {} руб.
Пришлите в ответ сумму списания бонусов или не отвечайте для их накопления.
Оплатите счёт до {}, получайте бонусы за раннюю оплату! 1 день = 2% бонусов!
'''
OTHER_DAYS_STRING = '''К оплате {} руб. Успейте оплатить, пока не сгорели все бонусы!
Бонусный счет {} руб.
Пришлите в ответ сумму списания бонусов или не отвечайте для их накопления.
Оплатите счёт до {}, получайте бонусы за раннюю оплату! 1 день = 2% бонусов!
'''
MISSED_DAYS_STRING = '''К оплате {} руб. Ждём от вас оплату, а пока ваши бонусы тают!
Бонусный счет {} руб.
'''
Раз в сутки проверяем, кто из клиентов успел оплатить и заслуживает поощрения, а кого следует поторопить:
def process_deals(opened_deals: list[dict]):
for d in opened_deals:
deadline = datetime.strptime(d['CLOSEDATE'].split('T')[0], '%Y-%m-%d')
cost = float(d['OPPORTUNITY'])*0.1
remained_time = (deadline-datetime.now()).days
contact = get_contact_by_ID(d['CONTACT_ID'])
bonus = float(contact[FIELD_NAME])
if remained_time == 5:
bonus += cost
send_str = FIVE_DAYS_STRING.format((float(d['OPPORTUNITY']), bonus, deadline.strftime('%d.%m.%Y')))
else:
bonus -= cost*0.2 # Уменьшаем зачисленные авансом бонусы или штрафуем за просрочку
send_str = OTHER_DAYS_STRING.format(float(d['OPPORTUNITY']), bonus, deadline.strftime('%d.%m.%Y'))
if remained_time < 0:
send_str = MISSED_DAYS_STRING.format(float(d['OPPORTUNITY']), bonus)
if bonus < 0: bonus = 0
if remained_time < -5:
close_deal(d['ID'])
send_str = 'Очень жаль, но мы вынуждены с Вами расстаться и разорвать контракт.'
update_bonus(d['CONTACT_ID'], bonus)
print(send_SMS(contact['PHONE'][0]['VALUE'].strip('+'), send_str))
Бонусы зачисляются на счёт при создании новой сделки, и их сумма уменьшается с каждым днём. В случае просрочки платежа более чем на пять дней сделка закрывается. Используемые здесь функции для работы с контактами будут раскрыты далее.
Создание сделки и установка срока оплаты
В примере создание сделки с клиентом и установка крайних сроков оплаты лежат на пользователе — вашем менеджере. При разработке вашей системы тоже понадобится создавать сделки для теста. Опишем алгоритм действий:
Создание сделки
Откройте «Битрикс24» и в левом меню выберите CRM → «Сделки».
В правом верхнем углу нажмите на «Добавить сделку».
Если используется упрощённая форма, можно сразу заполнить основные поля (название, контакт, компания). Для перехода к полной форме нажмите «Подробно».Заполните основные данные
Понятное название сделки (например, «Заказ на поставку оборудования»)
Выберите этап из воронки продаж (например, «Первичный контакт»)
Привяжите существующий контакт или компанию либо создайте новые
Сохраните сделку. Она появится в общем списке.

Установка срока оплаты
В списке сделок найдите нужную и откройте её
В форме сделки в разделе «О СДЕЛКЕ» выберите поле «Дата завершения»
Укажите дату и сохраните изменения
В настройках CRM можно создать бизнес-процесс, который автоматически устанавливает крайние сроки в зависимости от стадии сделки. Степень автоматизации, которую целесообразно выбрать, зависит от величины вашего бизнеса.
Взаимодействие с базой контактов
Библиотека fast_bitrix24 позволяет разработчикам легко интегрироваться с API «Битрикс24» через Python. Для получения информации о контактах или сделках достаточно вызвать методы crm.contact.list и crm.deal.list, передав необходимые фильтры (например, по дате или стадии).
Библиотека автоматически обрабатывает авторизацию, пагинацию и ограничения API, а асинхронные запросы ускоряют работу с большими объёмами данных. Это удобно для разработчика, так как сокращает код, упрощает обработку ошибок и позволяет быстро внедрять сложные сценарии — от синхронизации данных до аналитики, без необходимости глубокого погружения в нюансы REST API «Битрикс24».
Для работы с базой данных создадим функции получения сделок и контактов, а также их обновления:
def get_contacts(number: str | None = None):
truncated_contact_items = endpoint.get_all("crm.contact.list")
result = []
for tci in truncated_contact_items:
current_contact = get_contact_by_ID(tci["ID"])
phones = [p['VALUE'].strip('+') for p in current_contact['PHONE']]
if number is not None and number not in phones: continue
result.append(current_contact)
return result
def get_contact_by_ID(ID):
ans = endpoint.call("crm.contact.get", items={"ID": ID})
return ans['order0000000000']
def update_bonus(ID, number, bonus_to_pay=False):
fn = FIELD_NAME if not bonus_to_pay else FIELD_NAME2
if number < 0: number = 0
update_data = [{
"ID": ID,
"fields":
{
fn: number,
}
}]
endpoint.call("crm.contact.update", items=update_data)
def close_deal(ID):
update_data = [{
"ID": ID,
"fields":
{
'CLOSED': 'Y',
}
}]
endpoint.call("crm.deal.update", items=update_data)
def get_deals():
return endpoint.get_all("crm.deal.list", params={'filter': {'CLOSED': 'N'}})
Метод get_all позволяет фильтровать как сделки, так и контакты практически по любым признакам. Но по номерам телефонов получать клиентов не выйдет: по всей видимости, фильтрация по множественным полям, которые содержат список значений, не предусмотрена (хотя мы не нашли прямых указаний на это). Методы обновления записи требуют указания конкретных полей, подлежащих обновлению.
Для хранения счёта бонусов и их числа для списания создадим два пользовательских поля. Это нужно сделать только один раз. Тогда в словаре, содержащем всю информацию о контакте, появятся два поля с названием вида UF_CRM_1742576602338. Управлять этими названиями мы не можем, поэтому их придётся сохранить в конкретные константы. После создания соответствующего поля в нём будет отображаться текущее значение бонусного счёта:

Нам осталось только предусмотреть оплату. Автоматизировать полностью этот процесс здесь мы не будем, покажем только функцию, которая обрабатывает запрос, содержащий номер сделки и сумму оплаты. Функция проверит, хватает ли оплаченной суммы и бонусов, и закроет сделку в случае успеха.
@app.route('/get_paid', methods=['POST'])
def receive_data():
payment_data = request.get_json()
print(payment_data)
amount = payment_data['paid']
deal_ID = payment_data.get('deal_ID', None)
deal = endpoint.get_all("crm.deal.list", params={'filter': {'ID': deal_ID}})[0]
user_ID = deal['CONTACT_ID']
ct = get_contact_by_ID(user_ID)
bonuses_to_pay = ct[FIELD_NAME2]
if amount+bonuses_to_pay < deal['OPPORTUNITY']:
print('SMS not received')
return 'Not enough money', 200
# Close deal
close_deal(deal_ID)
# Subtract bonuses
bonus = float(ct[FIELD_NAME])
update_bonus(d['CONTACT_ID'], bonus-bonuses_to_pay)
return 'OK', 200
При каждом запуске проверки сделок (один раз в сутки) с бонусного счёта списывается 1/5 бонусов, начисленных в первый день. До дедлайна клиенту приходят мотивирующие SMS, а после — предупреждающее. Смотреть, как тают бонусы, можно на скринах с телефона:


Автоматизация напоминаний и система бонусов снижают количество просроченных платежей и выстраивают доверительные отношения с клиентами. Решение можно легко адаптировать под особенности вашей компании, изменяя параметры бонусов и временные интервалы напоминаний.
Принципы остаются универсальными, поэтому можно использовать любую CRM-систему или собственную базу данных, интегрируя их через API. Главное — сохранить человечность в общении, чтобы не только повысить эффективность работы, но и укрепить лояльность клиентов.