Guide
This guide walks you through the optimal client integrations for the Ivy checkout. Including:
Desktop Web - Use the React SDK and iframe to create a seamless experience for customers to complete their payment within your site.
Mobile Web - Redirect your users to the Ivy hosted checkout for a clean payment experience on smaller screens.
Mobile Native App - Redirect users to the Ivy hosted checkout to complete the payment and return directly to your app through deep linking.
SDK Backend Setup
The backend setup provides a secure, server-side integration to handle the communication between your application and Ivy’s API. It receives payment details from your frontend and return a checkout URL where users go to complete their payment.
Node SDK Backend Implementation
Node.js Rust PHP Python (FastAPI) Java
Install Dependencies
First, install our official Node SDK: npm install @getivy/node-sdk
# or
yarn add @getivy/node-sdk
Create a Checkout Session
Create an endpoint on your server that initializes a checkout session: server/routes/checkout.ts
import express from 'express' ;
import Ivy from '@getivy/node-sdk' ;
import { CheckoutsessionCreateParams } from '@getivy/node-sdk/resources/checkoutsession' ;
const router = express . Router ();
const ivy = new Ivy ( 'YOUR_API_KEY' );
router . post ( '/api/checkout' , async ( req , res ) => {
try {
const params : CheckoutsessionCreateParams = {
referenceId: 'order_123' , // Your internal unique order reference
price: {
total: req . body . amount ,
currency: req . body . currency || 'EUR' ,
},
locale: req . body . locale || 'en' , // The language of the checkout. Customers can change this in the checkout as well
errorCallbackUrl: "https://example.com/error" ,
successCallbackUrl: "https://example.com/success" ,
customer: {
email: "john.doe@example.com" ,
}
};
const session = await ivy . checkoutsession . create ( params );
res . json ({ url: session . redirectUrl });
} catch ( error ) {
console . error ( "Failed to create checkout session" , error );
res . status ( 400 ). json ({ error: 'Failed to create checkout session' });
}
});
export default router ;
Client Implementations
2.1. Desktop Web
For desktop web applications, iframe integration with our React SDK provides the best user experience and conversion rates.
embedded iframe : If you’re embedding the Ivy checkout into your page always append &iframe=true
to the checkout URL you receive back and render.modal iframe : If you’re overlaying your iframe in a modal always append &popup=true
to the checkout URL you receive back and render.The API can’t include these parameters by default as it doesn’t know your rendering method.
Desktop Web Implementation
Install Dependencies
npm install @getivy/react-sdk
# or
yarn add @getivy/react-sdk
Create IvyCheckout Component
components/IvyCheckout.tsx
import { IvyCheckout } from '@getivy/react-sdk'
export function Checkout ({
amount ,
currency = 'EUR' ,
locale = 'de' ,
handleSuccess ,
handleCancel
} : {
amount : number ,
currency ?: string ,
locale ?: string ,
handleSuccess : ( data : { redirectUrl : string , referenceId : string }) => void ,
handleCancel : ( data : { redirectUrl : string , referenceId : string }) => void
}) {
const response = await fetch ( '/api/checkout' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
amount ,
currency ,
locale
})
})
const { url } = await response . json ();
return (
< IvyCheckout
checkoutUrl = { url }
displayOptions = { {
type: "embedded" , // or "modal"
} }
onSuccess = { ({ redirectUrl , referenceId }) => {
handleSuccess ({ redirectUrl , referenceId });
} }
onCancel = { ({ redirectUrl , referenceId }) => {
handleCancel ({ redirectUrl , referenceId });
} }
/>
)
}
The React SDK automatically handles the &iframe=true
parameter for you when using embedded mode.
Add Styling
import " @getivy /react-sdk/dist/index.css";
.ivy-embedded-checkout-screen {
border : none ;
margin : 0 ;
padding : 0 ;
overflow : hidden ;
width : 100 % ;
height : 100 % ;
}
.ivy-modal-content {
position : fixed ;
z-index : 999999 ;
inset : 0 ;
color : #000 ;
background-color : rgba ( 10 , 10 , 10 , 0.25 );
display : -webkit-box ;
display : -ms-flexbox ;
display : flex ;
-webkit-box-orient : vertical ;
-webkit-box-direction : normal ;
-ms-flex-direction : column ;
flex-direction : column ;
-webkit-box-align : center ;
border : none ;
margin : 0 ;
padding : 0 ;
overflow : hidden ;
width : 100 % ;
height : 100 % ;
}
.ivy-modal-content {
position : fixed ;
z-index : 999999 ;
inset : 0 ;
color : #000 ;
background-color : rgba ( 10 , 10 , 10 , 0.25 );
display : -webkit-box ;
display : -ms-flexbox ;
display : flex ;
-webkit-box-orient : vertical ;
-webkit-box-direction : normal ;
-ms-flex-direction : column ;
flex-direction : column ;
-webkit-box-align : center ;
-ms-flex-align : center ;
align-items : center ;
-webkit-box-pack : center ;
-ms-flex-pack : center ;
justify-content : center ;
}
.ivy-modal-content .ivy-modal-iframe-container {
border : none ;
margin : 0 ;
padding : 0 ;
overflow : hidden ;
border-radius : 16 px ;
transform : translateZ ( 0 );
width : 100 % ;
height : 100 % ;
background-color : transparent ;
}
.ivy-modal-content .ivy-modal-checkout-screen {
border : none ;
margin : 0 ;
padding : 0 ;
overflow : hidden ;
width : 100 % !important ;
height : 100 % !important ;
}
@media ( min-width : 450 px ) {
.ivy-modal-content {
padding : 16 px 32 px 32 px ;
}
}
@media ( max-width : 450 px ) {
.ivy-modal-content .ivy-modal-iframe-container {
border-radius : 0 px ;
width : 100 % !important ;
height : 100 % !important ;
max-height : none !important ;
}
}
Plain HTML <! DOCTYPE html >
< html >
< head >
< title > Checkout </ title >
< style >
.ivy-checkout {
width : 100 % ;
max-width : 500 px ;
margin : 0 auto ;
}
.ivy-checkout-frame {
width : 100 % ;
height : 650 px ;
border : none ;
border-radius : 8 px ;
}
</ style >
</ head >
< body >
< div class = "ivy-checkout" >
< button onclick = " startCheckout ()" > Pay with Ivy </ button >
< div id = "iframe-container" ></ div >
</ div >
< script >
async function startCheckout () {
try {
const response = await fetch ( '/api/checkout' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
locale: 'de'
})
});
const { url } = await response . json ();
const container = document . getElementById ( 'iframe-container' );
// Always append iframe=true parameter for iframe rendering
const iframeUrl = url + '&iframe=true' ;
container . innerHTML = `
<iframe
src=" ${ iframeUrl } "
class="ivy-checkout-frame"
sandbox="allow-scripts allow-same-origin allow-popups allow-forms allow-popups-to-escape-sandbox allow-top-navigation"
allow="clipboard-write"
></iframe>
` ;
window . addEventListener ( 'message' , handleMessage );
} catch ( error ) {
console . error ( 'Checkout failed:' , error );
}
}
function handleMessage ( event ) {
try {
const { source , type , value , referenceId } = JSON . parse ( event . data );
if ( source !== 'ivy' || type !== 'iframe' ) return ;
if ( value === 'success' ) {
console . log ( 'Payment successful!' , referenceId );
// Handle success
} else if ( value === 'error' ) {
console . log ( 'Payment failed or cancelled' );
// Handle error
}
} catch ( error ) {
console . error ( 'Error processing message:' , error );
}
}
</ script >
</ body >
</ html >
Important : Always append &iframe=true
to the checkout URL when rendering in an iframe. The API doesn’t include this parameter by default.
Plain HTML Instead of redirecting to any callback URLs, the iframe communicates with the parent window via the postMessage
API . The iframe will send a message to the parent window with the following fields: Either "success"
or "error"
Your original checkoutSession
field referenceId
. Can also be used to fetch
a order
with the Retrieve an Order
endpoint. Security Considerations The iframe sandbox attributes are crucial for security. Each attribute serves a specific purpose:
allow-scripts
: Required for Ivy Checkout to function
allow-same-origin
: Enables secure communication
allow-forms
: Required for payment form input
allow-popups
and allow-popups-to-escape-sandbox
: Required for bank redirects
allow-top-navigation
: Required for completion redirects
For customers to be able to copy payment details with a button click we need clipboard access:
allow="clipboard-write"
: Required for Ivy Checkout to copy text into clipboard
2.2. Mobile Web
For mobile web applications, redirecting users to the Ivy checkout URL provides better performance, security, and user experience compared to iframe embedding.
An iframe on mobile web can break the flow for users while going to or returning from their bank.
Mobile Web Implementation
Detect Mobile Devices
// Detect mobile devices
function isMobile () {
return /Android | webOS | iPhone | iPad | iPod | BlackBerry | IEMobile | Opera Mini/ i . test ( navigator . userAgent );
}
Implement Redirect Logic
async function startCheckout () {
const response = await fetch ( '/api/checkout' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({ amount: 100 , currency: 'EUR' })
});
const { url } = await response . json ();
if ( isMobile ()) {
// Redirect to Ivy checkout on mobile
window . location . href = url ;
} else {
// Use iframe on desktop
const iframeUrl = url + '&iframe=true' ;
// ... iframe implementation
}
}
Mobile redirects provide better performance, security, and user experience compared to iframe embedding on mobile devices.
Configure Return URLs
Set up proper return URLs for mobile redirects: const params = {
referenceId: 'order_123' ,
price: { total: 100 , currency: 'EUR' },
successCallbackUrl: 'https://yourapp.com/payment-success' ,
errorCallbackUrl: 'https://yourapp.com/payment-error' ,
// ... other parameters
};
2.3. Mobile Native App
For native mobile applications, always open Ivy checkout in the user’s default browser. This is essential for proper deep linking and app redirects - using WebViews in native apps can interfere with:
Bank authentication flows
Deep linking back to your app
Security features and SSL certificate handling
User session management
Open Ivy’s checkout in browser for the best deeplinking user experience // ❌ Don't do this in native apps
< WebView source = { { uri: checkoutUrl } } />
// ✅ Do this instead
Linking . openURL ( checkoutUrl );
Mobile Native App Implementation
React Native Implementation
// React Native
import { Linking } from 'react-native' ;
const openCheckoutInBrowser = async () => {
const response = await fetch ( '/api/checkout' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({ amount: 100 , currency: 'EUR' })
});
const { url } = await response . json ();
// Open in default browser
const supported = await Linking . canOpenURL ( url );
if ( supported ) {
await Linking . openURL ( url );
} else {
console . error ( 'Cannot open URL:' , url );
}
};
Native App Requirement : Always open Ivy checkout in the user’s default browser, not in a WebView. This is essential for proper deep linking and app redirects.
iOS Swift Implementation
// iOS Swift
import SafariServices
func openCheckoutInBrowser () {
guard let url = URL ( string : checkoutUrl) else { return }
let safariVC = SFSafariViewController ( url : url)
safariVC. delegate = self
present (safariVC, animated : true )
}
Android Kotlin Implementation
// Android Kotlin
fun openCheckoutInBrowser () {
val intent = Intent (Intent.ACTION_VIEW, Uri. parse (checkoutUrl))
intent. addFlags (Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity (intent)
}
Configure Deep Linking
Set up deep linking to handle payment completion: // React Native
useEffect (() => {
const handleDeepLink = ( url ) => {
if ( url . includes ( 'payment-success' )) {
// Navigate to success screen
navigation . navigate ( 'PaymentSuccess' );
} else if ( url . includes ( 'payment-error' )) {
// Navigate to error screen
navigation . navigate ( 'PaymentError' );
}
};
Linking . addEventListener ( 'url' , handleDeepLink );
return () => Linking . removeEventListener ( 'url' , handleDeepLink );
}, []);
Configure your app’s deep linking scheme in your app configuration files to handle return URLs from Ivy checkout.
Need Help?