Unified Checkout Integration
Orsunpay’s Unified Checkout provides a seamless payment experience that automatically displays the best payment methods for each customer based on their location, currency, and preferences.
Overview
The Unified Checkout process involves three main steps:
Create Session : Your backend creates a checkout session
Load SDK : Your frontend loads the Orsunpay Checkout SDK
Handle Events : Process payment success/failure events
Step 1: Create Checkout Session
First, create a checkout session from your backend:
cURL
JavaScript (Fetch)
Node.js (Axios)
curl "https://api.orsunpay.com/v1/checkout/sessions" \
-X POST \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "USD",
"country": "US",
"customer": {
"email": "[email protected] ",
"firstName": "John",
"lastName": "Doe"
},
"returnUrl": "https://yoursite.com/payment/return",
"cancelUrl": "https://yoursite.com/payment/cancel",
"metadata": {
"orderId": "order_123",
"productId": "prod_456"
}
}'
Session Response
{
"id" : "cs_clkj3h2n0000qjr8x4c5h6e7b" ,
"clientSecret" : "cs_clkj3h2n0000qjr8x4c5h6e7b_secret_abc123" ,
"amount" : 5000 ,
"currency" : "USD" ,
"country" : "US" ,
"status" : "open" ,
"expiresAt" : "2023-11-15T11:30:00.000Z" ,
"createdAt" : "2023-11-15T10:30:00.000Z"
}
Never expose your secret API key in frontend code. The clientSecret from the session response is safe to use in client-side code.
Session Parameters
Parameter Type Required Description amountinteger Yes Amount in smallest currency unit currencystring Yes Three-letter currency code countrystring Yes Customer’s country (ISO 3166-1 alpha-2) customerobject Yes Customer information returnUrlstring Yes URL to redirect after payment cancelUrlstring No URL to redirect if canceled methodsFilterarray No Restrict to specific payment methods localestring No Language preference (default: en-US) metadataobject No Custom key-value data
Step 2: Load Checkout SDK
Add the Orsunpay Checkout SDK to your page:
< script src = "https://checkout.orsunpay.com/sdk.js" async ></ script >
For sandbox testing:
< script src = "https://sandbox-checkout.orsunpay.com/sdk.js" async ></ script >
Step 3: Initialize Checkout
Basic Integration
< div id = "orsunpay-checkout" ></ div >
< script >
document . addEventListener ( 'DOMContentLoaded' , function () {
OrsunpayCheckout . mount ( '#orsunpay-checkout' , {
clientSecret: 'cs_clkj3h2n0000qjr8x4c5h6e7b_secret_abc123' ,
onReady : function () {
console . log ( 'Checkout is ready' );
},
onSuccess : function ( result ) {
console . log ( 'Payment succeeded:' , result );
// Redirect to success page or update UI
window . location . href = '/payment/success?transaction_id=' + result . transactionId ;
},
onFail : function ( error ) {
console . error ( 'Payment failed:' , error );
// Show error message to user
alert ( 'Payment failed: ' + error . message );
},
onCancel : function () {
console . log ( 'Payment canceled' );
// Handle cancellation
window . location . href = '/payment/cancel' ;
}
});
});
</ script >
Advanced Configuration
OrsunpayCheckout . mount ( '#orsunpay-checkout' , {
clientSecret: 'cs_clkj3h2n0000qjr8x4c5h6e7b_secret_abc123' ,
// Appearance customization
theme: 'light' , // 'light', 'dark', or 'auto'
locale: 'en-US' ,
// Display options
amount: 5000 ,
currency: 'USD' ,
// Payment method filtering
methodsFilter: [ 'card' , 'paypal' , 'apple_pay' ],
// Event handlers
onReady : function () {
document . getElementById ( 'loading' ). style . display = 'none' ;
},
onSuccess : function ( result ) {
// result.transactionId - Orsunpay transaction ID
// result.status - Transaction status
// result.metadata - Your custom metadata
// Update your order status
fetch ( '/api/orders/update' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
orderId: result . metadata . orderId ,
transactionId: result . transactionId ,
status: 'paid'
})
}). then (() => {
window . location . href = '/order/confirmation' ;
});
},
onFail : function ( error ) {
// error.code - Error code
// error.message - Human-readable message
// error.details - Additional error details
showErrorMessage ( error . message );
},
onCancel : function () {
// User canceled the payment
analytics . track ( 'checkout_canceled' );
window . location . href = '/cart' ;
}
});
Next.js Integration (App Router)
Server Component (Create Session)
// app/api/checkout/session/route.ts
import { NextRequest , NextResponse } from 'next/server' ;
const ORSUNPAY_API_KEY = process . env . ORSUNPAY_SECRET_KEY ;
const ORSUNPAY_API_URL = process . env . NODE_ENV === 'production'
? 'https://api.orsunpay.com/v1'
: 'https://sandbox-api.orsunpay.com/v1' ;
export async function POST ( request : NextRequest ) {
try {
const { amount , currency , customer , orderId } = await request . json ();
const response = await fetch ( ` ${ ORSUNPAY_API_URL } /checkout/sessions` , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ ORSUNPAY_API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
amount ,
currency ,
country: 'US' , // You might detect this from user's IP
customer ,
returnUrl: ` ${ process . env . NEXT_PUBLIC_BASE_URL } /payment/return` ,
cancelUrl: ` ${ process . env . NEXT_PUBLIC_BASE_URL } /payment/cancel` ,
metadata: { orderId }
})
});
const session = await response . json ();
if ( ! response . ok ) {
throw new Error ( session . error ?. message || 'Failed to create session' );
}
return NextResponse . json ({ clientSecret: session . clientSecret });
} catch ( error ) {
console . error ( 'Checkout session error:' , error );
return NextResponse . json (
{ error: 'Failed to create checkout session' },
{ status: 500 }
);
}
}
// app/checkout/checkout-form.tsx
'use client' ;
import { useEffect , useRef , useState } from 'react' ;
interface CheckoutFormProps {
amount : number ;
currency : string ;
customer : {
email : string ;
firstName : string ;
lastName : string ;
};
orderId : string ;
}
export default function CheckoutForm ({
amount ,
currency ,
customer ,
orderId
} : CheckoutFormProps ) {
const checkoutRef = useRef < HTMLDivElement >( null );
const [ isLoading , setIsLoading ] = useState ( true );
const [ error , setError ] = useState < string | null >( null );
useEffect (() => {
let mounted = true ;
async function initializeCheckout () {
try {
// Create checkout session
const response = await fetch ( '/api/checkout/session' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
amount ,
currency ,
customer ,
orderId
})
});
const { clientSecret , error } = await response . json ();
if ( error ) {
throw new Error ( error );
}
// Wait for Orsunpay SDK to load
if ( typeof window !== 'undefined' && window . OrsunpayCheckout ) {
mountCheckout ( clientSecret );
} else {
// Wait for SDK to load
const script = document . querySelector ( 'script[src*="checkout.orsunpay.com"]' );
if ( script ) {
script . addEventListener ( 'load' , () => {
if ( mounted ) {
mountCheckout ( clientSecret );
}
});
}
}
} catch ( err ) {
if ( mounted ) {
setError ( err instanceof Error ? err . message : 'Failed to initialize checkout' );
setIsLoading ( false );
}
}
}
function mountCheckout ( clientSecret : string ) {
if ( ! mounted || ! checkoutRef . current ) return ;
window . OrsunpayCheckout . mount ( checkoutRef . current , {
clientSecret ,
theme: 'light' ,
locale: 'en-US' ,
onReady () {
if ( mounted ) {
setIsLoading ( false );
}
},
onSuccess ( result ) {
console . log ( 'Payment succeeded:' , result );
// Redirect to success page
window . location . href = `/order/success?transaction_id= ${ result . transactionId } ` ;
},
onFail ( error ) {
console . error ( 'Payment failed:' , error );
if ( mounted ) {
setError ( error . message );
setIsLoading ( false );
}
},
onCancel () {
console . log ( 'Payment canceled' );
// Redirect back to cart
window . location . href = '/cart' ;
}
});
}
initializeCheckout ();
return () => {
mounted = false ;
};
}, [ amount , currency , customer , orderId ]);
if ( error ) {
return (
< div className = "checkout-error" >
< p > Payment Error : { error }</ p >
< button onClick = {() => window.location.reload()} >
Try Again
</ button >
</ div >
);
}
return (
< div className = "checkout-container" >
{ isLoading && (
< div className = "checkout-loading" >
Loading payment methods ...
</ div >
)}
< div ref = { checkoutRef } id = "orsunpay-checkout" />
</ div >
);
}
Page Component
// app/checkout/page.tsx
import Script from 'next/script' ;
import CheckoutForm from './checkout-form' ;
// This would typically come from your cart/order context
const orderData = {
amount: 5000 , // $50.00
currency: 'USD' ,
customer: {
email: '[email protected] ' ,
firstName: 'John' ,
lastName: 'Doe'
},
orderId: 'order_123'
};
export default function CheckoutPage () {
const checkoutSDKUrl = process . env . NODE_ENV === 'production'
? 'https://checkout.orsunpay.com/sdk.js'
: 'https://sandbox-checkout.orsunpay.com/sdk.js' ;
return (
< div className = "container mx-auto px-4 py-8" >
< div className = "max-w-2xl mx-auto" >
< h1 className = "text-2xl font-bold mb-6" > Complete Your Payment </ h1 >
< div className = "bg-white p-6 rounded-lg shadow" >
< CheckoutForm { ... orderData } />
</ div >
</ div >
< Script
src = { checkoutSDKUrl }
strategy = "beforeInteractive"
/>
</ div >
);
}
Event Handling
Success Event
The onSuccess callback receives a result object:
{
transactionId : 'tr_clkj3h2n0000qjr8x4c5h6e7b' ,
status : 'SUCCESS' ,
amount : 5000 ,
currency : 'USD' ,
metadata : {
orderId : 'order_123' ,
productId : 'prod_456'
}
}
Error Event
The onFail callback receives an error object:
{
code : 'card_declined' ,
message : 'Your card was declined' ,
details : {
declineReason : 'insufficient_funds'
}
}
Cancel Event
The onCancel callback is triggered when users explicitly cancel the payment flow.
Styling and Theming
Theme Options
OrsunpayCheckout . mount ( '#checkout' , {
clientSecret: 'cs_...' ,
// Theme options
theme: 'light' , // 'light', 'dark', 'auto'
// Custom CSS variables
style: {
'--primary-color' : '#007bff' ,
'--border-radius' : '8px' ,
'--font-family' : 'Arial, sans-serif'
}
});
CSS Customization
/* Custom styles for checkout container */
#orsunpay-checkout {
max-width : 400 px ;
margin : 0 auto ;
padding : 20 px ;
border : 1 px solid #e0e0e0 ;
border-radius : 8 px ;
background : white ;
}
/* Responsive design */
@media ( max-width : 768 px ) {
#orsunpay-checkout {
padding : 15 px ;
margin : 10 px ;
}
}
Best Practices
Security
Server-side session creation : Always create sessions from your secure backend
Webhook verification : Use webhooks as the authoritative source of payment status
Client secret protection : The client secret is safe for frontend use but should be unique per session
User Experience
Loading states : Show loading indicators while the checkout initializes
Error handling : Provide clear error messages and recovery options
Mobile optimization : Ensure the checkout works well on mobile devices
Accessibility : The checkout SDK is built with accessibility in mind
Integration
Webhook backup : Always implement webhook handling as primary status source
Idempotency : Handle duplicate success callbacks gracefully
Testing : Thoroughly test all payment flows in sandbox environment
Troubleshooting
Common Issues
Issue Solution SDK not loading Check script URL and network connectivity Client secret invalid Verify session creation on backend Payment methods not showing Check country/currency configuration Success callback not firing Verify webhook endpoint configuration
Debug Mode
Enable debug mode for additional logging:
OrsunpayCheckout . mount ( '#checkout' , {
clientSecret: 'cs_...' ,
debug: true // Enable debug logging
});
Always test your integration thoroughly in sandbox mode before going live. Use webhook notifications as the primary source of truth for payment status updates.