chub-dev 0.1.0 → 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1330 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: payments
|
|
3
|
+
description: "Razorpay Python SDK coding guidelines for building payment systems with orders, payments, refunds, and subscriptions"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "python"
|
|
6
|
+
versions: "1.4.2"
|
|
7
|
+
updated-on: "2026-03-02"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "razorpay,payments,india,checkout,upi"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Razorpay Python Coding Guidelines
|
|
13
|
+
|
|
14
|
+
You are a Razorpay payment gateway coding expert. Help me with writing code using the Razorpay Python SDK for building payment systems with orders, payments, refunds, subscriptions, customers, and invoices.
|
|
15
|
+
|
|
16
|
+
Please follow the following guidelines when generating code.
|
|
17
|
+
|
|
18
|
+
You can find the official SDK documentation and code samples here:
|
|
19
|
+
https://razorpay.com/docs/payments/server-integration/python/
|
|
20
|
+
|
|
21
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
22
|
+
|
|
23
|
+
Always use the official Razorpay Python SDK, which is the standard library for all Razorpay API interactions.
|
|
24
|
+
|
|
25
|
+
**Library Name:** Razorpay Python SDK
|
|
26
|
+
**Python Package:** `razorpay`
|
|
27
|
+
**Current Version:** 1.4.2
|
|
28
|
+
**Supported Python Versions:** Python 3.12+
|
|
29
|
+
|
|
30
|
+
**Installation:**
|
|
31
|
+
- **Correct:** `pip install razorpay`
|
|
32
|
+
|
|
33
|
+
**APIs and Usage:**
|
|
34
|
+
- **Correct:** `import razorpay`
|
|
35
|
+
- **Correct:** `client = razorpay.Client(auth=("YOUR_KEY_ID", "YOUR_KEY_SECRET"))`
|
|
36
|
+
- **Correct:** `client.order.create(data={...})` for creating orders
|
|
37
|
+
- **Correct:** `client.payment.fetch(payment_id)` for fetching payments
|
|
38
|
+
- **Incorrect:** Using unofficial Razorpay libraries or wrappers
|
|
39
|
+
- **Incorrect:** Exposing credentials in client-side applications
|
|
40
|
+
|
|
41
|
+
## Authentication and Initialization
|
|
42
|
+
|
|
43
|
+
The Razorpay Python library requires your Key ID and Key Secret for authentication. You can obtain these from the Razorpay Dashboard at Settings > API Keys.
|
|
44
|
+
|
|
45
|
+
### Environment Variables (Recommended)
|
|
46
|
+
|
|
47
|
+
Set up environment variables for secure credential management:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import os
|
|
51
|
+
import razorpay
|
|
52
|
+
from dotenv import load_dotenv
|
|
53
|
+
|
|
54
|
+
# Load environment variables
|
|
55
|
+
load_dotenv()
|
|
56
|
+
|
|
57
|
+
# Initialize Razorpay client
|
|
58
|
+
client = razorpay.Client(auth=(
|
|
59
|
+
os.environ.get('RAZORPAY_KEY_ID'),
|
|
60
|
+
os.environ.get('RAZORPAY_KEY_SECRET')
|
|
61
|
+
))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Direct Initialization
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import razorpay
|
|
68
|
+
|
|
69
|
+
# Initialize client with credentials
|
|
70
|
+
client = razorpay.Client(auth=(
|
|
71
|
+
'rzp_test_xxxxxxxxxx',
|
|
72
|
+
'your_key_secret_here'
|
|
73
|
+
))
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Client Configuration Options
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import razorpay
|
|
80
|
+
|
|
81
|
+
# Enable retry on network errors
|
|
82
|
+
client = razorpay.Client(auth=(
|
|
83
|
+
os.environ.get('RAZORPAY_KEY_ID'),
|
|
84
|
+
os.environ.get('RAZORPAY_KEY_SECRET')
|
|
85
|
+
))
|
|
86
|
+
|
|
87
|
+
# Enable automatic retry
|
|
88
|
+
client.enable_retry(True)
|
|
89
|
+
|
|
90
|
+
# Set custom app details
|
|
91
|
+
client.set_app_details({
|
|
92
|
+
"title": "My Application",
|
|
93
|
+
"version": "1.0.0"
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Orders API
|
|
98
|
+
|
|
99
|
+
### Create Order
|
|
100
|
+
|
|
101
|
+
Create an order before accepting payments from customers:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import razorpay
|
|
105
|
+
import os
|
|
106
|
+
|
|
107
|
+
client = razorpay.Client(auth=(
|
|
108
|
+
os.environ.get('RAZORPAY_KEY_ID'),
|
|
109
|
+
os.environ.get('RAZORPAY_KEY_SECRET')
|
|
110
|
+
))
|
|
111
|
+
|
|
112
|
+
def create_order():
|
|
113
|
+
try:
|
|
114
|
+
data = {
|
|
115
|
+
'amount': 50000, # Amount in paise (50000 paise = ₹500)
|
|
116
|
+
'currency': 'INR',
|
|
117
|
+
'receipt': 'receipt_order_12345',
|
|
118
|
+
'notes': {
|
|
119
|
+
'key1': 'value1',
|
|
120
|
+
'key2': 'value2'
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
order = client.order.create(data=data)
|
|
125
|
+
print('Order ID:', order['id'])
|
|
126
|
+
print('Order:', order)
|
|
127
|
+
return order
|
|
128
|
+
except Exception as e:
|
|
129
|
+
print('Error creating order:', str(e))
|
|
130
|
+
raise
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Fetch Order Details
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
def fetch_order(order_id):
|
|
137
|
+
try:
|
|
138
|
+
order = client.order.fetch(order_id)
|
|
139
|
+
print('Order details:', order)
|
|
140
|
+
return order
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print('Error fetching order:', str(e))
|
|
143
|
+
raise
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Fetch All Orders
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
def fetch_all_orders():
|
|
150
|
+
try:
|
|
151
|
+
options = {
|
|
152
|
+
'count': 10, # Number of orders to fetch (default: 10, max: 100)
|
|
153
|
+
'skip': 0, # Number of orders to skip (for pagination)
|
|
154
|
+
'from': 1640995200, # Unix timestamp (fetch orders from this time)
|
|
155
|
+
'to': 1672531199 # Unix timestamp (fetch orders till this time)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
orders = client.order.all(options)
|
|
159
|
+
print('Orders:', orders)
|
|
160
|
+
return orders
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print('Error fetching orders:', str(e))
|
|
163
|
+
raise
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Fetch Payments for an Order
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
def fetch_order_payments(order_id):
|
|
170
|
+
try:
|
|
171
|
+
payments = client.order.payments(order_id)
|
|
172
|
+
print('Payments for order:', payments)
|
|
173
|
+
return payments
|
|
174
|
+
except Exception as e:
|
|
175
|
+
print('Error fetching order payments:', str(e))
|
|
176
|
+
raise
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Update Order
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
def update_order(order_id):
|
|
183
|
+
try:
|
|
184
|
+
data = {
|
|
185
|
+
'notes': {
|
|
186
|
+
'updated_key': 'updated_value'
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
order = client.order.edit(order_id, data)
|
|
191
|
+
print('Order updated:', order)
|
|
192
|
+
return order
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print('Error updating order:', str(e))
|
|
195
|
+
raise
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Payments API
|
|
199
|
+
|
|
200
|
+
### Fetch Payment Details
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
def fetch_payment(payment_id):
|
|
204
|
+
try:
|
|
205
|
+
payment = client.payment.fetch(payment_id)
|
|
206
|
+
print('Payment details:', payment)
|
|
207
|
+
return payment
|
|
208
|
+
except Exception as e:
|
|
209
|
+
print('Error fetching payment:', str(e))
|
|
210
|
+
raise
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Fetch All Payments
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
def fetch_all_payments():
|
|
217
|
+
try:
|
|
218
|
+
options = {
|
|
219
|
+
'count': 10,
|
|
220
|
+
'skip': 0,
|
|
221
|
+
'from': 1640995200,
|
|
222
|
+
'to': 1672531199
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
payments = client.payment.all(options)
|
|
226
|
+
print('Payments:', payments)
|
|
227
|
+
return payments
|
|
228
|
+
except Exception as e:
|
|
229
|
+
print('Error fetching payments:', str(e))
|
|
230
|
+
raise
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Capture Payment
|
|
234
|
+
|
|
235
|
+
Capture an authorized payment:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
def capture_payment(payment_id, amount):
|
|
239
|
+
try:
|
|
240
|
+
data = {
|
|
241
|
+
'amount': amount, # Amount in paise
|
|
242
|
+
'currency': 'INR'
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
payment = client.payment.capture(payment_id, amount)
|
|
246
|
+
print('Payment captured:', payment)
|
|
247
|
+
return payment
|
|
248
|
+
except Exception as e:
|
|
249
|
+
print('Error capturing payment:', str(e))
|
|
250
|
+
raise
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Update Payment
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
def update_payment(payment_id):
|
|
257
|
+
try:
|
|
258
|
+
data = {
|
|
259
|
+
'notes': {
|
|
260
|
+
'note_key_1': 'updated value 1',
|
|
261
|
+
'note_key_2': 'updated value 2'
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
payment = client.payment.edit(payment_id, data)
|
|
266
|
+
print('Payment updated:', payment)
|
|
267
|
+
return payment
|
|
268
|
+
except Exception as e:
|
|
269
|
+
print('Error updating payment:', str(e))
|
|
270
|
+
raise
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Fetch Card Details
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
def fetch_card_details(payment_id):
|
|
277
|
+
try:
|
|
278
|
+
card = client.payment.fetch_card_details(payment_id)
|
|
279
|
+
print('Card details:', card)
|
|
280
|
+
return card
|
|
281
|
+
except Exception as e:
|
|
282
|
+
print('Error fetching card details:', str(e))
|
|
283
|
+
raise
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Refunds API
|
|
287
|
+
|
|
288
|
+
### Create Refund
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
def create_refund(payment_id):
|
|
292
|
+
try:
|
|
293
|
+
data = {
|
|
294
|
+
'amount': 10000, # Amount in paise to refund
|
|
295
|
+
'speed': 'normal', # 'normal' or 'optimum'
|
|
296
|
+
'notes': {
|
|
297
|
+
'notes_key_1': 'Refund reason'
|
|
298
|
+
},
|
|
299
|
+
'receipt': 'Receipt No. 31'
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
refund = client.payment.refund(payment_id, data)
|
|
303
|
+
print('Refund created:', refund)
|
|
304
|
+
return refund
|
|
305
|
+
except Exception as e:
|
|
306
|
+
print('Error creating refund:', str(e))
|
|
307
|
+
raise
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Fetch Refund Details
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
def fetch_refund(refund_id):
|
|
314
|
+
try:
|
|
315
|
+
refund = client.refund.fetch(refund_id)
|
|
316
|
+
print('Refund details:', refund)
|
|
317
|
+
return refund
|
|
318
|
+
except Exception as e:
|
|
319
|
+
print('Error fetching refund:', str(e))
|
|
320
|
+
raise
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Fetch All Refunds for a Payment
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
def fetch_payment_refunds(payment_id):
|
|
327
|
+
try:
|
|
328
|
+
refunds = client.payment.refunds(payment_id)
|
|
329
|
+
print('Refunds:', refunds)
|
|
330
|
+
return refunds
|
|
331
|
+
except Exception as e:
|
|
332
|
+
print('Error fetching refunds:', str(e))
|
|
333
|
+
raise
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Fetch All Refunds
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
def fetch_all_refunds():
|
|
340
|
+
try:
|
|
341
|
+
options = {
|
|
342
|
+
'count': 10,
|
|
343
|
+
'skip': 0,
|
|
344
|
+
'from': 1640995200,
|
|
345
|
+
'to': 1672531199
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
refunds = client.refund.all(options)
|
|
349
|
+
print('All refunds:', refunds)
|
|
350
|
+
return refunds
|
|
351
|
+
except Exception as e:
|
|
352
|
+
print('Error fetching all refunds:', str(e))
|
|
353
|
+
raise
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Update Refund
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
def update_refund(refund_id):
|
|
360
|
+
try:
|
|
361
|
+
data = {
|
|
362
|
+
'notes': {
|
|
363
|
+
'updated_note': 'Updated refund note'
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
refund = client.refund.edit(refund_id, data)
|
|
368
|
+
print('Refund updated:', refund)
|
|
369
|
+
return refund
|
|
370
|
+
except Exception as e:
|
|
371
|
+
print('Error updating refund:', str(e))
|
|
372
|
+
raise
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Customers API
|
|
376
|
+
|
|
377
|
+
### Create Customer
|
|
378
|
+
|
|
379
|
+
```python
|
|
380
|
+
def create_customer():
|
|
381
|
+
try:
|
|
382
|
+
data = {
|
|
383
|
+
'name': 'John Doe',
|
|
384
|
+
'email': 'john.doe@example.com',
|
|
385
|
+
'contact': '+919876543210',
|
|
386
|
+
'fail_existing': 0, # 0 to return existing customer if exists, 1 to fail
|
|
387
|
+
'notes': {
|
|
388
|
+
'customer_type': 'premium'
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
customer = client.customer.create(data)
|
|
393
|
+
print('Customer created:', customer)
|
|
394
|
+
return customer
|
|
395
|
+
except Exception as e:
|
|
396
|
+
print('Error creating customer:', str(e))
|
|
397
|
+
raise
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Fetch Customer Details
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
def fetch_customer(customer_id):
|
|
404
|
+
try:
|
|
405
|
+
customer = client.customer.fetch(customer_id)
|
|
406
|
+
print('Customer details:', customer)
|
|
407
|
+
return customer
|
|
408
|
+
except Exception as e:
|
|
409
|
+
print('Error fetching customer:', str(e))
|
|
410
|
+
raise
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Update Customer
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
def update_customer(customer_id):
|
|
417
|
+
try:
|
|
418
|
+
data = {
|
|
419
|
+
'name': 'Jane Doe',
|
|
420
|
+
'email': 'jane.doe@example.com',
|
|
421
|
+
'contact': '+919876543211'
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
customer = client.customer.edit(customer_id, data)
|
|
425
|
+
print('Customer updated:', customer)
|
|
426
|
+
return customer
|
|
427
|
+
except Exception as e:
|
|
428
|
+
print('Error updating customer:', str(e))
|
|
429
|
+
raise
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Fetch All Customers
|
|
433
|
+
|
|
434
|
+
```python
|
|
435
|
+
def fetch_all_customers():
|
|
436
|
+
try:
|
|
437
|
+
options = {
|
|
438
|
+
'count': 10,
|
|
439
|
+
'skip': 0
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
customers = client.customer.all(options)
|
|
443
|
+
print('Customers:', customers)
|
|
444
|
+
return customers
|
|
445
|
+
except Exception as e:
|
|
446
|
+
print('Error fetching customers:', str(e))
|
|
447
|
+
raise
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Subscriptions API
|
|
451
|
+
|
|
452
|
+
### Create Subscription
|
|
453
|
+
|
|
454
|
+
```python
|
|
455
|
+
import time
|
|
456
|
+
|
|
457
|
+
def create_subscription():
|
|
458
|
+
try:
|
|
459
|
+
data = {
|
|
460
|
+
'plan_id': 'plan_xxxxxxxxxxxxx',
|
|
461
|
+
'customer_notify': 1,
|
|
462
|
+
'quantity': 1,
|
|
463
|
+
'total_count': 12,
|
|
464
|
+
'start_at': int(time.time()) + 86400, # Start after 1 day
|
|
465
|
+
'addons': [
|
|
466
|
+
{
|
|
467
|
+
'item': {
|
|
468
|
+
'name': 'Extra Storage',
|
|
469
|
+
'amount': 5000,
|
|
470
|
+
'currency': 'INR'
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
],
|
|
474
|
+
'notes': {
|
|
475
|
+
'subscription_type': 'premium'
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
subscription = client.subscription.create(data)
|
|
480
|
+
print('Subscription created:', subscription)
|
|
481
|
+
return subscription
|
|
482
|
+
except Exception as e:
|
|
483
|
+
print('Error creating subscription:', str(e))
|
|
484
|
+
raise
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Fetch Subscription Details
|
|
488
|
+
|
|
489
|
+
```python
|
|
490
|
+
def fetch_subscription(subscription_id):
|
|
491
|
+
try:
|
|
492
|
+
subscription = client.subscription.fetch(subscription_id)
|
|
493
|
+
print('Subscription details:', subscription)
|
|
494
|
+
return subscription
|
|
495
|
+
except Exception as e:
|
|
496
|
+
print('Error fetching subscription:', str(e))
|
|
497
|
+
raise
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Cancel Subscription
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
def cancel_subscription(subscription_id):
|
|
504
|
+
try:
|
|
505
|
+
data = {
|
|
506
|
+
'cancel_at_cycle_end': 0 # 0 to cancel immediately, 1 to cancel at end of cycle
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
subscription = client.subscription.cancel(subscription_id, data)
|
|
510
|
+
print('Subscription cancelled:', subscription)
|
|
511
|
+
return subscription
|
|
512
|
+
except Exception as e:
|
|
513
|
+
print('Error cancelling subscription:', str(e))
|
|
514
|
+
raise
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Fetch All Subscriptions
|
|
518
|
+
|
|
519
|
+
```python
|
|
520
|
+
def fetch_all_subscriptions():
|
|
521
|
+
try:
|
|
522
|
+
options = {
|
|
523
|
+
'count': 10,
|
|
524
|
+
'skip': 0
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
subscriptions = client.subscription.all(options)
|
|
528
|
+
print('Subscriptions:', subscriptions)
|
|
529
|
+
return subscriptions
|
|
530
|
+
except Exception as e:
|
|
531
|
+
print('Error fetching subscriptions:', str(e))
|
|
532
|
+
raise
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Update Subscription
|
|
536
|
+
|
|
537
|
+
```python
|
|
538
|
+
def update_subscription(subscription_id):
|
|
539
|
+
try:
|
|
540
|
+
data = {
|
|
541
|
+
'plan_id': 'plan_yyyyyyyyyyy',
|
|
542
|
+
'quantity': 2,
|
|
543
|
+
'remaining_count': 6
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
subscription = client.subscription.edit(subscription_id, data)
|
|
547
|
+
print('Subscription updated:', subscription)
|
|
548
|
+
return subscription
|
|
549
|
+
except Exception as e:
|
|
550
|
+
print('Error updating subscription:', str(e))
|
|
551
|
+
raise
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
## Plans API
|
|
555
|
+
|
|
556
|
+
### Create Plan
|
|
557
|
+
|
|
558
|
+
```python
|
|
559
|
+
def create_plan():
|
|
560
|
+
try:
|
|
561
|
+
data = {
|
|
562
|
+
'period': 'monthly',
|
|
563
|
+
'interval': 1,
|
|
564
|
+
'item': {
|
|
565
|
+
'name': 'Premium Plan',
|
|
566
|
+
'amount': 99900, # Amount in paise
|
|
567
|
+
'currency': 'INR',
|
|
568
|
+
'description': 'Premium monthly subscription'
|
|
569
|
+
},
|
|
570
|
+
'notes': {
|
|
571
|
+
'plan_type': 'premium'
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
plan = client.plan.create(data)
|
|
576
|
+
print('Plan created:', plan)
|
|
577
|
+
return plan
|
|
578
|
+
except Exception as e:
|
|
579
|
+
print('Error creating plan:', str(e))
|
|
580
|
+
raise
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Fetch Plan Details
|
|
584
|
+
|
|
585
|
+
```python
|
|
586
|
+
def fetch_plan(plan_id):
|
|
587
|
+
try:
|
|
588
|
+
plan = client.plan.fetch(plan_id)
|
|
589
|
+
print('Plan details:', plan)
|
|
590
|
+
return plan
|
|
591
|
+
except Exception as e:
|
|
592
|
+
print('Error fetching plan:', str(e))
|
|
593
|
+
raise
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Fetch All Plans
|
|
597
|
+
|
|
598
|
+
```python
|
|
599
|
+
def fetch_all_plans():
|
|
600
|
+
try:
|
|
601
|
+
options = {
|
|
602
|
+
'count': 10,
|
|
603
|
+
'skip': 0
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
plans = client.plan.all(options)
|
|
607
|
+
print('Plans:', plans)
|
|
608
|
+
return plans
|
|
609
|
+
except Exception as e:
|
|
610
|
+
print('Error fetching plans:', str(e))
|
|
611
|
+
raise
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
## Invoices API
|
|
615
|
+
|
|
616
|
+
### Create Invoice
|
|
617
|
+
|
|
618
|
+
```python
|
|
619
|
+
import time
|
|
620
|
+
|
|
621
|
+
def create_invoice():
|
|
622
|
+
try:
|
|
623
|
+
data = {
|
|
624
|
+
'type': 'invoice',
|
|
625
|
+
'description': 'Invoice for the month of January 2025',
|
|
626
|
+
'customer': {
|
|
627
|
+
'name': 'John Doe',
|
|
628
|
+
'email': 'john.doe@example.com',
|
|
629
|
+
'contact': '+919876543210'
|
|
630
|
+
},
|
|
631
|
+
'line_items': [
|
|
632
|
+
{
|
|
633
|
+
'name': 'Premium Subscription',
|
|
634
|
+
'description': 'Monthly subscription',
|
|
635
|
+
'amount': 99900,
|
|
636
|
+
'currency': 'INR',
|
|
637
|
+
'quantity': 1
|
|
638
|
+
}
|
|
639
|
+
],
|
|
640
|
+
'currency': 'INR',
|
|
641
|
+
'email_notify': 1,
|
|
642
|
+
'sms_notify': 1,
|
|
643
|
+
'date': int(time.time())
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
invoice = client.invoice.create(data)
|
|
647
|
+
print('Invoice created:', invoice)
|
|
648
|
+
return invoice
|
|
649
|
+
except Exception as e:
|
|
650
|
+
print('Error creating invoice:', str(e))
|
|
651
|
+
raise
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Fetch Invoice Details
|
|
655
|
+
|
|
656
|
+
```python
|
|
657
|
+
def fetch_invoice(invoice_id):
|
|
658
|
+
try:
|
|
659
|
+
invoice = client.invoice.fetch(invoice_id)
|
|
660
|
+
print('Invoice details:', invoice)
|
|
661
|
+
return invoice
|
|
662
|
+
except Exception as e:
|
|
663
|
+
print('Error fetching invoice:', str(e))
|
|
664
|
+
raise
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### Fetch All Invoices
|
|
668
|
+
|
|
669
|
+
```python
|
|
670
|
+
def fetch_all_invoices():
|
|
671
|
+
try:
|
|
672
|
+
options = {
|
|
673
|
+
'count': 10,
|
|
674
|
+
'skip': 0
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
invoices = client.invoice.all(options)
|
|
678
|
+
print('Invoices:', invoices)
|
|
679
|
+
return invoices
|
|
680
|
+
except Exception as e:
|
|
681
|
+
print('Error fetching invoices:', str(e))
|
|
682
|
+
raise
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### Cancel Invoice
|
|
686
|
+
|
|
687
|
+
```python
|
|
688
|
+
def cancel_invoice(invoice_id):
|
|
689
|
+
try:
|
|
690
|
+
invoice = client.invoice.cancel(invoice_id)
|
|
691
|
+
print('Invoice cancelled:', invoice)
|
|
692
|
+
return invoice
|
|
693
|
+
except Exception as e:
|
|
694
|
+
print('Error cancelling invoice:', str(e))
|
|
695
|
+
raise
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### Issue Invoice
|
|
699
|
+
|
|
700
|
+
```python
|
|
701
|
+
def issue_invoice(invoice_id):
|
|
702
|
+
try:
|
|
703
|
+
invoice = client.invoice.issue(invoice_id)
|
|
704
|
+
print('Invoice issued:', invoice)
|
|
705
|
+
return invoice
|
|
706
|
+
except Exception as e:
|
|
707
|
+
print('Error issuing invoice:', str(e))
|
|
708
|
+
raise
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### Update Invoice
|
|
712
|
+
|
|
713
|
+
```python
|
|
714
|
+
def update_invoice(invoice_id):
|
|
715
|
+
try:
|
|
716
|
+
data = {
|
|
717
|
+
'notes': {
|
|
718
|
+
'updated_note': 'Updated invoice note'
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
invoice = client.invoice.edit(invoice_id, data)
|
|
723
|
+
print('Invoice updated:', invoice)
|
|
724
|
+
return invoice
|
|
725
|
+
except Exception as e:
|
|
726
|
+
print('Error updating invoice:', str(e))
|
|
727
|
+
raise
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
## Payment Verification
|
|
731
|
+
|
|
732
|
+
### Verify Payment Signature
|
|
733
|
+
|
|
734
|
+
Verify payment signatures to ensure payment authenticity:
|
|
735
|
+
|
|
736
|
+
```python
|
|
737
|
+
import hmac
|
|
738
|
+
import hashlib
|
|
739
|
+
|
|
740
|
+
# Method 1: Using Razorpay's built-in utility
|
|
741
|
+
def verify_payment_signature(order_id, payment_id, signature):
|
|
742
|
+
try:
|
|
743
|
+
params_dict = {
|
|
744
|
+
'razorpay_order_id': order_id,
|
|
745
|
+
'razorpay_payment_id': payment_id,
|
|
746
|
+
'razorpay_signature': signature
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
# This will raise SignatureVerificationError if signature is invalid
|
|
750
|
+
client.utility.verify_payment_signature(params_dict)
|
|
751
|
+
print('Payment signature verified successfully')
|
|
752
|
+
return True
|
|
753
|
+
except razorpay.errors.SignatureVerificationError as e:
|
|
754
|
+
print('Invalid payment signature:', str(e))
|
|
755
|
+
return False
|
|
756
|
+
except Exception as e:
|
|
757
|
+
print('Error verifying payment signature:', str(e))
|
|
758
|
+
return False
|
|
759
|
+
|
|
760
|
+
# Method 2: Manual verification
|
|
761
|
+
def verify_payment_signature_manual(order_id, payment_id, signature):
|
|
762
|
+
try:
|
|
763
|
+
key_secret = os.environ.get('RAZORPAY_KEY_SECRET')
|
|
764
|
+
|
|
765
|
+
# Create the expected signature
|
|
766
|
+
message = f"{order_id}|{payment_id}"
|
|
767
|
+
generated_signature = hmac.new(
|
|
768
|
+
key_secret.encode(),
|
|
769
|
+
message.encode(),
|
|
770
|
+
hashlib.sha256
|
|
771
|
+
).hexdigest()
|
|
772
|
+
|
|
773
|
+
# Compare signatures
|
|
774
|
+
is_valid = hmac.compare_digest(generated_signature, signature)
|
|
775
|
+
|
|
776
|
+
if is_valid:
|
|
777
|
+
print('Payment signature verified successfully')
|
|
778
|
+
return True
|
|
779
|
+
else:
|
|
780
|
+
print('Invalid payment signature')
|
|
781
|
+
return False
|
|
782
|
+
except Exception as e:
|
|
783
|
+
print('Error verifying payment signature:', str(e))
|
|
784
|
+
return False
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
## Webhook Handling and Validation
|
|
788
|
+
|
|
789
|
+
### Flask Webhook Integration
|
|
790
|
+
|
|
791
|
+
Handle and validate incoming Razorpay webhooks:
|
|
792
|
+
|
|
793
|
+
```python
|
|
794
|
+
from flask import Flask, request, jsonify
|
|
795
|
+
import razorpay
|
|
796
|
+
import os
|
|
797
|
+
import hmac
|
|
798
|
+
import hashlib
|
|
799
|
+
|
|
800
|
+
app = Flask(__name__)
|
|
801
|
+
|
|
802
|
+
client = razorpay.Client(auth=(
|
|
803
|
+
os.environ.get('RAZORPAY_KEY_ID'),
|
|
804
|
+
os.environ.get('RAZORPAY_KEY_SECRET')
|
|
805
|
+
))
|
|
806
|
+
|
|
807
|
+
def validate_webhook_signature(webhook_body, signature, secret):
|
|
808
|
+
"""Validate webhook signature"""
|
|
809
|
+
try:
|
|
810
|
+
generated_signature = hmac.new(
|
|
811
|
+
secret.encode(),
|
|
812
|
+
webhook_body,
|
|
813
|
+
hashlib.sha256
|
|
814
|
+
).hexdigest()
|
|
815
|
+
|
|
816
|
+
return hmac.compare_digest(generated_signature, signature)
|
|
817
|
+
except Exception as e:
|
|
818
|
+
print('Error validating webhook:', str(e))
|
|
819
|
+
return False
|
|
820
|
+
|
|
821
|
+
@app.route('/webhook', methods=['POST'])
|
|
822
|
+
def handle_webhook():
|
|
823
|
+
# Get the webhook signature from headers
|
|
824
|
+
signature = request.headers.get('X-Razorpay-Signature')
|
|
825
|
+
webhook_secret = os.environ.get('RAZORPAY_WEBHOOK_SECRET')
|
|
826
|
+
|
|
827
|
+
# Get raw request body
|
|
828
|
+
webhook_body = request.get_data()
|
|
829
|
+
|
|
830
|
+
# Validate signature
|
|
831
|
+
is_valid = validate_webhook_signature(webhook_body, signature, webhook_secret)
|
|
832
|
+
|
|
833
|
+
if not is_valid:
|
|
834
|
+
return jsonify({'error': 'Invalid signature'}), 400
|
|
835
|
+
|
|
836
|
+
# Parse webhook event
|
|
837
|
+
event = request.get_json()
|
|
838
|
+
|
|
839
|
+
print('Webhook event:', event.get('event'))
|
|
840
|
+
|
|
841
|
+
# Handle different event types
|
|
842
|
+
event_type = event.get('event')
|
|
843
|
+
|
|
844
|
+
if event_type == 'payment.authorized':
|
|
845
|
+
handle_payment_authorized(event['payload']['payment']['entity'])
|
|
846
|
+
elif event_type == 'payment.captured':
|
|
847
|
+
handle_payment_captured(event['payload']['payment']['entity'])
|
|
848
|
+
elif event_type == 'payment.failed':
|
|
849
|
+
handle_payment_failed(event['payload']['payment']['entity'])
|
|
850
|
+
elif event_type == 'order.paid':
|
|
851
|
+
handle_order_paid(event['payload']['order']['entity'])
|
|
852
|
+
elif event_type == 'refund.created':
|
|
853
|
+
handle_refund_created(event['payload']['refund']['entity'])
|
|
854
|
+
elif event_type == 'subscription.activated':
|
|
855
|
+
handle_subscription_activated(event['payload']['subscription']['entity'])
|
|
856
|
+
elif event_type == 'subscription.cancelled':
|
|
857
|
+
handle_subscription_cancelled(event['payload']['subscription']['entity'])
|
|
858
|
+
elif event_type == 'invoice.paid':
|
|
859
|
+
handle_invoice_paid(event['payload']['invoice']['entity'])
|
|
860
|
+
else:
|
|
861
|
+
print('Unhandled event:', event_type)
|
|
862
|
+
|
|
863
|
+
return jsonify({'status': 'ok'}), 200
|
|
864
|
+
|
|
865
|
+
def handle_payment_authorized(payment):
|
|
866
|
+
print('Payment authorized:', payment['id'])
|
|
867
|
+
# Capture the payment or perform other actions
|
|
868
|
+
|
|
869
|
+
def handle_payment_captured(payment):
|
|
870
|
+
print('Payment captured:', payment['id'])
|
|
871
|
+
# Update order status, send confirmation email, etc.
|
|
872
|
+
|
|
873
|
+
def handle_payment_failed(payment):
|
|
874
|
+
print('Payment failed:', payment['id'])
|
|
875
|
+
# Notify customer, update order status, etc.
|
|
876
|
+
|
|
877
|
+
def handle_order_paid(order):
|
|
878
|
+
print('Order paid:', order['id'])
|
|
879
|
+
# Process order fulfillment
|
|
880
|
+
|
|
881
|
+
def handle_refund_created(refund):
|
|
882
|
+
print('Refund created:', refund['id'])
|
|
883
|
+
# Update records, notify customer
|
|
884
|
+
|
|
885
|
+
def handle_subscription_activated(subscription):
|
|
886
|
+
print('Subscription activated:', subscription['id'])
|
|
887
|
+
# Grant access to subscription features
|
|
888
|
+
|
|
889
|
+
def handle_subscription_cancelled(subscription):
|
|
890
|
+
print('Subscription cancelled:', subscription['id'])
|
|
891
|
+
# Revoke access, send cancellation email
|
|
892
|
+
|
|
893
|
+
def handle_invoice_paid(invoice):
|
|
894
|
+
print('Invoice paid:', invoice['id'])
|
|
895
|
+
# Update invoice status, send receipt
|
|
896
|
+
|
|
897
|
+
if __name__ == '__main__':
|
|
898
|
+
app.run(port=3000, debug=True)
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### Using Razorpay's Webhook Verification Utility
|
|
902
|
+
|
|
903
|
+
```python
|
|
904
|
+
@app.route('/webhook', methods=['POST'])
|
|
905
|
+
def handle_webhook_with_utility():
|
|
906
|
+
signature = request.headers.get('X-Razorpay-Signature')
|
|
907
|
+
webhook_secret = os.environ.get('RAZORPAY_WEBHOOK_SECRET')
|
|
908
|
+
webhook_body = request.get_data().decode('utf-8')
|
|
909
|
+
|
|
910
|
+
try:
|
|
911
|
+
# Verify webhook signature using Razorpay utility
|
|
912
|
+
client.utility.verify_webhook_signature(
|
|
913
|
+
webhook_body,
|
|
914
|
+
signature,
|
|
915
|
+
webhook_secret
|
|
916
|
+
)
|
|
917
|
+
|
|
918
|
+
# Process webhook event
|
|
919
|
+
event = request.get_json()
|
|
920
|
+
# Handle event...
|
|
921
|
+
|
|
922
|
+
return jsonify({'status': 'ok'}), 200
|
|
923
|
+
except razorpay.errors.SignatureVerificationError:
|
|
924
|
+
return jsonify({'error': 'Invalid signature'}), 400
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
## Transfers API (Route)
|
|
928
|
+
|
|
929
|
+
### Create Transfer
|
|
930
|
+
|
|
931
|
+
Transfer funds to linked accounts:
|
|
932
|
+
|
|
933
|
+
```python
|
|
934
|
+
def create_transfer(payment_id):
|
|
935
|
+
try:
|
|
936
|
+
data = {
|
|
937
|
+
'transfers': [
|
|
938
|
+
{
|
|
939
|
+
'account': 'acc_xxxxxxxxxxxxx',
|
|
940
|
+
'amount': 10000, # Amount in paise
|
|
941
|
+
'currency': 'INR',
|
|
942
|
+
'notes': {
|
|
943
|
+
'name': 'Transfer to vendor',
|
|
944
|
+
'roll_no': 'IEC2011025'
|
|
945
|
+
},
|
|
946
|
+
'linked_account_notes': ['branch'],
|
|
947
|
+
'on_hold': 0, # 0 to transfer immediately, 1 to hold
|
|
948
|
+
'on_hold_until': None
|
|
949
|
+
}
|
|
950
|
+
]
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
transfer = client.payment.transfer(payment_id, data)
|
|
954
|
+
print('Transfer created:', transfer)
|
|
955
|
+
return transfer
|
|
956
|
+
except Exception as e:
|
|
957
|
+
print('Error creating transfer:', str(e))
|
|
958
|
+
raise
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### Fetch Transfer Details
|
|
962
|
+
|
|
963
|
+
```python
|
|
964
|
+
def fetch_transfer(transfer_id):
|
|
965
|
+
try:
|
|
966
|
+
transfer = client.transfer.fetch(transfer_id)
|
|
967
|
+
print('Transfer details:', transfer)
|
|
968
|
+
return transfer
|
|
969
|
+
except Exception as e:
|
|
970
|
+
print('Error fetching transfer:', str(e))
|
|
971
|
+
raise
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### Fetch All Transfers
|
|
975
|
+
|
|
976
|
+
```python
|
|
977
|
+
def fetch_all_transfers():
|
|
978
|
+
try:
|
|
979
|
+
options = {
|
|
980
|
+
'count': 10,
|
|
981
|
+
'skip': 0
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
transfers = client.transfer.all(options)
|
|
985
|
+
print('Transfers:', transfers)
|
|
986
|
+
return transfers
|
|
987
|
+
except Exception as e:
|
|
988
|
+
print('Error fetching transfers:', str(e))
|
|
989
|
+
raise
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
## Virtual Accounts API
|
|
993
|
+
|
|
994
|
+
### Create Virtual Account
|
|
995
|
+
|
|
996
|
+
```python
|
|
997
|
+
import time
|
|
998
|
+
|
|
999
|
+
def create_virtual_account():
|
|
1000
|
+
try:
|
|
1001
|
+
data = {
|
|
1002
|
+
'receivers': {
|
|
1003
|
+
'types': ['bank_account']
|
|
1004
|
+
},
|
|
1005
|
+
'description': 'Virtual Account for customer XYZ',
|
|
1006
|
+
'customer_id': 'cust_xxxxxxxxxxxxx',
|
|
1007
|
+
'close_by': int(time.time()) + 86400 * 30, # Close after 30 days
|
|
1008
|
+
'notes': {
|
|
1009
|
+
'purpose': 'Rent collection'
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
virtual_account = client.virtual_account.create(data)
|
|
1014
|
+
print('Virtual Account created:', virtual_account)
|
|
1015
|
+
return virtual_account
|
|
1016
|
+
except Exception as e:
|
|
1017
|
+
print('Error creating virtual account:', str(e))
|
|
1018
|
+
raise
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
### Fetch Virtual Account Details
|
|
1022
|
+
|
|
1023
|
+
```python
|
|
1024
|
+
def fetch_virtual_account(va_id):
|
|
1025
|
+
try:
|
|
1026
|
+
virtual_account = client.virtual_account.fetch(va_id)
|
|
1027
|
+
print('Virtual Account details:', virtual_account)
|
|
1028
|
+
return virtual_account
|
|
1029
|
+
except Exception as e:
|
|
1030
|
+
print('Error fetching virtual account:', str(e))
|
|
1031
|
+
raise
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
### Fetch All Virtual Accounts
|
|
1035
|
+
|
|
1036
|
+
```python
|
|
1037
|
+
def fetch_all_virtual_accounts():
|
|
1038
|
+
try:
|
|
1039
|
+
options = {
|
|
1040
|
+
'count': 10,
|
|
1041
|
+
'skip': 0
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
virtual_accounts = client.virtual_account.all(options)
|
|
1045
|
+
print('Virtual Accounts:', virtual_accounts)
|
|
1046
|
+
return virtual_accounts
|
|
1047
|
+
except Exception as e:
|
|
1048
|
+
print('Error fetching virtual accounts:', str(e))
|
|
1049
|
+
raise
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
### Close Virtual Account
|
|
1053
|
+
|
|
1054
|
+
```python
|
|
1055
|
+
def close_virtual_account(va_id):
|
|
1056
|
+
try:
|
|
1057
|
+
virtual_account = client.virtual_account.close(va_id)
|
|
1058
|
+
print('Virtual Account closed:', virtual_account)
|
|
1059
|
+
return virtual_account
|
|
1060
|
+
except Exception as e:
|
|
1061
|
+
print('Error closing virtual account:', str(e))
|
|
1062
|
+
raise
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
## Error Handling
|
|
1066
|
+
|
|
1067
|
+
### Comprehensive Error Handling
|
|
1068
|
+
|
|
1069
|
+
```python
|
|
1070
|
+
import razorpay.errors as errors
|
|
1071
|
+
|
|
1072
|
+
def safe_api_call():
|
|
1073
|
+
try:
|
|
1074
|
+
data = {
|
|
1075
|
+
'amount': 50000,
|
|
1076
|
+
'currency': 'INR',
|
|
1077
|
+
'receipt': 'receipt_12345'
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
order = client.order.create(data=data)
|
|
1081
|
+
|
|
1082
|
+
return {
|
|
1083
|
+
'success': True,
|
|
1084
|
+
'data': order
|
|
1085
|
+
}
|
|
1086
|
+
except errors.BadRequestError as e:
|
|
1087
|
+
print('Bad Request Error:', e)
|
|
1088
|
+
return {
|
|
1089
|
+
'success': False,
|
|
1090
|
+
'error': {
|
|
1091
|
+
'code': 400,
|
|
1092
|
+
'message': str(e)
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
except errors.UnauthorizedError as e:
|
|
1096
|
+
print('Unauthorized Error:', e)
|
|
1097
|
+
return {
|
|
1098
|
+
'success': False,
|
|
1099
|
+
'error': {
|
|
1100
|
+
'code': 401,
|
|
1101
|
+
'message': 'Invalid API credentials'
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
except errors.ServerError as e:
|
|
1105
|
+
print('Server Error:', e)
|
|
1106
|
+
return {
|
|
1107
|
+
'success': False,
|
|
1108
|
+
'error': {
|
|
1109
|
+
'code': 500,
|
|
1110
|
+
'message': 'Razorpay server error'
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
except errors.GatewayError as e:
|
|
1114
|
+
print('Gateway Error:', e)
|
|
1115
|
+
return {
|
|
1116
|
+
'success': False,
|
|
1117
|
+
'error': {
|
|
1118
|
+
'code': 502,
|
|
1119
|
+
'message': 'Gateway error occurred'
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
except Exception as e:
|
|
1123
|
+
print('Unknown error:', e)
|
|
1124
|
+
return {
|
|
1125
|
+
'success': False,
|
|
1126
|
+
'error': {
|
|
1127
|
+
'code': 500,
|
|
1128
|
+
'message': str(e)
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
### Specific Error Handling
|
|
1134
|
+
|
|
1135
|
+
```python
|
|
1136
|
+
def handle_specific_errors():
|
|
1137
|
+
try:
|
|
1138
|
+
payment = client.payment.fetch('pay_xxxxxxxxxxxxx')
|
|
1139
|
+
return payment
|
|
1140
|
+
except errors.BadRequestError as e:
|
|
1141
|
+
print('Bad Request - Invalid parameters')
|
|
1142
|
+
raise
|
|
1143
|
+
except errors.UnauthorizedError as e:
|
|
1144
|
+
print('Unauthorized - Invalid API credentials')
|
|
1145
|
+
raise
|
|
1146
|
+
except errors.NotFoundError as e:
|
|
1147
|
+
print('Not Found - Resource does not exist')
|
|
1148
|
+
raise
|
|
1149
|
+
except errors.ServerError as e:
|
|
1150
|
+
print('Internal Server Error - Razorpay issue')
|
|
1151
|
+
raise
|
|
1152
|
+
except Exception as e:
|
|
1153
|
+
print('Unknown error:', str(e))
|
|
1154
|
+
raise
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
## Complete Payment Flow Example
|
|
1158
|
+
|
|
1159
|
+
### Flask Server Integration
|
|
1160
|
+
|
|
1161
|
+
```python
|
|
1162
|
+
from flask import Flask, request, jsonify
|
|
1163
|
+
import razorpay
|
|
1164
|
+
import os
|
|
1165
|
+
import time
|
|
1166
|
+
|
|
1167
|
+
app = Flask(__name__)
|
|
1168
|
+
|
|
1169
|
+
client = razorpay.Client(auth=(
|
|
1170
|
+
os.environ.get('RAZORPAY_KEY_ID'),
|
|
1171
|
+
os.environ.get('RAZORPAY_KEY_SECRET')
|
|
1172
|
+
))
|
|
1173
|
+
|
|
1174
|
+
# Step 1: Create order
|
|
1175
|
+
@app.route('/create-order', methods=['POST'])
|
|
1176
|
+
def create_order():
|
|
1177
|
+
try:
|
|
1178
|
+
data = request.get_json()
|
|
1179
|
+
amount = data.get('amount')
|
|
1180
|
+
currency = data.get('currency', 'INR')
|
|
1181
|
+
|
|
1182
|
+
order_data = {
|
|
1183
|
+
'amount': amount * 100, # Convert to paise
|
|
1184
|
+
'currency': currency,
|
|
1185
|
+
'receipt': f"receipt_{int(time.time())}",
|
|
1186
|
+
'notes': {
|
|
1187
|
+
'description': 'Order payment'
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
order = client.order.create(data=order_data)
|
|
1192
|
+
|
|
1193
|
+
return jsonify({
|
|
1194
|
+
'success': True,
|
|
1195
|
+
'order': {
|
|
1196
|
+
'id': order['id'],
|
|
1197
|
+
'amount': order['amount'],
|
|
1198
|
+
'currency': order['currency']
|
|
1199
|
+
},
|
|
1200
|
+
'key_id': os.environ.get('RAZORPAY_KEY_ID')
|
|
1201
|
+
}), 200
|
|
1202
|
+
except Exception as e:
|
|
1203
|
+
print('Error creating order:', str(e))
|
|
1204
|
+
return jsonify({
|
|
1205
|
+
'success': False,
|
|
1206
|
+
'message': 'Failed to create order'
|
|
1207
|
+
}), 500
|
|
1208
|
+
|
|
1209
|
+
# Step 2: Verify payment
|
|
1210
|
+
@app.route('/verify-payment', methods=['POST'])
|
|
1211
|
+
def verify_payment():
|
|
1212
|
+
try:
|
|
1213
|
+
data = request.get_json()
|
|
1214
|
+
razorpay_order_id = data.get('razorpay_order_id')
|
|
1215
|
+
razorpay_payment_id = data.get('razorpay_payment_id')
|
|
1216
|
+
razorpay_signature = data.get('razorpay_signature')
|
|
1217
|
+
|
|
1218
|
+
params_dict = {
|
|
1219
|
+
'razorpay_order_id': razorpay_order_id,
|
|
1220
|
+
'razorpay_payment_id': razorpay_payment_id,
|
|
1221
|
+
'razorpay_signature': razorpay_signature
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
# Verify signature
|
|
1225
|
+
client.utility.verify_payment_signature(params_dict)
|
|
1226
|
+
|
|
1227
|
+
# Payment is successful, fetch payment details
|
|
1228
|
+
payment = client.payment.fetch(razorpay_payment_id)
|
|
1229
|
+
|
|
1230
|
+
return jsonify({
|
|
1231
|
+
'success': True,
|
|
1232
|
+
'message': 'Payment verified successfully',
|
|
1233
|
+
'payment': {
|
|
1234
|
+
'id': payment['id'],
|
|
1235
|
+
'amount': payment['amount'],
|
|
1236
|
+
'status': payment['status']
|
|
1237
|
+
}
|
|
1238
|
+
}), 200
|
|
1239
|
+
except razorpay.errors.SignatureVerificationError:
|
|
1240
|
+
return jsonify({
|
|
1241
|
+
'success': False,
|
|
1242
|
+
'message': 'Payment verification failed'
|
|
1243
|
+
}), 400
|
|
1244
|
+
except Exception as e:
|
|
1245
|
+
print('Error verifying payment:', str(e))
|
|
1246
|
+
return jsonify({
|
|
1247
|
+
'success': False,
|
|
1248
|
+
'message': 'Payment verification error'
|
|
1249
|
+
}), 500
|
|
1250
|
+
|
|
1251
|
+
# Step 3: Fetch payment details
|
|
1252
|
+
@app.route('/payment/<payment_id>', methods=['GET'])
|
|
1253
|
+
def get_payment(payment_id):
|
|
1254
|
+
try:
|
|
1255
|
+
payment = client.payment.fetch(payment_id)
|
|
1256
|
+
return jsonify({
|
|
1257
|
+
'success': True,
|
|
1258
|
+
'payment': payment
|
|
1259
|
+
}), 200
|
|
1260
|
+
except Exception as e:
|
|
1261
|
+
print('Error fetching payment:', str(e))
|
|
1262
|
+
return jsonify({
|
|
1263
|
+
'success': False,
|
|
1264
|
+
'message': 'Failed to fetch payment'
|
|
1265
|
+
}), 500
|
|
1266
|
+
|
|
1267
|
+
if __name__ == '__main__':
|
|
1268
|
+
app.run(port=3000, debug=True)
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
## Security Best Practices
|
|
1272
|
+
|
|
1273
|
+
### Never Expose Credentials
|
|
1274
|
+
|
|
1275
|
+
```python
|
|
1276
|
+
# Good - Use environment variables
|
|
1277
|
+
import os
|
|
1278
|
+
|
|
1279
|
+
client = razorpay.Client(auth=(
|
|
1280
|
+
os.environ.get('RAZORPAY_KEY_ID'),
|
|
1281
|
+
os.environ.get('RAZORPAY_KEY_SECRET')
|
|
1282
|
+
))
|
|
1283
|
+
|
|
1284
|
+
# Bad - Hardcoded credentials
|
|
1285
|
+
client = razorpay.Client(auth=(
|
|
1286
|
+
'rzp_test_xxxxxxxxxxxx',
|
|
1287
|
+
'your_secret_here'
|
|
1288
|
+
))
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
### Always Validate Webhooks
|
|
1292
|
+
|
|
1293
|
+
```python
|
|
1294
|
+
# Always verify webhook signatures before processing
|
|
1295
|
+
is_valid = validate_webhook_signature(
|
|
1296
|
+
webhook_body,
|
|
1297
|
+
signature,
|
|
1298
|
+
os.environ.get('RAZORPAY_WEBHOOK_SECRET')
|
|
1299
|
+
)
|
|
1300
|
+
|
|
1301
|
+
if not is_valid:
|
|
1302
|
+
return jsonify({'error': 'Invalid signature'}), 400
|
|
1303
|
+
```
|
|
1304
|
+
|
|
1305
|
+
### Verify Payment Signatures
|
|
1306
|
+
|
|
1307
|
+
```python
|
|
1308
|
+
# Always verify payment signatures on the server
|
|
1309
|
+
params_dict = {
|
|
1310
|
+
'razorpay_order_id': order_id,
|
|
1311
|
+
'razorpay_payment_id': payment_id,
|
|
1312
|
+
'razorpay_signature': signature
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
client.utility.verify_payment_signature(params_dict)
|
|
1316
|
+
```
|
|
1317
|
+
|
|
1318
|
+
## Useful Links
|
|
1319
|
+
|
|
1320
|
+
- **Documentation:** https://razorpay.com/docs/
|
|
1321
|
+
- **API Reference:** https://razorpay.com/docs/api/
|
|
1322
|
+
- **Python SDK Docs:** https://razorpay.com/docs/payments/server-integration/python/
|
|
1323
|
+
- **PyPI Package:** https://pypi.org/project/razorpay/
|
|
1324
|
+
- **GitHub Repository:** https://github.com/razorpay/razorpay-python
|
|
1325
|
+
- **Dashboard:** https://dashboard.razorpay.com/
|
|
1326
|
+
- **Support:** https://razorpay.com/support/
|
|
1327
|
+
|
|
1328
|
+
## Notes
|
|
1329
|
+
|
|
1330
|
+
This guide covers the core functionality of the Razorpay Python SDK. The SDK provides comprehensive support for all Razorpay services including payments, orders, refunds, subscriptions, customers, invoices, transfers, and virtual accounts. Always use environment variables for credentials and validate all payment signatures and webhook signatures to ensure security. The SDK requires Python 3.12 or higher and provides utilities for payment verification and webhook signature validation.
|