A Step-by-Step Guide: How to Integrate a Payment Gateway in a Custom PHP Website
Choosing the Right Payment Gateway for Your Business in India
The first critical step to integrate a payment gateway in a PHP custom website is selecting the right partner. The Indian market is saturated with options, each with its unique pricing structures, features, and integration complexities. Making the wrong choice can lead to higher costs, a poor user experience, and administrative headaches. Your decision should be based on a clear analysis of your business model, expected transaction volume, and technical capabilities. Key factors to consider include the Transaction Discount Rate (TDR), setup fees, Annual Maintenance Charges (AMC), settlement cycles, and the quality of their developer documentation and support. For a startup, a gateway with zero setup fees and a simple API might be ideal, while a high-volume enterprise might prioritize a lower TDR and advanced features like subscription billing or international payments.
To provide clarity, we've compared three popular payment gateways in India. This data is illustrative and you should always check the provider's official website for the latest pricing.
| Feature | Razorpay | PayU | Cashfree Payments |
|---|---|---|---|
| Standard TDR (Domestic) | 2% on most instruments | 2% + GST | 1.95% on most instruments |
| Setup Fee | ₹0 | ₹0 for standard plans | ₹0 |
| Annual Maintenance | ₹0 | Potentially applicable for custom plans | ₹0 |
| Settlement Cycle | T+2 Business Days (Instant available) | T+2 Business Days | T+1 Business Days (Instant available) |
| Integration Support | Excellent documentation, SDKs for major platforms | Good documentation, dedicated support managers | Comprehensive APIs, responsive tech support |
Expert Insight: Don't just look at the headline TDR. Analyze your typical transaction value. A small difference in percentage can translate to significant savings or costs at scale. Also, evaluate the user interface of the checkout page; a confusing or slow checkout process is a leading cause of cart abandonment.
Essential Prerequisites: Setting Up Your Sandbox and Getting API Keys
Before you write a single line of code, you must prepare your development environment. Every reputable payment gateway provides a sandbox environment—a self-contained testing ecosystem that mimics the live production environment. This allows you to simulate transactions, test your integration logic, and handle potential errors without moving real money. Attempting to build your integration directly with live credentials is a recipe for disaster, risking accidental charges and security breaches. The first step is to create a business account with your chosen gateway. This usually involves providing your business details, contact information, and banking details. While your live account activation might take a few days pending document verification (KYC), the sandbox is typically available instantly.
Once your account is created, you need to locate your API keys. These are the credentials your PHP application will use to authenticate itself with the gateway's servers. You will typically find two pairs of keys:
- Sandbox Keys: Used exclusively for testing in the sandbox environment.
- Production Keys: Used only when your website is live and ready to accept real payments.
Each pair usually consists of a Key ID (a public identifier) and a Key Secret (a private password). Navigate to the developer or API settings section of your gateway's dashboard to generate and view these keys.
Treat these keys with the utmost confidentiality. They grant access to your payment operations.Security Warning: Your Key Secret is like a password. Never expose it in your frontend HTML/JavaScript code. It should only be stored and used on your server-side PHP scripts. Store it securely using environment variables (.env file) rather than hardcoding it directly in your PHP files.
Backend Logic: How to Integrate Payment Gateway in PHP Custom Website with a Payment Request
With your sandbox keys ready, it's time to build the backend engine. This PHP script will be responsible for securely communicating with the payment gateway. Its primary job is to create a payment order or request on the gateway's server and receive a unique identifier for that transaction. Let's illustrate this using a common workflow with a hypothetical gateway SDK, which simplifies cURL requests.
First, ensure you have the gateway's official PHP SDK installed via Composer:
composer require vendor/gateway-sdk
Next, create a PHP file, let's call it create_payment.php. This script will receive the amount and customer details from your frontend form, initialize the SDK, and create an order.
<?php
require 'vendor/autoload.php';
// Use your favorite .env library to load environment variables
use Gateway\Api\Client;
// These should be stored in an .env file, not in the code!
$keyId = getenv('GATEWAY_KEY_ID');
$keySecret = getenv('GATEWAY_KEY_SECRET');
$api = new Client($keyId, $keySecret);
// Basic input validation
if (!isset($_POST['amount']) || !is_numeric($_POST['amount'])) {
http_response_code(400);
echo json_encode(['error' => 'Invalid amount']);
exit;
}
// Amount should be in the smallest currency unit (e.g., paise for INR)
$amountInPaise = (int)$_POST['amount'] * 100;
$receiptId = 'order_rcptid_' . uniqid();
try {
$order = $api->order->create([
'receipt' => $receiptId,
'amount' => $amountInPaise,
'currency' => 'INR',
'payment_capture' => 1 // Auto-capture the payment
]);
// Send order details back to the frontend
header('Content-Type: application/json');
echo json_encode([
'order_id' => $order['id'],
'amount' => $order['amount'],
'currency' => $order['currency'],
'key_id' => $keyId // Send public key to frontend
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
?>
Core Concept: Notice the amount is converted to paise (100 paise = 1 Rupee). This is a standard practice for many gateways to avoid floating-point precision issues. Always check your gateway's documentation for the required currency format. This backend script never exposes the Key Secret to the client.
Securely Handling the Payment Gateway Callback and Verifying the Transaction
After a user completes the payment on the gateway's page, the gateway needs to inform your server about the transaction's status (success or failure). This is done via a callback URL (also known as a webhook). This is the most critical step for security. You must configure this URL in your gateway dashboard, pointing it to a script on your server, let's call it verify_payment.php.
A common mistake is to trust the redirection that brings the user back to your "thank you" page. This can be easily faked. The only source of truth is the server-to-server callback. This callback will contain the payment details, but most importantly, it will include a payment signature. Your server must use this signature to verify that the callback is authentic and has not been tampered with.
The verification process typically involves creating a HMAC-SHA256 hash of the order ID and payment ID (or the raw POST body) using your Key Secret. If the hash you generate matches the signature sent by the gateway, the payment is legitimate.
<?php
require 'vendor/autoload.php';
// Again, load your secret from a secure location
$keySecret = getenv('GATEWAY_KEY_SECRET');
// The gateway sends these in the POST request to your callback URL
$receivedSignature = $_POST['gateway_signature'];
$orderId = $_POST['gateway_order_id'];
$paymentId = $_POST['gateway_payment_id'];
$payload = $orderId . '|' . $paymentId; // The exact format is gateway-specific
try {
// This is the crucial verification step
$api->utility->verifyPaymentSignature([
'razorpay_signature' => $receivedSignature,
'razorpay_payment_id' => $paymentId,
'razorpay_order_id' => $orderId
]);
// If the above line does not throw an exception, the signature is valid.
$isSignatureValid = true;
} catch (SignatureVerificationError $e) {
$isSignatureValid = false;
// Log the error, this could be a fraudulent attempt
error_log("Payment verification failed: " . $e->getMessage());
}
if ($isSignatureValid) {
// **CRITICAL:** Update your database
// 1. Check if you have already processed this order_id. This prevents duplicate processing.
// 2. Mark the order as 'paid'.
// 3. Store the payment_id for reference.
// 4. Send a confirmation email to the customer.
// Respond to the gateway to acknowledge receipt
http_response_code(200);
echo "OK";
} else {
// Respond with an error status. The gateway might retry.
http_response_code(400);
echo "Invalid signature";
}
?>
Idempotency is Key: Your verification script must be idempotent. This means if the gateway sends the same callback multiple times for the same successful payment (which can happen due to network issues), your script should not process the order more than once. Always check your database to see if the order has already been marked as paid before taking any action.
Frontend Integration: Building the HTML Form to Initiate Payment
The frontend is where the user's journey begins. For a custom PHP site, this is typically a simple HTML form that collects the necessary payment information. This form does not interact with the payment gateway directly. Instead, it securely sends the data to your backend PHP script (create_payment.php) using a POST request. This separation is vital for security, as it ensures your API secrets are never exposed to the user's browser.
Here’s a basic HTML form structure. We’ll use JavaScript (AJAX/Fetch API) to submit this form in the background, call our backend, and then use the response to trigger the gateway's checkout process.
<!-- This is the payment button on your product page -->
<form id="payment-form">
<div class="form-group">
<label for="customer-name">Name</label>
<input type="text" id="customer-name" name="name" required>
</div>
<div class="form-group">
<label for="customer-email">Email</label>
<input type="email" id="customer-email" name="email" required>
</div>
<div class="form-group">
<label for="amount">Amount (INR)</label>
<input type="number" id="amount" name="amount" value="500" required>
</div>
<button type="submit" id="pay-button">Pay Now</button>
</form>
<!-- You will need to include the gateway's checkout.js script on your page -->
<!-- <script src="https://checkout.gateway.com/v1/checkout.js"></script> -->
<script>
// NOTE: This is a conceptual script. Do not just copy-paste without understanding.
// The <script> tags are for illustration; remember the prompt's rules.
document.getElementById('payment-form').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const payButton = document.getElementById('pay-button');
payButton.disabled = true;
payButton.textContent = 'Processing...';
// Call your backend to create the order
fetch('create_payment.php', {
method: 'POST',
body: new URLSearchParams(formData)
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert('Error: ' + data.error);
payButton.disabled = false;
payButton.textContent = 'Pay Now';
return;
}
// The backend returns the necessary details
const options = {
"key": data.key_id,
"amount": data.amount,
"currency": data.currency,
"name": "Your Company Name",
"description": "Test Transaction",
"order_id": data.order_id,
"handler": function (response){
// This function is called on successful payment
// You can redirect to a thank you page here, but the real verification
// is happening on your callback URL (verify_payment.php)
alert('Payment Successful! Payment ID: ' + response.razorpay_payment_id);
window.location.href = '/thank-you.php';
},
"prefill": {
"name": document.getElementById('customer-name').value,
"email": document.getElementById('customer-email').value
}
};
// This line creates and opens the payment gateway's modal
var rzp = new GatewayCheckout(options);
rzp.open();
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred. Please try again.');
payButton.disabled = false;
payButton.textContent = 'Pay Now';
});
});
</script>
This script prevents the default form submission, sends the data to your backend, and upon receiving a successful response containing the `order_id`, it initializes and opens the gateway's beautiful, mobile-responsive checkout modal. The user never has to leave your site.
Go Live with Confidence: Let WovLab Handle Your Payment Gateway Integration
As this guide demonstrates, to properly integrate a payment gateway in a PHP custom website requires more than just copying a few lines of code. It demands a solid understanding of server-side logic, frontend scripting, and, most importantly, robust security practices. A single mistake in signature verification, improper handling of secrets, or a non-idempotent callback script can lead to fraudulent transactions, lost revenue, and a damaged reputation.
Why take the risk? At WovLab, we are experts in building secure, scalable, and seamless payment integration solutions for businesses across India. Our development team lives and breathes this stuff. We handle the complexities of sandbox testing, secure key management, backend order creation, and bulletproof callback verification so you can focus on what you do best: running your business.
Focus on growth, not on debugging payment flows. WovLab delivers production-ready payment integrations that work flawlessly from day one, ensuring every transaction is secure and accounted for.
Our expertise isn't limited to payments. WovLab is a full-service digital powerhouse, offering a suite of services to elevate your business, including bespoke AI Agent development, enterprise-grade ERP solutions with Frappe/ERPNext, strategic SEO and GEO marketing, and scalable cloud operations. When you partner with us for your payment gateway integration, you're not just getting a developer; you're gaining a technology partner invested in your success. Contact WovLab today and let's get you paid.
Ready to Get Started?
Let WovLab handle it for you — zero hassle, expert execution.
💬 Chat on WhatsApp