"""Razorpay integration with a mock fallback for local dev. When RAZORPAY_KEY_ID/SECRET are unset (or MOCK), create_order returns a fake order and verify_signature always succeeds, so the whole billing flow can be exercised end-to-end without real keys. """ import hashlib import hmac import secrets from typing import Optional from app.core.config import settings def is_live() -> bool: return settings.razorpay_configured def create_order(amount_paise: int, receipt: str, notes: Optional[dict] = None) -> dict: if not is_live(): return { "id": f"order_mock_{secrets.token_hex(8)}", "amount": amount_paise, "currency": "INR", "receipt": receipt, "status": "created", "mock": True, } import razorpay client = razorpay.Client(auth=(settings.razorpay_key_id, settings.razorpay_key_secret)) return client.order.create({ "amount": amount_paise, "currency": "INR", "receipt": receipt, "notes": notes or {}, }) def verify_payment_signature(order_id: str, payment_id: str, signature: str) -> bool: if not is_live(): return True # mock: accept any payment body = f"{order_id}|{payment_id}" expected = hmac.new( settings.razorpay_key_secret.encode(), body.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature) def verify_webhook_signature(payload: bytes, signature: str) -> bool: if not settings.razorpay_webhook_secret: return not is_live() # accept in mock mode only expected = hmac.new( settings.razorpay_webhook_secret.encode(), payload, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature)