- Bazaar verify: a rejected token is not stored on the order or the failed transaction, so the same purchase can still pay the right order; a refunded order refuses a new token (409). - OrderAdmin: every field read-only, no add/delete; Transaction admin read-only. - grant_pack uses the order's snapshot when the plan was deleted, logs ERROR when nothing is granted. - Pay link: authority is stored only if the order amount did not change during PaymentRequest. - PlanAdmin accepts only IRT (the app always renders toman). - reconcile_pending_order logs unexpected exceptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
33 lines
1.2 KiB
Python
33 lines
1.2 KiB
Python
from django import forms
|
|
from django.contrib import admin
|
|
|
|
from .models import Plan, Subscription
|
|
|
|
|
|
class PlanAdminForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Plan
|
|
fields = '__all__'
|
|
|
|
def clean_currency(self):
|
|
# فرانت هر قیمتی را «تومان» نشان میدهد (src/lib/format.ts) و currency بیتغییر به زرینپال میرود؛
|
|
# پلنی با «IRR» در اپ ۴۵٬۰۰۰ تومان دیده میشد و درگاه ۴۵٬۰۰۰ ریال (یکدهم) میگرفت (بازتولید شد).
|
|
value = self.cleaned_data['currency']
|
|
if value != 'IRT':
|
|
raise forms.ValidationError('فقط IRT (تومان)؛ اپ قیمت را تومان نشان میدهد.')
|
|
return value
|
|
|
|
|
|
@admin.register(Plan)
|
|
class PlanAdmin(admin.ModelAdmin):
|
|
form = PlanAdminForm
|
|
list_display = ('id', 'title', 'price', 'currency', 'duration_days', 'ai_daily_quota', 'discount_percent', 'popular',
|
|
'bazaar_sku')
|
|
list_editable = ('ai_daily_quota',)
|
|
|
|
|
|
@admin.register(Subscription)
|
|
class SubscriptionAdmin(admin.ModelAdmin):
|
|
list_display = ('id', 'user_id', 'plan', 'status', 'started_at', 'expires_at', 'order_id')
|
|
list_filter = ('status',)
|