// Сценарий получения баланса клубной карты пользователя по номеру его телефона
// - сценарий считывает номер телефона из профиля пользователя, если номер в профиле не указан то сценарий запросит его у пользователя
// - сценарий отправит проверочный код через сервис отправки СМС и запрос ввод кода
// - сценарий получит в сервисе лояльности по номеру телефона список карт пользователя, для каждой карты запросит ее баланс и выведет суммарный баланс пользователю
//
// Настройки сценария
//
// Тексты сообщений сценария в сторону пользователя
//
// Текст просьбы указать номер телефона
установитьПеременную("messageAskPhoneNumber","Для уточнения информации по клубной карте напишите, пожалуйста, номер телефона, к которому она привязана.") +
// Текст сообщения в случае отсутствия привязанных к номеру телефона карт
установитьПеременную("messageUnknownPhoneNumber","Вы ввели некорректный номер телефона.") +
// Текст сообщения в случае отсутствия привязанных к номеру телефона карт
установитьПеременную("finalMessageUnknownPhoneNumber","Вы дважды ввели некорректный номер телефона. Я не могу вынести такое, поэтому завершаю свою работу.") +
// Текст сообщения об ошибке при отправке СМС
установитьПеременную("messageErrorSMS","К сожалению, мы не смогли отправить СМС-сообщение на указанный вами номер, попробуйте, пожалуйста, позднее.") +
// Текст сообщения c информацией об отправленном СМС
установитьПеременную("messageSMS","На ваш номер телефона отправлен СМС с проверочным кодом.") +
// Текст сообщения c просьбой ввести секретный код
установитьПеременную("messageAskCode","Пожалуйста, напишите проверочный код из СМС.") +
// Текст сообщения об ошибке в присланном коде
установитьПеременную("messageCodeError","Вы ввели неверный код.") +
// Текст финального сообщения об ошибке в присланном коде
установитьПеременную("messageFinalCodeError","Неверный проверочный код.") +
// Текст сообщения в случае отсутствия привязанных к номеру телефона карт
установитьПеременную("messageNoCardsForUser","К этому номеру телефона не привязана клубная карта.") +
// Текст сообщения в случае ошибки в запросе на получение списка привязанных к номеру телефона карт
установитьПеременную("messageUnknownErrorGetCards","При получении списка карт произошла неизвестная ошибка. Попробуйте позже") +
// Текст сообщения в случае ошибки в запросе на получение баланса по одной из карт
установитьПеременную("messageUnknownErrorGetBalance","При получении списка карт произошла неизвестная ошибка. Попробуйте позже") +
// Текст сообщения в случае непридвиденной ошибки
установитьПеременную("messageUnknownError","Произошла непредвиденная ошибка. Попробуйте запросить позже.") +
// Текст первой части финального сообщения с размером баланса по всем картам, между частями будет вставлен размер баланса
установитьПеременную("finalMessage_1","На вашей клубной карте <b>") +
// Текст второй части финального сообщения с размером баланса по всем картам
установитьПеременную("finalMessage_2","</b> баллов.") +
// Текст финального сообщения В случае нулевого баланса по карте
установитьПеременную("finalMessage_zeroBalance","На вашей клубной карте нет баллов. Грустно.") +
//
// Адрес сервиса по отправке СМС
установитьПеременную("sendSMSUrl","https://XXXXXX.ru/send")+
// Адрес сервиса программы лояльности для получения баланса по клубной карте
установитьПеременную("cardRequestUrl","https://XXXXXX.ru/processing")+ //
// Логин и пароль для отправки запросов в сервис программы лояльности в формате base64
установитьПеременную("query_token","XXXXXXXXXXXXXXXXX")+
//
// Основная часть сценария
//
// Служебная переменная для хранения суммарного баланса по всем картам
установитьПеременную("finalBalance","0")+
//
// Сценарий считывает номер телефона из профиля пользователя
установитьПеременную("phoneNumber", "{userPhone}") +
//
// Если в профиле пользователя не был указан номер телефона то сценарий запросит его у пользователя
если("{phoneNumber} == ").то(
задатьПользователюВопрос("{messageAskPhoneNumber}").сохранитьРезультат("phoneNumber")
)+
// Метка для повторного запроса номера телефона
установитьМетку("ask_phone_number")+
// Сценарий проверяет введеный текст на соотвествие формату номеру телефона, на выходе очищенный от лишних символов номер из 10 цифр без +7
выполнитьJs("""
var num = phoneNumber.replace(/[^+\d]/g, '');
if (num.length < 10) {
var numError = 1;
var number = '';
} else {
var numError = 0;
var number = num.substring(num.length-10,num.length);
}
var exit = {'number': number, 'numError':numError};
exit;
""") +
//
// В случае ошибочного формата номера телефона сценарий вернет пользователю просьбу ввести номер повторно и вернется на шаг ввода номера, но не более 2-х раз
если("{numError} != 0").то(
сообщениеПользователю.сШаблоном("{messageUnknownPhoneNumber}")+
перейтиНаМетку("ask_phone_number").неБольше(1)
)+
//
// Если после двух попыток пользователь все равно ввел неверный номер телефона, то сценарий вернет соответствущее сообщение и завершит работу
если("{numError} != 0").то(
сообщениеПользователю.сШаблоном("{finalMessageUnknownPhoneNumber}")+
завершить
)+
//
// Сценарий генерирует секретный код для верификации номера телефона через смс
выполнитьJs("""
var smscode = String(Math.floor(Math.random() * 898) + 101).substring(0,3);
var exit = {'smscode':smscode};
exit;
""") +
//
// Сценарий записывает проверочный комментарий с информацией о отправленном коде
комментарий("Отправлен код {smscode} на номер {number}")+
//
// Сценарий отправляет запрос на отправку СМС с секретным кодом
вызвать.внешнийСервис("{sendSMSUrl}", "POST").сЗаголовками(("Content-Type", "application/json"), ("charset", "utf-8")).сТеломСообщения("""{"phone":"{number}","auth":"{query_token}","text":"Проверочный код {smscode}"}""").сохранитьРезультат(("Status", "status"),("Error","error")) +
//
// Если запрос вернул ошибку сценарий записывает отладочный комментарий, возвращает пользователю сообщение об ошибке и завершает работу
если("{http_code} > 210").то(
комментарий("При отправке запроса на отправку СМС возникла ошибка: {http_code}")+
сообщениеПользователю.сШаблоном("{messageErrorSMS}") +
завершить
)+
//
// Если в результате попытки отправить СМС сценарий получает ошибку, то сценарий возвращает пользователю соответствующее сообщение и завершается
если("{status} == ERROR").то(
комментарий("При отправке запроса на отправку СМС произошла ошибка, статус - {http_body}")+
сообщениеПользователю.сШаблоном("{messageErrorSMS}") +
завершить
)+
//
// Сценарий сообщает об отправленном СМС с кодом
сообщениеПользователю.сШаблоном("{messageSMS}")+
//
// Метка для повторного запроса секретного кода
установитьМетку("getCode")+
//
// В случае успешной отправки СМС, сценарий просит пользователя ввести код
задатьПользователюВопрос("{messageAskCode}").сохранитьРезультат("smsCodeEntered")+
// Сценарий сохраняет отладочный комментарий с текстом введенного кода
комментарий("Введен проверочный код {smsCodeEntered}")+
//
// Сценарий очищает введенный код от возможных служебных символов
выполнитьJs("""
var code = smsCodeEntered.replace(/[^+\d]/g, '');
var exit = {'smsCodeEntered': code};
exit;
""") +
//
// В случае если введенный и отправленный код не совпадают то сценарий возвращает соотвествующее сообщение и переходит обратно на ввод кода, но не более двух раз
если("{smsCodeEntered} != {smscode}").то(
сообщениеПользователю.сШаблоном("{messageCodeError}")+
перейтиНаМетку("getCode").неБольше(1)
) +
//
// В случае если пользователь ввел неверный код 2 раза то сценарий сообщает об ошибке и завершает работу
если("{smsCodeEntered} != {smscode}").то(
сообщениеПользователю.сШаблоном("{messageFinalCodeError}")+
завершить
)+
//
// Сценарий сохраняет введенный номер телефона в профиле пользователя чтобы не спрашивать в следующий раз
комментарий("Номер телефона +7{number} сохранен в профиле пользователя")+
установитьПеременнуюПользователю("userPhone", "+7{number}") +
//
// Сценарий фомирует запрос на получение баланса списка карт по номеру телефона
выполнитьJs("""
var queryTemplate = <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header/>
<soapenv:Body>
<ProcessRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<request>
<CardRequest>
<RequestID>XXXXXXXX</RequestID>
<DateTime>2022-10-12T07:07:06.21</DateTime>
<Organization>XXXXXXXX</Organization>
<POS>XXXXXXXX</POS>
<Phone>+7${number}</Phone>
</CardRequest>
</request>
<orgName>XXXXXXXX</orgName>
</ProcessRequest>
</soapenv:Body>
</soapenv:Envelope>
EOF
var exit = {'queryEncoded': queryTemplate.replace(/(\r\n|\n|\r)/gm,'')};
exit;
""")+
//
// Сценарий отправляет запрос на получение списка карт по номеру телефона в сервис лояльности
вызвать.внешнийСервис("{cardRequestUrl}", "POST").сЗаголовками(("Content-Type", "text/xml;charset=UTF-8"), ("Authorization", "Basic {query_token}")).сТеломСообщения("{queryEncoded}").сохранитьРезультатКакСтроку("answerCardRequest")+
//
// Если запрос вернул ошибку сценарий записывает отладочный комментарий, возвращает пользователю сообщение об ошибке и завершает работу
если("{http_code} > 210").то(
комментарий("При отправке запроса CardRequest возникла ошибка: {http_code}, {answerCardRequest}")+
сообщениеПользователю.сШаблоном("{messageUnknownErrorGetCards}")+
завершить
)+
//
// Сценарий разбирает ответа от сервиса лояльности
выполнитьJs("""
var parser = new marknote.Parser();
var error = 1;
var ReturnCode = '';
var messageFrom = '';
var message = messageUnknownErrorGetCards;
var cardList = [];
try {
var doc = parser.parse(answerCardRequest);
var body = doc.getRootElement();
var CardResponse = body.getChildElement("ProcessRequestResponse").getChildElement("ProcessRequestResult").getChildElement("CardResponse");
var cards = CardResponse.getChildElements("Card");
ReturnCode = CardResponse.getChildElement("ReturnCode").getText();
messageFrom = CardResponse.getChildElement("Message").getText();
} catch(e) {
ReturnCode = '-1';
}
if (ReturnCode == '1') {
message = messageNoCardsForUser;
error = 1;
}
if (ReturnCode == '0') {
for (var i in cards){
var CardNumber = cards[i].getChildElement("CardNumber").getText();
var CardTypeID = cards[i].getChildElement("CardTypeID").getText();
if (CardTypeID == '1' || CardTypeID == '4') {
cardList.push(CardNumber);
}
}
if (cardList.length > 0) {
message = 'OK';
error = 0;
} else {
message = messageNoCardsForUser;
error = 1;
}
}
var exit = {'error': error, 'message': message, 'ReturnCode': ReturnCode, 'cardList': JSON.stringify(cardList), 'messageFrom':messageFrom};
exit;
""")+
//
// Сценарий сохраняет отладочные комментарии с ReturnCode и списком карточек пользователя (отбираются карты только тип 1 и тип 4)
комментарий("Код ответа от сервиса лояльности {ReturnCode}")+
комментарий("Получен список карт пользователя {cardList}")+
//
// Если запрос прошел с ошибкой сценарий сообщит об этом пользователю и завершится. В комментарии будет отображет текст ошибки сервиса лояльности
если("{error} == 1").то(
сообщениеПользователю.сШаблоном("{message}")+
комментарий("Запрос неуспешен. Текст ошибки из сервиса лояльности: {messageFrom}")+
завершить
) +
//
// Метка для запроса баланса по следующей карте из списка карт
установитьМетку("balance_request")+
//
// Сценарий выбирает первую карту из списка для получения баланса карты
выполнитьJs("""
try {
var cardListParced = JSON.parse(cardList);
} catch(e) {
var cardListParced = [];
}
var cardId = '';
if (cardListParced.length > 0) {
cardId = cardListParced.shift();
}
var cardListLength = cardListParced.length;
var exit = {'cardId': cardId, 'cardList': JSON.stringify(cardListParced), 'cardListLength':cardListLength};
exit;
""")+
//
// Сценарий оставляет отладочный комментарий с номуром выбранной карты
комментарий("Получаем данные о балансе карты {cardId}")+
//
// Аварийная проверка - если номер карта не определен, то сценарий возвращает пользователю сообщение об ошибке и завершается
если("{cardId} == ").то(
сообщениеПользователю.сШаблоном("{messageUnknownError}")+
завершить
)+
//
// Сценарий формирует запрос на получение баланса карты
выполнитьJs("""
var queryTemplate = <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header/>
<soapenv:Body>
<ProcessRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<request>
<BalanceRequest>
<RequestID>XXXXXXXX</RequestID>
<DateTime>2022-07-16T08:01:06.21</DateTime>
<Organization>XXXXXXXX</Organization>
<POS>XXXXXXXX</POS>
<Card>
<CardNumber>${cardId}</CardNumber>
</Card>
</BalanceRequest>
</request>
<orgName>XXXXXXXX</orgName>
</ProcessRequest>
</soapenv:Body>
</soapenv:Envelope>
EOF
var exit = {'queryEncoded': queryTemplate.replace(/(\r\n|\n|\r)/gm,'')};
exit;
""")+
//
// Сценарий отправляет запрос на получение баланса карты
вызвать.внешнийСервис("{cardRequestUrl}", "POST").сЗаголовками(("Content-Type", "text/xml;charset=UTF-8"), ("Authorization", "Basic {query_token}")).сТеломСообщения("{queryEncoded}").сохранитьРезультатКакСтроку("answerBalanceRequest")+
//
// В случае ошибки при выполнении запроса сценарий записывает отладочный комментарий, сообщает пользователю об ошибке и завершается
если("{http_code} > 210").то(
комментарий("При отправке запроса BalanceRequest возникла ошибка: {http_code}, {answerBalanceRequest}")+
сообщениеПользователю.сШаблоном("{messageUnknownErrorGetCards}")+
завершить
)+
//
// Сценарий разбирает ответ системы лояльности c данными баланса по карте, полученный баланс суммируется с общим балансом
выполнитьJs("""
var parser = new marknote.Parser();
var error = 1;
var ReturnCode = '';
var messageFrom = '';
var balance = 0;
var message = messageUnknownErrorGetBalance;
var _finalBalance = parseInt(finalBalance);
var doc = parser.parse(answerBalanceRequest);
var body = doc.getRootElement();
var BalanceResponse = body.getChildElement("ProcessRequestResponse").getChildElement("ProcessRequestResult").getChildElement("BalanceResponse");
try {
messageFrom = BalanceResponse.getChildElement("Message").getText();
ReturnCode = BalanceResponse.getChildElement("ReturnCode").getText();
} catch(e) {
ReturnCode = '-2';
}
if (ReturnCode != '0') {
error = 1;
}
if (ReturnCode == '0') {
try {
var CardBalance = BalanceResponse.getChildElement("CardBalance").getText();
balance = parseInt(CardBalance);
_finalBalance = _finalBalance + balance;
} catch(e) {
balance = 0;
}
}
var exit = {'error': error, 'message': message, 'ReturnCode': ReturnCode, 'balance': balance, 'finalBalance': _finalBalance};
exit;
""")+
//
// Сценарий оставляет отладочный комментарий о полученном статусе ответа и балансе по каждой карте
комментарий("Для карты {cardId} получен код ответа от системы лояльности {ReturnCode} с балансом {balance}, суммарный баланс {finalBalance}")+
//
// Если в списке карт еще остались карты то сценарий переходит назад и запрашивает баланс у системы лояльности до тех пор пока список карт не опустеет
если("{cardListLength} != 0").то(
перейтиНаМетку("balance_request").неБольше(5)
)+
//
// В случае если баланс по картам нулевой, то сценарий отдает пользователю соотвествующее сообщение и завершает работу
если("{finalBalance} == 0").то(
сообщениеПользователю.сШаблоном("{finalMessage_zeroBalance}")+
завершить
) +
//
// В случае если баланс не нулевой, то сценарий формирует финальное сообщение пользователю с указанием его суммарного баланса и завершает работу
сообщениеПользователю.сШаблоном("{finalMessage_1}{finalBalance}{finalMessage_2}")+
завершить |