56 lines
1.7 KiB
Python
56 lines
1.7 KiB
Python
"""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)
|