Getting Started
Welcome to the Rabbit Payment Gateway API. This documentation will help you integrate our payment system into your applications.
Base URL
https://rabbitpay.in.net/api
Quick Start
- Create a merchant account at rabbitpay.in.net and generate API tokens
- Implement authentication using your API token and secret
- Create payment orders using the API
- Handle webhook notifications for payment status updates
Supported Languages
We provide integration examples for the following programming languages:
- PHP
- Python
- Node.js
- Java
- Ruby
- Go
- C# / .NET
- React
- Vue.js
Need Help?
Contact our support team for integration assistance.
Test APIAuthentication
Rabbit API uses API tokens for authentication. You need to include your API token in the request headers.
API Token Structure
- API Token:
pk_xxxxxxxxxxxxxxxxxxxxxxxxx(Public Key) - API Secret:
sk_xxxxxxxxxxxxxxxxxxxxxxxxx(Secret Key)
Authentication Methods
Header Authentication
Include your API token in the request header:
X-API-Token: pk_your_api_token_here
Content-Type: application/json
Signature Authentication (Recommended)
For enhanced security, use HMAC signature authentication:
X-API-Token: pk_your_api_token_here
X-Signature: sha256_signature_here
Content-Type: application/json
Signature Generation
// PHP Example
$string_to_sign = $api_token . '|' . json_encode($request_data) . '|' . time();
$signature = hash_hmac('sha256', $string_to_sign, $api_secret);
Create Payment Order
Create a new payment order for processing.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | Yes | Must be "create_order" |
amount |
decimal | Yes | Payment amount (e.g., 100.00) |
order_id |
string | Yes | Your unique order identifier |
customer_name |
string | Optional | Customer's full name |
customer_email |
string | Optional | Customer's email address |
customer_phone |
string | Optional | Customer's phone number |
product_name |
string | Optional | Description of the product/service |
redirect_url |
string | Optional | URL to redirect after payment |
webhook_url |
string | Optional | URL for payment notifications |
Example Request
// JavaScript/Node.js Example
const response = await fetch('https://yourdomain.com/api/payment.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Token': 'pk_your_api_token_here'
},
body: JSON.stringify({
action: 'create_order',
amount: 100.00,
order_id: 'ORDER_12345',
customer_name: 'John Doe',
customer_email: 'john@example.com',
customer_phone: '+91 9876543210',
product_name: 'Premium Subscription',
redirect_url: 'https://yoursite.com/success',
webhook_url: 'https://yoursite.com/webhook'
})
});
const data = await response.json();
// PHP Example
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://yourdomain.com/api/payment.php',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Token: pk_your_api_token_here'
],
CURLOPT_POSTFIELDS => json_encode([
'action' => 'create_order',
'amount' => 100.00,
'order_id' => 'ORDER_12345',
'customer_name' => 'John Doe',
'customer_email' => 'john@example.com',
'customer_phone' => '+91 9876543210',
'product_name' => 'Premium Subscription',
'redirect_url' => 'https://yoursite.com/success',
'webhook_url' => 'https://yoursite.com/webhook'
])
]);
$response = curl_exec($curl);
$data = json_decode($response, true);
// Java Example (using HttpClient - Java 11+)
import java.net.http.*;
import java.net.URI;
import org.json.JSONObject;
HttpClient client = HttpClient.newHttpClient();
JSONObject requestData = new JSONObject();
requestData.put("action", "create_order");
requestData.put("amount", 100.00);
requestData.put("order_id", "ORDER_12345");
requestData.put("customer_name", "John Doe");
requestData.put("customer_email", "john@example.com");
requestData.put("customer_phone", "+91 9876543210");
requestData.put("product_name", "Premium Subscription");
requestData.put("redirect_url", "https://yoursite.com/success");
requestData.put("webhook_url", "https://yoursite.com/webhook");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://yourdomain.com/api/payment.php"))
.header("Content-Type", "application/json")
.header("X-API-Token", "pk_your_api_token_here")
.POST(HttpRequest.BodyPublishers.ofString(requestData.toString()))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
JSONObject responseData = new JSONObject(response.body());
// Java Spring Boot Example (using RestTemplate)
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@Service
public class RabbitPayService {
private final RestTemplate restTemplate = new RestTemplate();
private final String API_URL = "https://yourdomain.com/api/payment.php";
private final String API_TOKEN = "pk_your_api_token_here";
public ResponseEntity<Map> createPaymentOrder(
double amount,
String orderId,
String customerName,
String customerEmail,
String customerPhone,
String productName,
String redirectUrl,
String webhookUrl
) {
// Prepare headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-API-Token", API_TOKEN);
// Prepare request body
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("action", "create_order");
requestBody.put("amount", amount);
requestBody.put("order_id", orderId);
requestBody.put("customer_name", customerName);
requestBody.put("customer_email", customerEmail);
requestBody.put("customer_phone", customerPhone);
requestBody.put("product_name", productName);
requestBody.put("redirect_url", redirectUrl);
requestBody.put("webhook_url", webhookUrl);
// Create request entity
HttpEntity<Map<String, Object>> entity =
new HttpEntity<>(requestBody, headers);
// Make API call
return restTemplate.exchange(
API_URL,
HttpMethod.POST,
entity,
Map.class
);
}
}
// Usage in Controller
@RestController
@RequestMapping("/api/payments")
public class PaymentController {
@Autowired
private RabbitPayService rabbitPayService;
@PostMapping("/create")
public ResponseEntity<?> createPayment(@RequestBody PaymentRequest request) {
ResponseEntity<Map> response = rabbitPayService.createPaymentOrder(
request.getAmount(),
request.getOrderId(),
request.getCustomerName(),
request.getCustomerEmail(),
request.getCustomerPhone(),
request.getProductName(),
"https://yoursite.com/success",
"https://yoursite.com/webhook"
);
return ResponseEntity.ok(response.getBody());
}
}
Success Response
{
"success": true,
"message": "Payment order created successfully",
"data": {
"transaction_id": "TXN_1728655234_5678",
"order_id": "ORDER_12345",
"amount": 100.00,
"currency": "INR",
"payment_url": "https://yourdomain.com/checkout.php?transaction_id=TXN_1728655234_5678",
"razorpay_order_id": "order_MkT6xhqPVeGpNl",
"status": "pending",
"created_at": "2025-10-11T10:30:00Z"
}
}
Error Response
{
"success": false,
"message": "Invalid API token",
"error_code": "AUTH_001"
}
Complete Integration Examples
Full working examples for integrating RabbitPay in different programming languages.
PHP Integration
1. Create Payment Order
<?php
// RabbitPay API Configuration
$apiUrl = 'https://rabbitpay.in.net/api/create-order.php';
$apiToken = 'pk_your_api_token_here';
// Payment Data
$paymentData = [
'user_token' => $apiToken,
'amount' => 100.00,
'order_id' => 'ORDER_' . time(),
'customer_mobile' => '9876543210',
'redirect_url' => 'https://yoursite.com/payment-callback.php',
'webhook_url' => 'https://yoursite.com/webhook.php',
'remark1' => 'Product Purchase',
'remark2' => 'customer@example.com'
];
// Make API Request
$curl = curl_init($apiUrl);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($paymentData),
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded'
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
$result = json_decode($response, true);
if ($result && $result['status']) {
// Success - redirect user to payment page
$paymentUrl = $result['result']['payment_url'];
header('Location: ' . $paymentUrl);
exit;
} else {
die('Error: ' . ($result['message'] ?? 'Payment creation failed'));
}
?>
2. Handle Webhook
<?php
// webhook.php - Handle RabbitPay webhooks
header('Content-Type: application/json');
// Get webhook data
$webhookData = json_decode(file_get_contents('php://input'), true);
// Log webhook for debugging
file_put_contents('webhook.log', date('Y-m-d H:i:s') . ': ' . json_encode($webhookData) . PHP_EOL, FILE_APPEND);
// Process webhook
if ($webhookData && isset($webhookData['status'])) {
$orderId = $webhookData['merchant_order_id'] ?? $webhookData['order_id'];
$status = $webhookData['status'];
$transactionId = $webhookData['transaction_id'];
if ($status === 'completed') {
// Update order status in your database
// updateOrderStatus($orderId, 'paid', $transactionId);
echo json_encode(['success' => true, 'message' => 'Webhook processed']);
}
} else {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid webhook data']);
}
?>
3. Check Order Status
<?php
$apiUrl = 'https://rabbitpay.in.net/api/check-order-status.php';
$apiToken = 'pk_your_api_token_here';
$orderId = 'ORDER_12345';
$curl = curl_init($apiUrl);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'user_token' => $apiToken,
'order_id' => $orderId
])
]);
$response = curl_exec($curl);
$result = json_decode($response, true);
curl_close($curl);
if ($result && $result['status'] !== 'ERROR') {
$paymentStatus = $result['result']['status'];
echo "Order Status: $paymentStatus";
}
?>
Python Integration
Installation
pip install requests
1. Create Payment Order
import requests
import time
class RabbitPayClient:
def __init__(self, api_token):
self.api_token = api_token
self.base_url = 'https://rabbitpay.in.net/api'
def create_order(self, amount, order_id, customer_mobile,
redirect_url, webhook_url, remark1='', remark2=''):
"""Create a payment order"""
url = f'{self.base_url}/create-order.php'
data = {
'user_token': self.api_token,
'amount': amount,
'order_id': order_id,
'customer_mobile': customer_mobile,
'redirect_url': redirect_url,
'webhook_url': webhook_url,
'remark1': remark1,
'remark2': remark2
}
response = requests.post(url, data=data)
return response.json()
def check_order_status(self, order_id):
"""Check order payment status"""
url = f'{self.base_url}/check-order-status.php'
data = {
'user_token': self.api_token,
'order_id': order_id
}
response = requests.post(url, data=data)
return response.json()
# Usage Example
client = RabbitPayClient('pk_your_api_token_here')
# Create payment order
order_id = f'ORDER_{int(time.time())}'
result = client.create_order(
amount=100.00,
order_id=order_id,
customer_mobile='9876543210',
redirect_url='https://yoursite.com/callback',
webhook_url='https://yoursite.com/webhook',
remark1='Product Purchase',
remark2='customer@example.com'
)
if result.get('status'):
payment_url = result['result']['payment_url']
print(f'Payment URL: {payment_url}')
else:
print(f'Error: {result.get("message")}')
2. Flask Webhook Handler
from flask import Flask, request, jsonify
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
@app.route('/webhook', methods=['POST'])
def webhook_handler():
"""Handle RabbitPay webhook notifications"""
try:
webhook_data = request.get_json()
logging.info(f'Webhook received: {webhook_data}')
if webhook_data and webhook_data.get('status') == 'completed':
order_id = webhook_data.get('merchant_order_id') or webhook_data.get('order_id')
transaction_id = webhook_data.get('transaction_id')
# Update order in your database
# update_order_status(order_id, 'paid', transaction_id)
return jsonify({'success': True, 'message': 'Webhook processed'})
return jsonify({'success': False, 'message': 'Invalid data'}), 400
except Exception as e:
logging.error(f'Webhook error: {str(e)}')
return jsonify({'success': False, 'message': str(e)}), 500
if __name__ == '__main__':
app.run(port=5000)
Node.js Integration
Installation
npm install axios express
1. Create Payment Order
const axios = require('axios');
class RabbitPayClient {
constructor(apiToken) {
this.apiToken = apiToken;
this.baseUrl = 'https://rabbitpay.in.net/api';
}
async createOrder({ amount, orderId, customerMobile, redirectUrl, webhookUrl, remark1 = '', remark2 = '' }) {
try {
const response = await axios.post(
`${this.baseUrl}/create-order.php`,
new URLSearchParams({
user_token: this.apiToken,
amount: amount,
order_id: orderId,
customer_mobile: customerMobile,
redirect_url: redirectUrl,
webhook_url: webhookUrl,
remark1: remark1,
remark2: remark2
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
return response.data;
} catch (error) {
throw new Error(`Payment creation failed: ${error.message}`);
}
}
async checkOrderStatus(orderId) {
try {
const response = await axios.post(
`${this.baseUrl}/check-order-status.php`,
new URLSearchParams({
user_token: this.apiToken,
order_id: orderId
})
);
return response.data;
} catch (error) {
throw new Error(`Status check failed: ${error.message}`);
}
}
}
// Usage Example
const client = new RabbitPayClient('pk_your_api_token_here');
async function processPayment() {
const orderId = `ORDER_${Date.now()}`;
try {
const result = await client.createOrder({
amount: 100.00,
orderId: orderId,
customerMobile: '9876543210',
redirectUrl: 'https://yoursite.com/callback',
webhookUrl: 'https://yoursite.com/webhook',
remark1: 'Product Purchase',
remark2: 'customer@example.com'
});
if (result.status) {
console.log('Payment URL:', result.result.payment_url);
// Redirect user to payment_url
}
} catch (error) {
console.error('Error:', error.message);
}
}
processPayment();
2. Express.js Webhook Handler
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const webhookData = req.body;
console.log('Webhook received:', webhookData);
if (webhookData && webhookData.status === 'completed') {
const orderId = webhookData.merchant_order_id || webhookData.order_id;
const transactionId = webhookData.transaction_id;
// Update order status in your database
// updateOrderStatus(orderId, 'paid', transactionId);
res.json({ success: true, message: 'Webhook processed' });
} else {
res.status(400).json({ success: false, message: 'Invalid webhook data' });
}
});
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
Ruby Integration
Installation
gem install httparty
Ruby Client
require 'httparty'
require 'json'
class RabbitPayClient
include HTTParty
base_uri 'https://rabbitpay.in.net/api'
def initialize(api_token)
@api_token = api_token
end
def create_order(amount:, order_id:, customer_mobile:, redirect_url:, webhook_url:, remark1: '', remark2: '')
response = self.class.post('/create-order.php',
body: {
user_token: @api_token,
amount: amount,
order_id: order_id,
customer_mobile: customer_mobile,
redirect_url: redirect_url,
webhook_url: webhook_url,
remark1: remark1,
remark2: remark2
}
)
JSON.parse(response.body)
end
def check_order_status(order_id)
response = self.class.post('/check-order-status.php',
body: {
user_token: @api_token,
order_id: order_id
}
)
JSON.parse(response.body)
end
end
# Usage
client = RabbitPayClient.new('pk_your_api_token_here')
result = client.create_order(
amount: 100.00,
order_id: "ORDER_#{Time.now.to_i}",
customer_mobile: '9876543210',
redirect_url: 'https://yoursite.com/callback',
webhook_url: 'https://yoursite.com/webhook',
remark1: 'Product Purchase',
remark2: 'customer@example.com'
)
if result['status']
puts "Payment URL: #{result['result']['payment_url']}"
else
puts "Error: #{result['message']}"
end
Rails Webhook Handler
# config/routes.rb
post '/webhook', to: 'webhooks#rabbit_pay'
# app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
def rabbit_pay
webhook_data = JSON.parse(request.body.read)
Rails.logger.info "Webhook received: #{webhook_data}"
if webhook_data['status'] == 'completed'
order_id = webhook_data['merchant_order_id'] || webhook_data['order_id']
transaction_id = webhook_data['transaction_id']
# Update order
# Order.find_by(order_id: order_id)&.update(status: 'paid', transaction_id: transaction_id)
render json: { success: true, message: 'Webhook processed' }
else
render json: { success: false, message: 'Invalid data' }, status: :bad_request
end
rescue JSON::ParserError => e
render json: { success: false, message: 'Invalid JSON' }, status: :bad_request
end
end
Go Integration
Go Client
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
)
type RabbitPayClient struct {
APIToken string
BaseURL string
}
type OrderRequest struct {
UserToken string `json:"user_token"`
Amount float64 `json:"amount"`
OrderID string `json:"order_id"`
CustomerMobile string `json:"customer_mobile"`
RedirectURL string `json:"redirect_url"`
WebhookURL string `json:"webhook_url"`
Remark1 string `json:"remark1"`
Remark2 string `json:"remark2"`
}
func NewRabbitPayClient(apiToken string) *RabbitPayClient {
return &RabbitPayClient{
APIToken: apiToken,
BaseURL: "https://rabbitpay.in.net/api",
}
}
func (c *RabbitPayClient) CreateOrder(amount float64, orderID, customerMobile, redirectURL, webhookURL, remark1, remark2 string) (map[string]interface{}, error) {
// Prepare form data
data := url.Values{}
data.Set("user_token", c.APIToken)
data.Set("amount", fmt.Sprintf("%.2f", amount))
data.Set("order_id", orderID)
data.Set("customer_mobile", customerMobile)
data.Set("redirect_url", redirectURL)
data.Set("webhook_url", webhookURL)
data.Set("remark1", remark1)
data.Set("remark2", remark2)
// Make request
resp, err := http.PostForm(c.BaseURL+"/create-order.php", data)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Parse response
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result map[string]interface{}
err = json.Unmarshal(body, &result)
return result, err
}
func (c *RabbitPayClient) CheckOrderStatus(orderID string) (map[string]interface{}, error) {
data := url.Values{}
data.Set("user_token", c.APIToken)
data.Set("order_id", orderID)
resp, err := http.PostForm(c.BaseURL+"/check-order-status.php", data)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result map[string]interface{}
err = json.Unmarshal(body, &result)
return result, err
}
func main() {
client := NewRabbitPayClient("pk_your_api_token_here")
orderID := fmt.Sprintf("ORDER_%d", time.Now().Unix())
result, err := client.CreateOrder(
100.00,
orderID,
"9876543210",
"https://yoursite.com/callback",
"https://yoursite.com/webhook",
"Product Purchase",
"customer@example.com",
)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
if result["status"].(bool) {
paymentURL := result["result"].(map[string]interface{})["payment_url"]
fmt.Printf("Payment URL: %s\n", paymentURL)
}
}
Go Webhook Handler
package main
import (
"encoding/json"
"log"
"net/http"
)
type WebhookData struct {
Event string `json:"event"`
TransactionID string `json:"transaction_id"`
OrderID string `json:"order_id"`
MerchantOrderID string `json:"merchant_order_id"`
Status string `json:"status"`
Amount float64 `json:"amount"`
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var webhookData WebhookData
err := json.NewDecoder(r.Body).Decode(&webhookData)
if err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
log.Printf("Webhook received: %+v", webhookData)
if webhookData.Status == "completed" {
orderID := webhookData.MerchantOrderID
if orderID == "" {
orderID = webhookData.OrderID
}
// Update order in database
// updateOrderStatus(orderID, "paid", webhookData.TransactionID)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
"message": "Webhook processed",
})
} else {
http.Error(w, "Invalid webhook data", http.StatusBadRequest)
}
}
func main() {
http.HandleFunc("/webhook", webhookHandler)
log.Println("Webhook server running on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
C# / .NET Integration
Installation
dotnet add package Newtonsoft.Json
C# Client
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class RabbitPayClient
{
private readonly string _apiToken;
private readonly string _baseUrl = "https://rabbitpay.in.net/api";
private readonly HttpClient _httpClient;
public RabbitPayClient(string apiToken)
{
_apiToken = apiToken;
_httpClient = new HttpClient();
}
public async Task CreateOrder(
decimal amount,
string orderId,
string customerMobile,
string redirectUrl,
string webhookUrl,
string remark1 = "",
string remark2 = "")
{
var data = new Dictionary
{
{ "user_token", _apiToken },
{ "amount", amount.ToString("F2") },
{ "order_id", orderId },
{ "customer_mobile", customerMobile },
{ "redirect_url", redirectUrl },
{ "webhook_url", webhookUrl },
{ "remark1", remark1 },
{ "remark2", remark2 }
};
var content = new FormUrlEncodedContent(data);
var response = await _httpClient.PostAsync($"{_baseUrl}/create-order.php", content);
var responseString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject(responseString);
}
public async Task CheckOrderStatus(string orderId)
{
var data = new Dictionary
{
{ "user_token", _apiToken },
{ "order_id", orderId }
};
var content = new FormUrlEncodedContent(data);
var response = await _httpClient.PostAsync($"{_baseUrl}/check-order-status.php", content);
var responseString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject(responseString);
}
}
// Usage Example
class Program
{
static async Task Main(string[] args)
{
var client = new RabbitPayClient("pk_your_api_token_here");
var orderId = $"ORDER_{DateTimeOffset.Now.ToUnixTimeSeconds()}";
var result = await client.CreateOrder(
amount: 100.00m,
orderId: orderId,
customerMobile: "9876543210",
redirectUrl: "https://yoursite.com/callback",
webhookUrl: "https://yoursite.com/webhook",
remark1: "Product Purchase",
remark2: "customer@example.com"
);
if (result.status == true)
{
Console.WriteLine($"Payment URL: {result.result.payment_url}");
}
else
{
Console.WriteLine($"Error: {result.message}");
}
}
}
ASP.NET Core Webhook Handler
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
[ApiController]
[Route("api/[controller]")]
public class WebhookController : ControllerBase
{
private readonly ILogger _logger;
public WebhookController(ILogger logger)
{
_logger = logger;
}
[HttpPost]
public async Task RabbitPayWebhook([FromBody] JsonElement webhookData)
{
_logger.LogInformation($"Webhook received: {webhookData}");
try
{
var status = webhookData.GetProperty("status").GetString();
if (status == "completed")
{
var orderId = webhookData.TryGetProperty("merchant_order_id", out var merchantOrderId)
? merchantOrderId.GetString()
: webhookData.GetProperty("order_id").GetString();
var transactionId = webhookData.GetProperty("transaction_id").GetString();
// Update order in database
// await UpdateOrderStatusAsync(orderId, "paid", transactionId);
return Ok(new { success = true, message = "Webhook processed" });
}
return BadRequest(new { success = false, message = "Invalid webhook data" });
}
catch (Exception ex)
{
_logger.LogError($"Webhook processing error: {ex.Message}");
return StatusCode(500, new { success = false, message = ex.Message });
}
}
}
Verify Payment Status
Verify the status of a payment transaction.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | Yes | Must be "verify_payment" |
transaction_id |
string | Yes | Transaction ID from create order response |
order_id |
string | Optional | Your original order ID |
Example Request
const response = await fetch('https://yourdomain.com/api/payment.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Token': 'pk_your_api_token_here'
},
body: JSON.stringify({
action: 'verify_payment',
transaction_id: 'TXN_1728655234_5678'
})
});
const data = await response.json();
// Java Example
import java.net.http.*;
import org.json.JSONObject;
HttpClient client = HttpClient.newHttpClient();
JSONObject requestData = new JSONObject();
requestData.put("action", "verify_payment");
requestData.put("transaction_id", "TXN_1728655234_5678");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://yourdomain.com/api/payment.php"))
.header("Content-Type", "application/json")
.header("X-API-Token", "pk_your_api_token_here")
.POST(HttpRequest.BodyPublishers.ofString(requestData.toString()))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
JSONObject data = new JSONObject(response.body());
// Java Spring Boot Example
@Service
public class RabbitPayService {
private final RestTemplate restTemplate = new RestTemplate();
private final String API_URL = "https://yourdomain.com/api/payment.php";
private final String API_TOKEN = "pk_your_api_token_here";
public ResponseEntity<Map> verifyPayment(String transactionId) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-API-Token", API_TOKEN);
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("action", "verify_payment");
requestBody.put("transaction_id", transactionId);
HttpEntity<Map<String, Object>> entity =
new HttpEntity<>(requestBody, headers);
return restTemplate.exchange(
API_URL,
HttpMethod.POST,
entity,
Map.class
);
}
}
// Usage in Controller
@GetMapping("/verify/{transactionId}")
public ResponseEntity<?> verifyPayment(@PathVariable String transactionId) {
ResponseEntity<Map> response = rabbitPayService.verifyPayment(transactionId);
return ResponseEntity.ok(response.getBody());
}
Success Response
{
"success": true,
"data": {
"transaction_id": "TXN_1728655234_5678",
"order_id": "ORDER_12345",
"amount": 100.00,
"currency": "INR",
"status": "completed",
"payment_method": "razorpay",
"razorpay_payment_id": "pay_MkT6xhqPVeGpNl",
"created_at": "2025-10-11T10:30:00Z",
"completed_at": "2025-10-11T10:32:15Z",
"customer": {
"name": "John Doe",
"email": "john@example.com",
"phone": "+91 9876543210"
}
}
}
Check Order Status
Get the current status of an order by order ID or transaction ID.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
order_id |
string | Yes* | Your order ID |
transaction_id |
string | Yes* | Rabbit Pay transaction ID |
Example Request
GET https://yourdomain.com/api/check-order-status.php?order_id=ORDER_12345
X-API-Token: pk_your_api_token_here
Response
{
"success": true,
"status": "completed",
"data": {
"order_id": "ORDER_12345",
"transaction_id": "TXN_1728655234_5678",
"amount": 100.00,
"currency": "INR",
"status": "completed",
"created_at": "2025-10-11T10:30:00Z",
"updated_at": "2025-10-11T10:32:15Z"
}
}
Webhooks
Webhooks allow you to receive real-time notifications when payment status changes.
Webhook Configuration
Configure webhook URLs in your merchant dashboard or include them in payment creation requests.
Webhook Payload
{
"event": "payment.completed",
"data": {
"transaction_id": "TXN_1728655234_5678",
"order_id": "ORDER_12345",
"amount": 100.00,
"currency": "INR",
"status": "completed",
"payment_method": "razorpay",
"razorpay_payment_id": "pay_MkT6xhqPVeGpNl",
"customer": {
"name": "John Doe",
"email": "john@example.com",
"phone": "+91 9876543210"
},
"timestamp": "2025-10-11T10:32:15Z"
},
"signature": "sha256_signature_for_verification"
}
Webhook Verification
// PHP Webhook Verification
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$calculated_signature = hash_hmac('sha256', $payload, $your_api_secret);
if (hash_equals($calculated_signature, $signature)) {
// Webhook is authentic
$data = json_decode($payload, true);
// Process the webhook data
} else {
// Invalid webhook
http_response_code(400);
echo 'Invalid signature';
}
// Node.js Webhook Handler
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const payload = JSON.stringify(req.body);
const signature = req.headers['x-signature'];
const apiSecret = 'sk_your_api_secret_here';
const calculatedSignature = crypto
.createHmac('sha256', apiSecret)
.update(payload)
.digest('hex');
if (signature === calculatedSignature) {
// Webhook is authentic
const data = req.body;
// Process webhook based on event type
switch(data.event) {
case 'payment.completed':
// Handle successful payment
console.log('Payment completed:', data.data);
break;
case 'payment.failed':
// Handle failed payment
console.log('Payment failed:', data.data);
break;
}
res.status(200).json({ success: true });
} else {
res.status(400).json({ error: 'Invalid signature' });
}
});
// Java Webhook Handler (using Servlet)
import javax.servlet.http.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.util.Base64;
import org.json.JSONObject;
public class WebhookServlet extends HttpServlet {
private static final String API_SECRET = "sk_your_api_secret_here";
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
// Read the payload
StringBuilder payload = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
payload.append(line);
}
String payloadStr = payload.toString();
String receivedSignature = request.getHeader("X-Signature");
// Calculate signature
String calculatedSignature = calculateHMAC(payloadStr, API_SECRET);
// Verify signature
if (calculatedSignature.equals(receivedSignature)) {
// Webhook is authentic
JSONObject data = new JSONObject(payloadStr);
String event = data.getString("event");
JSONObject eventData = data.getJSONObject("data");
// Process webhook based on event type
switch (event) {
case "payment.completed":
handlePaymentCompleted(eventData);
break;
case "payment.failed":
handlePaymentFailed(eventData);
break;
case "payment.cancelled":
handlePaymentCancelled(eventData);
break;
}
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\": true}");
} else {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("{\"error\": \"Invalid signature\"}");
}
}
private String calculateHMAC(String data, String secret) {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(
secret.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] hash = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException("Error calculating HMAC", e);
}
}
private void handlePaymentCompleted(JSONObject data) {
String transactionId = data.getString("transaction_id");
String orderId = data.getString("order_id");
double amount = data.getDouble("amount");
// Update your database
// Send confirmation email
// Fulfill the order
System.out.println("Payment completed for order: " + orderId);
}
private void handlePaymentFailed(JSONObject data) {
String transactionId = data.getString("transaction_id");
String orderId = data.getString("order_id");
// Update order status
// Notify customer
System.out.println("Payment failed for order: " + orderId);
}
private void handlePaymentCancelled(JSONObject data) {
String transactionId = data.getString("transaction_id");
String orderId = data.getString("order_id");
// Handle cancellation
System.out.println("Payment cancelled for order: " + orderId);
}
}
// Java Spring Boot Webhook Handler
import org.springframework.web.bind.annotation.*;
import org.springframework.http.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Map;
@RestController
@RequestMapping("/webhook")
public class WebhookController {
private static final String API_SECRET = "sk_your_api_secret_here";
@PostMapping("/rabbitpay")
public ResponseEntity<Map<String, Object>> handleWebhook(
@RequestBody Map<String, Object> payload,
@RequestHeader("X-Signature") String signature
) {
try {
// Convert payload to JSON string for verification
String payloadStr = new ObjectMapper().writeValueAsString(payload);
// Calculate and verify signature
String calculatedSignature = calculateHMAC(payloadStr, API_SECRET);
if (!calculatedSignature.equals(signature)) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("error", "Invalid signature"));
}
// Signature is valid, process webhook
String event = (String) payload.get("event");
Map<String, Object> data =
(Map<String, Object>) payload.get("data");
switch (event) {
case "payment.completed":
handlePaymentCompleted(data);
break;
case "payment.failed":
handlePaymentFailed(data);
break;
case "payment.cancelled":
handlePaymentCancelled(data);
break;
default:
System.out.println("Unknown event: " + event);
}
return ResponseEntity.ok(Map.of("success", true));
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", "Internal server error"));
}
}
private String calculateHMAC(String data, String secret) {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(
secret.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] hash = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException("Error calculating HMAC", e);
}
}
private void handlePaymentCompleted(Map<String, Object> data) {
String transactionId = (String) data.get("transaction_id");
String orderId = (String) data.get("order_id");
Double amount = (Double) data.get("amount");
// Process successful payment
// 1. Update order status in database
// 2. Send confirmation email to customer
// 3. Trigger order fulfillment
// 4. Update inventory
System.out.println("Payment completed for order: " + orderId
+ ", Amount: " + amount);
}
private void handlePaymentFailed(Map<String, Object> data) {
String transactionId = (String) data.get("transaction_id");
String orderId = (String) data.get("order_id");
// Handle failed payment
// 1. Update order status to failed
// 2. Notify customer about failure
// 3. Log the failure reason
System.out.println("Payment failed for order: " + orderId);
}
private void handlePaymentCancelled(Map<String, Object> data) {
String transactionId = (String) data.get("transaction_id");
String orderId = (String) data.get("order_id");
// Handle cancelled payment
// 1. Update order status to cancelled
// 2. Release reserved inventory
// 3. Notify relevant parties
System.out.println("Payment cancelled for order: " + orderId);
}
}
// Optional: Create a Service layer for better organization
@Service
public class PaymentWebhookService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private EmailService emailService;
@Transactional
public void processPaymentCompleted(Map<String, Object> data) {
String orderId = (String) data.get("order_id");
Double amount = (Double) data.get("amount");
// Update order in database
Order order = orderRepository.findByOrderId(orderId);
if (order != null) {
order.setStatus("COMPLETED");
order.setPaymentStatus("PAID");
order.setPaidAmount(amount);
order.setCompletedAt(LocalDateTime.now());
orderRepository.save(order);
// Send confirmation email
emailService.sendOrderConfirmation(order);
// Additional business logic
}
}
}
Webhook Security Best Practices
- Always verify signatures: Never trust webhook data without verification
- Use HTTPS: Ensure your webhook endpoint uses HTTPS
- Implement idempotency: Handle duplicate webhook deliveries gracefully
- Respond quickly: Return 200 OK within 5 seconds to avoid retries
- Process asynchronously: Queue webhook processing for heavy operations
- Log everything: Keep detailed logs of all webhook requests
- Handle retries: We retry failed webhooks up to 3 times with exponential backoff
Webhook Events
payment.created- Payment order createdpayment.completed- Payment successfully completedpayment.failed- Payment failedpayment.cancelled- Payment cancelled by user
Error Codes
| Error Code | HTTP Status | Description |
|---|---|---|
AUTH_001 |
401 | Invalid or missing API token |
AUTH_002 |
401 | Inactive API token |
AUTH_003 |
401 | Invalid signature |
PARAM_001 |
400 | Missing required parameter |
PARAM_002 |
400 | Invalid parameter format |
ORDER_001 |
409 | Duplicate order ID |
ORDER_002 |
404 | Order not found |
PAYMENT_001 |
422 | Payment processing failed |
RATE_001 |
429 | Rate limit exceeded |
SERVER_001 |
500 | Internal server error |
Java & Spring Boot Integration Guide
1. Maven Dependencies
Add these dependencies to your pom.xml:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA (for database) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- For Java 8 HTTP Client Alternative -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!-- Lombok (Optional - for cleaner code) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2. Configuration Class
Create a configuration class for Rabbit Pay settings:
// RabbitPayConfig.java
package com.yourcompany.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
@Configuration
@ConfigurationProperties(prefix = "rabbitpay")
@Data
public class RabbitPayConfig {
private String apiUrl;
private String apiToken;
private String apiSecret;
private String webhookUrl;
private String redirectUrl;
}
// application.properties or application.yml
rabbitpay.api-url=https://yourdomain.com/api/payment.php
rabbitpay.api-token=pk_your_api_token_here
rabbitpay.api-secret=sk_your_api_secret_here
rabbitpay.webhook-url=https://yoursite.com/webhook/rabbitpay
rabbitpay.redirect-url=https://yoursite.com/payment/success
3. DTOs (Data Transfer Objects)
// PaymentRequest.java
package com.yourcompany.dto;
import lombok.Data;
import lombok.Builder;
import com.fasterxml.jackson.annotation.JsonProperty;
@Data
@Builder
public class PaymentRequest {
private String action;
private Double amount;
@JsonProperty("order_id")
private String orderId;
@JsonProperty("customer_name")
private String customerName;
@JsonProperty("customer_email")
private String customerEmail;
@JsonProperty("customer_phone")
private String customerPhone;
@JsonProperty("product_name")
private String productName;
@JsonProperty("redirect_url")
private String redirectUrl;
@JsonProperty("webhook_url")
private String webhookUrl;
}
// PaymentResponse.java
package com.yourcompany.dto;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonProperty;
@Data
public class PaymentResponse {
private Boolean success;
private String message;
private PaymentData data;
@JsonProperty("error_code")
private String errorCode;
}
@Data
class PaymentData {
@JsonProperty("transaction_id")
private String transactionId;
@JsonProperty("order_id")
private String orderId;
private Double amount;
private String currency;
@JsonProperty("payment_url")
private String paymentUrl;
@JsonProperty("razorpay_order_id")
private String razorpayOrderId;
private String status;
@JsonProperty("created_at")
private String createdAt;
}
// WebhookPayload.java
package com.yourcompany.dto;
import lombok.Data;
import java.util.Map;
@Data
public class WebhookPayload {
private String event;
private Map<String, Object> data;
private String signature;
private String timestamp;
}
4. Service Layer - Complete Implementation
// RabbitPayService.java
package com.yourcompany.service;
import com.yourcompany.config.RabbitPayConfig;
import com.yourcompany.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpClientErrorException;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
@Service
@Slf4j
public class RabbitPayService {
@Autowired
private RabbitPayConfig config;
@Autowired
private RestTemplate restTemplate;
/**
* Create a payment order
*/
public PaymentResponse createPaymentOrder(
Double amount,
String orderId,
String customerName,
String customerEmail,
String customerPhone,
String productName
) {
try {
// Prepare headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-API-Token", config.getApiToken());
// Build request
PaymentRequest request = PaymentRequest.builder()
.action("create_order")
.amount(amount)
.orderId(orderId)
.customerName(customerName)
.customerEmail(customerEmail)
.customerPhone(customerPhone)
.productName(productName)
.redirectUrl(config.getRedirectUrl())
.webhookUrl(config.getWebhookUrl())
.build();
HttpEntity<PaymentRequest> entity =
new HttpEntity<>(request, headers);
// Make API call
ResponseEntity<PaymentResponse> response = restTemplate.exchange(
config.getApiUrl(),
HttpMethod.POST,
entity,
PaymentResponse.class
);
log.info("Payment order created successfully: {}",
response.getBody().getData().getTransactionId());
return response.getBody();
} catch (HttpClientErrorException e) {
log.error("Error creating payment order: {}", e.getMessage());
throw new RuntimeException("Payment creation failed: "
+ e.getResponseBodyAsString());
} catch (Exception e) {
log.error("Unexpected error: {}", e.getMessage());
throw new RuntimeException("Payment creation failed", e);
}
}
/**
* Verify payment status
*/
public PaymentResponse verifyPayment(String transactionId) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-API-Token", config.getApiToken());
PaymentRequest request = PaymentRequest.builder()
.action("verify_payment")
.build();
// Add transaction_id to the request
Map<String, Object> requestMap = new HashMap<>();
requestMap.put("action", "verify_payment");
requestMap.put("transaction_id", transactionId);
HttpEntity<Map<String, Object>> entity =
new HttpEntity<>(requestMap, headers);
ResponseEntity<PaymentResponse> response = restTemplate.exchange(
config.getApiUrl(),
HttpMethod.POST,
entity,
PaymentResponse.class
);
log.info("Payment verification result: {}",
response.getBody().getData().getStatus());
return response.getBody();
} catch (Exception e) {
log.error("Error verifying payment: {}", e.getMessage());
throw new RuntimeException("Payment verification failed", e);
}
}
/**
* Calculate HMAC signature for webhook verification
*/
public String calculateHMAC(String data, String secret) {
try {
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(
secret.getBytes(StandardCharsets.UTF_8),
"HmacSHA256"
);
sha256HMAC.init(secretKey);
byte[] hash = sha256HMAC.doFinal(
data.getBytes(StandardCharsets.UTF_8)
);
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException("Error calculating HMAC", e);
}
}
/**
* Verify webhook signature
*/
public boolean verifyWebhookSignature(String payload, String signature) {
String calculatedSignature = calculateHMAC(
payload,
config.getApiSecret()
);
return calculatedSignature.equals(signature);
}
}
// RestTemplate Configuration
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
5. Controller - Payment Endpoints
// PaymentController.java
package com.yourcompany.controller;
import com.yourcompany.dto.*;
import com.yourcompany.service.RabbitPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequestMapping("/api/payments")
@Slf4j
public class PaymentController {
@Autowired
private RabbitPayService rabbitPayService;
/**
* Create a new payment order
*/
@PostMapping("/create")
public ResponseEntity<?> createPayment(
@RequestBody CreatePaymentRequest request
) {
try {
PaymentResponse response = rabbitPayService.createPaymentOrder(
request.getAmount(),
request.getOrderId(),
request.getCustomerName(),
request.getCustomerEmail(),
request.getCustomerPhone(),
request.getProductName()
);
if (response.getSuccess()) {
return ResponseEntity.ok(response);
} else {
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
log.error("Error creating payment", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", e.getMessage()));
}
}
/**
* Verify payment status
*/
@GetMapping("/verify/{transactionId}")
public ResponseEntity<?> verifyPayment(
@PathVariable String transactionId
) {
try {
PaymentResponse response =
rabbitPayService.verifyPayment(transactionId);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("Error verifying payment", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", e.getMessage()));
}
}
/**
* Payment success callback page
*/
@GetMapping("/success")
public ResponseEntity<String> paymentSuccess(
@RequestParam(required = false) String transaction_id,
@RequestParam(required = false) String order_id
) {
log.info("Payment success callback - Transaction: {}, Order: {}",
transaction_id, order_id);
// Redirect to frontend success page or return success view
return ResponseEntity.ok("Payment successful!");
}
}
// CreatePaymentRequest.java
@Data
public class CreatePaymentRequest {
private Double amount;
private String orderId;
private String customerName;
private String customerEmail;
private String customerPhone;
private String productName;
}
6. Webhook Controller - Complete Implementation
// WebhookController.java
package com.yourcompany.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yourcompany.dto.WebhookPayload;
import com.yourcompany.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@RestController
@RequestMapping("/webhook")
@Slf4j
public class WebhookController {
@Autowired
private RabbitPayService rabbitPayService;
@Autowired
private OrderService orderService;
@Autowired
private EmailService emailService;
@Autowired
private ObjectMapper objectMapper;
/**
* Handle Rabbit Pay webhook notifications
*/
@PostMapping("/rabbitpay")
public ResponseEntity<Map<String, Object>> handleRabbitPayWebhook(
@RequestBody String payload,
@RequestHeader(value = "X-Signature", required = false) String signature
) {
log.info("Received webhook payload");
try {
// Verify signature
if (signature == null || signature.isEmpty()) {
log.error("Missing webhook signature");
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("error", "Missing signature"));
}
if (!rabbitPayService.verifyWebhookSignature(payload, signature)) {
log.error("Invalid webhook signature");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(Map.of("error", "Invalid signature"));
}
// Parse payload
WebhookPayload webhookData =
objectMapper.readValue(payload, WebhookPayload.class);
String event = webhookData.getEvent();
Map<String, Object> data = webhookData.getData();
log.info("Processing webhook event: {}", event);
// Process webhook based on event type
switch (event) {
case "payment.created":
handlePaymentCreated(data);
break;
case "payment.completed":
handlePaymentCompleted(data);
break;
case "payment.failed":
handlePaymentFailed(data);
break;
case "payment.cancelled":
handlePaymentCancelled(data);
break;
default:
log.warn("Unknown webhook event: {}", event);
}
// Return success response
return ResponseEntity.ok(Map.of(
"success", true,
"message", "Webhook processed successfully"
));
} catch (Exception e) {
log.error("Error processing webhook", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", "Internal server error"));
}
}
private void handlePaymentCreated(Map<String, Object> data) {
String orderId = (String) data.get("order_id");
String transactionId = (String) data.get("transaction_id");
log.info("Payment created - Order: {}, Transaction: {}",
orderId, transactionId);
// Update order status to 'payment_initiated'
orderService.updateOrderStatus(orderId, "PAYMENT_INITIATED");
}
private void handlePaymentCompleted(Map<String, Object> data) {
String orderId = (String) data.get("order_id");
String transactionId = (String) data.get("transaction_id");
Double amount = ((Number) data.get("amount")).doubleValue();
String paymentMethod = (String) data.get("payment_method");
log.info("Payment completed - Order: {}, Amount: {}",
orderId, amount);
// Update order in database
orderService.markOrderAsPaid(
orderId,
transactionId,
amount,
paymentMethod
);
// Send confirmation email
emailService.sendPaymentConfirmation(orderId);
// Trigger order fulfillment
orderService.initiateOrderFulfillment(orderId);
// Update inventory
orderService.updateInventory(orderId);
}
private void handlePaymentFailed(Map<String, Object> data) {
String orderId = (String) data.get("order_id");
String transactionId = (String) data.get("transaction_id");
log.warn("Payment failed - Order: {}", orderId);
// Update order status
orderService.updateOrderStatus(orderId, "PAYMENT_FAILED");
// Notify customer
emailService.sendPaymentFailureNotification(orderId);
// Release any reserved inventory
orderService.releaseReservedInventory(orderId);
}
private void handlePaymentCancelled(Map<String, Object> data) {
String orderId = (String) data.get("order_id");
String transactionId = (String) data.get("transaction_id");
log.info("Payment cancelled - Order: {}", orderId);
// Update order status
orderService.updateOrderStatus(orderId, "PAYMENT_CANCELLED");
// Release inventory
orderService.releaseReservedInventory(orderId);
}
}
7. Usage Example - Complete Flow
// Example: E-commerce Checkout Flow
@Service
public class CheckoutService {
@Autowired
private RabbitPayService rabbitPayService;
@Autowired
private OrderRepository orderRepository;
@Transactional
public PaymentResponse initiateCheckout(CheckoutRequest request) {
// 1. Create order in your database
Order order = new Order();
order.setOrderId(generateOrderId());
order.setCustomerName(request.getCustomerName());
order.setCustomerEmail(request.getCustomerEmail());
order.setCustomerPhone(request.getCustomerPhone());
order.setAmount(request.getAmount());
order.setStatus("PENDING");
order.setCreatedAt(LocalDateTime.now());
orderRepository.save(order);
// 2. Create payment with Rabbit Pay
PaymentResponse paymentResponse = rabbitPayService.createPaymentOrder(
order.getAmount(),
order.getOrderId(),
order.getCustomerName(),
order.getCustomerEmail(),
order.getCustomerPhone(),
request.getProductName()
);
// 3. Store transaction ID
if (paymentResponse.getSuccess()) {
order.setTransactionId(
paymentResponse.getData().getTransactionId()
);
order.setPaymentUrl(
paymentResponse.getData().getPaymentUrl()
);
orderRepository.save(order);
}
return paymentResponse;
}
private String generateOrderId() {
return "ORD_" + System.currentTimeMillis() +
"_" + (int)(Math.random() * 10000);
}
}
// Usage in your checkout API
@PostMapping("/checkout")
public ResponseEntity<?> checkout(@RequestBody CheckoutRequest request) {
PaymentResponse response = checkoutService.initiateCheckout(request);
if (response.getSuccess()) {
// Redirect user to payment URL
return ResponseEntity.ok(Map.of(
"payment_url", response.getData().getPaymentUrl(),
"transaction_id", response.getData().getTransactionId()
));
} else {
return ResponseEntity.badRequest().body(response);
}
}
8. Testing Your Integration
// Unit Test Example
@SpringBootTest
class RabbitPayServiceTest {
@Autowired
private RabbitPayService rabbitPayService;
@Test
void testCreatePaymentOrder() {
PaymentResponse response = rabbitPayService.createPaymentOrder(
100.00,
"TEST_ORDER_123",
"Test Customer",
"test@example.com",
"+91 9876543210",
"Test Product"
);
assertNotNull(response);
assertTrue(response.getSuccess());
assertNotNull(response.getData().getTransactionId());
assertNotNull(response.getData().getPaymentUrl());
}
@Test
void testWebhookSignatureVerification() {
String payload = "{\"event\":\"payment.completed\"}";
String signature = rabbitPayService.calculateHMAC(
payload,
"sk_test_secret"
);
boolean isValid = rabbitPayService.verifyWebhookSignature(
payload,
signature
);
assertTrue(isValid);
}
}
Testing Tips:
- Use test API credentials during development
- Test webhook signatures with sample payloads
- Implement idempotency for webhook handlers
- Log all API requests and responses
- Test error scenarios (network failures, invalid tokens, etc.)
Integration Checklist
- ✅ Add Maven dependencies
- ✅ Configure API credentials
- ✅ Implement payment creation
- ✅ Implement payment verification
- ✅ Set up webhook endpoint
- ✅ Verify webhook signatures
- ✅ Handle all webhook events
- ✅ Implement error handling
- ✅ Add logging
- ✅ Write unit tests
- ✅ Test in production with real credentials