Adding online payments to a web application can make it easier for customers to purchase products, subscribe to services, book appointments, or pay invoices. But payment integration involves more than adding a payment button to a website.
A reliable integration needs a payment gateway, backend APIs, secure authentication, payment status handling, webhooks, and proper error management.
This guide explains the basic process of integrating a payment gateway into a web application, using Razorpay as an example.
1. Understand How Payment Gateway Integration Works
A typical payment flow looks like this:
Customer → Web App → Backend → Payment Gateway → Bank/Payment Network
The customer starts the payment from your website. Your backend creates the payment order through the gateway. The customer then completes the payment using a supported payment method.
After the transaction, your application needs to confirm whether the payment was successful before providing the product or service.
A simplified flow is:
- Customer selects a product or service.
- Your backend creates an order.
- The payment gateway generates the required payment details.
- Checkout opens for the customer.
- Customer completes the payment.
- The gateway returns payment information.
- Your backend verifies the payment.
- A webhook can update your system about payment events.
- Your database records the final payment status.
- The application confirms the order.
2. Choose the Right Payment Gateway
Before starting development, compare payment gateways based on factors such as:
- Supported payment methods
- Transaction fees
- API documentation
- Developer tools
- Settlement process
- Refund support
- International payment support
- Webhook capabilities
- Security requirements
- Customer support
For an Indian web application, gateways such as Razorpay can support common payment methods including UPI, cards, net banking, and wallets, depending on the account and applicable availability.
The important thing is to choose a gateway that fits your application's payment requirements rather than selecting one based only on pricing.
3. Create a Merchant Account
Once you select a gateway, create a merchant account and complete the required verification process.
For Razorpay, developers can use the available test environment to build and test the integration before processing live transactions.
You will generally receive API credentials that allow your backend to communicate with the payment gateway.
Keep these credentials secure.
Never expose secret API keys in frontend JavaScript, HTML, mobile applications, or public repositories.
4. Set Up Your Backend
The backend should handle sensitive payment operations.
For example, your backend might have an endpoint such as:
POST /api/create-payment-order
When the customer clicks "Pay Now", the frontend sends the order information to your backend.
The backend then:
- Validates the order.
- Calculates the amount.
- Creates an order with the payment gateway.
- Stores the order information in your database.
- Returns the required payment details to the frontend.
The amount should ideally be calculated and validated on the server rather than trusting a price sent directly by the browser.
This prevents customers from manipulating the amount through frontend code.
5. Create a Payment Order
The backend communicates with the gateway API to create a payment order.
For example, suppose a customer is purchasing a product worth ₹2,500.
Your application can create an internal order such as:
Order ID: ORD-1050
Amount: ₹2,500
Currency: INR
Status: Pending
The payment gateway can then create its own corresponding payment order.
Keeping both your internal order ID and the gateway's order ID is useful for tracking and reconciliation.
6. Open the Payment Checkout
After creating the payment order, your frontend can use the payment gateway's checkout mechanism.
The customer may see options such as:
- UPI
- Credit card
- Debit card
- Net banking
- Wallets
The exact options depend on the gateway, merchant configuration, customer location, and other factors.
The frontend should not decide whether the transaction is finally successful. It should only handle the customer-facing checkout experience.
7. Verify the Payment on the Server
One of the most important steps is payment verification.
After checkout, the frontend may receive payment information. That information should be sent to your backend for verification.
Your backend can then validate the payment using the gateway's server-side mechanisms.
For Razorpay integrations, signature verification is an important part of confirming that the payment response has not been tampered with.
The general flow is:
Checkout → Payment response → Backend → Signature verification → Payment status
Only after successful verification should your application update the order as paid.
8. Use Webhooks
Payment processing is asynchronous, so your application should also use webhooks.
A webhook allows the payment gateway to send an event directly to your server.
For example:
Razorpay → Webhook → Your Backend
The webhook can notify your application about events such as payment status changes, refunds, settlements, and other payment-related activities.
This is useful when the customer completes a transaction but the browser closes before your frontend receives the final response.
Your webhook endpoint might look conceptually like:
POST /api/payment/webhook
The endpoint receives the event, verifies its authenticity, and updates your database.
9. Make Webhooks Idempotent
Your webhook handler should be designed to handle duplicate events safely.
For example, suppose your system receives the same payment event twice.
Without proper handling, the application might:
- Mark an order as paid twice
- Send two confirmation emails
- Add inventory twice
- Credit a customer account twice
Store a unique event identifier and check whether the event has already been processed.
A simple approach is:
Receive event → Validate event → Check event ID → Process once → Store event ID
This is called idempotent processing and is essential for reliable payment systems.
10. Handle Payment Failures
Not every payment attempt will succeed.
A transaction can fail because of:
- Insufficient funds
- Incorrect card information
- Bank rejection
- UPI issues
- Network problems
- Payment gateway errors
- Customer cancellation
- Session timeout
Your application should clearly distinguish between different states.
Instead of only having:
Success / Failed
consider using:
- Created
- Pending
- Authorized
- Captured
- Failed
- Refunded
- Unknown
An Unknown or Pending state can be useful when a request times out and you cannot immediately determine whether the transaction succeeded.
11. Do Not Treat a Timeout as an Automatic Failure
This is a common payment integration mistake.
Imagine the following:
Customer → Gateway → Bank
The bank processes the payment successfully, but the response to your application is delayed.
Your server receives a timeout.
If your application immediately marks the transaction as failed, the customer might attempt another payment.
The first transaction could later be successful, resulting in a duplicate payment.
A better approach is to verify the transaction status through the gateway's APIs or wait for the relevant webhook before deciding what happened.
12. Store Payment Information Properly
Your database should maintain a clear relationship between your application order and the payment gateway transaction.
A payment record might contain:
| Field | Example |
|---|---|
| Internal Order ID | ORD-1050 |
| Gateway Order ID | order_xyz |
| Payment ID | pay_xyz |
| Amount | ₹2,500 |
| Currency | INR |
| Status | Captured |
| Created At | Timestamp |
| Updated At | Timestamp |
Do not store sensitive card information unless your payment architecture and compliance requirements explicitly support it.
In most cases, the payment gateway handles sensitive payment credentials.
13. Protect Your API Credentials
Security should be considered from the beginning.
Follow basic practices such as:
- Keep secret keys on the backend.
- Store credentials in environment variables or a secrets manager.
- Use HTTPS.
- Validate incoming requests.
- Verify webhook signatures.
- Avoid logging sensitive payment information.
- Restrict access to production credentials.
- Rotate credentials when necessary.
Never put a secret API key directly into frontend source code.
14. Test Before Going Live
Do not test a payment integration only with a successful transaction.
Test different scenarios, including:
- Successful payment
- Failed payment
- Cancelled payment
- Payment timeout
- Duplicate webhook
- Delayed webhook
- Invalid webhook signature
- Server failure
- Database failure
- Refund
- Multiple payment attempts
Razorpay's test environment can be used to validate payment flows without immediately processing real transactions.
Testing these edge cases is especially important because payment problems often happen outside the normal successful flow.
15. Move to Production Carefully
Once testing is complete, review the entire payment flow before switching to live transactions.
Check:
- Production API credentials
- HTTPS configuration
- Webhook URL
- Webhook signature verification
- Database updates
- Error handling
- Refund handling
- Logging and monitoring
- Email/SMS notifications
- Order confirmation logic
Make sure the production environment does not accidentally use test credentials or test endpoints.
16. Monitor the Integration
After launch, payment integration still needs monitoring.
Track metrics such as:
- Payment success rate
- Payment failure rate
- API response time
- Webhook failures
- Pending transactions
- Refund failures
- Duplicate events
- Reconciliation mismatches
For example, if payment failures suddenly increase from 3% to 15%, your monitoring system should help identify the problem quickly.
A Simple Payment Integration Architecture
A practical architecture can look like this:
Customer
↓
Frontend
↓
Your Backend
↓
Razorpay API
↓
Payment Network
↓
Webhook
↓
Your Webhook Handler
↓
Payment Database
↓
Order Confirmation
This approach keeps sensitive operations on the server while allowing the frontend to provide a smooth checkout experience.
Common Mistakes to Avoid
Trusting the frontend for payment confirmation
The frontend response should not be treated as the final source of truth.
Exposing API secrets
Secret credentials should never be included in browser-side code.
Ignoring webhooks
A browser can close or lose connectivity after a payment, so your backend needs another way to receive payment events.
Processing duplicate webhooks
Always design webhook processing to be idempotent.
Marking every timeout as a failed payment
A timeout means the result may be unknown, not necessarily unsuccessful.
Not testing edge cases
Successful payments are only one part of the payment lifecycle.
Conclusion
Integrating a payment gateway into a web application involves several connected components: APIs, checkout, server-side verification, webhooks, database updates, security, and failure handling.
Razorpay can be used as an example of how a modern payment gateway fits into this architecture, but the same principles apply to other payment providers.
The key is to build the integration around server-side verification, secure API communication, reliable webhook handling, idempotency, and clear payment states.
A payment integration should not simply answer the question, "Did the customer click Pay?" It should reliably determine whether the transaction actually succeeded and ensure that your application records that result correctly.
Top comments (1)
Great breakdown! Payment systems look simple from the outside, but the details really matter. Learned a lot from this guide.