Kavenegar is being switched on, so every OTP request now costs credit: - phone numbers are normalised to 09XXXXXXXXX everywhere (login, OTP, family invite, TEST_LOGIN_PHONES keys) and existing rows are migrated; non-Iranian and landline numbers are rejected before any SMS - cooldown and a daily per-phone cap are counted in the database, so the three gunicorn workers no longer triple them - a global daily OTP SMS cap (default 300) stops SMS pumping - per-IP rates widened for CGNAT / school Wi-Fi (env-overridable) - provider message id is logged; SMS text warns not to share the code Second audit round: an offer no longer shortens a longer paid plan, Zarinpal error bodies no longer 500, island XP reaches the parent report, a non-numeric questionId no longer 500s after the answer is generated. 596 tests green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
48 lines
2.4 KiB
Python
48 lines
2.4 KiB
Python
"""
|
||
پورسانتِ معرف: فقط روی پرداختِ واقعی، فقط یک بار برای هر کاربرِ معرفیشده.
|
||
|
||
تا ۲۰۲۶-۰۹-۲۵ هیچ کدی Conversion را از «pending» جلو نمیبرد و پورسانتی
|
||
حساب نمیشد؛ برنامهی معرفی فقط یک جدولِ ثبت بود. حالا اولین سفارشِ
|
||
پرداختشدهی کاربرِ معرفیشده *بعد از ثبتِ کد* Conversion را `paid` میکند و
|
||
`commission_amount = مبلغِ پرداختی × commission_rate` را ثبت میکند.
|
||
|
||
⚠️ «کاربرِ تازه» بررسی نمیشود: مشتریِ قدیمی هم هر وقت بخواهد کد ثبت میکند
|
||
و تمدیدِ بعدیاش پورسانت میسازد (بازبینیِ ۲۰۲۶-۰۹-۲۵، بازتولید شد). جلوی
|
||
معرفیِ خود با شمارهی دوم هم چیزی نیست.
|
||
پرداختِ خودِ پورسانت به معرف (تسویه) هنوز دستی و از پنلِ ادمین است.
|
||
|
||
ثبتنام یا ورودِ مهمان هیچ پورسانتی نمیسازد؛ فقط پول جابهجا شدنِ واقعی.
|
||
"""
|
||
import logging
|
||
|
||
from django.db import transaction
|
||
|
||
from .models import Conversion
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def record_paid_order(order) -> Conversion | None:
|
||
"""از apply_order_payment_result صدا زده میشود، داخلِ همان تراکنش."""
|
||
if not order.amount:
|
||
return None
|
||
try:
|
||
with transaction.atomic():
|
||
conversion = (
|
||
Conversion.objects.select_for_update()
|
||
.select_related('affiliate_account')
|
||
.filter(referred_user_id=order.user_id, status='pending')
|
||
.first()
|
||
)
|
||
if conversion is None:
|
||
return None
|
||
rate = conversion.affiliate_account.commission_rate or 0
|
||
conversion.status = 'paid'
|
||
conversion.order_id = order.id
|
||
conversion.commission_amount = int(order.amount * rate)
|
||
conversion.save(update_fields=['status', 'order_id', 'commission_amount'])
|
||
return conversion
|
||
except Exception: # noqa: BLE001 — پورسانت نباید تاییدِ پرداختِ خودِ کاربر را خراب کند
|
||
logger.exception('affiliate: ثبتِ پورسانتِ سفارش %s شکست خورد', order.id)
|
||
return None
|