Overview
Opinix Trade uses Cashfree Payment Gateway to process all financial transactions securely. You can deposit funds using UPI, cards, or net banking, and your balance is updated in real-time.
All payment processing is PCI-DSS compliant and uses bank-grade encryption to protect your financial information.
Payment gateway integration
Opinix integrates with Cashfree’s API version 2023-08-01:
Configuration
const CASHFREE_CONFIG = {
url: process . env . CASHFREE_URL ,
clientId: process . env . CASHFREE_CLIENT_ID ,
clientSecret: process . env . CASHFREE_CLIENT_SECRET ,
webhookUrl: process . env . CASHFREE_WEBHOOK_URL ,
apiVersion: "2023-08-01"
};
Environment variables
CASHFREE_URL = https://api.cashfree.com/pg/orders
CASHFREE_CLIENT_ID = your_client_id
CASHFREE_CLIENT_SECRET = your_client_secret
CASHFREE_WEBHOOK_URL = https://yourdomain.com/api/webhooks/cashfree
Never expose Cashfree credentials in client-side code. All API calls must be made from your backend.
Deposit flow
Here’s the complete deposit process from user action to balance update:
User initiates deposit
Navigate to /wallet/deposit and enter amount: const [ depositAmount , setDepositAmount ] = useState ( 0 );
const handleQuickAdd = ( amount : number ) => {
setDepositAmount ( prev => prev + amount );
};
// Quick-add buttons
< Button onClick = {() => handleQuickAdd (250)} >+ 250 </ Button >
< Button onClick = {() => handleQuickAdd (500)} >+ 500 </ Button >
< Button onClick = {() => handleQuickAdd (1000)} >+ 1000 </ Button >
Calculate transaction summary
System calculates GST and credits: const rechargeAmount = depositAmount ;
const gst = - ( depositAmount * 0.18 ). toFixed ( 2 );
const depositBalCredit = Number (
( depositAmount - Math . abs ( gst )). toFixed ( 2 )
);
const promotionalBalCredit = Math . abs ( gst );
const netBalance = depositAmount ;
setSummary ({
rechargeAmount ,
gst ,
depositBalCredit ,
promotionalBalCredit ,
netBalance
});
Example for ₹500 deposit: Recharge amount: ₹500.00
GST applicable: -₹90.00
Deposit bal. credit: ₹410.00
Promotional bal. credit: +₹90.00
Net Balance: ₹500.00
Click Recharge button
User confirms and initiates payment: async function handleRechargeClick () {
if ( ! data ?. user ) {
redirect ( "/api/auth/signin" );
}
const userid = data . user . id ;
const isRechargeDone = await DepositeMoneyInWallet (
userid ! ,
depositAmount
);
if ( isRechargeDone ?. success ) {
toast . success ( "Recharge Success" );
} else {
toast . error ( "Error While Recharging" );
}
}
Create payment order
Backend calls Cashfree API to initiate payment: export async function POST ( req : NextRequest ) {
try {
const body = await req . json ();
const response = await fetch ( process . env . CASHFREE_URL ! , {
method: "POST" ,
headers: {
"accept" : "application/json" ,
"content-type" : "application/json" ,
"x-api-version" : "2023-08-01" ,
"x-client-id" : process . env . CASHFREE_CLIENT_ID as string ,
"x-client-secret" : process . env . CASHFREE_CLIENT_SECRET as string
},
body: JSON . stringify ({
customer_details: {
customer_id: body . customer_id ,
customer_phone: body . customer_phone
},
order_id: body . order_id ,
order_amount: body . order_amount ,
order_currency: "INR" ,
order_meta: {
notify_url: process . env . CASHFREE_WEBHOOK_URL ,
payment_methods: "cc,dc,upi"
}
})
});
const data = await response . json ();
if ( ! response . ok ) {
return NextResponse . json (
{ message: data . message },
{ status: response . status }
);
}
return NextResponse . json ( data , { status: 200 });
} catch ( error ) {
return NextResponse . json (
{ message: "Something went wrong" , error },
{ status: 500 }
);
}
}
Redirect to Cashfree
User is redirected to Cashfree’s secure payment page where they:
Choose payment method (UPI/Card/Net Banking)
Enter payment details
Complete authentication (OTP/PIN)
Receive payment confirmation
Payment verification
After payment, Cashfree redirects to status endpoint: export async function GET (
req : NextRequest ,
{ params } : { params : { order_id : string } }
) {
const { order_id } = params ;
const response = await fetch (
process . env . CASHFREE_URL + order_id ,
{
method: "GET" ,
headers: {
"accept" : "application/json" ,
"content-type" : "application/json" ,
"x-api-version" : "2023-08-01" ,
"x-client-id" : process . env . CASHFREE_CLIENT_ID as string ,
"x-client-secret" : process . env . CASHFREE_CLIENT_SECRET as string
}
}
);
const data = await response . json ();
if ( data . order_status === "PAID" ) {
return NextResponse . redirect ( new URL ( "/" , req . url ));
} else {
return NextResponse . redirect ( new URL ( "/404" , req . url ));
}
}
Update wallet balance
On successful payment, wallet is credited: export const DepositeMoneyInWallet = async (
userId : string ,
amount : number
) => {
try {
const user = await prisma . user . findUnique ({
where: { id: userId }
});
if ( ! user ) {
return { success: false , message: "User not found." };
}
const newBalance = Math . max ( 0 , user . balance + amount );
await prisma . user . update ({
where: { id: userId },
data: { balance: newBalance }
});
return { success: true , message: "Deposit successful." };
} catch ( e ) {
console . error ( "Error depositing money:" , e );
return {
success: false ,
message: "Error while depositing money."
};
}
};
Supported payment methods
Cashfree supports multiple payment options:
UPI
Debit Cards
Credit Cards
Net Banking
Unified Payments Interface Supported UPI apps:
Google Pay
PhonePe
Paytm
BHIM
Amazon Pay
All bank UPI apps
Process:
Select UPI as payment method
Choose your UPI app or enter VPA
Approve payment in your UPI app
Instant credit (typically < 10 seconds)
Limits:
Minimum: ₹100
Maximum: ₹1,00,000 per transaction
Daily limit: As per your bank
Fees:
No processing fees
18% GST (credited back as promotional balance)
Bank debit cards Accepted cards:
Visa
Mastercard
RuPay
Maestro
Process:
Enter card number, expiry, CVV
Complete 3D Secure authentication
Enter OTP sent to registered mobile
Payment processed (2-5 minutes)
Limits:
Minimum: ₹100
Maximum: As per your bank’s limits
Fees:
No processing fees
18% GST (credited back)
International and domestic credit cards Accepted:
Visa
Mastercard
American Express (selected merchants)
Diners Club (selected merchants)
Process:
Enter card details
Complete 3D Secure verification
Enter OTP
Payment processed
Limits:
Minimum: ₹100
Maximum: Based on credit limit
Fees:
No processing fees
18% GST (credited back)
Direct bank transfer Supported banks:
State Bank of India
HDFC Bank
ICICI Bank
Axis Bank
All major PSU and private banks
Process:
Select your bank
Login to net banking
Authorize payment
Redirect back to Opinix
Limits:
Minimum: ₹100
Maximum: High limits (varies by bank)
Fees:
No processing fees
18% GST (credited back)
Payment methods configuration
Cashfree payment methods are specified in the order creation:
order_meta : {
notify_url : process . env . CASHFREE_WEBHOOK_URL ,
payment_methods : "cc,dc,upi" // Credit card, debit card, UPI
}
Available codes:
cc - Credit cards
dc - Debit cards
upi - UPI
nb - Net banking
app - Wallets (Paytm, etc.)
paylater - Pay later options
GST handling
India mandates 18% GST on online gaming and prediction markets:
GST calculation
const calculateGST = ( amount : number ) => {
const gstRate = 0.18 ; // 18%
const gstAmount = amount * gstRate ;
return {
grossAmount: amount ,
gst: gstAmount ,
netAmount: amount - gstAmount ,
promotionalCredit: gstAmount ,
finalBalance: amount // Net + promotional = gross
};
};
Example calculation
User deposits ₹1000
Gross deposit amount: ₹1000
GST deducted
GST (18%): ₹1000 × 0.18 = ₹180
Deposit balance credited
Net amount: ₹1000 - ₹180 = ₹820
Promotional balance credited
Promotional credit: ₹180
Final balance
Total available: ₹820 + ₹180 = ₹1000
While GST is technically deducted, you receive the full amount through promotional credits, so your trading balance equals your deposit.
Webhooks
Cashfree sends payment status updates via webhooks:
export async function POST ( req : NextRequest ) {
try {
const body = await req . json ();
// Verify webhook signature (recommended)
const isValid = verifyCashfreeSignature (
body ,
req . headers . get ( 'x-webhook-signature' )
);
if ( ! isValid ) {
return NextResponse . json (
{ error: "Invalid signature" },
{ status: 401 }
);
}
// Process payment based on event type
switch ( body . type ) {
case "PAYMENT_SUCCESS" :
await handleSuccessfulPayment ( body . data );
break ;
case "PAYMENT_FAILED" :
await handleFailedPayment ( body . data );
break ;
case "PAYMENT_PENDING" :
await handlePendingPayment ( body . data );
break ;
}
return NextResponse . json ({ success: true });
} catch ( error ) {
console . error ( "Webhook error:" , error );
return NextResponse . json (
{ error: "Webhook processing failed" },
{ status: 500 }
);
}
}
async function handleSuccessfulPayment ( data : any ) {
const { order_id , customer_id , order_amount } = data ;
// Credit user wallet
await DepositeMoneyInWallet ( customer_id , order_amount );
// Log transaction
await prisma . transaction . create ({
data: {
userId: customer_id ,
orderId: order_id ,
amount: order_amount ,
status: "SUCCESS" ,
type: "DEPOSIT"
}
});
}
Payment statuses
Cashfree payments can have various statuses:
ACTIVE
PAID
EXPIRED
FAILED
PENDING
Order created, awaiting payment
User redirected to payment page
Payment method not yet selected
No funds transferred
Next steps: User completes paymentPayment successful
Funds received by Cashfree
Wallet credited
User can start trading
Action: Redirect to homepagePayment session expired
User didn’t complete payment in time
Order canceled
No funds transferred
Action: User must create new orderPayment failed
Insufficient funds
Bank declined
Technical error
Action: User can retry or use different methodPayment processing
Payment initiated
Awaiting bank confirmation
May take a few minutes
Action: Wait for final status
Transaction tracking
All payment transactions are logged:
interface Transaction {
id : string ;
userId : string ;
orderId : string ;
amount : number ;
status : "SUCCESS" | "FAILED" | "PENDING" ;
type : "DEPOSIT" | "WITHDRAWAL" ;
createdAt : Date ;
updatedAt : Date ;
}
// Fetch user transactions
export async function getTransactions ( userId : string ) {
return await prisma . transaction . findMany ({
where: { userId },
orderBy: { createdAt: 'desc' },
take: 50
});
}
Withdrawals
Withdrawal functionality is currently under development. Users can request manual withdrawals by contacting support.
Planned withdrawal flow:
Request withdrawal
User enters amount and bank details
Validate request
Check available balance
Verify bank account
Apply withdrawal limits
Process payout
Use Cashfree Payout API: await fetch ( 'https://payout-api.cashfree.com/payout' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
beneId: user . bankAccountId ,
amount: withdrawalAmount ,
transferId: uniqueId
})
});
Deduct balance
Update user wallet: await prisma . user . update ({
where: { id: userId },
data: {
balance: { decrement: withdrawalAmount }
}
});
Confirm transfer
Bank transfer completes in 1-3 business days
Security measures
Cashfree is PCI-DSS Level 1 certified:
Card details never touch Opinix servers
All data encrypted in transit (TLS 1.2+)
Secure token-based transactions
Regular security audits
Verify webhook authenticity: function verifyCashfreeSignature (
payload : any ,
signature : string | null
) : boolean {
if ( ! signature ) return false ;
const computed = crypto
. createHmac ( 'sha256' , process . env . CASHFREE_CLIENT_SECRET ! )
. update ( JSON . stringify ( payload ))
. digest ( 'hex' );
return computed === signature ;
}
Generate unique, unpredictable order IDs: import { v4 as uuidv4 } from 'uuid' ;
const orderId = `OPN_ ${ Date . now () } _ ${ uuidv4 (). slice ( 0 , 8 ) } ` ;
// Example: OPN_1709308800000_a1b2c3d4
Server-side validation prevents tampering: if ( amount < 100 ) {
return { error: "Minimum deposit is ₹100" };
}
if ( amount > 100000 ) {
return { error: "Maximum deposit is ₹1,00,000" };
}
if ( ! Number . isInteger ( amount )) {
return { error: "Amount must be a whole number" };
}
Troubleshooting
Payment succeeded but balance not updated
Possible causes:
Webhook delay (wait 5-10 minutes)
Webhook failure (check server logs)
Database sync issue
Resolution:
Check transaction status via Cashfree dashboard
Verify webhook endpoint is accessible
Manually trigger balance update if needed
Contact support with order ID
Payment failed but amount deducted
What happened:
Bank authorized payment
Cashfree/Opinix rejected it
Bank holds amount as pending
Resolution:
Automatic reversal in 5-7 business days
Check with your bank for faster reversal
Don’t retry until reversal completes
Possible reasons:
VPA (UPI ID) entered incorrectly
UPI app not installed/updated
Network issues
Resolution:
Verify VPA format: username@bank
Update UPI app to latest version
Try different payment method
Use QR code option instead
Common reasons:
Insufficient balance
Daily limit exceeded
International transactions disabled
Incorrect CVV/OTP
Resolution:
Check card balance and limits
Enable online transactions in bank app
Verify CVV and try again
Contact your bank if issue persists
Payment limits
Payment Method Minimum Maximum (Per Transaction) Processing Time UPI ₹100 ₹1,00,000 Instant Debit Card ₹100 As per bank 2-5 minutes Credit Card ₹100 As per credit limit 2-5 minutes Net Banking ₹100 As per bank 5-15 minutes
Limits may vary based on your bank’s policies. Check with your bank for specific transaction limits.
Next steps