added paddle
This commit is contained in:
@@ -35,7 +35,7 @@ class EmailTests(TestCase):
|
||||
)
|
||||
self.document = Document.objects.create(organization=self.organization)
|
||||
|
||||
@patch("backend.accounts.utils.send_mail")
|
||||
@patch('backend.accounts.utils.EmailMultiAlternatives')
|
||||
def test_send_confirmation_email(self, mock_send_mail):
|
||||
confirmation = EmailConfirmation.objects.create(email=self.email, uuid=uuid.uuid4(), created_at=now())
|
||||
send_confirmation_email(self.email)
|
||||
@@ -44,7 +44,7 @@ class EmailTests(TestCase):
|
||||
self.assertIsNotNone(confirmation.uuid)
|
||||
self.assertEqual(mock_send_mail.call_count, 1)
|
||||
|
||||
@patch("backend.accounts.utils.send_mail")
|
||||
@patch('backend.accounts.utils.EmailMultiAlternatives')
|
||||
def test_send_payment_email(self, mock_send_mail):
|
||||
send_payment_email(self.email)
|
||||
self.assertEqual(mock_send_mail.call_count, 1)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import Document, DocumentSegment, Organization, Risk, Control, DocumentTemplate, DocumentRiskControl, DemoCode
|
||||
from .models import Document, DocumentSegment, Organization, Risk, Control, DocumentTemplate, DocumentRiskControl, DemoCode, PaymentRecord
|
||||
from django.urls import reverse, path
|
||||
from django.utils.html import format_html
|
||||
from .utils import generate_demo_code, get_top_risk, get_controls_for_risk, generate_key_findings, generate_recommendations
|
||||
@@ -460,7 +460,13 @@ class DemoCodeAdmin(admin.ModelAdmin):
|
||||
form = GenerateCodesForm()
|
||||
return render(request, 'admin/generate_codes.html', {'form': form})
|
||||
|
||||
class PaymentRecordAdmin(admin.ModelAdmin):
|
||||
list_display = ('company', 'amount', 'currency', 'payment_date', 'transaction_id')
|
||||
search_fields = ('company__name', 'transaction_id')
|
||||
list_filter = ('payment_date',)
|
||||
|
||||
|
||||
admin.site.register(PaymentRecord, PaymentRecordAdmin)
|
||||
admin.site.register(Document, DocumentAdmin)
|
||||
admin.site.register(Organization, OrganizationAdmin)
|
||||
admin.site.register(Risk ,RiskAdmin)
|
||||
|
||||
25
backend/core/migrations/0029_paymentrecord.py
Normal file
25
backend/core/migrations/0029_paymentrecord.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 5.1.3 on 2025-10-20 13:14
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0028_document_risk_explanations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PaymentRecord',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=10)),
|
||||
('currency', models.CharField(max_length=10)),
|
||||
('payment_date', models.DateTimeField(auto_now_add=True)),
|
||||
('transaction_id', models.CharField(max_length=255, unique=True)),
|
||||
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.organization')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -200,3 +200,14 @@ class DemoCode(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return (f"{self.code} - {'Used' if self.used else 'Available'}")
|
||||
|
||||
class PaymentRecord(models.Model):
|
||||
company = models.ForeignKey(Organization, on_delete=models.CASCADE)
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
currency = models.CharField(max_length=10)
|
||||
payment_date = models.DateTimeField(auto_now_add=True)
|
||||
transaction_id = models.CharField(max_length=255, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.company} - {self.amount} {self.currency}"
|
||||
|
||||
|
||||
@@ -1,41 +1,60 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
<section class="py-24 bg-gradient-to-br from-teal-100 to-primary flex items-center justify-center">
|
||||
<div class="max-w-md w-full mx-auto text-center shadow-2xl border border-accent rounded-2xl p-4 bg-white/90 backdrop-blur">
|
||||
<h2 class="text-3xl font-extrabold mb-6 text-accent">Payment</h2>
|
||||
<p class="text-lg text-gray-700 mb-6">Please enter your demo code to proceed to document.</p>
|
||||
<div class="max-w-md w-full mx-auto shadow-2xl border border-accent rounded-2xl p-4 bg-white/90 backdrop-blur">
|
||||
<h2 class="text-3xl font-extrabold mb-6 text-accent text-center">Payment</h2>
|
||||
|
||||
{% if success %}
|
||||
<p class="text-green-600 font-semibold mb-4">{{ success }}</p>
|
||||
{% endif %}
|
||||
<form method="post" class="space-y-6">
|
||||
{% csrf_token %}
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
id="code-input"
|
||||
name="code"
|
||||
maxlength="10"
|
||||
class="w-full px-4 py-3 border-2 border-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-accent text-lg tracking-widest text-center font-mono mb-2"
|
||||
placeholder="Enter your code"
|
||||
required
|
||||
autocomplete="off"
|
||||
<!-- Tabs -->
|
||||
<div class="flex justify-center mb-6 border-b border-accent">
|
||||
<button id="tab-demo" class="px-4 py-2 text-accent font-semibold border-b-2 border-accent">Demo Code</button>
|
||||
<button id="tab-paddle" class="px-4 py-2 text-gray-500 hover:text-accent font-semibold">Paddle</button>
|
||||
</div>
|
||||
|
||||
<!-- Demo Code Tab -->
|
||||
<div id="demo-tab-content">
|
||||
<p class="text-lg text-gray-700 mb-4">Enter your demo code to access the document:</p>
|
||||
<form method="post" class="space-y-6">
|
||||
{% csrf_token %}
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
id="code-input"
|
||||
name="code"
|
||||
maxlength="10"
|
||||
class="w-full px-4 py-3 border-2 border-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-accent text-lg tracking-widest text-center font-mono mb-2"
|
||||
placeholder="Enter your code"
|
||||
required
|
||||
autocomplete="off"
|
||||
>
|
||||
<span id="code-status" class="absolute right-3 top-1/2 -translate-y-1/2 text-2xl"></span>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full bg-accent text-primary hover:bg-yellow-400 font-bold py-3 px-8 rounded-lg shadow-lg text-lg transition-all duration-200 ease-in-out transform hover:scale-105"
|
||||
id="submit-btn"
|
||||
disabled
|
||||
>
|
||||
<span id="code-status" class="absolute right-3 top-1/2 -translate-y-1/2 text-2xl"></span>
|
||||
</div>
|
||||
Enter Code
|
||||
</button>
|
||||
<p id="code-error" class="mt-2 font-semibold text-lg"></p>
|
||||
{% if error %}
|
||||
<p id="backend-error" class="text-red-600 mt-2 font-semibold text-lg">{{ error }}</p>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Paddle Tab -->
|
||||
<div id="paddle-tab-content" class="hidden text-center">
|
||||
<p class="text-lg text-gray-700 mb-4">Pay with Paddle to access the document:</p>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full bg-accent text-primary hover:bg-yellow-400 font-bold py-3 px-8 rounded-lg shadow-lg text-lg transition-all duration-200 ease-in-out transform hover:scale-105"
|
||||
id="submit-btn"
|
||||
disabled
|
||||
id="paddle-button"
|
||||
class="mt-4 bg-green-600 text-white font-bold py-3 px-8 rounded-lg shadow-lg text-lg transition-all duration-200 ease-in-out transform hover:scale-105"
|
||||
data-client="{{ pdl_client }}"
|
||||
data-price="{{ pdl_price }}"
|
||||
>
|
||||
Enter Code
|
||||
Buy Now
|
||||
</button>
|
||||
</form>
|
||||
<p id="code-error" class="mt-6 font-semibold text-lg"></p>
|
||||
{% if error %}
|
||||
<p id="backend-error" class="text-red-600 mt-6 font-semibold text-lg">{{ error }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
@@ -101,4 +120,76 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
const demoTabBtn = document.getElementById('tab-demo');
|
||||
const paddleTabBtn = document.getElementById('tab-paddle');
|
||||
const demoContent = document.getElementById('demo-tab-content');
|
||||
const paddleContent = document.getElementById('paddle-tab-content');
|
||||
|
||||
demoTabBtn.addEventListener('click', () => {
|
||||
demoContent.classList.remove('hidden');
|
||||
paddleContent.classList.add('hidden');
|
||||
demoTabBtn.classList.add('border-b-2', 'border-accent', 'text-accent');
|
||||
paddleTabBtn.classList.remove('border-b-2', 'border-accent', 'text-accent');
|
||||
paddleTabBtn.classList.add('text-gray-500');
|
||||
});
|
||||
|
||||
paddleTabBtn.addEventListener('click', () => {
|
||||
paddleContent.classList.remove('hidden');
|
||||
demoContent.classList.add('hidden');
|
||||
paddleTabBtn.classList.add('border-b-2', 'border-accent', 'text-accent');
|
||||
demoTabBtn.classList.remove('border-b-2', 'border-accent', 'text-accent');
|
||||
demoTabBtn.classList.add('text-gray-500');
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdn.paddle.com/paddle/v2/paddle.js"></script>
|
||||
<script>
|
||||
const paddleBtn = document.getElementById('paddle-button');
|
||||
const email = "{{ email|escapejs }}";
|
||||
const client = paddleBtn.dataset.client;
|
||||
const price = paddleBtn.dataset.price;
|
||||
|
||||
Paddle.Environment.set('sandbox');
|
||||
Paddle.Initialize({
|
||||
token: client,
|
||||
eventCallback: function(event) {
|
||||
if (event.name === 'checkout.completed') {
|
||||
handlePaymentSuccess(event.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
paddleBtn.addEventListener('click', function() {
|
||||
Paddle.Checkout.open({
|
||||
customer: { email: email },
|
||||
items: [{ priceId: price, quantity: 1 }]
|
||||
});
|
||||
});
|
||||
|
||||
function handlePaymentSuccess(data) {
|
||||
const payload = {
|
||||
transaction_id: data.transaction_id,
|
||||
customer_email: data.customer.email,
|
||||
amount: data.totals.total,
|
||||
currency: data.currency_code
|
||||
};
|
||||
|
||||
fetch("{% url 'core:payment_page' %}", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': '{{ csrf_token }}',
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(result => {
|
||||
if (result.redirect_url) window.location.href = result.redirect_url;
|
||||
})
|
||||
.catch(error => console.error('Fetch error:', error));
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -11,11 +11,11 @@ urlpatterns = [
|
||||
# url document/ recieves a parameter named 'uuid' and passes it to the view
|
||||
path('document/<uuid:document_id>/', v.document, name='document'),
|
||||
path('preview/<str:name>/', v.template_preview, name='template_preview'),
|
||||
path("payment/", v.payment_page, name="payment_page"),
|
||||
path("payment/", v.PaymentView.as_view(), name="payment_page"),
|
||||
path('pdf/<uuid:document_id>/', v.pdf_view, name='pdf_view'),
|
||||
path('api/validate_form_fields/', v.validate_form_fields, name='validate_form_fields'),
|
||||
path('no_confidential_data/', v.no_confidential_data, name='no_confidential_data'),
|
||||
path('validate_code/', v.validate_code, name='validate_code'),
|
||||
path('validate_code/', v.PaymentView.validate_code, name='validate_code'),
|
||||
path('terms-and-conditions/', v.terms_and_conditions, name='terms_and_conditions'),
|
||||
path('refund-policy/', v.refund_policy, name='refund_policy'),
|
||||
path('privacy-policy/', v.privacy_policy, name='privacy_policy'),
|
||||
|
||||
@@ -6,7 +6,7 @@ import time
|
||||
|
||||
from django.shortcuts import render, redirect , get_object_or_404
|
||||
from .forms import OrganizationForm, ContactForm
|
||||
from .models import Organization,Document, DocumentTemplate, DemoCode, DocumentRiskControl, Risk, Control
|
||||
from .models import Organization,Document, DocumentTemplate, DemoCode, PaymentRecord, DocumentRiskControl, Risk, Control
|
||||
from backend.accounts.utils import send_confirmation_email, send_document_email, send_documet_to_expert, send_document_to_reviewer
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from .utils import generate_pdf, generate_risk_graph, generate_residual_risk_graph
|
||||
@@ -22,7 +22,11 @@ from django.template.loader import render_to_string
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from backend.accounts.models import ExpertAnalysisEmails
|
||||
from django.core.mail import send_mail
|
||||
|
||||
from django.views import View
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from django.urls import reverse
|
||||
from urllib.parse import urlencode
|
||||
|
||||
# @login_required
|
||||
# def index(request):
|
||||
@@ -118,57 +122,141 @@ def pdf_view(request, document_id):
|
||||
document = get_object_or_404(Document, id=document_id)
|
||||
return generate_pdf(document)
|
||||
|
||||
def payment_page(request):
|
||||
error = None
|
||||
email = request.GET.get('email')
|
||||
if request.method == 'POST':
|
||||
import re
|
||||
class PaymentView(View):
|
||||
|
||||
template_name = 'payment.html'
|
||||
|
||||
def get(self,request):
|
||||
email = request.GET.get('email','').strip()
|
||||
status_view = request.GET.get('status')
|
||||
|
||||
pdl_client = settings.PADDLE_CLIENT_ID
|
||||
pdl_price = settings.PADDLE_PRICE_ID
|
||||
|
||||
if status_view == 'review':
|
||||
document = Document.objects.filter(organization__email__iexact=email).first()
|
||||
return render(request, 'payment_review.html', {'email': email, 'document': document})
|
||||
|
||||
if status_view == 'expert':
|
||||
document = Document.objects.filter(organization__email__iexact=email).first()
|
||||
return render(request, 'payment_expert_analysis.html', {'email': email, 'document': document})
|
||||
|
||||
return render(request, self.template_name, {
|
||||
'email': email,
|
||||
'pdl_client': pdl_client,
|
||||
'pdl_price': pdl_price})
|
||||
|
||||
def post(self, request):
|
||||
content_type = request.META.get('CONTENT_TYPE', '')
|
||||
if 'application/json' in content_type:
|
||||
try:
|
||||
payload = json.loads(request.body)
|
||||
return self._handle_paddle_payment(payload)
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({'status': 'error', 'message': 'Invalid JSON'}, status=400)
|
||||
else:
|
||||
return self._handle_demo_code(request)
|
||||
|
||||
|
||||
def _handle_demo_code(self, request):
|
||||
email = request.GET.get('email', '').strip()
|
||||
error = None
|
||||
code = re.sub(r'\s+', '', request.POST.get('code', '')).upper()[:10]
|
||||
try:
|
||||
payment_code = DemoCode.objects.get(code=code)
|
||||
if payment_code.used:
|
||||
error = "CODE INVALID"
|
||||
else:
|
||||
org = Organization.objects.filter(email__iexact=email).first()
|
||||
payment_code.used = True
|
||||
payment_code.used_at = timezone.now()
|
||||
payment_code.company = org
|
||||
payment_code.save()
|
||||
document = Document.objects.get(organization = org)
|
||||
url = f"{site_domain}/pdf/{document.id}/"
|
||||
expert_emails_qs = ExpertAnalysisEmails.objects.values_list('email', flat=True).distinct()
|
||||
error = "Invalid demo code"
|
||||
return render(request, self.template_name, {'email': email, 'error': error})
|
||||
|
||||
# If document is incomplete, notify reviewers
|
||||
if document.status == Document.STATUS_INCOMPLETE:
|
||||
if expert_emails_qs:
|
||||
for email_addr in expert_emails_qs:
|
||||
try:
|
||||
send_document_to_reviewer(email_addr, document)
|
||||
except Exception:
|
||||
logger.exception("Failed to send incomplete document email to %s", email_addr)
|
||||
return render(request, 'payment_review.html', {'email': email, 'document': document})
|
||||
org = Organization.objects.filter(email__iexact=email).first()
|
||||
payment_code.used = True
|
||||
payment_code.used_at = timezone.now()
|
||||
payment_code.company = org
|
||||
payment_code.save()
|
||||
|
||||
print(org, email)
|
||||
document = Document.objects.get(organization=org)
|
||||
return self._handle_document_flow(org, document, email)
|
||||
|
||||
# If organization requested expert analysis, mark waiting and notify experts
|
||||
if org.expert_analysis:
|
||||
document.status = Document.STATUS_WAITING
|
||||
document.save(update_fields=['status', 'modified_at'])
|
||||
expert_emails = [e for e in expert_emails_qs if e]
|
||||
if expert_emails:
|
||||
for email_addr in expert_emails:
|
||||
try:
|
||||
send_documet_to_expert(email_addr, document)
|
||||
except Exception:
|
||||
logger.exception("Failed to send expert analysis email to %s", email_addr)
|
||||
else:
|
||||
logger.info("No expert emails configured; skipping expert notifications.")
|
||||
return render(request, 'payment_expert_analysis.html', {'email': email, 'document': document})
|
||||
send_document_email(email, url, document)
|
||||
document.status = Document.STATUS_DONE
|
||||
document.save(update_fields=['status', 'modified_at'])
|
||||
return redirect(url)
|
||||
except DemoCode.DoesNotExist:
|
||||
error = "❌ Invalid code"
|
||||
return render(request, 'payment.html', {'error': error})
|
||||
error = "Invalid demo code"
|
||||
return render(request, self.template_name, {'email': email, 'error': error})
|
||||
|
||||
def _handle_paddle_payment(self, data):
|
||||
try:
|
||||
customer_email = data.get('customer_email')
|
||||
transaction_id = data.get('transaction_id')
|
||||
amount = Decimal(data.get('amount', 0))
|
||||
currency = data.get('currency', 'EUR')
|
||||
|
||||
org = Organization.objects.filter(email__iexact=customer_email).first()
|
||||
if not org:
|
||||
return JsonResponse({'status': 'error', 'message': 'Organization not found'}, status=404)
|
||||
|
||||
PaymentRecord.objects.create(
|
||||
company=org,
|
||||
amount=amount,
|
||||
currency=currency,
|
||||
transaction_id=transaction_id,
|
||||
payment_date=timezone.now()
|
||||
)
|
||||
document = Document.objects.get(organization=org)
|
||||
return self._handle_document_flow(org, document, customer_email, json_response=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Error saving Paddle payment: %s", e)
|
||||
return JsonResponse({'status': 'error', 'message': str(e)}, status=500)
|
||||
|
||||
def validate_code(request):
|
||||
if request.method == "POST":
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
code = data.get("code", "").strip().upper()
|
||||
valid = DemoCode.objects.filter(code=code, used=False).exists()
|
||||
time.sleep(1)
|
||||
return JsonResponse({"valid": valid})
|
||||
except Exception:
|
||||
return JsonResponse({"valid": False})
|
||||
return JsonResponse({"valid": False})
|
||||
|
||||
def _handle_document_flow(self, org, document, email, json_response=False):
|
||||
site_domain = settings.SITE_DOMAIN
|
||||
url = f"{site_domain}/pdf/{document.id}/"
|
||||
expert_emails_qs = ExpertAnalysisEmails.objects.values_list('email', flat=True).distinct()
|
||||
|
||||
if document.status == Document.STATUS_INCOMPLETE:
|
||||
review_url = f"{reverse('core:payment_page')}?{urlencode({'email': email, 'status': 'review'})}"
|
||||
for email_addr in expert_emails_qs:
|
||||
try:
|
||||
send_document_to_reviewer(email_addr, document)
|
||||
except Exception:
|
||||
logger.exception("Failed to send incomplete document email to %s", email_addr)
|
||||
if json_response:
|
||||
return JsonResponse({'status': 'review', 'redirect_url': review_url})
|
||||
return render(self.request, 'payment_review.html', {'email': email, 'document': document})
|
||||
|
||||
if org.expert_analysis:
|
||||
document.status = Document.STATUS_WAITING
|
||||
document.save(update_fields=['status', 'modified_at'])
|
||||
expert_url = f"{reverse('core:payment_page')}?{urlencode({'email': email, 'status': 'expert'})}"
|
||||
expert_emails = [e for e in expert_emails_qs if e]
|
||||
for email_addr in expert_emails:
|
||||
try:
|
||||
send_documet_to_expert(email_addr, document)
|
||||
except Exception:
|
||||
logger.exception("Failed to send expert analysis email to %s", email_addr)
|
||||
if json_response:
|
||||
return JsonResponse({'status': 'expert', 'redirect_url': expert_url})
|
||||
return render(self.request, 'payment_expert_analysis.html', {'email': email, 'document': document})
|
||||
|
||||
send_document_email(email, url, document)
|
||||
document.status = Document.STATUS_DONE
|
||||
document.save(update_fields=['status', 'modified_at'])
|
||||
|
||||
if json_response:
|
||||
return JsonResponse({'status': 'ok', 'redirect_url': url})
|
||||
return redirect(url)
|
||||
|
||||
|
||||
def no_confidential_data(request):
|
||||
return render(request, "no_confidential_data.html")
|
||||
@@ -217,16 +305,3 @@ def demo_codes_pdf_view(request):
|
||||
response['Content-Disposition'] = f'inline; filename=demo_codes_{timezone.now().strftime("%Y%m%d_%H%M%S")}.pdf'
|
||||
return response
|
||||
|
||||
@csrf_exempt
|
||||
def validate_code(request):
|
||||
if request.method == "POST":
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
code = data.get("code", "").strip().upper()
|
||||
from .models import DemoCode
|
||||
valid = DemoCode.objects.filter(code=code, used=False).exists()
|
||||
time.sleep(3)
|
||||
return JsonResponse({"valid": valid})
|
||||
except Exception:
|
||||
return JsonResponse({"valid": False})
|
||||
return JsonResponse({"valid": False})
|
||||
@@ -22,6 +22,10 @@ load_dotenv()
|
||||
#API key
|
||||
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
||||
|
||||
# Paddle Keys
|
||||
PADDLE_CLIENT_ID = config('PADDLE_CLIENT_ID','')
|
||||
PADDLE_PRICE_ID = config('PADDLE_PRICE_ID','')
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
@@ -172,6 +176,7 @@ CELERY_ACCEPT_CONTENT = ['json']
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_TIMEZONE = 'UTC'
|
||||
|
||||
|
||||
SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False, cast=bool)
|
||||
SESSION_COOKIE_SECURE = config('SESSION_COOKIE_SECURE', default=False, cast=bool)
|
||||
CSRF_COOKIE_SECURE = config('CSRF_COOKIE_SECURE', default=False, cast=bool)
|
||||
|
||||
Reference in New Issue
Block a user