[{"data":1,"prerenderedAt":4091},["ShallowReactive",2],{"blog-servis-podtverzhdeniya-nomera":3,"blog-related-servis-podtverzhdeniya-nomera":501,"mdc--9dblse-key":4035,"mdc--rmukcm-key":4046,"mdc-ysl7l0-key":4055,"mdc-2elt3f-key":4064,"mdc--dih0zq-key":4073,"mdc-5fcbpw-key":4082},{"id":4,"title":5,"body":6,"category":462,"cta":463,"description":468,"draft":469,"extension":470,"faq":471,"h1":487,"meta":488,"navigation":489,"order":490,"path":491,"published":492,"related":493,"seo":497,"stem":498,"summary":499,"updated":492,"__hash__":500},"blog\u002Fblog\u002Fservis-podtverzhdeniya-nomera.md","Сервис подтверждения номера телефона — выбор и цены",{"type":7,"value":8,"toc":451},"minimark",[9,13,16,21,24,110,117,120,129,133,139,145,151,157,163,169,175,179,186,197,262,268,275,279,282,298,301,305,346,358,362,365,415,419],[10,11,12],"p",{},"Рынок подтверждения номера выглядит однородно: почти все продают «верификацию по цене X за штуку». Разница вылезает позже — когда оказывается, что списывают за попытку, а не за результат, что при недоставке деньги всё равно ушли, а на интеграцию нужен не день, а две недели.",[10,14,15],{},"Ниже — критерии, которые видно до подписания договора, и честный расчёт по FlashCall.",[17,18,20],"h2",{"id":19},"sposoby-podtverzhdeniya-i-ih-sebestoimost","Способы подтверждения и их себестоимость",[10,22,23],{},"Первое решение — не «какой провайдер», а «каким способом». От него зависит и цена, и что именно вы покупаете.",[25,26,29],"div",{"className":27},[28],"md-table",[30,31,32,51],"table",{},[33,34,35],"thead",{},[36,37,38,42,45,48],"tr",{},[39,40,41],"th",{},"Способ",[39,43,44],{},"За что списывают",[39,46,47],{},"От чего зависит успех",[39,49,50],{},"Что делает пользователь",[52,53,54,69,83,96],"tbody",{},[36,55,56,60,63,66],{},[57,58,59],"td",{},"SMS-код",[57,61,62],{},"за отправку сообщения",[57,64,65],{},"доставляемость, фильтры операторов, роуминг",[57,67,68],{},"ждёт, читает, вводит код",[36,70,71,74,77,80],{},[57,72,73],{},"Голосовой звонок с кодом",[57,75,76],{},"за совершённый вызов",[57,78,79],{},"дозвон, качество распознавания на слух",[57,81,82],{},"принимает вызов, слушает, вводит код",[36,84,85,88,90,93],{},[57,86,87],{},"Обычный flash call",[57,89,76],{},[57,91,92],{},"разрешение приложению на журнал вызовов",[57,94,95],{},"ничего либо вводит цифры номера",[36,97,98,101,104,107],{},[57,99,100],{},"Обратный звонок",[57,102,103],{},"за состоявшийся звонок",[57,105,106],{},"определяемость номера вызывающего",[57,108,109],{},"нажимает вызов",[10,111,112,116],{},[113,114,115],"strong",{},"О ценах конкурентов."," Публичные прайсы на SMS зависят от оператора получателя, объёма и типа трафика, поэтому единой цифры не существует, а «средняя цена SMS» из статей обычно ничем не подтверждена. Если сравниваете — берите свой счёт за прошлый месяц и делите на число доставленных сообщений. Это единственная цифра, которая имеет отношение к вашей ситуации.",[10,118,119],{},"Обратите внимание на третий столбец таблицы. Он объясняет, почему два сервиса с одинаковой ценой за штуку дают разный итоговый счёт: у способов разная доля успеха, и оплачиваете вы в одном случае попытки, в другом — результат.",[10,121,122,123,128],{},"Разбор терминологии и разницы между обычным и обратным flash call — в статье ",[124,125,127],"a",{"href":126},"\u002Fblog\u002Fchto-takoe-flash-call","что такое flash call",".",[17,130,132],{"id":131},"sem-kriteriev-vybora-servisa","Семь критериев выбора сервиса",[10,134,135,138],{},[113,136,137],{},"1. Модель тарификации: за попытку или за результат."," Главный критерий, который почти никогда не стоит первым в презентации. При оплате за отправку вы платите и за недоставленные сообщения, и за пользователей, которые передумали. При оплате за результат несостоявшаяся проверка стоит ноль. На объёме разница между этими моделями больше, чем разница в цене за штуку.",[10,140,141,144],{},[113,142,143],{},"2. Прозрачность статусов."," Вы должны в любой момент видеть, что происходит с конкретной проверкой, и различать «ещё ждём», «подтверждено» и «не сложилось». Если API отдаёт только «успех\u002Fнеуспех» без промежуточного состояния, вы не сможете нормально нарисовать интерфейс ожидания.",[10,146,147,150],{},[113,148,149],{},"3. REST API без SDK."," Проприетарная библиотека — это зависимость, которую придётся обновлять, и ограничение по языкам. Обычный HTTP-интерфейс встраивается в любой стек и не устаревает.",[10,152,153,156],{},[113,154,155],{},"4. Время интеграции."," Считается не по документации, а по числу сущностей, которые надо завести на своей стороне. Два запроса и три статуса — это день работы. Очередь, подписи, отдельный обработчик и своя таблица состояний — это спринт.",[10,158,159,162],{},[113,160,161],{},"5. Отсутствие абонентской платы и минимального объёма."," Обязательный ежемесячный платёж превращает эксперимент в обязательство. Если вы не уверены, что способ подойдёт вашей аудитории, стартовать надо там, где неудачная гипотеза стоит только потраченного времени.",[10,164,165,168],{},[113,166,167],{},"6. Документы и юридическая сторона."," Оферта, счёт для юрлиц и ИП, корректный НДС по налоговому статусу. Проверяется до интеграции, а не после, когда бухгалтерия откажется проводить платёж.",[10,170,171,174],{},[113,172,173],{},"7. Поддержка живыми людьми."," На этапе внедрения важнее тарифа: половина вопросов возникает в первые два дня и снимается одним ответом.",[17,176,178],{"id":177},"skolko-stoit-podtverzhdenie-v-flashcall","Сколько стоит подтверждение в FlashCall",[10,180,181,182,185],{},"Одно подтверждение — ",[113,183,184],{},"0,20 ₽",". Абонентской платы нет, минимального месячного объёма нет, плата за подключение отсутствует.",[10,187,188,189,193,194,128],{},"Списание происходит в момент, когда звонок зафиксирован. Проверка, по которой звонка не было и которая ушла в статус ",[190,191,192],"code",{},"EXPIRED",", ",[113,195,196],{},"не тарифицируется вообще",[25,198,200],{"className":199},[28],[30,201,202,218],{},[33,203,204],{},[36,205,206,209,212,215],{},[39,207,208],{},"Подтверждений в месяц",[39,210,211],{},"При 100 % дозвона",[39,213,214],{},"При 80 % дозвона",[39,216,217],{},"При 60 % дозвона",[52,219,220,234,248],{},[36,221,222,225,228,231],{},[57,223,224],{},"1 000",[57,226,227],{},"200 ₽",[57,229,230],{},"160 ₽",[57,232,233],{},"120 ₽",[36,235,236,239,242,245],{},[57,237,238],{},"10 000",[57,240,241],{},"2 000 ₽",[57,243,244],{},"1 600 ₽",[57,246,247],{},"1 200 ₽",[36,249,250,253,256,259],{},[57,251,252],{},"100 000",[57,254,255],{},"20 000 ₽",[57,257,258],{},"16 000 ₽",[57,260,261],{},"12 000 ₽",[10,263,264,265,128],{},"Столбцы справа — не скидка, а прямое следствие модели: платите за то, что состоялось. Формула для подстановки своих цифр: ",[190,266,267],{},"запущенные проверки × доля дозвона × 0,20 ₽",[10,269,270,271,128],{},"Цены в таблице актуальны на дату публикации; источник правды — страница ",[124,272,274],{"href":273},"\u002Fpricing","тарифов",[17,276,278],{"id":277},"kak-oplatit","Как оплатить",[10,280,281],{},"Баланс пополняется двумя способами:",[283,284,285,292],"ul",{},[286,287,288,291],"li",{},[113,289,290],{},"Счёт"," — для юрлиц и ИП. Реквизиты подставляются из карточки в кабинете, счёт приходит готовым к оплате. Зачисление на баланс происходит автоматически, когда банк подтверждает поступление.",[286,293,294,297],{},[113,295,296],{},"Картой"," — если удобнее платить сразу и без бухгалтерии.",[10,299,300],{},"Минимальная сумма пополнения по умолчанию — 100 ₽; фактическое значение показано в кабинете. Неизрасходованный баланс не сгорает.",[17,302,304],{"id":303},"kak-podklyuchit","Как подключить",[306,307,308,314,320,326,340],"ol",{},[286,309,310,313],{},[113,311,312],{},"Регистрация."," Занимает минуту, карта не требуется.",[286,315,316,319],{},[113,317,318],{},"Проект."," Создаётся в кабинете, сразу получает собственный ключ. Ключ хранится на сервере — во фронтенде ему не место.",[286,321,322,325],{},[113,323,324],{},"Тестовая проверка."," Запускается с первого же запроса, по реальному номеру.",[286,327,328,331,332,193,335,193,338,128],{},[113,329,330],{},"Интеграция."," Два запроса: создать проверку и получить её статус. Обработать три состояния — ",[190,333,334],{},"PENDING",[190,336,337],{},"CONFIRMED",[190,339,192],{},[286,341,342,345],{},[113,343,344],{},"Запасной способ."," Оставьте пользователю альтернативу на случай скрытого номера или зарубежной SIM.",[10,347,348,349,353,354,128],{},"Пошаговое описание запросов с примерами кода — в материале про ",[124,350,352],{"href":351},"\u002Fblog\u002Fapi-podtverzhdeniya-nomera","API подтверждения номера",". Как устроен сам механизм проверки — в статье про ",[124,355,357],{"href":356},"\u002Fblog\u002Fpodtverzhdenie-nomera-zvonkom","подтверждение номера звонком",[17,359,361],{"id":360},"chek-list-pered-integraciey","Чек-лист перед интеграцией",[10,363,364],{},"Пройдите его до того, как разработчик начнёт писать код, — все пункты проверяются за час и снимают большую часть будущих сюрпризов.",[306,366,367,373,379,385,391,397,403,409],{},[286,368,369,372],{},[113,370,371],{},"Посмотрите на модель списания в документации, а не в презентации."," Найдите, в какой момент уменьшается баланс. Если это момент отправки или создания запроса, вы платите за попытки.",[286,374,375,378],{},[113,376,377],{},"Уточните, что происходит с неудачной проверкой."," Есть ли отдельный статус, тарифицируется ли она, видно ли её в статистике.",[286,380,381,384],{},[113,382,383],{},"Проверьте формат номера."," E.164 — стандарт; если сервис требует собственный формат, это лишний слой преобразований и источник ошибок.",[286,386,387,390],{},[113,388,389],{},"Найдите, где хранится ключ."," Он должен использоваться только с сервера. Если инструкция предлагает положить его во фронтенд, это плохой знак для всей архитектуры.",[286,392,393,396],{},[113,394,395],{},"Оцените, что будет на пике."," Спросите про ограничения на число одновременных проверок: узкое место обычно там, а не в тарифе.",[286,398,399,402],{},[113,400,401],{},"Прочитайте оферту до слова «ответственность»."," Особенно раздел про возврат неизрасходованного баланса.",[286,404,405,408],{},[113,406,407],{},"Задайте один вопрос в поддержку."," Скорость и содержательность ответа на этапе выбора хорошо предсказывают, как будет во время инцидента.",[286,410,411,414],{},[113,412,413],{},"Заложите запасной способ."," Ни один канал не работает у ста процентов аудитории; альтернатива в интерфейсе стоит дешевле, чем потерянные регистрации.",[17,416,418],{"id":417},"na-chem-chasche-vsego-oshibayutsya-pri-vybore","На чём чаще всего ошибаются при выборе",[283,420,421,427,433,439,445],{},[286,422,423,426],{},[113,424,425],{},"Сравнивают цену за штуку, а не стоимость успешного подтверждения."," Это разные величины, и расходятся они тем сильнее, чем хуже доставляемость.",[286,428,429,432],{},[113,430,431],{},"Не считают стоимость потерянных регистраций."," Пользователь, не получивший код, стоит дороже, чем сам код.",[286,434,435,438],{},[113,436,437],{},"Берут провайдера под текущий объём."," Проверять надо поведение на пике, а не на среднем.",[286,440,441,444],{},[113,442,443],{},"Оставляют один способ подтверждения."," Любой канал иногда не срабатывает; запасной вариант дешевле, чем разбор инцидента.",[286,446,447,450],{},[113,448,449],{},"Откладывают документы на потом."," Оферту и порядок выставления счетов проще посмотреть до интеграции.",{"title":452,"searchDepth":453,"depth":453,"links":454},"",2,[455,456,457,458,459,460,461],{"id":19,"depth":453,"text":20},{"id":131,"depth":453,"text":132},{"id":177,"depth":453,"text":178},{"id":277,"depth":453,"text":278},{"id":303,"depth":453,"text":304},{"id":360,"depth":453,"text":361},{"id":417,"depth":453,"text":418},"Выбор решения",{"title":464,"text":465,"label":466,"href":467},"Посчитайте на своём объёме","Регистрация бесплатная, абонентской платы нет. Тестовую проверку можно запустить сразу после создания проекта.","Начать бесплатно","\u002Fregister","На что смотреть при выборе сервиса подтверждения номера: способ проверки, цена за подтверждение, модель списания, интеграция, документы и поддержка.",false,"md",[472,475,478,481,484],{"q":473,"a":474},"Есть ли минимальный платёж?","Абонентской платы и обязательного месячного объёма нет — вы платите только\nза подтверждения. Минимум есть у пополнения баланса: по умолчанию 100 ₽,\nактуальное значение показано в кабинете на странице пополнения.\n",{"q":476,"a":477},"Какие документы получит бухгалтерия?","Юрлицам и ИП выставляется счёт, отношения регулирует публичная оферта.\nРеквизиты компании подставляются в счёт из карточки в кабинете, а НДС\nуказывается согласно налоговому статусу исполнителя.\n",{"q":479,"a":480},"Можно ли протестировать до оплаты?","Да. Проект и ключ создаются сразу после регистрации, а стоимость проверки\nтакая, что полноценное тестирование сценария укладывается в минимальное\nпополнение. Отдельного песочного контура с фиктивными звонками нет —\nпроверки идут по реальному пулу номеров.\n",{"q":482,"a":483},"Что будет с неизрасходованным балансом?","Он не сгорает и остаётся на счёте: списание идёт по факту подтверждений,\nбез ежемесячного обнуления. Порядок возврата описан в оферте.\n",{"q":485,"a":486},"Что делать, если объём сильно больше типового?","Написать до интеграции. Крупные объёмы упираются не в тариф, а в ёмкость\nпула номеров, и это вопрос планирования, а не прайса.\n","Сервис подтверждения номера телефона: как выбрать и сколько стоит",{},true,3,"\u002Fblog\u002Fservis-podtverzhdeniya-nomera","2026-08-08",[494,495,496],"podtverzhdenie-nomera-zvonkom","flash-call-ili-sms","api-podtverzhdeniya-nomera",{"title":5,"description":468},"blog\u002Fservis-podtverzhdeniya-nomera","При выборе сервиса подтверждения номера почти всё решают три вопроса: за что именно\nсписывают деньги — за попытку или за результат; что происходит, когда подтверждение\nне доходит до пользователя; за сколько это встраивается в ваш продукт. Цена за штуку\nвторична — на реальную стоимость сильнее влияет модель списания. Ниже разбор критериев\nи расчёт: в FlashCall подтверждение стоит 0,20 ₽, и платите вы только за состоявшиеся.\n","cKsNAXF6yYZKwaXx9i9qxM3vrxbBahCb-ceO101blQ0",[502,1000,1393],{"id":503,"title":504,"body":505,"category":965,"cta":966,"description":969,"draft":469,"extension":470,"faq":970,"h1":989,"meta":990,"navigation":489,"order":991,"path":356,"published":492,"related":992,"seo":996,"stem":997,"summary":998,"updated":492,"__hash__":999},"blog\u002Fblog\u002Fpodtverzhdenie-nomera-zvonkom.md","Подтверждение номера телефона звонком — верификация без SMS",{"type":7,"value":506,"toc":956},[507,510,513,517,520,523,526,608,611,615,618,661,668,671,675,678,684,687,693,775,781,785,788,791,794,801,804,808,813,819,861,864,873,877,880,915,922,924,927,950],[10,508,509],{},"Способ выглядит непривычно ровно один раз — пока не увидишь его в деле. Пользователь не ждёт SMS, не переключается в приложение сообщений, не копирует шесть цифр и не возвращается обратно в форму. Он нажимает одну кнопку, телефон начинает набор, через мгновение вызов обрывается — и регистрация продолжается сама.",[10,511,512],{},"Ниже — что происходит под капотом, чем это отличается от обычного flash call, почему звонок доходит там, где SMS теряется, и сколько всё это стоит.",[17,514,516],{"id":515},"chto-takoe-proverka-telefona-zvonkom","Что такое проверка телефона звонком",[10,518,519],{},"Задача любой телефонной верификации одна: убедиться, что человек, который вводит номер в форму, действительно имеет к этому номеру доступ. Классическое решение — отправить на него одноразовый код и попросить ввести обратно. Раз код дошёл и был введён, значит, телефон у пользователя в руках.",[10,521,522],{},"Проверка номера звонком решает ту же задачу с другой стороны. Вместо того чтобы отправлять что-то абоненту и ждать, что он это получит и перепечатает, сервис просит его сделать исходящий вызов. Телефонная сеть передаёт номер вызывающего абонента вместе с вызовом — это тот же механизм, благодаря которому вы видите, кто вам звонит. Сервис сравнивает определившийся номер с тем, который пользователь ввёл в форму. Совпало — номер подтверждён.",[10,524,525],{},"Разница принципиальная. В SMS-сценарии доказательством служит доставленное сообщение, а его доставка зависит от цепочки посредников. В сценарии со звонком доказательством служит сам факт исходящего вызова с проверяемого номера, и никакого содержимого передавать не требуется.",[25,527,529],{"className":528},[28],[30,530,531,542],{},[33,532,533],{},[36,534,535,537,539],{},[39,536],{},[39,538,59],{},[39,540,541],{},"Звонок на выделенный номер",[52,543,544,554,565,576,587,597],{},[36,545,546,548,551],{},[57,547,50],{},[57,549,550],{},"ждёт сообщение, открывает его, запоминает или копирует код, возвращается в форму, вводит",[57,552,553],{},"нажимает кнопку вызова и сбрасывает",[36,555,556,559,562],{},[57,557,558],{},"Число действий",[57,560,561],{},"пять",[57,563,564],{},"два",[36,566,567,570,573],{},[57,568,569],{},"Что подтверждает факт",[57,571,572],{},"что сообщение дошло и было прочитано",[57,574,575],{},"что вызов совершён с проверяемого номера",[36,577,578,581,584],{},[57,579,580],{},"Где ломается",[57,582,583],{},"сообщение не дошло, пришло с задержкой, код введён с ошибкой",[57,585,586],{},"номер вызывающего не определяется, звонок с другого телефона",[36,588,589,592,595],{},[57,590,591],{},"За что списываются деньги",[57,593,594],{},"за отправку, независимо от доставки",[57,596,103],{},[36,598,599,602,605],{},[57,600,601],{},"Ввод кода",[57,603,604],{},"обязателен",[57,606,607],{},"не нужен",[10,609,610],{},"У способа есть и обратная сторона, о ней честно в разделе про ограничения: он работает, только пока номер вызывающего абонента передаётся сетью, и только для российских номеров.",[17,612,614],{"id":613},"kak-prohodit-podtverzhdenie-chetyre-shaga","Как проходит подтверждение: четыре шага",[10,616,617],{},"Со стороны интеграции всё сводится к двум запросам к API — на создание проверки и на получение её результата.",[306,619,620,630,640,646],{},[286,621,622,625,626,629],{},[113,623,624],{},"Ваша система создаёт проверку."," Отправляет ключ проекта и номер пользователя в формате E.164 (",[190,627,628],{},"+79161234567","). В ответ приходит идентификатор проверки и выделенный номер, на который нужно позвонить.",[286,631,632,635,636,639],{},[113,633,634],{},"Вы показываете этот номер пользователю."," На мобильных — ссылкой ",[190,637,638],{},"tel:",", чтобы набор открывался в одно касание и номер не пришлось переписывать руками. Рядом полезен таймер: у проверки есть срок.",[286,641,642,645],{},[113,643,644],{},"Пользователь звонит."," Отвечать на вызов не нужно и невозможно — сервис фиксирует входящий и сбрасывает соединение. Звонок сопоставляется с проверкой по паре «выделенный номер + номер вызывающего абонента».",[286,647,648,651,652,654,655,657,658,660],{},[113,649,650],{},"Ваша система забирает результат."," Опрашивает статус проверки по её идентификатору: ",[190,653,334],{}," — звонка ещё не было, ",[190,656,337],{}," — номер подтверждён, ",[190,659,192],{}," — 60 секунд истекли, звонка не поступило.",[10,662,663,664,667],{},"Важная деталь, которую часто понимают неправильно: ",[113,665,666],{},"выделенный номер не закрепляется за пользователем",". Один и тот же номер из пула одновременно обслуживает проверки разных людей — уникальной должна быть только пара «номер плюс звонящий». Поэтому пул не «заканчивается» при всплеске регистраций и не требует запаса номеров под пиковую нагрузку: сервис сам подбирает свободную комбинацию и распределяет вызовы по пулу.",[10,669,670],{},"Вторая деталь — про 60 секунд. Это срок жизни проверки, а не срок аренды номера. Если пользователь отвлёкся и позвонил через две минуты, вызов уже ни к чему не привяжется: проверка к тому моменту помечена как истёкшая. Правильная реакция интерфейса — не «ошибка», а кнопка «Позвонить ещё раз», создающая новую проверку.",[17,672,674],{"id":673},"chem-eto-otlichaetsya-ot-obychnogo-flash-call","Чем это отличается от обычного flash call",[10,676,677],{},"Термин flash call описывает два зеркальных сценария, и путаница между ними — главный источник недопонимания при выборе провайдера.",[10,679,680,683],{},[113,681,682],{},"Обычный flash call."," Звонит сервис. Пользователь видит входящий вызов, который тут же обрывается, а приложение читает номер входящего и подставляет его последние цифры как код подтверждения. Пользователю иногда даже не нужно ничего делать — но только если приложение получило доступ к журналу вызовов.",[10,685,686],{},"Здесь и находится слабое место. Доступ к журналу вызовов — чувствительное разрешение: в Google Play его выдача ограничена и требует обоснования, в iOS публичного API для чтения журнала нет вообще, а в браузере такой сценарий недоступен по определению. На практике обычный flash call — это история про Android-приложение с одобренным разрешением, и больше почти нигде.",[10,688,689,692],{},[113,690,691],{},"Обратный (reverse) flash call."," Звонит пользователь. Сервису не нужен доступ ни к журналу вызовов, ни к SMS — он видит вызов на своей стороне, потому что вызов пришёл к нему. Отсюда следует, что сценарий одинаково работает и в мобильном приложении, и на обычном сайте в браузере, и никаких особых разрешений не требует.",[25,694,696],{"className":695},[28],[30,697,698,709],{},[33,699,700],{},[36,701,702,704,706],{},[39,703],{},[39,705,87],{},[39,707,708],{},"Обратный flash call",[52,710,711,722,733,744,753,764],{},[36,712,713,716,719],{},[57,714,715],{},"Кто звонит",[57,717,718],{},"сервис пользователю",[57,720,721],{},"пользователь сервису",[36,723,724,727,730],{},[57,725,726],{},"Разрешения в приложении",[57,728,729],{},"доступ к журналу вызовов",[57,731,732],{},"не нужны",[36,734,735,738,741],{},[57,736,737],{},"Работает на сайте",[57,739,740],{},"нет",[57,742,743],{},"да",[36,745,746,749,751],{},[57,747,748],{},"Работает на iOS",[57,750,740],{},[57,752,743],{},[36,754,755,758,761],{},[57,756,757],{},"Что вводит пользователь",[57,759,760],{},"цифры из номера (или автоподстановка)",[57,762,763],{},"ничего",[36,765,766,769,772],{},[57,767,768],{},"Что может помешать",[57,770,771],{},"отказ в разрешении, ограничения магазинов приложений",[57,773,774],{},"номер вызывающего не определяется",[10,776,777,778,128],{},"FlashCall работает по второй схеме. Подробный разбор терминологии — включая написания «флеш колл» и синоним call to auth — в отдельном материале ",[124,779,780],{"href":126},"что такое flash call и обратный flash call",[17,782,784],{"id":783},"pochemu-zvonok-dohodit-tam-gde-sms-ne-doshla","Почему звонок доходит там, где SMS не дошла",[10,786,787],{},"Чтобы SMS с кодом оказалась на экране, она проходит цепочку: ваш сервис → агрегатор → оператор получателя → устройство. На каждом переходе есть очередь, фильтр и возможность потерять сообщение. Массовые рассылки фильтруются как спам, роуминг и запрет на приём международных сообщений отсекают часть абонентов, переполненная память устройства не даёт сообщение принять, а задержка на стороне агрегатора превращает код в бесполезный — к моменту доставки он уже истёк.",[10,789,790],{},"Для бизнеса это не абстрактная проблема, а прямые потери: недоставленная SMS — это не отправленный впустую рубль, а несостоявшаяся регистрация. Причём отправка тарифицируется независимо от того, дошло сообщение или нет.",[10,792,793],{},"Исходящий вызов идёт по голосовой сети напрямую. Там нет ни фильтрации массовых рассылок, ни очереди на доставку, ни спам-папки — соединение либо устанавливается, либо нет, и результат виден мгновенно. Отдельно важно, что инициатива на стороне пользователя: он не ждёт, пока до него что-то доедет, а сам совершает действие и сразу видит результат.",[10,795,796,797,128],{},"Подробный разбор причин, по которым коды не доходят, и что с этим делать пользователю — в материале ",[124,798,800],{"href":799},"\u002Fblog\u002Fne-prihodit-kod-podtverzhdeniya","почему не приходит код подтверждения",[10,802,803],{},"Обещать стопроцентную доставляемость было бы нечестно, поэтому назовём и границы. Верификация телефона звонком не сработает в трёх случаях: у абонента скрыт номер, звонок идёт через корпоративную АТС с подменой исходящего номера, или пользователь находится за пределами России — пул состоит из российских номеров. Разумная архитектура оставляет запасной способ подтверждения: звонок как основной сценарий, SMS как резервный для тех, у кого он не прошёл.",[17,805,807],{"id":806},"skolko-stoit-podtverzhdenie-nomera","Сколько стоит подтверждение номера",[10,809,181,810,812],{},[113,811,184],{},". Абонентской платы нет, минимального ежемесячного объёма нет, за подключение платить не нужно.",[10,814,815,816,818],{},"Ключевое отличие от SMS — в модели списания. Деньги списываются в момент, когда звонок зафиксирован, то есть за результат. Проверка, по которой звонка не было и которая ушла в ",[190,817,192],{},", не тарифицируется вообще. Вы не платите за попытки, за недоставку и за пользователей, которые передумали на середине формы.",[25,820,822],{"className":821},[28],[30,823,824,835],{},[33,825,826],{},[36,827,828,830,833],{},[39,829,208],{},[39,831,832],{},"Стоимость при 100 % дозвона",[39,834,214],{},[52,836,837,845,853],{},[36,838,839,841,843],{},[57,840,224],{},[57,842,227],{},[57,844,230],{},[36,846,847,849,851],{},[57,848,238],{},[57,850,241],{},[57,852,244],{},[36,854,855,857,859],{},[57,856,252],{},[57,858,255],{},[57,860,258],{},[10,862,863],{},"Второй столбец — не скидка, а прямое следствие модели: если из десяти тысяч запущенных проверок звонком завершились восемь тысяч, платить вы будете за восемь. В SMS-сценарии заплатить пришлось бы за все десять тысяч отправок.",[10,865,866,867,869,870,128],{},"Проверка не создастся, если на балансе меньше стоимости одного подтверждения — API вернёт ошибку о недостатке средств. Это стоит обработать в коде заранее и завести оповещение о низком балансе. Актуальные цены и условия — на странице ",[124,868,274],{"href":273},"; подробнее о том, как вообще выбирать провайдера и на что смотреть кроме цены, — в разборе ",[124,871,872],{"href":491},"сервисов подтверждения номера",[17,874,876],{"id":875},"gde-primenyayut-verifikaciyu-nomera-zvonkom","Где применяют верификацию номера звонком",[10,878,879],{},"Сценариев ровно столько же, сколько у SMS-кода, — способ подтверждения меняется, задача остаётся.",[283,881,882,887,897,903,909],{},[286,883,884,886],{},[113,885,312],{}," Отсечь фейковые аккаунты и получить рабочий контакт. Здесь короткий сценарий даёт больше всего: чем меньше шагов между «ввёл номер» и «зарегистрирован», тем меньше отвалов.",[286,888,889,892,893,128],{},[113,890,891],{},"Вход в аккаунт."," Замена SMS-кода при авторизации, в том числе полностью без пароля. Как это устроено — в материале про ",[124,894,896],{"href":895},"\u002Fblog\u002Favtorizaciya-po-zvonku","авторизацию по звонку",[286,898,899,902],{},[113,900,901],{},"Подтверждение операции."," Смена пароля, изменение реквизитов, подтверждение заказа — там, где нужно доказать, что действие совершает владелец аккаунта.",[286,904,905,908],{},[113,906,907],{},"Оформление заказа и доставка."," Проверка контактного номера до того, как заказ уйдёт в сборку, а курьер — по адресу с неверным телефоном.",[286,910,911,914],{},[113,912,913],{},"Объявления и маркетплейсы."," Верификация продавца, борьба с мультиаккаунтами и фейковыми регистрациями.",[10,916,917,918,128],{},"Разбор по отраслям с конкретными задачами — в статье ",[124,919,921],{"href":920},"\u002Fblog\u002Fgde-nuzhna-verifikaciya-telefona","где нужна верификация телефона",[17,923,304],{"id":303},[10,925,926],{},"Интеграция занимает несколько часов и не требует SDK: это два HTTP-запроса к REST API.",[306,928,929,932,935,941],{},[286,930,931],{},"Зарегистрируйтесь и создайте проект — он получит собственный ключ.",[286,933,934],{},"На бэкенде вызовите создание проверки, передав ключ проекта и номер пользователя. Ключ хранится только на сервере: во фронтенде его размещать нельзя.",[286,936,937,938,940],{},"Отдайте выделенный номер во фронтенд и покажите его пользователю ссылкой ",[190,939,638],{}," с таймером на 60 секунд.",[286,942,943,944,946,947,949],{},"Опрашивайте статус проверки, пока он не станет ",[190,945,337],{}," или ",[190,948,192],{},", и обработайте оба исхода.",[10,951,952,953,955],{},"Пошаговое описание запросов, форматов и типичных ошибок интеграции — в материале про ",[124,954,352],{"href":351},". Инструкция с вашим собственным ключом открывается в кабинете на странице проекта сразу после регистрации.",{"title":452,"searchDepth":453,"depth":453,"links":957},[958,959,960,961,962,963,964],{"id":515,"depth":453,"text":516},{"id":613,"depth":453,"text":614},{"id":673,"depth":453,"text":674},{"id":783,"depth":453,"text":784},{"id":806,"depth":453,"text":807},{"id":875,"depth":453,"text":876},{"id":303,"depth":453,"text":304},"Как это работает",{"title":967,"text":968,"label":466,"href":467},"Попробуйте на своём проекте","Регистрация занимает минуту, ключ проекта выдаётся сразу. Тестовую проверку можно запустить с первого же запроса.","Разбираем, как подтвердить номер телефона звонком вместо SMS-кода: схема проверки, отличие от обычного flash call, стоимость и порядок подключения.",[971,974,977,980,983,986],{"q":972,"a":973},"Что будет, если пользователь позвонит с другого номера?","Проверка не подтвердится. Совпадение ищется по паре «выделенный номер + номер\nвызывающего абонента», и звонок с телефона, который не был указан при создании\nпроверки, к ней не привяжется. Через 60 секунд она перейдёт в статус `EXPIRED`,\nи деньги за неё списаны не будут.\n",{"q":975,"a":976},"Звонок платный для пользователя?","Оператор тарифицирует его как обычный вызов на российский номер, по тарифу абонента.\nНо соединение сбрасывается до ответа, а неотвеченные вызовы операторы не тарифицируют —\nна практике подтверждение обходится пользователю бесплатно.\n",{"q":978,"a":979},"Сработает ли проверка, если у вызывающего скрыт номер?","Нет. Определение номера вызывающего абонента — основа всего способа: если номер\nне передаётся, сопоставить звонок с проверкой не с чем. Так бывает при включённом\nантиопределителе и на корпоративных АТС, которые подменяют исходящий номер.\nДля таких пользователей нужен запасной способ подтверждения.\n",{"q":981,"a":982},"Что делать, если звонка так и не было?","Через 60 секунд после создания проверка сама переходит в `EXPIRED` — опрос статуса\nвернёт его вместо `PENDING`. Это штатная ситуация: покажите пользователю кнопку\n«Позвонить ещё раз» и создайте новую проверку. Тарифицируется только состоявшийся\nзвонок, поэтому повторные попытки ничего не стоят.\n",{"q":984,"a":985},"Сколько проверок можно запускать одновременно?","Ограничения на общее число параллельных проверок в проекте нет. Единственное\nтребование — уникальность пары «выделенный номер + номер звонящего» среди\nожидающих. Сервис сам подбирает подходящий номер из пула и распределяет по нему\nнагрузку, так что со стороны интеграции об этом думать не нужно.\n",{"q":987,"a":988},"Что произойдёт, если на балансе закончатся деньги?","Проверка просто не создастся: запрос на выдачу номера вернёт ошибку о недостатке\nсредств. Уже созданные проверки при этом дорабатывают штатно. Обработайте эту\nошибку в коде заранее — иначе форма регистрации молча перестанет работать,\nкогда баланс дойдёт до нуля.\n","Подтверждение номера телефона звонком: как это работает",{},1,[993,994,995],"avtorizaciya-po-zvonku","servis-podtverzhdeniya-nomera","chto-takoe-flash-call",{"title":504,"description":969},"blog\u002Fpodtverzhdenie-nomera-zvonkom","Подтверждение номера звонком — способ проверить телефон без SMS и без одноразового кода.\nСервис выдаёт выделенный номер, пользователь звонит на него со своего телефона, вызов\nсбрасывается, а система по номеру вызывающего абонента понимает, что телефон принадлежит\nименно ему. Вводить ничего не нужно, отвечать на звонок тоже. Проверка живёт 60 секунд\nи стоит 0,20 ₽, причём списание происходит только за состоявшийся звонок.\n","Vs3hHENdsOH5tdu4ypInD_PfVG5qPC2vwutGGv1h_7s",{"id":1001,"title":1002,"body":1003,"category":462,"cta":1367,"description":1370,"draft":469,"extension":470,"faq":1371,"h1":1384,"meta":1385,"navigation":489,"order":1386,"path":1387,"published":492,"related":1388,"seo":1389,"stem":1390,"summary":1391,"updated":492,"__hash__":1392},"blog\u002Fblog\u002Fflash-call-ili-sms.md","Flash Call или SMS — чем заменить SMS-коды подтверждения",{"type":7,"value":1004,"toc":1359},[1005,1008,1012,1015,1041,1047,1051,1056,1062,1066,1163,1166,1170,1173,1182,1185,1252,1255,1261,1265,1268,1300,1303,1307,1310,1342,1345],[10,1006,1007],{},"Вопрос «чем заменить SMS-коды» возникает обычно из двух соображений: расходы растут вместе с числом регистраций, а часть пользователей всё равно не получает сообщения. Разбираем, что даёт замена на подтверждение звонком, где реальная экономия и когда SMS всё-таки нужны.",[17,1009,1011],{"id":1010},"skolko-stoit-sms-podtverzhdenie","Сколько стоит SMS-подтверждение",[10,1013,1014],{},"Затраты на SMS складываются не только из цены сообщения.",[283,1016,1017,1023,1029,1035],{},[286,1018,1019,1022],{},[113,1020,1021],{},"Цена за отправку."," Зависит от оператора получателя, объёма и типа трафика. Единой цифры не существует, поэтому ориентироваться на «среднюю цену SMS» из статей бессмысленно — считайте по своему счёту.",[286,1024,1025,1028],{},[113,1026,1027],{},"Оплата независимо от доставки."," Сообщение, отфильтрованное оператором или не дошедшее из-за роуминга, оплачено так же, как доставленное.",[286,1030,1031,1034],{},[113,1032,1033],{},"Повторные отправки."," Пользователь жмёт «Отправить снова» — счёт растёт, конверсия чаще всего нет: если сообщение не прошло фильтр, второе тоже не пройдёт.",[286,1036,1037,1040],{},[113,1038,1039],{},"Стоимость потерянных регистраций."," Самая крупная и самая невидимая статья. Пользователь, не получивший код, просто уходит.",[10,1042,1043,1046],{},[113,1044,1045],{},"Практическая формула вашей фактической стоимости:"," счёт за SMS за месяц, делённый на число подтверждённых номеров за тот же месяц. Полученное почти всегда заметно больше цены за одно сообщение — на разницу влияет доля недоставок и повторных отправок.",[17,1048,1050],{"id":1049},"skolko-stoit-podtverzhdenie-zvonkom","Сколько стоит подтверждение звонком",[10,1052,181,1053,1055],{},[113,1054,184],{},". Абонентской платы нет, минимального месячного объёма нет.",[10,1057,1058,1059,1061],{},"Списание происходит в момент, когда звонок зафиксирован. Проверка, по которой звонка не было и которая перешла в статус ",[190,1060,192],{},", не тарифицируется. То есть цена за штуку и фактическая стоимость успешного подтверждения здесь совпадают — в отличие от SMS-сценария.",[17,1063,1065],{"id":1064},"sravnenie-po-shesti-parametram","Сравнение по шести параметрам",[25,1067,1069],{"className":1068},[28],[30,1070,1071,1084],{},[33,1072,1073],{},[36,1074,1075,1078,1080,1082],{},[39,1076,1077],{},"Параметр",[39,1079,59],{},[39,1081,73],{},[39,1083,100],{},[52,1085,1086,1099,1110,1124,1138,1150],{},[36,1087,1088,1091,1094,1097],{},[57,1089,1090],{},"Цена за подтверждение",[57,1092,1093],{},"зависит от оператора и объёма",[57,1095,1096],{},"обычно выше SMS",[57,1098,184],{},[36,1100,1101,1103,1106,1108],{},[57,1102,44],{},[57,1104,1105],{},"за отправку",[57,1107,76],{},[57,1109,103],{},[36,1111,1112,1115,1118,1121],{},[57,1113,1114],{},"Зависимость от доставки",[57,1116,1117],{},"высокая: фильтры, роуминг, память",[57,1119,1120],{},"средняя: нужен дозвон",[57,1122,1123],{},"отсутствует: звонит пользователь",[36,1125,1126,1129,1132,1135],{},[57,1127,1128],{},"Время подтверждения",[57,1130,1131],{},"от секунд до минут, непредсказуемо",[57,1133,1134],{},"ограничено длительностью вызова",[57,1136,1137],{},"ограничено 60 секундами",[36,1139,1140,1143,1145,1148],{},[57,1141,1142],{},"Действия пользователя",[57,1144,68],{},[57,1146,1147],{},"принимает, слушает, вводит код",[57,1149,109],{},[36,1151,1152,1155,1158,1160],{},[57,1153,1154],{},"География",[57,1156,1157],{},"практически любая",[57,1159,1157],{},[57,1161,1162],{},"Россия",[10,1164,1165],{},"Последняя строка — главный ограничитель, и его надо учитывать до внедрения, а не после.",[17,1167,1169],{"id":1168},"raschet-ekonomii","Расчёт экономии",[10,1171,1172],{},"Формула для подстановки своих цифр:",[1174,1175,1180],"pre",{"className":1176,"code":1178,"language":1179},[1177],"language-text","Стоимость SMS      = запущенные проверки × цена отправки\nСтоимость звонка   = запущенные проверки × доля дозвона × 0,20 ₽\n","text",[190,1181,1178],{"__ignoreMap":452},[10,1183,1184],{},"Разница между формулами принципиальная: в первой доля успеха отсутствует — платите за все отправки. Во второй она стоит множителем, потому что неудачная проверка бесплатна.",[25,1186,1188],{"className":1187},[28],[30,1189,1190,1206],{},[33,1191,1192],{},[36,1193,1194,1197,1200,1203],{},[39,1195,1196],{},"Проверок в месяц",[39,1198,1199],{},"Звонок при 100 %",[39,1201,1202],{},"Звонок при 80 %",[39,1204,1205],{},"Звонок при 60 %",[52,1207,1208,1218,1228,1238],{},[36,1209,1210,1212,1214,1216],{},[57,1211,224],{},[57,1213,227],{},[57,1215,230],{},[57,1217,233],{},[36,1219,1220,1222,1224,1226],{},[57,1221,238],{},[57,1223,241],{},[57,1225,244],{},[57,1227,247],{},[36,1229,1230,1232,1234,1236],{},[57,1231,252],{},[57,1233,255],{},[57,1235,258],{},[57,1237,261],{},[36,1239,1240,1243,1246,1249],{},[57,1241,1242],{},"500 000",[57,1244,1245],{},"100 000 ₽",[57,1247,1248],{},"80 000 ₽",[57,1250,1251],{},"60 000 ₽",[10,1253,1254],{},"Чтобы сравнить со своей ситуацией, подставьте в левый столбец число запущенных проверок за прошлый месяц и сопоставьте результат со счётом за SMS за тот же период. Считать нужно именно по запущенным проверкам, а не по подтверждённым: в SMS-сценарии вы платили за все.",[10,1256,1257,1258,128],{},"Второй эффект, который стоит посчитать отдельно, — доля пользователей, не получивших код. Каждый из них не просто не заплатил вам за оплаченную отправку, но и не стал клиентом. Разбор причин недоставки — в материале ",[124,1259,1260],{"href":799},"не приходит код подтверждения",[17,1262,1264],{"id":1263},"kogda-sms-vse-taki-nuzhny","Когда SMS всё-таки нужны",[10,1266,1267],{},"Раздел, без которого текст читался бы рекламой.",[283,1269,1270,1276,1282,1288,1294],{},[286,1271,1272,1275],{},[113,1273,1274],{},"Зарубежные абоненты."," Пул состоит из российских номеров. Если заметная доля аудитории за границей, SMS для неё остаются.",[286,1277,1278,1281],{},[113,1279,1280],{},"Скрытый номер и корпоративные АТС."," Если номер вызывающего не передаётся, сопоставить звонок не с чем.",[286,1283,1284,1287],{},[113,1285,1286],{},"Код нужен в текстовом виде."," Сценарии, где пользователь должен сохранить или переслать подтверждение, — например, код получения заказа в пункте выдачи.",[286,1289,1290,1293],{},[113,1291,1292],{},"Транзакционные уведомления."," Статус заказа, чек, напоминание — это не верификация, и звонком их не заменить.",[286,1295,1296,1299],{},[113,1297,1298],{},"Пользователь без голосовой связи."," Зарубежная SIM с одним интернетом, звонки недоступны.",[10,1301,1302],{},"Практический вывод: SMS не исчезают из продукта, они перестают быть единственным способом подтверждения.",[17,1304,1306],{"id":1305},"kak-pereyti-postepenno","Как перейти постепенно",[10,1308,1309],{},"Одномоментная замена не нужна и рискованна. Рабочая последовательность:",[306,1311,1312,1318,1324,1330,1336],{},[286,1313,1314,1317],{},[113,1315,1316],{},"Подключите звонок параллельно."," Интеграция — два запроса, существующий SMS-сценарий не трогается.",[286,1319,1320,1323],{},[113,1321,1322],{},"Включите звонок части аудитории."," Например, десяти процентам новых регистраций, остальным оставьте прежний путь.",[286,1325,1326,1329],{},[113,1327,1328],{},"Сравните две воронки"," от «ввёл номер» до «подтвердил». Это единственный способ узнать эффект на своих данных, а не на чужих обещаниях.",[286,1331,1332,1335],{},[113,1333,1334],{},"Сделайте звонок основным, SMS — запасным."," Кнопка «Подтвердить другим способом» рядом с таймером снимает и вопрос зарубежных номеров, и главное возражение «а если не сработает».",[286,1337,1338,1341],{},[113,1339,1340],{},"Следите за долей ухода на запасной способ."," Она покажет реальный размер сегмента, для которого звонок не подходит.",[10,1343,1344],{},"Такая схема заодно решает задачу отказоустойчивости: два независимых канала подтверждения означают, что сбой у одного провайдера не останавливает регистрации.",[10,1346,1347,1348,1350,1351,1353,1354,1356,1357,128],{},"Как устроен сам механизм — в статье про ",[124,1349,357],{"href":356},". Чем обратный flash call отличается от обычного — в материале ",[124,1352,127],{"href":126},". Критерии выбора провайдера и расчёт стоимости — в разборе ",[124,1355,872],{"href":491},". Актуальные цены — на странице ",[124,1358,274],{"href":273},{"title":452,"searchDepth":453,"depth":453,"links":1360},[1361,1362,1363,1364,1365,1366],{"id":1010,"depth":453,"text":1011},{"id":1049,"depth":453,"text":1050},{"id":1064,"depth":453,"text":1065},{"id":1168,"depth":453,"text":1169},{"id":1263,"depth":453,"text":1264},{"id":1305,"depth":453,"text":1306},{"title":1368,"text":1369,"label":466,"href":467},"Посчитайте на своих объёмах","0,20 ₽ за состоявшееся подтверждение, без абонентской платы и минимального объёма.","Сравнение SMS-кодов и подтверждения звонком: стоимость, доставляемость, время, действия пользователя. Считаем экономию при замене SMS-авторизации и когда SMS нужны.",[1372,1375,1378,1381],{"q":1373,"a":1374},"Обязательно отказываться от SMS полностью?","Нет, и в большинстве случаев не нужно. Рабочая схема — звонок основным\nспособом, SMS запасным для тех, у кого он не прошёл. Так закрываются\nи зарубежные абоненты, и скрытые номера, и риск сбоя у одного провайдера.\n",{"q":1376,"a":1377},"Как посчитать выгоду, не внедряя?","Возьмите счёт за SMS за прошлый месяц и число подтверждённых номеров\nза тот же период. Первое, делённое на второе, — ваша фактическая стоимость\nодного успешного подтверждения. Её и сравнивайте с 0,20 ₽.\n",{"q":1379,"a":1380},"Что делать, если часть пользователей не может позвонить?","Оставить им прежний способ. Доля таких пользователей обычно невелика,\nи запасной вариант в интерфейсе решает вопрос полностью — переход\nне обязан быть одномоментным и стопроцентным.\n",{"q":1382,"a":1383},"Насколько сложно перейти?","Интеграция — два HTTP-запроса и три статуса, обычно день работы.\nДольше идёт не техническая часть, а переделка интерфейса и A\u002FB-сравнение\nдвух воронок.\n","Flash Call или SMS: чем заменить SMS-коды и сколько это экономит",{},13,"\u002Fblog\u002Fflash-call-ili-sms",[994,494,995],{"title":1002,"description":1370},"blog\u002Fflash-call-ili-sms","Короткий вывод до аргументации: подтверждение звонком дешевле SMS и не зависит\nот доставляемости, но работает только для российских номеров и требует, чтобы номер\nвызывающего абонента определялся. Главная экономия не в цене за штуку, а в модели\nсписания: за SMS платят при отправке независимо от доставки, за звонок — только когда\nон состоялся. Ниже формула, по которой можно подставить свои цифры.\n","Gracrg0iFIfyW4kz0m05TI0qd-Ru_dSsatIsySzzCRQ",{"id":1394,"title":1395,"body":1396,"category":4004,"cta":4005,"description":4009,"draft":469,"extension":470,"faq":4010,"h1":4026,"meta":4027,"navigation":489,"order":2054,"path":351,"published":492,"related":4028,"seo":4031,"stem":4032,"summary":4033,"updated":492,"__hash__":4034},"blog\u002Fblog\u002Fapi-podtverzhdeniya-nomera.md","API подтверждения номера телефона — REST с примерами",{"type":7,"value":1397,"toc":3987},[1398,1401,1405,1461,1468,1478,1482,1529,1532,1567,1580,1583,1646,1650,1656,1671,1675,1687,1764,1770,1773,1795,1799,1804,2388,2392,2555,2559,2707,2711,2718,2722,3250,3254,3858,3862,3914,3918,3975,3978,3983],[10,1399,1400],{},"Интеграция подтверждения номера обычно занимает не больше рабочего дня. Ниже — весь путь: от первого запроса до обработки граничных случаев, с примерами, которые можно копировать.",[17,1402,1404],{"id":1403},"kak-ustroen-api","Как устроен API",[283,1406,1407,1416,1422,1440,1448],{},[286,1408,1409,1412,1413],{},[113,1410,1411],{},"Базовый адрес:"," ",[190,1414,1415],{},"https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1",[286,1417,1418,1421],{},[113,1419,1420],{},"Формат:"," JSON, обычный REST.",[286,1423,1424,1427,1428,1431,1432,1435,1436,1439],{},[113,1425,1426],{},"Авторизация:"," ключ проекта передаётся полем ",[190,1429,1430],{},"token"," — в теле ",[190,1433,1434],{},"POST \u002Fget-phone"," или в query-строке ",[190,1437,1438],{},"GET \u002Fstatus",". Отдельных заголовков авторизации нет.",[286,1441,1442,1445,1446,128],{},[113,1443,1444],{},"Формат номера:"," E.164 — знак плюс, код страны и номер без разделителей: ",[190,1447,628],{},[286,1449,1450,1412,1453,1455,1456,657,1458,1460],{},[113,1451,1452],{},"Статусы проверки:",[190,1454,334],{}," — ждём звонок, ",[190,1457,337],{},[190,1459,192],{}," — за 60 секунд звонка не было.",[10,1462,1463,1464,1467],{},"Всего два запроса. Третий эндпоинт, ",[190,1465,1466],{},"POST \u002Finbound-call",", вызывает оператор телефонии при поступлении звонка — со стороны интеграции он не используется.",[10,1469,1470,1471,1474,1475,128],{},"Если механизм в целом ещё не знаком, начните с разбора в статье про ",[124,1472,1473],{"href":356},"подтверждение номера телефона звонком",": дальше по тексту предполагается, что схема «выдали номер — пользователь позвонил — забрали статус» уже понятна. Модель тарификации и критерии выбора провайдера — в материале про ",[124,1476,1477],{"href":491},"сервисы подтверждения номера",[17,1479,1481],{"id":1480},"shag-1-sozdat-proverku","Шаг 1. Создать проверку",[1174,1483,1487],{"className":1484,"code":1485,"language":1486,"meta":452,"style":452},"language-bash shiki shiki-themes github-light github-dark","curl -X POST \"https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1\u002Fget-phone\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"token\": \"YOUR_API_KEY\", \"phone\": \"+79161234567\" }'\n","bash",[190,1488,1489,1511,1521],{"__ignoreMap":452},[1490,1491,1493,1497,1501,1505,1508],"span",{"class":1492,"line":991},"line",[1490,1494,1496],{"class":1495},"sScJk","curl",[1490,1498,1500],{"class":1499},"sj4cs"," -X",[1490,1502,1504],{"class":1503},"sZZnC"," POST",[1490,1506,1507],{"class":1503}," \"https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1\u002Fget-phone\"",[1490,1509,1510],{"class":1499}," \\\n",[1490,1512,1513,1516,1519],{"class":1492,"line":453},[1490,1514,1515],{"class":1499},"  -H",[1490,1517,1518],{"class":1503}," \"Content-Type: application\u002Fjson\"",[1490,1520,1510],{"class":1499},[1490,1522,1523,1526],{"class":1492,"line":490},[1490,1524,1525],{"class":1499},"  -d",[1490,1527,1528],{"class":1503}," '{ \"token\": \"YOUR_API_KEY\", \"phone\": \"+79161234567\" }'\n",[10,1530,1531],{},"Ответ:",[1174,1533,1537],{"className":1534,"code":1535,"language":1536,"meta":452,"style":452},"language-json shiki shiki-themes github-light github-dark","{ \"id\": \"c1x8f2k9b0001\", \"phone\": \"+74950000000\" }\n","json",[190,1538,1539],{"__ignoreMap":452},[1490,1540,1541,1545,1548,1551,1554,1556,1559,1561,1564],{"class":1492,"line":991},[1490,1542,1544],{"class":1543},"sVt8B","{ ",[1490,1546,1547],{"class":1499},"\"id\"",[1490,1549,1550],{"class":1543},": ",[1490,1552,1553],{"class":1503},"\"c1x8f2k9b0001\"",[1490,1555,193],{"class":1543},[1490,1557,1558],{"class":1499},"\"phone\"",[1490,1560,1550],{"class":1543},[1490,1562,1563],{"class":1503},"\"+74950000000\"",[1490,1565,1566],{"class":1543}," }\n",[10,1568,1569,1572,1573,1576,1577,1579],{},[190,1570,1571],{},"id"," — идентификатор проверки, по нему запрашивается статус. ",[190,1574,1575],{},"phone"," — выделенный номер, на который пользователь должен позвонить. Проверка создаётся в статусе ",[190,1578,334],{}," и живёт 60 секунд.",[10,1581,1582],{},"Возможные ошибки:",[25,1584,1586],{"className":1585},[28],[30,1587,1588,1601],{},[33,1589,1590],{},[36,1591,1592,1595,1598],{},[39,1593,1594],{},"Код",[39,1596,1597],{},"Причина",[39,1599,1600],{},"Что делать",[52,1602,1603,1614,1624,1635],{},[36,1604,1605,1608,1611],{},[57,1606,1607],{},"400",[57,1609,1610],{},"неверный формат номера",[57,1612,1613],{},"привести к E.164 до отправки",[36,1615,1616,1618,1621],{},[57,1617,1607],{},[57,1619,1620],{},"недостаточно средств на балансе",[57,1622,1623],{},"пополнить, заранее завести оповещение",[36,1625,1626,1629,1632],{},[57,1627,1628],{},"404",[57,1630,1631],{},"ключ проекта не найден",[57,1633,1634],{},"проверить ключ и окружение",[36,1636,1637,1640,1643],{},[57,1638,1639],{},"503",[57,1641,1642],{},"нет доступных номеров в пуле",[57,1644,1645],{},"повторить попытку, показать запасной способ",[17,1647,1649],{"id":1648},"shag-2-pokazat-nomer-polzovatelyu","Шаг 2. Показать номер пользователю",[10,1651,1652,1653,1655],{},"Во фронтенд отдаются только ",[190,1654,1571],{}," и выделенный номер — ключ проекта наружу не уходит.",[10,1657,1658,1659,1661,1662,1666,1667,128],{},"Что должно быть на экране: номер крупно и с разделителями, на мобильных — ссылкой ",[190,1660,638],{},"; таймер на 60 секунд; пояснение, что трубку брать не нужно; подсказка, что звонить надо именно с подтверждаемого номера. Разбор интерфейса — в материалах про ",[124,1663,1665],{"href":1664},"\u002Fblog\u002Fpodtverzhdenie-nomera-na-sayte","подтверждение на сайте"," и ",[124,1668,1670],{"href":1669},"\u002Fblog\u002Fpodtverzhdenie-nomera-v-prilozhenii","в мобильном приложении",[17,1672,1674],{"id":1673},"shag-3-poluchit-rezultat","Шаг 3. Получить результат",[1174,1676,1678],{"className":1484,"code":1677,"language":1486,"meta":452,"style":452},"curl \"https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1\u002Fstatus?token=YOUR_API_KEY&id=c1x8f2k9b0001\"\n",[190,1679,1680],{"__ignoreMap":452},[1490,1681,1682,1684],{"class":1492,"line":991},[1490,1683,1496],{"class":1495},[1490,1685,1686],{"class":1503}," \"https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1\u002Fstatus?token=YOUR_API_KEY&id=c1x8f2k9b0001\"\n",[1174,1688,1690],{"className":1534,"code":1689,"language":1536,"meta":452,"style":452},"{\n  \"id\": \"c1x8f2k9b0001\",\n  \"status\": \"PENDING\",\n  \"callerPhone\": \"+79161234567\",\n  \"cost\": \"0.20\",\n  \"createdAt\": \"2026-08-08T18:00:00.000Z\"\n}\n",[190,1691,1692,1697,1709,1721,1734,1747,1758],{"__ignoreMap":452},[1490,1693,1694],{"class":1492,"line":991},[1490,1695,1696],{"class":1543},"{\n",[1490,1698,1699,1702,1704,1706],{"class":1492,"line":453},[1490,1700,1701],{"class":1499},"  \"id\"",[1490,1703,1550],{"class":1543},[1490,1705,1553],{"class":1503},[1490,1707,1708],{"class":1543},",\n",[1490,1710,1711,1714,1716,1719],{"class":1492,"line":490},[1490,1712,1713],{"class":1499},"  \"status\"",[1490,1715,1550],{"class":1543},[1490,1717,1718],{"class":1503},"\"PENDING\"",[1490,1720,1708],{"class":1543},[1490,1722,1724,1727,1729,1732],{"class":1492,"line":1723},4,[1490,1725,1726],{"class":1499},"  \"callerPhone\"",[1490,1728,1550],{"class":1543},[1490,1730,1731],{"class":1503},"\"+79161234567\"",[1490,1733,1708],{"class":1543},[1490,1735,1737,1740,1742,1745],{"class":1492,"line":1736},5,[1490,1738,1739],{"class":1499},"  \"cost\"",[1490,1741,1550],{"class":1543},[1490,1743,1744],{"class":1503},"\"0.20\"",[1490,1746,1708],{"class":1543},[1490,1748,1750,1753,1755],{"class":1492,"line":1749},6,[1490,1751,1752],{"class":1499},"  \"createdAt\"",[1490,1754,1550],{"class":1543},[1490,1756,1757],{"class":1503},"\"2026-08-08T18:00:00.000Z\"\n",[1490,1759,1761],{"class":1492,"line":1760},7,[1490,1762,1763],{"class":1543},"}\n",[10,1765,1766,1769],{},[113,1767,1768],{},"Вебхуков нет"," — результат забирается опросом. Это заметно упрощает интеграцию: не нужен публично доступный обработчик, не нужна проверка подписи, не нужна защита от повторной доставки.",[10,1771,1772],{},"Правила опроса:",[283,1774,1775,1778,1786,1789],{},[286,1776,1777],{},"Интервал одна-две секунды. Чаще бессмысленно, реже — заметная задержка на экране.",[286,1779,1780,1781,193,1783,1785],{},"Три условия остановки: ",[190,1782,337],{},[190,1784,192],{}," и истечение локального таймера. Без последнего цикл может не завершиться никогда.",[286,1787,1788],{},"Решение «номер подтверждён» принимается на сервере по ответу API, а не по тому, что прислал браузер.",[286,1790,1791,1792,1794],{},"Привязка номера должна отрабатывать один раз, даже если ",[190,1793,337],{}," увиден несколькими запросами подряд.",[17,1796,1798],{"id":1797},"primery-integracii","Примеры интеграции",[1800,1801,1803],"h3",{"id":1802},"node-js","Node.js",[1174,1805,1809],{"className":1806,"code":1807,"language":1808,"meta":452,"style":452},"language-js shiki shiki-themes github-light github-dark","const API = 'https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1'\nconst TOKEN = process.env.FLASHCALL_TOKEN\n\nexport async function createVerification(phone) {\n  const res = await fetch(`${API}\u002Fget-phone`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application\u002Fjson' },\n    body: JSON.stringify({ token: TOKEN, phone }),\n  })\n  if (!res.ok) throw new Error(`get-phone: ${res.status}`)\n\n  \u002F\u002F callTo показываем пользователю — на него нужно позвонить\n  const { id, phone: callTo } = await res.json()\n  return { id, callTo }\n}\n\nexport async function getStatus(id) {\n  const url = `${API}\u002Fstatus?token=${encodeURIComponent(TOKEN)}&id=${encodeURIComponent(id)}`\n  const res = await fetch(url)\n  if (!res.ok) throw new Error(`status: ${res.status}`)\n  return (await res.json()).status\n}\n\n\u002F** Ждём результат, пока проверка жива: TTL 60 секунд. *\u002F\nexport async function waitForCall(id, timeoutMs = 60_000) {\n  const deadline = Date.now() + timeoutMs\n  while (Date.now() \u003C deadline) {\n    const status = await getStatus(id)\n    if (status === 'CONFIRMED') return true\n    if (status === 'EXPIRED') return false\n    await new Promise((r) => setTimeout(r, 2000))\n  }\n  return false\n}\n","js",[190,1810,1811,1826,1841,1846,1869,1899,1909,1925,1948,1954,1997,2002,2009,2043,2052,2057,2062,2080,2122,2138,2170,2187,2192,2197,2203,2231,2256,2275,2293,2317,2336,2370,2376,2383],{"__ignoreMap":452},[1490,1812,1813,1817,1820,1823],{"class":1492,"line":991},[1490,1814,1816],{"class":1815},"szBVR","const",[1490,1818,1819],{"class":1499}," API",[1490,1821,1822],{"class":1815}," =",[1490,1824,1825],{"class":1503}," 'https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1'\n",[1490,1827,1828,1830,1833,1835,1838],{"class":1492,"line":453},[1490,1829,1816],{"class":1815},[1490,1831,1832],{"class":1499}," TOKEN",[1490,1834,1822],{"class":1815},[1490,1836,1837],{"class":1543}," process.env.",[1490,1839,1840],{"class":1499},"FLASHCALL_TOKEN\n",[1490,1842,1843],{"class":1492,"line":490},[1490,1844,1845],{"emptyLinePlaceholder":489},"\n",[1490,1847,1848,1851,1854,1857,1860,1863,1866],{"class":1492,"line":1723},[1490,1849,1850],{"class":1815},"export",[1490,1852,1853],{"class":1815}," async",[1490,1855,1856],{"class":1815}," function",[1490,1858,1859],{"class":1495}," createVerification",[1490,1861,1862],{"class":1543},"(",[1490,1864,1575],{"class":1865},"s4XuR",[1490,1867,1868],{"class":1543},") {\n",[1490,1870,1871,1874,1877,1879,1882,1885,1887,1890,1893,1896],{"class":1492,"line":1736},[1490,1872,1873],{"class":1815},"  const",[1490,1875,1876],{"class":1499}," res",[1490,1878,1822],{"class":1815},[1490,1880,1881],{"class":1815}," await",[1490,1883,1884],{"class":1495}," fetch",[1490,1886,1862],{"class":1543},[1490,1888,1889],{"class":1503},"`${",[1490,1891,1892],{"class":1499},"API",[1490,1894,1895],{"class":1503},"}\u002Fget-phone`",[1490,1897,1898],{"class":1543},", {\n",[1490,1900,1901,1904,1907],{"class":1492,"line":1749},[1490,1902,1903],{"class":1543},"    method: ",[1490,1905,1906],{"class":1503},"'POST'",[1490,1908,1708],{"class":1543},[1490,1910,1911,1914,1917,1919,1922],{"class":1492,"line":1760},[1490,1912,1913],{"class":1543},"    headers: { ",[1490,1915,1916],{"class":1503},"'Content-Type'",[1490,1918,1550],{"class":1543},[1490,1920,1921],{"class":1503},"'application\u002Fjson'",[1490,1923,1924],{"class":1543}," },\n",[1490,1926,1928,1931,1934,1936,1939,1942,1945],{"class":1492,"line":1927},8,[1490,1929,1930],{"class":1543},"    body: ",[1490,1932,1933],{"class":1499},"JSON",[1490,1935,128],{"class":1543},[1490,1937,1938],{"class":1495},"stringify",[1490,1940,1941],{"class":1543},"({ token: ",[1490,1943,1944],{"class":1499},"TOKEN",[1490,1946,1947],{"class":1543},", phone }),\n",[1490,1949,1951],{"class":1492,"line":1950},9,[1490,1952,1953],{"class":1543},"  })\n",[1490,1955,1957,1960,1963,1966,1969,1972,1975,1978,1980,1983,1986,1988,1991,1994],{"class":1492,"line":1956},10,[1490,1958,1959],{"class":1815},"  if",[1490,1961,1962],{"class":1543}," (",[1490,1964,1965],{"class":1815},"!",[1490,1967,1968],{"class":1543},"res.ok) ",[1490,1970,1971],{"class":1815},"throw",[1490,1973,1974],{"class":1815}," new",[1490,1976,1977],{"class":1495}," Error",[1490,1979,1862],{"class":1543},[1490,1981,1982],{"class":1503},"`get-phone: ${",[1490,1984,1985],{"class":1543},"res",[1490,1987,128],{"class":1503},[1490,1989,1990],{"class":1543},"status",[1490,1992,1993],{"class":1503},"}`",[1490,1995,1996],{"class":1543},")\n",[1490,1998,2000],{"class":1492,"line":1999},11,[1490,2001,1845],{"emptyLinePlaceholder":489},[1490,2003,2005],{"class":1492,"line":2004},12,[1490,2006,2008],{"class":2007},"sJ8bj","  \u002F\u002F callTo показываем пользователю — на него нужно позвонить\n",[1490,2010,2011,2013,2016,2018,2020,2022,2024,2027,2030,2033,2035,2038,2040],{"class":1492,"line":1386},[1490,2012,1873],{"class":1815},[1490,2014,2015],{"class":1543}," { ",[1490,2017,1571],{"class":1499},[1490,2019,193],{"class":1543},[1490,2021,1575],{"class":1865},[1490,2023,1550],{"class":1543},[1490,2025,2026],{"class":1499},"callTo",[1490,2028,2029],{"class":1543}," } ",[1490,2031,2032],{"class":1815},"=",[1490,2034,1881],{"class":1815},[1490,2036,2037],{"class":1543}," res.",[1490,2039,1536],{"class":1495},[1490,2041,2042],{"class":1543},"()\n",[1490,2044,2046,2049],{"class":1492,"line":2045},14,[1490,2047,2048],{"class":1815},"  return",[1490,2050,2051],{"class":1543}," { id, callTo }\n",[1490,2053,2055],{"class":1492,"line":2054},15,[1490,2056,1763],{"class":1543},[1490,2058,2060],{"class":1492,"line":2059},16,[1490,2061,1845],{"emptyLinePlaceholder":489},[1490,2063,2065,2067,2069,2071,2074,2076,2078],{"class":1492,"line":2064},17,[1490,2066,1850],{"class":1815},[1490,2068,1853],{"class":1815},[1490,2070,1856],{"class":1815},[1490,2072,2073],{"class":1495}," getStatus",[1490,2075,1862],{"class":1543},[1490,2077,1571],{"class":1865},[1490,2079,1868],{"class":1543},[1490,2081,2083,2085,2088,2090,2093,2095,2098,2101,2103,2105,2108,2111,2113,2115,2117,2119],{"class":1492,"line":2082},18,[1490,2084,1873],{"class":1815},[1490,2086,2087],{"class":1499}," url",[1490,2089,1822],{"class":1815},[1490,2091,2092],{"class":1503}," `${",[1490,2094,1892],{"class":1499},[1490,2096,2097],{"class":1503},"}\u002Fstatus?token=${",[1490,2099,2100],{"class":1495},"encodeURIComponent",[1490,2102,1862],{"class":1503},[1490,2104,1944],{"class":1499},[1490,2106,2107],{"class":1503},")",[1490,2109,2110],{"class":1503},"}&id=${",[1490,2112,2100],{"class":1495},[1490,2114,1862],{"class":1503},[1490,2116,1571],{"class":1543},[1490,2118,2107],{"class":1503},[1490,2120,2121],{"class":1503},"}`\n",[1490,2123,2125,2127,2129,2131,2133,2135],{"class":1492,"line":2124},19,[1490,2126,1873],{"class":1815},[1490,2128,1876],{"class":1499},[1490,2130,1822],{"class":1815},[1490,2132,1881],{"class":1815},[1490,2134,1884],{"class":1495},[1490,2136,2137],{"class":1543},"(url)\n",[1490,2139,2141,2143,2145,2147,2149,2151,2153,2155,2157,2160,2162,2164,2166,2168],{"class":1492,"line":2140},20,[1490,2142,1959],{"class":1815},[1490,2144,1962],{"class":1543},[1490,2146,1965],{"class":1815},[1490,2148,1968],{"class":1543},[1490,2150,1971],{"class":1815},[1490,2152,1974],{"class":1815},[1490,2154,1977],{"class":1495},[1490,2156,1862],{"class":1543},[1490,2158,2159],{"class":1503},"`status: ${",[1490,2161,1985],{"class":1543},[1490,2163,128],{"class":1503},[1490,2165,1990],{"class":1543},[1490,2167,1993],{"class":1503},[1490,2169,1996],{"class":1543},[1490,2171,2173,2175,2177,2180,2182,2184],{"class":1492,"line":2172},21,[1490,2174,2048],{"class":1815},[1490,2176,1962],{"class":1543},[1490,2178,2179],{"class":1815},"await",[1490,2181,2037],{"class":1543},[1490,2183,1536],{"class":1495},[1490,2185,2186],{"class":1543},"()).status\n",[1490,2188,2190],{"class":1492,"line":2189},22,[1490,2191,1763],{"class":1543},[1490,2193,2195],{"class":1492,"line":2194},23,[1490,2196,1845],{"emptyLinePlaceholder":489},[1490,2198,2200],{"class":1492,"line":2199},24,[1490,2201,2202],{"class":2007},"\u002F** Ждём результат, пока проверка жива: TTL 60 секунд. *\u002F\n",[1490,2204,2206,2208,2210,2212,2215,2217,2219,2221,2224,2226,2229],{"class":1492,"line":2205},25,[1490,2207,1850],{"class":1815},[1490,2209,1853],{"class":1815},[1490,2211,1856],{"class":1815},[1490,2213,2214],{"class":1495}," waitForCall",[1490,2216,1862],{"class":1543},[1490,2218,1571],{"class":1865},[1490,2220,193],{"class":1543},[1490,2222,2223],{"class":1865},"timeoutMs",[1490,2225,1822],{"class":1815},[1490,2227,2228],{"class":1499}," 60_000",[1490,2230,1868],{"class":1543},[1490,2232,2234,2236,2239,2241,2244,2247,2250,2253],{"class":1492,"line":2233},26,[1490,2235,1873],{"class":1815},[1490,2237,2238],{"class":1499}," deadline",[1490,2240,1822],{"class":1815},[1490,2242,2243],{"class":1543}," Date.",[1490,2245,2246],{"class":1495},"now",[1490,2248,2249],{"class":1543},"() ",[1490,2251,2252],{"class":1815},"+",[1490,2254,2255],{"class":1543}," timeoutMs\n",[1490,2257,2259,2262,2265,2267,2269,2272],{"class":1492,"line":2258},27,[1490,2260,2261],{"class":1815},"  while",[1490,2263,2264],{"class":1543}," (Date.",[1490,2266,2246],{"class":1495},[1490,2268,2249],{"class":1543},[1490,2270,2271],{"class":1815},"\u003C",[1490,2273,2274],{"class":1543}," deadline) {\n",[1490,2276,2278,2281,2284,2286,2288,2290],{"class":1492,"line":2277},28,[1490,2279,2280],{"class":1815},"    const",[1490,2282,2283],{"class":1499}," status",[1490,2285,1822],{"class":1815},[1490,2287,1881],{"class":1815},[1490,2289,2073],{"class":1495},[1490,2291,2292],{"class":1543},"(id)\n",[1490,2294,2296,2299,2302,2305,2308,2311,2314],{"class":1492,"line":2295},29,[1490,2297,2298],{"class":1815},"    if",[1490,2300,2301],{"class":1543}," (status ",[1490,2303,2304],{"class":1815},"===",[1490,2306,2307],{"class":1503}," 'CONFIRMED'",[1490,2309,2310],{"class":1543},") ",[1490,2312,2313],{"class":1815},"return",[1490,2315,2316],{"class":1499}," true\n",[1490,2318,2320,2322,2324,2326,2329,2331,2333],{"class":1492,"line":2319},30,[1490,2321,2298],{"class":1815},[1490,2323,2301],{"class":1543},[1490,2325,2304],{"class":1815},[1490,2327,2328],{"class":1503}," 'EXPIRED'",[1490,2330,2310],{"class":1543},[1490,2332,2313],{"class":1815},[1490,2334,2335],{"class":1499}," false\n",[1490,2337,2339,2342,2344,2347,2350,2353,2355,2358,2361,2364,2367],{"class":1492,"line":2338},31,[1490,2340,2341],{"class":1815},"    await",[1490,2343,1974],{"class":1815},[1490,2345,2346],{"class":1499}," Promise",[1490,2348,2349],{"class":1543},"((",[1490,2351,2352],{"class":1865},"r",[1490,2354,2310],{"class":1543},[1490,2356,2357],{"class":1815},"=>",[1490,2359,2360],{"class":1495}," setTimeout",[1490,2362,2363],{"class":1543},"(r, ",[1490,2365,2366],{"class":1499},"2000",[1490,2368,2369],{"class":1543},"))\n",[1490,2371,2373],{"class":1492,"line":2372},32,[1490,2374,2375],{"class":1543},"  }\n",[1490,2377,2379,2381],{"class":1492,"line":2378},33,[1490,2380,2048],{"class":1815},[1490,2382,2335],{"class":1499},[1490,2384,2386],{"class":1492,"line":2385},34,[1490,2387,1763],{"class":1543},[1800,2389,2391],{"id":2390},"php","PHP",[1174,2393,2396],{"className":2394,"code":2395,"language":2390,"meta":452,"style":452},"language-php shiki shiki-themes github-light github-dark","\u003C?php\nconst FLASHCALL_API = 'https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1';\n\nfunction flashcallRequest(string $url, ?array $body = null): array {\n    $ch = curl_init($url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    curl_setopt($ch, CURLOPT_TIMEOUT, 10);\n    if ($body !== null) {\n        curl_setopt($ch, CURLOPT_POST, true);\n        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));\n        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application\u002Fjson']);\n    }\n    $response = curl_exec($ch);\n    $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\n    curl_close($ch);\n\n    if ($code >= 400) {\n        throw new RuntimeException(\"FlashCall API вернул $code: $response\");\n    }\n    return json_decode($response, true);\n}\n\nfunction createVerification(string $phone): array {\n    return flashcallRequest(FLASHCALL_API . '\u002Fget-phone', [\n        'token' => getenv('FLASHCALL_TOKEN'),\n        'phone' => $phone,\n    ]);\n}\n\nfunction getStatus(string $id): string {\n    $query = http_build_query(['token' => getenv('FLASHCALL_TOKEN'), 'id' => $id]);\n    return flashcallRequest(FLASHCALL_API . '\u002Fstatus?' . $query)['status'];\n}\n",[190,2397,2398,2403,2408,2412,2417,2422,2427,2432,2437,2442,2447,2452,2457,2462,2467,2472,2476,2481,2486,2490,2495,2499,2503,2508,2513,2518,2523,2528,2532,2536,2541,2546,2551],{"__ignoreMap":452},[1490,2399,2400],{"class":1492,"line":991},[1490,2401,2402],{},"\u003C?php\n",[1490,2404,2405],{"class":1492,"line":453},[1490,2406,2407],{},"const FLASHCALL_API = 'https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1';\n",[1490,2409,2410],{"class":1492,"line":490},[1490,2411,1845],{"emptyLinePlaceholder":489},[1490,2413,2414],{"class":1492,"line":1723},[1490,2415,2416],{},"function flashcallRequest(string $url, ?array $body = null): array {\n",[1490,2418,2419],{"class":1492,"line":1736},[1490,2420,2421],{},"    $ch = curl_init($url);\n",[1490,2423,2424],{"class":1492,"line":1749},[1490,2425,2426],{},"    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n",[1490,2428,2429],{"class":1492,"line":1760},[1490,2430,2431],{},"    curl_setopt($ch, CURLOPT_TIMEOUT, 10);\n",[1490,2433,2434],{"class":1492,"line":1927},[1490,2435,2436],{},"    if ($body !== null) {\n",[1490,2438,2439],{"class":1492,"line":1950},[1490,2440,2441],{},"        curl_setopt($ch, CURLOPT_POST, true);\n",[1490,2443,2444],{"class":1492,"line":1956},[1490,2445,2446],{},"        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));\n",[1490,2448,2449],{"class":1492,"line":1999},[1490,2450,2451],{},"        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application\u002Fjson']);\n",[1490,2453,2454],{"class":1492,"line":2004},[1490,2455,2456],{},"    }\n",[1490,2458,2459],{"class":1492,"line":1386},[1490,2460,2461],{},"    $response = curl_exec($ch);\n",[1490,2463,2464],{"class":1492,"line":2045},[1490,2465,2466],{},"    $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\n",[1490,2468,2469],{"class":1492,"line":2054},[1490,2470,2471],{},"    curl_close($ch);\n",[1490,2473,2474],{"class":1492,"line":2059},[1490,2475,1845],{"emptyLinePlaceholder":489},[1490,2477,2478],{"class":1492,"line":2064},[1490,2479,2480],{},"    if ($code >= 400) {\n",[1490,2482,2483],{"class":1492,"line":2082},[1490,2484,2485],{},"        throw new RuntimeException(\"FlashCall API вернул $code: $response\");\n",[1490,2487,2488],{"class":1492,"line":2124},[1490,2489,2456],{},[1490,2491,2492],{"class":1492,"line":2140},[1490,2493,2494],{},"    return json_decode($response, true);\n",[1490,2496,2497],{"class":1492,"line":2172},[1490,2498,1763],{},[1490,2500,2501],{"class":1492,"line":2189},[1490,2502,1845],{"emptyLinePlaceholder":489},[1490,2504,2505],{"class":1492,"line":2194},[1490,2506,2507],{},"function createVerification(string $phone): array {\n",[1490,2509,2510],{"class":1492,"line":2199},[1490,2511,2512],{},"    return flashcallRequest(FLASHCALL_API . '\u002Fget-phone', [\n",[1490,2514,2515],{"class":1492,"line":2205},[1490,2516,2517],{},"        'token' => getenv('FLASHCALL_TOKEN'),\n",[1490,2519,2520],{"class":1492,"line":2233},[1490,2521,2522],{},"        'phone' => $phone,\n",[1490,2524,2525],{"class":1492,"line":2258},[1490,2526,2527],{},"    ]);\n",[1490,2529,2530],{"class":1492,"line":2277},[1490,2531,1763],{},[1490,2533,2534],{"class":1492,"line":2295},[1490,2535,1845],{"emptyLinePlaceholder":489},[1490,2537,2538],{"class":1492,"line":2319},[1490,2539,2540],{},"function getStatus(string $id): string {\n",[1490,2542,2543],{"class":1492,"line":2338},[1490,2544,2545],{},"    $query = http_build_query(['token' => getenv('FLASHCALL_TOKEN'), 'id' => $id]);\n",[1490,2547,2548],{"class":1492,"line":2372},[1490,2549,2550],{},"    return flashcallRequest(FLASHCALL_API . '\u002Fstatus?' . $query)['status'];\n",[1490,2552,2553],{"class":1492,"line":2378},[1490,2554,1763],{},[1800,2556,2558],{"id":2557},"python","Python",[1174,2560,2563],{"className":2561,"code":2562,"language":2557,"meta":452,"style":452},"language-python shiki shiki-themes github-light github-dark","import os, time, requests\n\nAPI = \"https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1\"\nTOKEN = os.environ[\"FLASHCALL_TOKEN\"]\n\n\ndef create_verification(phone: str) -> tuple[str, str]:\n    r = requests.post(f\"{API}\u002Fget-phone\", json={\"token\": TOKEN, \"phone\": phone}, timeout=10)\n    r.raise_for_status()\n    data = r.json()\n    # второй элемент — номер, на который звонит пользователь\n    return data[\"id\"], data[\"phone\"]\n\n\ndef get_status(check_id: str) -> str:\n    r = requests.get(f\"{API}\u002Fstatus\", params={\"token\": TOKEN, \"id\": check_id}, timeout=10)\n    r.raise_for_status()\n    return r.json()[\"status\"]\n\n\ndef wait_for_call(check_id: str, timeout: float = 60.0) -> bool:\n    deadline = time.monotonic() + timeout\n    while time.monotonic() \u003C deadline:\n        status = get_status(check_id)\n        if status == \"CONFIRMED\":\n            return True\n        if status == \"EXPIRED\":\n            return False\n        time.sleep(2)\n    return False\n",[190,2564,2565,2570,2574,2579,2584,2588,2592,2597,2602,2607,2612,2617,2622,2626,2630,2635,2640,2644,2649,2653,2657,2662,2667,2672,2677,2682,2687,2692,2697,2702],{"__ignoreMap":452},[1490,2566,2567],{"class":1492,"line":991},[1490,2568,2569],{},"import os, time, requests\n",[1490,2571,2572],{"class":1492,"line":453},[1490,2573,1845],{"emptyLinePlaceholder":489},[1490,2575,2576],{"class":1492,"line":490},[1490,2577,2578],{},"API = \"https:\u002F\u002Fflashcall.ru\u002Fapi\u002Fv1\"\n",[1490,2580,2581],{"class":1492,"line":1723},[1490,2582,2583],{},"TOKEN = os.environ[\"FLASHCALL_TOKEN\"]\n",[1490,2585,2586],{"class":1492,"line":1736},[1490,2587,1845],{"emptyLinePlaceholder":489},[1490,2589,2590],{"class":1492,"line":1749},[1490,2591,1845],{"emptyLinePlaceholder":489},[1490,2593,2594],{"class":1492,"line":1760},[1490,2595,2596],{},"def create_verification(phone: str) -> tuple[str, str]:\n",[1490,2598,2599],{"class":1492,"line":1927},[1490,2600,2601],{},"    r = requests.post(f\"{API}\u002Fget-phone\", json={\"token\": TOKEN, \"phone\": phone}, timeout=10)\n",[1490,2603,2604],{"class":1492,"line":1950},[1490,2605,2606],{},"    r.raise_for_status()\n",[1490,2608,2609],{"class":1492,"line":1956},[1490,2610,2611],{},"    data = r.json()\n",[1490,2613,2614],{"class":1492,"line":1999},[1490,2615,2616],{},"    # второй элемент — номер, на который звонит пользователь\n",[1490,2618,2619],{"class":1492,"line":2004},[1490,2620,2621],{},"    return data[\"id\"], data[\"phone\"]\n",[1490,2623,2624],{"class":1492,"line":1386},[1490,2625,1845],{"emptyLinePlaceholder":489},[1490,2627,2628],{"class":1492,"line":2045},[1490,2629,1845],{"emptyLinePlaceholder":489},[1490,2631,2632],{"class":1492,"line":2054},[1490,2633,2634],{},"def get_status(check_id: str) -> str:\n",[1490,2636,2637],{"class":1492,"line":2059},[1490,2638,2639],{},"    r = requests.get(f\"{API}\u002Fstatus\", params={\"token\": TOKEN, \"id\": check_id}, timeout=10)\n",[1490,2641,2642],{"class":1492,"line":2064},[1490,2643,2606],{},[1490,2645,2646],{"class":1492,"line":2082},[1490,2647,2648],{},"    return r.json()[\"status\"]\n",[1490,2650,2651],{"class":1492,"line":2124},[1490,2652,1845],{"emptyLinePlaceholder":489},[1490,2654,2655],{"class":1492,"line":2140},[1490,2656,1845],{"emptyLinePlaceholder":489},[1490,2658,2659],{"class":1492,"line":2172},[1490,2660,2661],{},"def wait_for_call(check_id: str, timeout: float = 60.0) -> bool:\n",[1490,2663,2664],{"class":1492,"line":2189},[1490,2665,2666],{},"    deadline = time.monotonic() + timeout\n",[1490,2668,2669],{"class":1492,"line":2194},[1490,2670,2671],{},"    while time.monotonic() \u003C deadline:\n",[1490,2673,2674],{"class":1492,"line":2199},[1490,2675,2676],{},"        status = get_status(check_id)\n",[1490,2678,2679],{"class":1492,"line":2205},[1490,2680,2681],{},"        if status == \"CONFIRMED\":\n",[1490,2683,2684],{"class":1492,"line":2233},[1490,2685,2686],{},"            return True\n",[1490,2688,2689],{"class":1492,"line":2258},[1490,2690,2691],{},"        if status == \"EXPIRED\":\n",[1490,2693,2694],{"class":1492,"line":2277},[1490,2695,2696],{},"            return False\n",[1490,2698,2699],{"class":1492,"line":2295},[1490,2700,2701],{},"        time.sleep(2)\n",[1490,2703,2704],{"class":1492,"line":2319},[1490,2705,2706],{},"    return False\n",[17,2708,2710],{"id":2709},"frontend","Фронтенд",[10,2712,2713,2714,2717],{},"Оба примера ходят в ",[113,2715,2716],{},"ваш"," эндпоинт, а не в API напрямую: ключ проекта в браузере размещать нельзя.",[1800,2719,2721],{"id":2720},"vue","Vue",[1174,2723,2726],{"className":2724,"code":2725,"language":2720,"meta":452,"style":452},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nimport { ref, onUnmounted } from 'vue'\n\nconst callTo = ref('')\nconst secondsLeft = ref(0)\nconst done = ref(false)\nlet timer = null\n\nasync function start(phone) {\n  const res = await fetch('\u002Fapi\u002Fverification\u002Fstart', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application\u002Fjson' },\n    body: JSON.stringify({ phone }),\n  })\n  const { id, callTo: number } = await res.json()\n\n  callTo.value = number\n  secondsLeft.value = 60\n\n  timer = setInterval(async () => {\n    secondsLeft.value--\n    const { status } = await (await fetch(`\u002Fapi\u002Fverification\u002F${id}`)).json()\n\n    if (status === 'CONFIRMED') {\n      done.value = true\n      stop()\n    } else if (status === 'EXPIRED' || secondsLeft.value \u003C= 0) {\n      stop()\n    }\n  }, 2000)\n}\n\nfunction stop() {\n  clearInterval(timer)\n  timer = null\n}\n\nonUnmounted(stop)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Ca v-if=\"callTo && !done\" :href=\"`tel:${callTo}`\">Позвонить на {{ callTo }}\u003C\u002Fa>\n  \u003Cp v-if=\"secondsLeft > 0\">Ждём звонок: {{ secondsLeft }} с\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[190,2727,2728,2742,2756,2760,2779,2797,2815,2828,2832,2848,2867,2875,2887,2900,2904,2933,2937,2947,2957,2961,2983,2991,3027,3031,3043,3052,3059,3090,3096,3100,3109,3113,3117,3128,3136,3145,3150,3155,3164,3174,3179,3189,3220,3241],{"__ignoreMap":452},[1490,2729,2730,2732,2736,2739],{"class":1492,"line":991},[1490,2731,2271],{"class":1543},[1490,2733,2735],{"class":2734},"s9eBZ","script",[1490,2737,2738],{"class":1495}," setup",[1490,2740,2741],{"class":1543},">\n",[1490,2743,2744,2747,2750,2753],{"class":1492,"line":453},[1490,2745,2746],{"class":1815},"import",[1490,2748,2749],{"class":1543}," { ref, onUnmounted } ",[1490,2751,2752],{"class":1815},"from",[1490,2754,2755],{"class":1503}," 'vue'\n",[1490,2757,2758],{"class":1492,"line":490},[1490,2759,1845],{"emptyLinePlaceholder":489},[1490,2761,2762,2764,2767,2769,2772,2774,2777],{"class":1492,"line":1723},[1490,2763,1816],{"class":1815},[1490,2765,2766],{"class":1499}," callTo",[1490,2768,1822],{"class":1815},[1490,2770,2771],{"class":1495}," ref",[1490,2773,1862],{"class":1543},[1490,2775,2776],{"class":1503},"''",[1490,2778,1996],{"class":1543},[1490,2780,2781,2783,2786,2788,2790,2792,2795],{"class":1492,"line":1736},[1490,2782,1816],{"class":1815},[1490,2784,2785],{"class":1499}," secondsLeft",[1490,2787,1822],{"class":1815},[1490,2789,2771],{"class":1495},[1490,2791,1862],{"class":1543},[1490,2793,2794],{"class":1499},"0",[1490,2796,1996],{"class":1543},[1490,2798,2799,2801,2804,2806,2808,2810,2813],{"class":1492,"line":1749},[1490,2800,1816],{"class":1815},[1490,2802,2803],{"class":1499}," done",[1490,2805,1822],{"class":1815},[1490,2807,2771],{"class":1495},[1490,2809,1862],{"class":1543},[1490,2811,2812],{"class":1499},"false",[1490,2814,1996],{"class":1543},[1490,2816,2817,2820,2823,2825],{"class":1492,"line":1760},[1490,2818,2819],{"class":1815},"let",[1490,2821,2822],{"class":1543}," timer ",[1490,2824,2032],{"class":1815},[1490,2826,2827],{"class":1499}," null\n",[1490,2829,2830],{"class":1492,"line":1927},[1490,2831,1845],{"emptyLinePlaceholder":489},[1490,2833,2834,2837,2839,2842,2844,2846],{"class":1492,"line":1950},[1490,2835,2836],{"class":1815},"async",[1490,2838,1856],{"class":1815},[1490,2840,2841],{"class":1495}," start",[1490,2843,1862],{"class":1543},[1490,2845,1575],{"class":1865},[1490,2847,1868],{"class":1543},[1490,2849,2850,2852,2854,2856,2858,2860,2862,2865],{"class":1492,"line":1956},[1490,2851,1873],{"class":1815},[1490,2853,1876],{"class":1499},[1490,2855,1822],{"class":1815},[1490,2857,1881],{"class":1815},[1490,2859,1884],{"class":1495},[1490,2861,1862],{"class":1543},[1490,2863,2864],{"class":1503},"'\u002Fapi\u002Fverification\u002Fstart'",[1490,2866,1898],{"class":1543},[1490,2868,2869,2871,2873],{"class":1492,"line":1999},[1490,2870,1903],{"class":1543},[1490,2872,1906],{"class":1503},[1490,2874,1708],{"class":1543},[1490,2876,2877,2879,2881,2883,2885],{"class":1492,"line":2004},[1490,2878,1913],{"class":1543},[1490,2880,1916],{"class":1503},[1490,2882,1550],{"class":1543},[1490,2884,1921],{"class":1503},[1490,2886,1924],{"class":1543},[1490,2888,2889,2891,2893,2895,2897],{"class":1492,"line":1386},[1490,2890,1930],{"class":1543},[1490,2892,1933],{"class":1499},[1490,2894,128],{"class":1543},[1490,2896,1938],{"class":1495},[1490,2898,2899],{"class":1543},"({ phone }),\n",[1490,2901,2902],{"class":1492,"line":2045},[1490,2903,1953],{"class":1543},[1490,2905,2906,2908,2910,2912,2914,2916,2918,2921,2923,2925,2927,2929,2931],{"class":1492,"line":2054},[1490,2907,1873],{"class":1815},[1490,2909,2015],{"class":1543},[1490,2911,1571],{"class":1499},[1490,2913,193],{"class":1543},[1490,2915,2026],{"class":1865},[1490,2917,1550],{"class":1543},[1490,2919,2920],{"class":1499},"number",[1490,2922,2029],{"class":1543},[1490,2924,2032],{"class":1815},[1490,2926,1881],{"class":1815},[1490,2928,2037],{"class":1543},[1490,2930,1536],{"class":1495},[1490,2932,2042],{"class":1543},[1490,2934,2935],{"class":1492,"line":2059},[1490,2936,1845],{"emptyLinePlaceholder":489},[1490,2938,2939,2942,2944],{"class":1492,"line":2064},[1490,2940,2941],{"class":1543},"  callTo.value ",[1490,2943,2032],{"class":1815},[1490,2945,2946],{"class":1543}," number\n",[1490,2948,2949,2952,2954],{"class":1492,"line":2082},[1490,2950,2951],{"class":1543},"  secondsLeft.value ",[1490,2953,2032],{"class":1815},[1490,2955,2956],{"class":1499}," 60\n",[1490,2958,2959],{"class":1492,"line":2124},[1490,2960,1845],{"emptyLinePlaceholder":489},[1490,2962,2963,2966,2968,2971,2973,2975,2978,2980],{"class":1492,"line":2140},[1490,2964,2965],{"class":1543},"  timer ",[1490,2967,2032],{"class":1815},[1490,2969,2970],{"class":1495}," setInterval",[1490,2972,1862],{"class":1543},[1490,2974,2836],{"class":1815},[1490,2976,2977],{"class":1543}," () ",[1490,2979,2357],{"class":1815},[1490,2981,2982],{"class":1543}," {\n",[1490,2984,2985,2988],{"class":1492,"line":2172},[1490,2986,2987],{"class":1543},"    secondsLeft.value",[1490,2989,2990],{"class":1815},"--\n",[1490,2992,2993,2995,2997,2999,3001,3003,3005,3007,3009,3011,3013,3016,3018,3020,3023,3025],{"class":1492,"line":2189},[1490,2994,2280],{"class":1815},[1490,2996,2015],{"class":1543},[1490,2998,1990],{"class":1499},[1490,3000,2029],{"class":1543},[1490,3002,2032],{"class":1815},[1490,3004,1881],{"class":1815},[1490,3006,1962],{"class":1543},[1490,3008,2179],{"class":1815},[1490,3010,1884],{"class":1495},[1490,3012,1862],{"class":1543},[1490,3014,3015],{"class":1503},"`\u002Fapi\u002Fverification\u002F${",[1490,3017,1571],{"class":1543},[1490,3019,1993],{"class":1503},[1490,3021,3022],{"class":1543},")).",[1490,3024,1536],{"class":1495},[1490,3026,2042],{"class":1543},[1490,3028,3029],{"class":1492,"line":2194},[1490,3030,1845],{"emptyLinePlaceholder":489},[1490,3032,3033,3035,3037,3039,3041],{"class":1492,"line":2199},[1490,3034,2298],{"class":1815},[1490,3036,2301],{"class":1543},[1490,3038,2304],{"class":1815},[1490,3040,2307],{"class":1503},[1490,3042,1868],{"class":1543},[1490,3044,3045,3048,3050],{"class":1492,"line":2205},[1490,3046,3047],{"class":1543},"      done.value ",[1490,3049,2032],{"class":1815},[1490,3051,2316],{"class":1499},[1490,3053,3054,3057],{"class":1492,"line":2233},[1490,3055,3056],{"class":1495},"      stop",[1490,3058,2042],{"class":1543},[1490,3060,3061,3064,3067,3070,3072,3074,3076,3079,3082,3085,3088],{"class":1492,"line":2258},[1490,3062,3063],{"class":1543},"    } ",[1490,3065,3066],{"class":1815},"else",[1490,3068,3069],{"class":1815}," if",[1490,3071,2301],{"class":1543},[1490,3073,2304],{"class":1815},[1490,3075,2328],{"class":1503},[1490,3077,3078],{"class":1815}," ||",[1490,3080,3081],{"class":1543}," secondsLeft.value ",[1490,3083,3084],{"class":1815},"\u003C=",[1490,3086,3087],{"class":1499}," 0",[1490,3089,1868],{"class":1543},[1490,3091,3092,3094],{"class":1492,"line":2277},[1490,3093,3056],{"class":1495},[1490,3095,2042],{"class":1543},[1490,3097,3098],{"class":1492,"line":2295},[1490,3099,2456],{"class":1543},[1490,3101,3102,3105,3107],{"class":1492,"line":2319},[1490,3103,3104],{"class":1543},"  }, ",[1490,3106,2366],{"class":1499},[1490,3108,1996],{"class":1543},[1490,3110,3111],{"class":1492,"line":2338},[1490,3112,1763],{"class":1543},[1490,3114,3115],{"class":1492,"line":2372},[1490,3116,1845],{"emptyLinePlaceholder":489},[1490,3118,3119,3122,3125],{"class":1492,"line":2378},[1490,3120,3121],{"class":1815},"function",[1490,3123,3124],{"class":1495}," stop",[1490,3126,3127],{"class":1543},"() {\n",[1490,3129,3130,3133],{"class":1492,"line":2385},[1490,3131,3132],{"class":1495},"  clearInterval",[1490,3134,3135],{"class":1543},"(timer)\n",[1490,3137,3139,3141,3143],{"class":1492,"line":3138},35,[1490,3140,2965],{"class":1543},[1490,3142,2032],{"class":1815},[1490,3144,2827],{"class":1499},[1490,3146,3148],{"class":1492,"line":3147},36,[1490,3149,1763],{"class":1543},[1490,3151,3153],{"class":1492,"line":3152},37,[1490,3154,1845],{"emptyLinePlaceholder":489},[1490,3156,3158,3161],{"class":1492,"line":3157},38,[1490,3159,3160],{"class":1495},"onUnmounted",[1490,3162,3163],{"class":1543},"(stop)\n",[1490,3165,3167,3170,3172],{"class":1492,"line":3166},39,[1490,3168,3169],{"class":1543},"\u003C\u002F",[1490,3171,2735],{"class":2734},[1490,3173,2741],{"class":1543},[1490,3175,3177],{"class":1492,"line":3176},40,[1490,3178,1845],{"emptyLinePlaceholder":489},[1490,3180,3182,3184,3187],{"class":1492,"line":3181},41,[1490,3183,2271],{"class":1543},[1490,3185,3186],{"class":2734},"template",[1490,3188,2741],{"class":1543},[1490,3190,3192,3195,3197,3200,3202,3205,3208,3210,3213,3216,3218],{"class":1492,"line":3191},42,[1490,3193,3194],{"class":1543},"  \u003C",[1490,3196,124],{"class":2734},[1490,3198,3199],{"class":1495}," v-if",[1490,3201,2032],{"class":1543},[1490,3203,3204],{"class":1503},"\"callTo && !done\"",[1490,3206,3207],{"class":1495}," :href",[1490,3209,2032],{"class":1543},[1490,3211,3212],{"class":1503},"\"`tel:${callTo}`\"",[1490,3214,3215],{"class":1543},">Позвонить на {{ callTo }}\u003C\u002F",[1490,3217,124],{"class":2734},[1490,3219,2741],{"class":1543},[1490,3221,3223,3225,3227,3229,3231,3234,3237,3239],{"class":1492,"line":3222},43,[1490,3224,3194],{"class":1543},[1490,3226,10],{"class":2734},[1490,3228,3199],{"class":1495},[1490,3230,2032],{"class":1543},[1490,3232,3233],{"class":1503},"\"secondsLeft > 0\"",[1490,3235,3236],{"class":1543},">Ждём звонок: {{ secondsLeft }} с\u003C\u002F",[1490,3238,10],{"class":2734},[1490,3240,2741],{"class":1543},[1490,3242,3244,3246,3248],{"class":1492,"line":3243},44,[1490,3245,3169],{"class":1543},[1490,3247,3186],{"class":2734},[1490,3249,2741],{"class":1543},[1800,3251,3253],{"id":3252},"react","React",[1174,3255,3259],{"className":3256,"code":3257,"language":3258,"meta":452,"style":452},"language-jsx shiki shiki-themes github-light github-dark","import { useEffect, useRef, useState } from 'react'\n\nexport function CallVerification({ phone, onConfirmed }) {\n  const [callTo, setCallTo] = useState('')\n  const [secondsLeft, setSecondsLeft] = useState(0)\n  const idRef = useRef(null)\n\n  useEffect(() => {\n    let cancelled = false\n\n    async function start() {\n      const res = await fetch('\u002Fapi\u002Fverification\u002Fstart', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\u002Fjson' },\n        body: JSON.stringify({ phone }),\n      })\n      const data = await res.json()\n      if (cancelled) return\n\n      idRef.current = data.id\n      setCallTo(data.callTo)\n      setSecondsLeft(60)\n    }\n\n    start()\n    return () => { cancelled = true }\n  }, [phone])\n\n  useEffect(() => {\n    if (!idRef.current || secondsLeft \u003C= 0) return\n\n    const timer = setTimeout(async () => {\n      const { status } = await (await fetch(`\u002Fapi\u002Fverification\u002F${idRef.current}`)).json()\n      if (status === 'CONFIRMED') onConfirmed()\n      else setSecondsLeft((s) => s - 2)\n    }, 2000)\n\n    return () => clearTimeout(timer)\n  }, [secondsLeft, onConfirmed])\n\n  if (!callTo) return null\n  return (\n    \u003C>\n      \u003Ca href={`tel:${callTo}`}>Позвонить на {callTo}\u003C\u002Fa>\n      {secondsLeft > 0 && \u003Cp>Ждём звонок: {secondsLeft} с\u003C\u002Fp>}\n    \u003C\u002F>\n  )\n}\n","jsx",[190,3260,3261,3273,3277,3299,3327,3353,3372,3376,3388,3400,3404,3415,3434,3443,3456,3469,3474,3491,3502,3506,3516,3524,3536,3540,3544,3551,3570,3575,3579,3589,3614,3618,3639,3679,3695,3723,3732,3736,3749,3754,3758,3773,3780,3785,3814,3841,3847,3853],{"__ignoreMap":452},[1490,3262,3263,3265,3268,3270],{"class":1492,"line":991},[1490,3264,2746],{"class":1815},[1490,3266,3267],{"class":1543}," { useEffect, useRef, useState } ",[1490,3269,2752],{"class":1815},[1490,3271,3272],{"class":1503}," 'react'\n",[1490,3274,3275],{"class":1492,"line":453},[1490,3276,1845],{"emptyLinePlaceholder":489},[1490,3278,3279,3281,3283,3286,3289,3291,3293,3296],{"class":1492,"line":490},[1490,3280,1850],{"class":1815},[1490,3282,1856],{"class":1815},[1490,3284,3285],{"class":1495}," CallVerification",[1490,3287,3288],{"class":1543},"({ ",[1490,3290,1575],{"class":1865},[1490,3292,193],{"class":1543},[1490,3294,3295],{"class":1865},"onConfirmed",[1490,3297,3298],{"class":1543}," }) {\n",[1490,3300,3301,3303,3306,3308,3310,3313,3316,3318,3321,3323,3325],{"class":1492,"line":1723},[1490,3302,1873],{"class":1815},[1490,3304,3305],{"class":1543}," [",[1490,3307,2026],{"class":1499},[1490,3309,193],{"class":1543},[1490,3311,3312],{"class":1499},"setCallTo",[1490,3314,3315],{"class":1543},"] ",[1490,3317,2032],{"class":1815},[1490,3319,3320],{"class":1495}," useState",[1490,3322,1862],{"class":1543},[1490,3324,2776],{"class":1503},[1490,3326,1996],{"class":1543},[1490,3328,3329,3331,3333,3336,3338,3341,3343,3345,3347,3349,3351],{"class":1492,"line":1736},[1490,3330,1873],{"class":1815},[1490,3332,3305],{"class":1543},[1490,3334,3335],{"class":1499},"secondsLeft",[1490,3337,193],{"class":1543},[1490,3339,3340],{"class":1499},"setSecondsLeft",[1490,3342,3315],{"class":1543},[1490,3344,2032],{"class":1815},[1490,3346,3320],{"class":1495},[1490,3348,1862],{"class":1543},[1490,3350,2794],{"class":1499},[1490,3352,1996],{"class":1543},[1490,3354,3355,3357,3360,3362,3365,3367,3370],{"class":1492,"line":1749},[1490,3356,1873],{"class":1815},[1490,3358,3359],{"class":1499}," idRef",[1490,3361,1822],{"class":1815},[1490,3363,3364],{"class":1495}," useRef",[1490,3366,1862],{"class":1543},[1490,3368,3369],{"class":1499},"null",[1490,3371,1996],{"class":1543},[1490,3373,3374],{"class":1492,"line":1760},[1490,3375,1845],{"emptyLinePlaceholder":489},[1490,3377,3378,3381,3384,3386],{"class":1492,"line":1927},[1490,3379,3380],{"class":1495},"  useEffect",[1490,3382,3383],{"class":1543},"(() ",[1490,3385,2357],{"class":1815},[1490,3387,2982],{"class":1543},[1490,3389,3390,3393,3396,3398],{"class":1492,"line":1950},[1490,3391,3392],{"class":1815},"    let",[1490,3394,3395],{"class":1543}," cancelled ",[1490,3397,2032],{"class":1815},[1490,3399,2335],{"class":1499},[1490,3401,3402],{"class":1492,"line":1956},[1490,3403,1845],{"emptyLinePlaceholder":489},[1490,3405,3406,3409,3411,3413],{"class":1492,"line":1999},[1490,3407,3408],{"class":1815},"    async",[1490,3410,1856],{"class":1815},[1490,3412,2841],{"class":1495},[1490,3414,3127],{"class":1543},[1490,3416,3417,3420,3422,3424,3426,3428,3430,3432],{"class":1492,"line":2004},[1490,3418,3419],{"class":1815},"      const",[1490,3421,1876],{"class":1499},[1490,3423,1822],{"class":1815},[1490,3425,1881],{"class":1815},[1490,3427,1884],{"class":1495},[1490,3429,1862],{"class":1543},[1490,3431,2864],{"class":1503},[1490,3433,1898],{"class":1543},[1490,3435,3436,3439,3441],{"class":1492,"line":1386},[1490,3437,3438],{"class":1543},"        method: ",[1490,3440,1906],{"class":1503},[1490,3442,1708],{"class":1543},[1490,3444,3445,3448,3450,3452,3454],{"class":1492,"line":2045},[1490,3446,3447],{"class":1543},"        headers: { ",[1490,3449,1916],{"class":1503},[1490,3451,1550],{"class":1543},[1490,3453,1921],{"class":1503},[1490,3455,1924],{"class":1543},[1490,3457,3458,3461,3463,3465,3467],{"class":1492,"line":2054},[1490,3459,3460],{"class":1543},"        body: ",[1490,3462,1933],{"class":1499},[1490,3464,128],{"class":1543},[1490,3466,1938],{"class":1495},[1490,3468,2899],{"class":1543},[1490,3470,3471],{"class":1492,"line":2059},[1490,3472,3473],{"class":1543},"      })\n",[1490,3475,3476,3478,3481,3483,3485,3487,3489],{"class":1492,"line":2064},[1490,3477,3419],{"class":1815},[1490,3479,3480],{"class":1499}," data",[1490,3482,1822],{"class":1815},[1490,3484,1881],{"class":1815},[1490,3486,2037],{"class":1543},[1490,3488,1536],{"class":1495},[1490,3490,2042],{"class":1543},[1490,3492,3493,3496,3499],{"class":1492,"line":2082},[1490,3494,3495],{"class":1815},"      if",[1490,3497,3498],{"class":1543}," (cancelled) ",[1490,3500,3501],{"class":1815},"return\n",[1490,3503,3504],{"class":1492,"line":2124},[1490,3505,1845],{"emptyLinePlaceholder":489},[1490,3507,3508,3511,3513],{"class":1492,"line":2140},[1490,3509,3510],{"class":1543},"      idRef.current ",[1490,3512,2032],{"class":1815},[1490,3514,3515],{"class":1543}," data.id\n",[1490,3517,3518,3521],{"class":1492,"line":2172},[1490,3519,3520],{"class":1495},"      setCallTo",[1490,3522,3523],{"class":1543},"(data.callTo)\n",[1490,3525,3526,3529,3531,3534],{"class":1492,"line":2189},[1490,3527,3528],{"class":1495},"      setSecondsLeft",[1490,3530,1862],{"class":1543},[1490,3532,3533],{"class":1499},"60",[1490,3535,1996],{"class":1543},[1490,3537,3538],{"class":1492,"line":2194},[1490,3539,2456],{"class":1543},[1490,3541,3542],{"class":1492,"line":2199},[1490,3543,1845],{"emptyLinePlaceholder":489},[1490,3545,3546,3549],{"class":1492,"line":2205},[1490,3547,3548],{"class":1495},"    start",[1490,3550,2042],{"class":1543},[1490,3552,3553,3556,3558,3560,3563,3565,3568],{"class":1492,"line":2233},[1490,3554,3555],{"class":1815},"    return",[1490,3557,2977],{"class":1543},[1490,3559,2357],{"class":1815},[1490,3561,3562],{"class":1543}," { cancelled ",[1490,3564,2032],{"class":1815},[1490,3566,3567],{"class":1499}," true",[1490,3569,1566],{"class":1543},[1490,3571,3572],{"class":1492,"line":2258},[1490,3573,3574],{"class":1543},"  }, [phone])\n",[1490,3576,3577],{"class":1492,"line":2277},[1490,3578,1845],{"emptyLinePlaceholder":489},[1490,3580,3581,3583,3585,3587],{"class":1492,"line":2295},[1490,3582,3380],{"class":1495},[1490,3584,3383],{"class":1543},[1490,3586,2357],{"class":1815},[1490,3588,2982],{"class":1543},[1490,3590,3591,3593,3595,3597,3600,3603,3606,3608,3610,3612],{"class":1492,"line":2319},[1490,3592,2298],{"class":1815},[1490,3594,1962],{"class":1543},[1490,3596,1965],{"class":1815},[1490,3598,3599],{"class":1543},"idRef.current ",[1490,3601,3602],{"class":1815},"||",[1490,3604,3605],{"class":1543}," secondsLeft ",[1490,3607,3084],{"class":1815},[1490,3609,3087],{"class":1499},[1490,3611,2310],{"class":1543},[1490,3613,3501],{"class":1815},[1490,3615,3616],{"class":1492,"line":2338},[1490,3617,1845],{"emptyLinePlaceholder":489},[1490,3619,3620,3622,3625,3627,3629,3631,3633,3635,3637],{"class":1492,"line":2372},[1490,3621,2280],{"class":1815},[1490,3623,3624],{"class":1499}," timer",[1490,3626,1822],{"class":1815},[1490,3628,2360],{"class":1495},[1490,3630,1862],{"class":1543},[1490,3632,2836],{"class":1815},[1490,3634,2977],{"class":1543},[1490,3636,2357],{"class":1815},[1490,3638,2982],{"class":1543},[1490,3640,3641,3643,3645,3647,3649,3651,3653,3655,3657,3659,3661,3663,3666,3668,3671,3673,3675,3677],{"class":1492,"line":2378},[1490,3642,3419],{"class":1815},[1490,3644,2015],{"class":1543},[1490,3646,1990],{"class":1499},[1490,3648,2029],{"class":1543},[1490,3650,2032],{"class":1815},[1490,3652,1881],{"class":1815},[1490,3654,1962],{"class":1543},[1490,3656,2179],{"class":1815},[1490,3658,1884],{"class":1495},[1490,3660,1862],{"class":1543},[1490,3662,3015],{"class":1503},[1490,3664,3665],{"class":1543},"idRef",[1490,3667,128],{"class":1503},[1490,3669,3670],{"class":1543},"current",[1490,3672,1993],{"class":1503},[1490,3674,3022],{"class":1543},[1490,3676,1536],{"class":1495},[1490,3678,2042],{"class":1543},[1490,3680,3681,3683,3685,3687,3689,3691,3693],{"class":1492,"line":2385},[1490,3682,3495],{"class":1815},[1490,3684,2301],{"class":1543},[1490,3686,2304],{"class":1815},[1490,3688,2307],{"class":1503},[1490,3690,2310],{"class":1543},[1490,3692,3295],{"class":1495},[1490,3694,2042],{"class":1543},[1490,3696,3697,3700,3703,3705,3708,3710,3712,3715,3718,3721],{"class":1492,"line":3138},[1490,3698,3699],{"class":1815},"      else",[1490,3701,3702],{"class":1495}," setSecondsLeft",[1490,3704,2349],{"class":1543},[1490,3706,3707],{"class":1865},"s",[1490,3709,2310],{"class":1543},[1490,3711,2357],{"class":1815},[1490,3713,3714],{"class":1543}," s ",[1490,3716,3717],{"class":1815},"-",[1490,3719,3720],{"class":1499}," 2",[1490,3722,1996],{"class":1543},[1490,3724,3725,3728,3730],{"class":1492,"line":3147},[1490,3726,3727],{"class":1543},"    }, ",[1490,3729,2366],{"class":1499},[1490,3731,1996],{"class":1543},[1490,3733,3734],{"class":1492,"line":3152},[1490,3735,1845],{"emptyLinePlaceholder":489},[1490,3737,3738,3740,3742,3744,3747],{"class":1492,"line":3157},[1490,3739,3555],{"class":1815},[1490,3741,2977],{"class":1543},[1490,3743,2357],{"class":1815},[1490,3745,3746],{"class":1495}," clearTimeout",[1490,3748,3135],{"class":1543},[1490,3750,3751],{"class":1492,"line":3166},[1490,3752,3753],{"class":1543},"  }, [secondsLeft, onConfirmed])\n",[1490,3755,3756],{"class":1492,"line":3176},[1490,3757,1845],{"emptyLinePlaceholder":489},[1490,3759,3760,3762,3764,3766,3769,3771],{"class":1492,"line":3181},[1490,3761,1959],{"class":1815},[1490,3763,1962],{"class":1543},[1490,3765,1965],{"class":1815},[1490,3767,3768],{"class":1543},"callTo) ",[1490,3770,2313],{"class":1815},[1490,3772,2827],{"class":1499},[1490,3774,3775,3777],{"class":1492,"line":3191},[1490,3776,2048],{"class":1815},[1490,3778,3779],{"class":1543}," (\n",[1490,3781,3782],{"class":1492,"line":3222},[1490,3783,3784],{"class":1543},"    \u003C>\n",[1490,3786,3787,3790,3792,3795,3797,3800,3803,3805,3807,3810,3812],{"class":1492,"line":3243},[1490,3788,3789],{"class":1543},"      \u003C",[1490,3791,124],{"class":2734},[1490,3793,3794],{"class":1495}," href",[1490,3796,2032],{"class":1815},[1490,3798,3799],{"class":1543},"{",[1490,3801,3802],{"class":1503},"`tel:${",[1490,3804,2026],{"class":1543},[1490,3806,1993],{"class":1503},[1490,3808,3809],{"class":1543},"}>Позвонить на {callTo}\u003C\u002F",[1490,3811,124],{"class":2734},[1490,3813,2741],{"class":1543},[1490,3815,3817,3820,3823,3825,3828,3831,3833,3836,3838],{"class":1492,"line":3816},45,[1490,3818,3819],{"class":1543},"      {secondsLeft ",[1490,3821,3822],{"class":1815},">",[1490,3824,3087],{"class":1499},[1490,3826,3827],{"class":1815}," &&",[1490,3829,3830],{"class":1543}," \u003C",[1490,3832,10],{"class":2734},[1490,3834,3835],{"class":1543},">Ждём звонок: {secondsLeft} с\u003C\u002F",[1490,3837,10],{"class":2734},[1490,3839,3840],{"class":1543},">}\n",[1490,3842,3844],{"class":1492,"line":3843},46,[1490,3845,3846],{"class":1543},"    \u003C\u002F>\n",[1490,3848,3850],{"class":1492,"line":3849},47,[1490,3851,3852],{"class":1543},"  )\n",[1490,3854,3856],{"class":1492,"line":3855},48,[1490,3857,1763],{"class":1543},[17,3859,3861],{"id":3860},"tipichnye-oshibki-pri-integracii","Типичные ошибки при интеграции",[283,3863,3864,3872,3878,3884,3890,3896,3902,3908],{},[286,3865,3866,3871],{},[113,3867,3868,3869,128],{},"Не обрабатывают ",[190,3870,192],{}," Пользователь видит вечный спиннер вместо кнопки «Позвонить ещё раз». Самая частая ошибка.",[286,3873,3874,3877],{},[113,3875,3876],{},"Держат ключ во фронтенде."," Ключ забирают из исходников страницы и тратят ваш баланс.",[286,3879,3880,3883],{},[113,3881,3882],{},"Опрашивают статус слишком часто."," Десять запросов в секунду ничего не ускоряют.",[286,3885,3886,3889],{},[113,3887,3888],{},"Не останавливают опрос."," Без условия по таймеру цикл продолжается после ухода пользователя со страницы.",[286,3891,3892,3895],{},[113,3893,3894],{},"Не проверяют баланс."," При нулевом балансе проверка не создастся, и форма перестанет работать молча.",[286,3897,3898,3901],{},[113,3899,3900],{},"Не ограничивают частоту на своём эндпоинте."," Создание проверок стоит денег — лимит на попытки обязателен.",[286,3903,3904,3907],{},[113,3905,3906],{},"Не приводят номер к E.164."," Номер с восьмёркой, скобками и дефисами не пройдёт валидацию.",[286,3909,3910,3913],{},[113,3911,3912],{},"Забывают предупредить, что звонить надо с подтверждаемого номера."," Звонок с рабочего телефона не подтвердится, а причина будет неочевидна.",[17,3915,3917],{"id":3916},"skolko-eto-zanimaet","Сколько это занимает",[25,3919,3921],{"className":3920},[28],[30,3922,3923,3933],{},[33,3924,3925],{},[36,3926,3927,3930],{},[39,3928,3929],{},"Шаг",[39,3931,3932],{},"Время",[52,3934,3935,3943,3951,3960,3967],{},[36,3936,3937,3940],{},[57,3938,3939],{},"Регистрация, проект, ключ",[57,3941,3942],{},"5 минут",[36,3944,3945,3948],{},[57,3946,3947],{},"Два эндпоинта на бэкенде",[57,3949,3950],{},"1–2 часа",[36,3952,3953,3958],{},[57,3954,3955,3956],{},"Экран ожидания с таймером и ",[190,3957,638],{},[57,3959,3950],{},[36,3961,3962,3965],{},[57,3963,3964],{},"Обработка граничных случаев и лимиты",[57,3966,3950],{},[36,3968,3969,3972],{},[57,3970,3971],{},"Тестирование на реальных номерах",[57,3973,3974],{},"1 час",[10,3976,3977],{},"Итого рабочий день на разработчика. Дольше обычно идёт не техническая часть, а согласование интерфейса и сравнение воронок.",[10,3979,3980,3981,955],{},"Актуальные цены — на странице ",[124,3982,274],{"href":273},[3984,3985,3986],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":452,"searchDepth":453,"depth":453,"links":3988},[3989,3990,3991,3992,3993,3998,4002,4003],{"id":1403,"depth":453,"text":1404},{"id":1480,"depth":453,"text":1481},{"id":1648,"depth":453,"text":1649},{"id":1673,"depth":453,"text":1674},{"id":1797,"depth":453,"text":1798,"children":3994},[3995,3996,3997],{"id":1802,"depth":490,"text":1803},{"id":2390,"depth":490,"text":2391},{"id":2557,"depth":490,"text":2558},{"id":2709,"depth":453,"text":2710,"children":3999},[4000,4001],{"id":2720,"depth":490,"text":2721},{"id":3252,"depth":490,"text":3253},{"id":3860,"depth":453,"text":3861},{"id":3916,"depth":453,"text":3917},"Реализация",{"title":4006,"text":4007,"label":4008,"href":467},"Получите ключ и попробуйте","Проект и ключ создаются сразу после регистрации. Инструкция с вашим ключом открывается в кабинете.","Получить API-ключ","Как интегрировать подтверждение номера через REST API: создание проверки, опрос статуса, обработка ошибок и примеры на Node.js, PHP, Python, Vue и React.",[4011,4014,4017,4020,4023],{"q":4012,"a":4013},"Есть ли вебхуки?","Нет. Результат забирается опросом `GET \u002Fstatus`. Это упрощает интеграцию:\nне нужен публично доступный обработчик, проверка подписи и защита\nот повторной доставки. Если вебхуки появятся, опрос продолжит работать.\n",{"q":4015,"a":4016},"Можно ли вызывать API прямо из браузера?","Нет. Запрос содержит ключ проекта, а всё, что попало во фронтенд, доступно\nпользователю. Оба запроса должны идти с вашего сервера, а браузер общается\nс вашим собственным эндпоинтом.\n",{"q":4018,"a":4019},"Сколько живёт проверка?","60 секунд с момента создания. После этого она переходит в `EXPIRED`,\nи звонок, поступивший позже, к ней уже не привяжется. Создавайте новую\nпроверку вместо продления старой.\n",{"q":4021,"a":4022},"Что вернётся, если на балансе нет денег?","Создание проверки завершится ошибкой о недостатке средств — проверка\nне будет создана. Уже существующие проверки при этом дорабатывают штатно.\n",{"q":4024,"a":4025},"Нужен ли SDK?","Нет. Это обычный REST с JSON: два запроса, которые делаются штатным\nHTTP-клиентом любого языка. Отдельной библиотеки не требуется.\n","API подтверждения номера телефона: интеграция за час",{},[4029,4030,994],"podtverzhdenie-nomera-na-sayte","podtverzhdenie-nomera-v-prilozhenii",{"title":1395,"description":4009},"blog\u002Fapi-podtverzhdeniya-nomera","Интеграция состоит из двух запросов: создать проверку и узнать её статус. Первый\nвозвращает выделенный номер, который нужно показать пользователю, второй — одно из трёх\nсостояний: `PENDING`, `CONFIRMED` или `EXPIRED`. Вебхуков нет, результат забирается\nопросом — поэтому не нужен публичный обработчик, проверка подписи и защита от повторных\nдоставок. Ключ проекта используется только с сервера.\n","FCFlMRQUYKfE8axfA1BS8q2zx_WEzpDY3DgBePLh8Ho",{"data":4036,"body":4037},{},{"type":4038,"children":4039},"root",[4040],{"type":4041,"tag":10,"props":4042,"children":4043},"element",{},[4044],{"type":1179,"value":4045},"При выборе сервиса подтверждения номера почти всё решают три вопроса: за что именно\nсписывают деньги — за попытку или за результат; что происходит, когда подтверждение\nне доходит до пользователя; за сколько это встраивается в ваш продукт. Цена за штуку\nвторична — на реальную стоимость сильнее влияет модель списания. Ниже разбор критериев\nи расчёт: в FlashCall подтверждение стоит 0,20 ₽, и платите вы только за состоявшиеся.",{"data":4047,"body":4048},{},{"type":4038,"children":4049},[4050],{"type":4041,"tag":10,"props":4051,"children":4052},{},[4053],{"type":1179,"value":4054},"Абонентской платы и обязательного месячного объёма нет — вы платите только\nза подтверждения. Минимум есть у пополнения баланса: по умолчанию 100 ₽,\nактуальное значение показано в кабинете на странице пополнения.",{"data":4056,"body":4057},{},{"type":4038,"children":4058},[4059],{"type":4041,"tag":10,"props":4060,"children":4061},{},[4062],{"type":1179,"value":4063},"Юрлицам и ИП выставляется счёт, отношения регулирует публичная оферта.\nРеквизиты компании подставляются в счёт из карточки в кабинете, а НДС\nуказывается согласно налоговому статусу исполнителя.",{"data":4065,"body":4066},{},{"type":4038,"children":4067},[4068],{"type":4041,"tag":10,"props":4069,"children":4070},{},[4071],{"type":1179,"value":4072},"Да. Проект и ключ создаются сразу после регистрации, а стоимость проверки\nтакая, что полноценное тестирование сценария укладывается в минимальное\nпополнение. Отдельного песочного контура с фиктивными звонками нет —\nпроверки идут по реальному пулу номеров.",{"data":4074,"body":4075},{},{"type":4038,"children":4076},[4077],{"type":4041,"tag":10,"props":4078,"children":4079},{},[4080],{"type":1179,"value":4081},"Он не сгорает и остаётся на счёте: списание идёт по факту подтверждений,\nбез ежемесячного обнуления. Порядок возврата описан в оферте.",{"data":4083,"body":4084},{},{"type":4038,"children":4085},[4086],{"type":4041,"tag":10,"props":4087,"children":4088},{},[4089],{"type":1179,"value":4090},"Написать до интеграции. Крупные объёмы упираются не в тариф, а в ёмкость\nпула номеров, и это вопрос планирования, а не прайса.",1787687891022]