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 |