Невозможно отправить OTP через Twilio в Django
Я работаю над реализацией системы OTP в моем Django-проекте с использованием Twilio. Я успешно получил номер телефона Twilio, но сообщение OTP не отправляется на мобильный номер пользователя. Ниже приведены детали моей установки:
Реализация кода
views.py
:
def send_otp_view(request):
if request.method == "POST":
data = json.loads(request.body)
phone_number = data.get('phone_number')
# Generate a random 6-digit OTP
otp = random.randint(100000, 999999)
# Send OTP via Twilio (or any other SMS provider)
try:
client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN)
message = client.messages.create(
body=f"Your OTP is {otp}. Please use this to verify your number.",
from_=settings.TWILIO_PHONE_NUMBER,
to=phone_number
)
# Optionally, store the OTP in a session or database
request.session['otp'] = otp
request.session['phone_number'] = phone_number
return JsonResponse({'status': 'success', 'otp': otp}) # Return OTP for validation
except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)})
return JsonResponse({'status': 'error', 'message': 'Invalid request'})
def address_view(request):
if request.user.is_authenticated:
user_addresses = Address.objects.filter(user=request.user)
if request.method == "POST":
# Get the OTP from the form
entered_otp = request.POST.get("otp")
# Check if the OTP matches
if entered_otp == str(request.session.get('otp')):
# Proceed with saving the address if OTP is correct
mobile = request.POST.get("mobile")
email = request.POST.get("email")
pin = request.POST.get("pin")
region = request.POST.get("region")
address = request.POST.get("address")
landmark = request.POST.get("landmark")
name = request.POST.get("name")
new_address = Address.objects.create(
user=request.user,
mobile=mobile,
email=email,
pin=pin,
region=region,
address=address,
landmark=landmark,
name=name,
)
messages.success(request, "Address Added Successfully.")
return redirect("core:address")
else:
messages.error(request, "Invalid OTP. Please try again.")
return redirect("core:address")
context = {
"user_addresses": user_addresses,
}
return render(request, 'others/address-book.html', context)
else:
print("User is not authenticated")
return redirect("core:login")
2. HTML-форма:
<div class="gl-inline">
<div class="u-s-m-b-30">
<label class="gl-label" for="address-phone">PHONE *</label>
<input
class="input-text input-text--primary-style"
type="text"
id="address-phone"
name="mobile"
placeholder="Enter 10-digit phone number"
oninput="validatePhoneNumber()"
maxlength="10">
<button type="button" id="send-otp-btn" style="display:none;" onclick="sendOTP()">Send OTP</button>
</div>
<div id="otp-box" style="display: none;" class="u-s-m-b-30">
<label class="gl-label" for="otp-input">ENTER OTP *</label>
<input
class="input-text input-text--primary-style"
type="text"
id="otp-input"
name="otp"
placeholder="Enter OTP"
maxlength="6"
oninput="validateOTP()">
<span id="otp-feedback" style="display: none; font-size: 14px;"></span>
</div>
<div class="u-s-m-b-30">
<label class="gl-label" for="address-street">STREET ADDRESS *</label>
<input class="input-text input-text--primary-style" type="text" id="address-street" placeholder="House Name and Street" name="address">
</div>
</div>
3. Настройки Twilio (settings.py
):
TWILIO_ACCOUNT_SID = 'YOUR_ACCOUNT_SID'
TWILIO_AUTH_TOKEN = 'YOUR_AUTH_TOKEN'
TWILIO_PHONE_NUMBER = '+1234567890' # Twilio number with country code
4. Внешняя проверка (JavaScript):
console.log("OTP");
let sentOTP = null; // Store the sent OTP
function validatePhoneNumber() {
const phoneInput = document.getElementById('address-phone');
const otpBox = document.getElementById('otp-box');
const sendOtpBtn = document.getElementById('send-otp-btn');
// Check if the phone number is 10 digits long
if (phoneInput.value.length === 10) {
otpBox.style.display = 'block'; // Show OTP box
sendOtpBtn.style.display = 'inline-block'; // Show OTP button
} else {
otpBox.style.display = 'none'; // Hide OTP box
sendOtpBtn.style.display = 'none'; // Hide OTP button
}
}
function sendOTP() {
const phoneNumber = document.getElementById('address-phone').value;
// Send an AJAX request to the Django view to generate and send OTP
fetch('/send-otp/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken') // Handle CSRF token for security
},
body: JSON.stringify({ phone_number: phoneNumber })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
sentOTP = data.otp; // Save the OTP for later validation
console.log("OTP sent successfully");
} else {
alert("Error sending OTP");
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to send OTP');
});
}
function validateOTP() {
const otpInput = document.getElementById('otp-input');
const feedback = document.getElementById('otp-feedback');
// Compare entered OTP with the sent OTP
if (otpInput.value === sentOTP) {
feedback.style.display = 'block';
feedback.style.color = 'green';
feedback.textContent = 'OTP is correct!';
} else if (otpInput.value.length === sentOTP.length) {
feedback.style.display = 'block';
feedback.style.color = 'red';
feedback.textContent = 'Incorrect OTP, please try again.';
} else {
feedback.style.display = 'none'; // Hide feedback when incomplete
}
}
// Utility to get the CSRF token from cookies
function getCookie(name) {
const cookieValue = document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)');
return cookieValue ? cookieValue.pop() : '';
}
Проблема:
- В форму вводится правильный номер мобильного телефона, и POST-запрос достигает представления Django.
- Вызываются методы
Client
иmessages.create
Twilio, но SMS не отправляется. - В журналах Django я вижу следующую ошибку:
Error sending OTP: TwilioRestException: [Error message here]
Что я пробовал:
- Убедитесь, что учетные данные Twilio (SID аккаунта, Auth Token и номер телефона) верны.
- Убедитесь, что номер телефона имеет формат E.164 с правильным кодом страны.
- Подтвердите, что пробная учетная запись Twilio проверила номер телефона получателя.
- Проверили журналы сообщений Twilio, но никаких записей о неудачном сообщении не появилось.
- Проверил функциональность Twilio с помощью отдельного скрипта, который работает:
from twilio.rest import Client
client = Client('YOUR_ACCOUNT_SID', 'YOUR_AUTH_TOKEN')
message = client.messages.create(
body="Test message",
from_='+1234567890',
to='+9876543210'
)
print(message.sid)
Ожидаемое поведение: Сообщение OTP должно быть отправлено на введенный номер мобильного телефона.
Вопрос: Что может быть причиной этой проблемы и как я могу ее решить? Может ли это быть связано с настройками моего аккаунта Twilio или конфигурацией Django? Любая помощь или понимание будут оценены по достоинству!
Дополнительные примечания:
- Я использую пробную учетную запись Twilio.
- Номер телефона получателя проверен на панели управления Twilio.
- В моей настройке нет ограничений сети или брандмауэра.
Основные улучшения и шаги по устранению неполадок:
1. Форматирование телефонных номеров:
Добавлена правильная проверка и форматирование телефонных номеров с помощью библиотеки phonenumbers
Убедитесь, что номера имеют формат E.164, требуемый Twilio
Правильно обрабатывает коды разных стран
from django.http import JsonResponse
from django.conf import settings
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
import random
import json
import phonenumbers
def format_phone_number(phone_number, country_code='US'):
try:
parsed_number = phonenumbers.parse(phone_number, country_code)
if not phonenumbers.is_valid_number(parsed_number):
return None
return phonenumbers.format_number(parsed_number,
phonenumbers.PhoneNumberFormat.E164)
except phonenumbers.NumberParseException:
return None
def send_otp_view(request):
if request.method != "POST":
return JsonResponse({'status': 'error', 'message': 'Invalid
request method'})
try:
data = json.loads(request.body)
phone_number = data.get('phone_number')
if not phone_number:
return JsonResponse({'status': 'error', 'message': 'Phone
number is required'})
formatted_number = format_phone_number(phone_number)
if not formatted_number:
return JsonResponse({
'status': 'error',
'message': 'Invalid phone number format. Please include
country code.'
})
otp = str(random.randint(100000, 999999))
try:
client = Client(settings.TWILIO_ACCOUNT_SID,
settings.TWILIO_AUTH_TOKEN)
message = client.messages.create(
body=f"Your verification code is: {otp}",
from_=settings.TWILIO_PHONE_NUMBER,
to=formatted_number
)
request.session['otp'] = otp
request.session['phone_number'] = formatted_number
return JsonResponse({
'status': 'success',
'message': 'OTP sent successfully',
'otp': otp
})
except TwilioRestException as e:
print(f"Twilio Error: {str(e)}")
error_message = 'Failed to send OTP. '
if e.code == 20003:
error_message += 'Authentication failed. Check Twilio
credentials.'
elif e.code == 21211:
error_message += 'Invalid phone number format.'
elif e.code == 21608:
error_message += 'Phone number not verified in trial
account.'
else:
error_message += str(e)
return JsonResponse({'status': 'error', 'message':
error_message})
except Exception as e:
return JsonResponse({'status': 'error', 'message': f'Server error:
{str(e)}'})
2. Обработка ошибок:
Добавлена специальная обработка ошибок для распространенных кодов ошибок Twilio
Предоставляет четкие сообщения об ошибках для отладки
Правильно ловит и сообщает о проблемах аутентификации
3. Улучшения в области безопасности:
Улучшенная обработка сессий для хранения OTP
Более надежная проверка ввода
Правильная санация сообщений об ошибках
Для реализации этого решения:
# Установите необходимые пакеты:
pip install twilio phonenumbers
# Во фронтенде JavaScript измените обработку ошибок:
function sendOTP() {
const phoneNumber = document.getElementById('address-phone').value;
// Add country code if not provided by user
const formattedNumber = phoneNumber.startsWith('+') ? phoneNumber :
`+${phoneNumber}`;
fetch('/send-otp/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({ phone_number: formattedNumber })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
alert('OTP sent successfully!');
} else {
alert(`Error: ${data.message}`);
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to send OTP. Please try again.');
});
}
Дополнительные шаги по устранению неисправностей:
# Проверьте свою консоль Twilio, чтобы убедиться:
- Ваш счет активен и имеет достаточное количество кредитов
Номер получателя проверен (требуется для пробных счетов)
- Ваш телефонный номер Twilio способен отправлять SMS
# Проверьте настройки Django:
- Все учетные данные Twilio установлены правильно
- Режим EBUG включен во время тестирования
- На вашем сервере есть доступ в Интернет для доступа к API Twilio