Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 5,590 Bytes
335a9ba | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | # Razorpay Payments Setup (India + International)
LarShield pricing uses **Razorpay** for one-time plan purchases:
- **INR** checkout for Indian customers (UPI, cards, netbanking, wallets)
- **USD** checkout for international customers (international cards)
- A **platform fee** is added on top of the plan price at checkout
## 1. What the customer pays
| Line item | Calculation |
|---|---|
| Service price | Plan price from `subscription_tiers` |
| Platform fee | `PLATFORM_FEE_PERCENT` (default **2%**) of the service price |
| GST | `PLATFORM_FEE_GST_PERCENT` (default **18%**) of the platform fee, **INR only** |
| Total | Service + platform fee + GST |
Example (Quick Scan, INR):
```
Service price Rs.499.00
Platform fee (2%) Rs. 9.98
GST (18%) Rs. 1.80
Total payable Rs.510.78
```
USD checkouts add the 2% platform fee only (no Indian GST).
All amounts are recalculated server-side in minor units (paise/cents).
The browser never sends an amount.
## 2. Create the Razorpay account
1. Sign up at <https://dashboard.razorpay.com/signup>.
2. Complete KYC (business PAN, bank account) to move from Test to Live.
3. **Enable international payments**: Dashboard -> Account & Settings -> Payment methods -> International payments -> Request activation.
4. **Enable UPI / wallets / netbanking** under Payment methods if they are not already on.
## 3. Get the API keys
Dashboard -> Account & Settings -> API Keys -> Generate Key.
You get:
```
Key ID rzp_test_xxxxxxxxxxxx (or rzp_live_...)
Key Secret xxxxxxxxxxxxxxxxxxxxxxxx
```
Store the **Key ID** and **Key Secret** only on the server (HF Secrets). The Key ID is sent to the browser as part of the order response; the Secret is never exposed.
## 4. Configure the webhook
Dashboard -> Account & Settings -> Webhooks -> Add New Webhook.
- **URL**: `https://<your-space>.hf.space/api/billing/webhook`
- **Secret**: choose a strong random string, save it as `RAZORPAY_WEBHOOK_SECRET`
- **Active events**:
- `payment.captured`
- `order.paid`
- `payment.failed`
The webhook is the safety net: if the customer closes the tab after paying, the webhook still activates the plan. Fulfilment is idempotent (same payment is never credited twice).
## 5. Environment variables
Set these in **Hugging Face Space -> Settings -> Secrets** (or in `.env` locally):
```env
RAZORPAY_KEY_ID=rzp_test_xxxxxxxxxxxx
RAZORPAY_KEY_SECRET=xxxxxxxxxxxxxxxxxxxxxxxx
RAZORPAY_WEBHOOK_SECRET=whsec_your_random_secret
PLATFORM_FEE_PERCENT=2
PLATFORM_FEE_GST_PERCENT=18
USD_TO_INR_RATE=88
```
Then **Factory Rebuild** the Space.
## 6. How the flow works
```
Pricing card "Get Started"
|
v
POST /api/billing/quote -> service + fee + GST + total (displayed in modal)
|
v
POST /api/billing/create-order -> Razorpay order created, pending Payment row saved
|
v
Razorpay Checkout (UPI / card / netbanking / wallet / international card)
|
v
handler -> POST /api/billing/verify-payment
- HMAC SHA256 signature check
- amount + currency + order id confirmed with Razorpay
- organization tier upgraded
- +3 scan credits added
- audit log written
|
v
Success modal and page reload
Fallback: Razorpay webhook (payment.captured / order.paid)
performs the same activation if the browser never returns.
```
## 7. Test cards
Razorpay test mode:
| Method | Details |
|---|---|
| Card (success) | `4111 1111 1111 1111`, any future expiry, CVV `123` |
| Card (failure) | `5104 0600 0000 0008` |
| UPI (success) | `success@razorpay` |
| UPI (failure) | `failure@razorpay` |
Test mode does not move real money.
## 8. What gets written to the database
`payments` row:
| Column | Meaning |
|---|---|
| `amount` | Total charged (service + fee + GST), minor units |
| `base_amount` | Service price, minor units |
| `platform_fee` | Platform fee, minor units |
| `tax_amount` | GST, minor units |
| `currency` | `INR` or `USD` |
| `razorpay_order_id` | Razorpay order id |
| `razorpay_payment_id` | Razorpay payment id (unique - idempotency) |
| `status` | `created` -> `successful` / `failed` |
| `tier_id` | `quick` / `advanced` / `deep` |
On success the app also:
- sets `organizations.subscription_tier` to the purchased tier
- adds `+3` to `organization_scan_quotas` for that scan type
- writes an `audit_logs` entry
## 9. Admin controls
Super Admin -> Dashboard -> **Dynamic Billing Control** edits USD and INR prices.
The API is `PUT /api/billing/tiers/<tier_id>` and accepts:
```json
{
"monthly_price": 499,
"yearly_price": 4990,
"monthly_price_inr": 49900,
"yearly_price_inr": 499000
}
```
All values are minor units.
## 10. Troubleshooting
| Symptom | Cause / fix |
|---|---|
| `Payment gateway is not configured yet` | `RAZORPAY_KEY_ID` / `RAZORPAY_KEY_SECRET` missing in Space Secrets, or Space not rebuilt |
| `Could not start the payment` | Invalid keys, or the Razorpay account is not activated for that currency |
| International cards fail | International payments not enabled in Razorpay Dashboard |
| Payment succeeds but plan not upgraded | Webhook URL/secret wrong, or `verify-payment` never reached. Open Razorpay Dashboard -> Payments, copy `pay_...` id, then contact support; the audit log will show whether fulfilment ran |
| `Invalid payment signature` | Wrong `RAZORPAY_KEY_SECRET` for the key id used to create the order |
| Old INR price shown | Update the tier in Super Admin -> Dynamic Billing Control |
|