Question packs removed at the root; all subscriptions 4 assistant questions a day; Bazaar is subscription-only
- subscription.0017/0018, payment.0013: pack plans deleted, QuestionCredit/HeartsPass models and Plan.kind / question_credits / hearts_days / Order.question_credits / hearts_days dropped. The only paid pack order was the company owner's test account (user 26). - Assistant: no credit spending; quota is the daily cap only (free 2, every plan 4, gift subscription 4). - Bazaar: verify_purchase is subscription-only (no inapp guess), start/purchase responses lose productType, verify refuses an order without a subscription. A paid order with no subscription now logs ERROR. - Removed POST /api/payment/orders (internal; no caller). Its permission tests stay. - Pack-based tests rewritten on subscriptions (refund, security, Bazaar token); pack-only test files deleted. - docs: profit table for 4/day on both payment routes plus free users; handbook, operations, CLAUDE.md current. 949 tests green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -110,7 +110,7 @@ class BazaarVerifyPurchaseFailClosedTests(TestCase):
|
||||
}
|
||||
mock_get.return_value.status_code = 200
|
||||
mock_get.return_value.json.return_value = {
|
||||
'purchaseState': 0, 'consumptionState': 0, 'developerPayload': '',
|
||||
'validUntilTimestampMsec': (time.time() + 86400) * 1000, 'autoRenewing': True,
|
||||
}
|
||||
|
||||
result = bazaar.verify_purchase(product_sku='sku1', purchase_token='real-token')
|
||||
@@ -135,10 +135,9 @@ class BazaarVerifyPurchaseFailClosedTests(TestCase):
|
||||
'access_token': 'real-access-token', 'expires_in': 3600,
|
||||
}
|
||||
mock_get.return_value.status_code = 200
|
||||
# purchaseState=1 یعنی لغوشده (طبق سند بازار)، یعنی خودِ سرور بازار
|
||||
# میگه این خرید معتبر نیست.
|
||||
# اشتراکِ منقضیشده: بازار پاسخ میدهد ولی validUntil گذشته است.
|
||||
mock_get.return_value.json.return_value = {
|
||||
'purchaseState': 1, 'consumptionState': 0, 'developerPayload': '',
|
||||
'validUntilTimestampMsec': (time.time() - 60) * 1000, 'autoRenewing': False,
|
||||
}
|
||||
|
||||
result = bazaar.verify_purchase(product_sku='sku1', purchase_token='cancelled-token')
|
||||
@@ -166,7 +165,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
def _make_order(self, key):
|
||||
return Order.objects.create(
|
||||
user_id=self.user.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key=key,
|
||||
subscription_id=1, bazaar_product_sku='sku_one_time', idempotency_key=key,
|
||||
)
|
||||
|
||||
def _verify(self, order_id, purchase_token, token=None, remote_addr=None):
|
||||
@@ -189,7 +188,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
def test_requests_within_the_limit_still_succeed(self):
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '3/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True)
|
||||
for i in range(3):
|
||||
order = self._make_order(f'order-bazaar-within-{i}')
|
||||
response = self._verify(order.id, f'token-within-{i}', token=self.token)
|
||||
@@ -198,7 +197,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
def test_excessive_verify_attempts_from_same_user_are_blocked(self):
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '3/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True)
|
||||
for i in range(3):
|
||||
order = self._make_order(f'order-bazaar-block-{i}')
|
||||
response = self._verify(order.id, f'token-block-{i}', token=self.token)
|
||||
@@ -216,11 +215,11 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '3/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True)
|
||||
for i in range(3):
|
||||
order = Order.objects.create(
|
||||
user_id=registered_user.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key=f'order-bazaar-ip-{i}',
|
||||
subscription_id=1, bazaar_product_sku='sku_one_time', idempotency_key=f'order-bazaar-ip-{i}',
|
||||
)
|
||||
response = self._verify(
|
||||
order.id, f'token-ip-{i}', token=registered_token, remote_addr=f'10.4.0.{i + 1}',
|
||||
@@ -229,7 +228,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
|
||||
extra_order = Order.objects.create(
|
||||
user_id=registered_user.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-ip-extra',
|
||||
subscription_id=1, bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-ip-extra',
|
||||
)
|
||||
blocked_response = self._verify(
|
||||
extra_order.id, 'token-ip-extra', token=registered_token, remote_addr='10.4.0.99',
|
||||
@@ -239,7 +238,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
def test_throttled_request_does_not_call_bazaar_or_mutate_order(self):
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '1/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True)
|
||||
first_order = self._make_order('order-bazaar-mutate-1')
|
||||
first_response = self._verify(first_order.id, 'token-mutate-1', token=self.token)
|
||||
self.assertEqual(first_response.status_code, 200)
|
||||
@@ -276,12 +275,12 @@ class BazaarVerifyPurchasePerUserThrottleTests(TestCase):
|
||||
def test_registered_users_keep_independent_per_user_buckets(self):
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '1/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True)
|
||||
|
||||
user_a = User.objects.create(name='', phone='+989120000761')
|
||||
order_a = Order.objects.create(
|
||||
user_id=user_a.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-reg-a',
|
||||
subscription_id=1, bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-reg-a',
|
||||
)
|
||||
response_a = self._verify(order_a.id, 'token-reg-a', issue_access_token(user_a.id))
|
||||
self.assertEqual(response_a.status_code, 200)
|
||||
@@ -289,7 +288,7 @@ class BazaarVerifyPurchasePerUserThrottleTests(TestCase):
|
||||
user_b = User.objects.create(name='', phone='+989120000762')
|
||||
order_b = Order.objects.create(
|
||||
user_id=user_b.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-reg-b',
|
||||
subscription_id=1, bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-reg-b',
|
||||
)
|
||||
response_b = self._verify(order_b.id, 'token-reg-b', issue_access_token(user_b.id))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user