adding disable payments
This commit is contained in:
parent
df3c7ad9f5
commit
ce3101780f
22
GEMINI_RESPONSE.md
Normal file
22
GEMINI_RESPONSE.md
Normal file
@ -0,0 +1,22 @@
|
||||
Plan:
|
||||
|
||||
1. **Add Configuration:** I have added a new field `enable_payment` (checkbox) to the **Platform Profile** model.
|
||||
2. **Enforce Logic:** I updated the payment initiation view (`initiate_payment`) to check this flag. If disabled, users cannot start a payment.
|
||||
3. **Update UI:** I updated the **Shipper Dashboard** to hide the "Pay Now" button if payments are disabled.
|
||||
4. **Deploy:** Created migrations and restarted the server.
|
||||
|
||||
Changed (if any):
|
||||
|
||||
* `core/models.py`: Added `enable_payment` boolean field to `PlatformProfile`.
|
||||
* `core/views.py`: Added a check in `initiate_payment` to block payments if disabled.
|
||||
* `core/templates/core/shipper_dashboard.html`: Wrapped the "Pay Now" button in a conditional check.
|
||||
|
||||
Notes:
|
||||
|
||||
* **Action Required:** Go to **Django Admin > Platform Profiles**. You will see a new checkbox **Enable Payment**.
|
||||
* **Checked:** Users can pay.
|
||||
* **Unchecked:** The "Pay Now" button disappears, and direct URL access is blocked.
|
||||
|
||||
Next: You can test this by toggling the checkbox in the admin and refreshing your dashboard. What's next?
|
||||
|
||||
Reminder: click Save in the editor to sync changes.
|
||||
Binary file not shown.
@ -174,8 +174,8 @@ EMAIL_BACKEND = os.getenv(
|
||||
"EMAIL_BACKEND",
|
||||
"django.core.mail.backends.smtp.EmailBackend"
|
||||
)
|
||||
EMAIL_HOST = os.getenv("EMAIL_HOST", "pop.gmail.com")
|
||||
EMAIL_PORT = int(os.getenv("EMAIL_PORT", "995"))
|
||||
EMAIL_HOST = os.getenv("EMAIL_HOST", "smtp.gmail.com")
|
||||
EMAIL_PORT = int(os.getenv("EMAIL_PORT", "587"))
|
||||
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "aalabry@gmail.com")
|
||||
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "accd uacy kzdq aejp")
|
||||
EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", "true").lower() == "true"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -8,6 +8,8 @@ from django.shortcuts import render
|
||||
from django.utils.html import format_html
|
||||
from django.contrib import messages
|
||||
from .whatsapp_utils import send_whatsapp_message_detailed
|
||||
from django.core.mail import send_mail
|
||||
from django.conf import settings
|
||||
import logging
|
||||
|
||||
class ProfileInline(admin.StackedInline):
|
||||
@ -47,6 +49,7 @@ class PlatformProfileAdmin(admin.ModelAdmin):
|
||||
urls = super().get_urls()
|
||||
custom_urls = [
|
||||
path('test-whatsapp/', self.admin_site.admin_view(self.test_whatsapp_view), name='test-whatsapp'),
|
||||
path('test-email/', self.admin_site.admin_view(self.test_email_view), name='test-email'),
|
||||
]
|
||||
return custom_urls + urls
|
||||
|
||||
@ -69,11 +72,39 @@ class PlatformProfileAdmin(admin.ModelAdmin):
|
||||
)
|
||||
return render(request, "admin/core/platformprofile/test_whatsapp.html", context)
|
||||
|
||||
def test_email_view(self, request):
|
||||
email = ''
|
||||
if request.method == 'POST':
|
||||
email = request.POST.get('email')
|
||||
if email:
|
||||
try:
|
||||
send_mail(
|
||||
subject="Test Email from Platform",
|
||||
message="This is a test email to verify your platform's email configuration.",
|
||||
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||
recipient_list=[email],
|
||||
fail_silently=False,
|
||||
)
|
||||
messages.success(request, f"Success: Test email sent to {email}.")
|
||||
except Exception as e:
|
||||
messages.error(request, f"Error sending email: {str(e)}")
|
||||
else:
|
||||
messages.warning(request, "Please enter an email address.")
|
||||
|
||||
context = dict(
|
||||
self.admin_site.each_context(request),
|
||||
email=email,
|
||||
)
|
||||
return render(request, "admin/core/platformprofile/test_email.html", context)
|
||||
|
||||
def test_connection_link(self, obj):
|
||||
return format_html(
|
||||
'<a class="button" href="{}" style="margin-right: 10px;">{}</a>'
|
||||
'<a class="button" href="{}">{}</a>',
|
||||
reverse('admin:test-whatsapp'),
|
||||
_('Test WhatsApp Configuration')
|
||||
_('Test WhatsApp'),
|
||||
reverse('admin:test-email'),
|
||||
_('Test Email')
|
||||
)
|
||||
test_connection_link.short_description = _("Actions")
|
||||
test_connection_link.allow_tags = True
|
||||
@ -84,6 +115,8 @@ class PlatformProfileAdmin(admin.ModelAdmin):
|
||||
fieldsets = super().get_fieldsets(request, obj)
|
||||
# Add the test link to the first fieldset or a new one
|
||||
if obj:
|
||||
# Check if 'Tools' fieldset already exists to avoid duplication if called multiple times (though get_fieldsets is usually fresh)
|
||||
# Easier: just append it.
|
||||
fieldsets += ((_('Tools'), {'fields': ('test_connection_link',)}),)
|
||||
return fieldsets
|
||||
|
||||
|
||||
18
core/migrations/0013_platformprofile_enable_payment.py
Normal file
18
core/migrations/0013_platformprofile_enable_payment.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2026-01-25 13:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0012_alter_platformprofile_whatsapp_access_token_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='platformprofile',
|
||||
name='enable_payment',
|
||||
field=models.BooleanField(default=True, help_text='Toggle to enable or disable payments on the platform.', verbose_name='Enable Payment'),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -166,12 +166,22 @@ class PlatformProfile(models.Model):
|
||||
whatsapp_business_phone_number_id = models.CharField(_('Wablas Domain'), max_length=100, blank=True, default="https://deu.wablas.com", help_text=_("The Wablas API domain (e.g., https://deu.wablas.com)."))
|
||||
whatsapp_app_secret = models.CharField(_('Wablas Secret Key'), max_length=255, blank=True, help_text=_("Your Wablas API Secret Key (if required)."))
|
||||
|
||||
# Payment Configuration
|
||||
enable_payment = models.BooleanField(_('Enable Payment'), default=True, help_text=_("Toggle to enable or disable payments on the platform."))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Auto-clean whitespace from credentials
|
||||
if self.whatsapp_access_token:
|
||||
self.whatsapp_access_token = self.whatsapp_access_token.strip()
|
||||
if self.whatsapp_business_phone_number_id:
|
||||
self.whatsapp_business_phone_number_id = self.whatsapp_business_phone_number_id.strip()
|
||||
val = self.whatsapp_business_phone_number_id.strip()
|
||||
# Remove common path suffixes if user pasted full URL
|
||||
for suffix in ['/api/send-message', '/api/v2/send-message']:
|
||||
if val.endswith(suffix):
|
||||
val = val[:-len(suffix)]
|
||||
if val.endswith('/'):
|
||||
val = val[:-1]
|
||||
self.whatsapp_business_phone_number_id = val
|
||||
if self.whatsapp_app_secret:
|
||||
self.whatsapp_app_secret = self.whatsapp_app_secret.strip()
|
||||
|
||||
|
||||
39
core/templates/admin/core/platformprofile/test_email.html
Normal file
39
core/templates/admin/core/platformprofile/test_email.html
Normal file
@ -0,0 +1,39 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n admin_urls static admin_modify %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script src="{% url 'admin:jsi18n' %}"></script>
|
||||
{{ media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label='core' %}">Core</a>
|
||||
› <a href="{% url 'admin:core_platformprofile_changelist' %}">Platform Profiles</a>
|
||||
› Test Email Configuration
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
<fieldset class="module aligned">
|
||||
<h2>Test Email Configuration</h2>
|
||||
<div class="form-row">
|
||||
<label for="id_email" class="required">Target Email Address:</label>
|
||||
<input type="email" name="email" id="id_email" value="{{ email }}" required size="40">
|
||||
<div class="help">Enter the email address to receive the test message.</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="submit-row">
|
||||
<input type="submit" value="Send Test Email" class="default" name="_save">
|
||||
<a href="../" class="button closelink" style="margin-left: 10px;">Go Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -32,9 +32,11 @@
|
||||
</div>
|
||||
|
||||
{% if parcel.payment_status == 'pending' %}
|
||||
{% if platform_profile.enable_payment %}
|
||||
<a href="{% url 'initiate_payment' parcel.id %}" class="btn btn-sm btn-outline-primary w-100 mb-3">
|
||||
<i class="fas fa-credit-card me-1"></i> {% trans "Pay Now" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<hr>
|
||||
|
||||
@ -2,7 +2,7 @@ from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth import login, authenticate, logout
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from .models import Parcel, Profile, Country, Governate, City, OTPVerification
|
||||
from .models import Parcel, Profile, Country, Governate, City, OTPVerification, PlatformProfile
|
||||
from .forms import UserRegistrationForm, ParcelForm, ContactForm, UserProfileForm
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import get_language
|
||||
@ -125,6 +125,12 @@ def update_status(request, parcel_id):
|
||||
|
||||
@login_required
|
||||
def initiate_payment(request, parcel_id):
|
||||
# Check if payments are enabled
|
||||
platform_profile = PlatformProfile.objects.first()
|
||||
if platform_profile and not platform_profile.enable_payment:
|
||||
messages.error(request, _("Payments are currently disabled by the administrator."))
|
||||
return redirect('dashboard')
|
||||
|
||||
parcel = get_object_or_404(Parcel, id=parcel_id, shipper=request.user, payment_status='pending')
|
||||
|
||||
thawani = ThawaniPay()
|
||||
@ -319,4 +325,4 @@ def verify_otp_view(request):
|
||||
except OTPVerification.DoesNotExist:
|
||||
messages.error(request, _("Invalid code."))
|
||||
|
||||
return render(request, 'core/verify_otp.html')
|
||||
return render(request, 'core/verify_otp.html', {'form': form})
|
||||
@ -15,7 +15,7 @@ def get_whatsapp_credentials():
|
||||
api_token = settings.WHATSAPP_API_KEY if hasattr(settings, 'WHATSAPP_API_KEY') else ""
|
||||
# We repurpose Phone ID as Domain in settings if needed, or default to Wablas DEU
|
||||
domain = "https://deu.wablas.com"
|
||||
secret_key = "" # Add this to settings if you want env support, but for now mostly DB
|
||||
secret_key = ""
|
||||
source = "Settings/Env"
|
||||
|
||||
# Try to fetch from PlatformProfile
|
||||
@ -53,7 +53,7 @@ def send_whatsapp_message(phone_number, message):
|
||||
|
||||
def send_whatsapp_message_detailed(phone_number, message):
|
||||
"""
|
||||
Sends a WhatsApp message via Wablas V2 API and returns detailed status.
|
||||
Sends a WhatsApp message via Wablas API and returns detailed status.
|
||||
Returns tuple: (success: bool, response_msg: str)
|
||||
"""
|
||||
if not getattr(settings, 'WHATSAPP_ENABLED', True):
|
||||
@ -69,43 +69,57 @@ def send_whatsapp_message_detailed(phone_number, message):
|
||||
return False, msg
|
||||
|
||||
# Normalize phone number (Wablas expects international format without +, e.g. 628123...)
|
||||
# Remove all non-digits
|
||||
clean_phone = "".join(filter(str.isdigit, str(phone_number)))
|
||||
clean_phone = str(phone_number).replace('+', '').replace(' ', '')
|
||||
|
||||
# Construct Authorization Header
|
||||
# Wablas V2: Authorization: {$token}.{$secret_key}
|
||||
# Some Wablas servers just need Token, but docs say Token.Secret
|
||||
# Endpoint: /api/send-message (Simple Text)
|
||||
# Ensure domain has schema
|
||||
if not domain.startswith('http'):
|
||||
domain = f"https://{domain}"
|
||||
|
||||
# Using the exact endpoint provided in user example
|
||||
url = f"{domain}/api/send-message"
|
||||
|
||||
# Header construction logic from user example
|
||||
auth_header = api_token
|
||||
if secret_key:
|
||||
auth_header = f"{api_token}.{secret_key}"
|
||||
|
||||
# Endpoint V2
|
||||
url = f"{domain}/api/v2/send-message"
|
||||
|
||||
|
||||
headers = {
|
||||
"Authorization": auth_header,
|
||||
"Content-Type": "application/json",
|
||||
# requests will set Content-Type to application/x-www-form-urlencoded when using 'data' param
|
||||
}
|
||||
|
||||
payload = {
|
||||
"data": [
|
||||
{
|
||||
"phone": clean_phone,
|
||||
"message": message,
|
||||
"isGroup": "false",
|
||||
"flag": "instant" # Priority
|
||||
}
|
||||
]
|
||||
# Payload as form data (not JSON)
|
||||
data = {
|
||||
"phone": clean_phone,
|
||||
"message": message,
|
||||
}
|
||||
|
||||
# Note: User's example didn't add 'secret' to payload, only to header.
|
||||
# We will stick to user's example strictly.
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=payload, timeout=15)
|
||||
response_data = response.json()
|
||||
# Use data=data for form-urlencoded
|
||||
response = requests.post(url, headers=headers, data=data, timeout=15)
|
||||
|
||||
# Handle non-JSON response (HTML error pages)
|
||||
try:
|
||||
response_data = response.json()
|
||||
except ValueError:
|
||||
response_data = response.text
|
||||
|
||||
# Wablas success usually has status: true
|
||||
if response.status_code == 200 and response_data.get('status') is not False:
|
||||
logger.info(f"WhatsApp message sent to {clean_phone} via Wablas")
|
||||
return True, f"Message sent successfully via Wablas. (Source: {source})"
|
||||
if response.status_code == 200:
|
||||
# Check for logical success in JSON
|
||||
if isinstance(response_data, dict):
|
||||
if response_data.get('status') is True:
|
||||
logger.info(f"WhatsApp message sent to {clean_phone} via Wablas")
|
||||
return True, f"Message sent successfully via Wablas. (Source: {source})"
|
||||
else:
|
||||
return False, f"Wablas API Logic Error (Source: {source}): {response_data}"
|
||||
else:
|
||||
# If text, assume success if 200 OK? Or inspect text.
|
||||
return True, f"Message sent (Raw Response). (Source: {source})"
|
||||
else:
|
||||
error_msg = f"Wablas API error (Source: {source}): {response.status_code} - {response_data}"
|
||||
logger.error(error_msg)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user