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 SDK Backend Implementation
- Node.js
- Rust
- PHP
- Python (FastAPI)
- Java
1
Install Dependencies
First, install our official Node SDK:
Copy
Ask AI
npm install @getivy/node-sdk
# or
yarn add @getivy/node-sdk
2
Create a Checkout Session
Create an endpoint on your server that initializes a checkout session:
server/routes/checkout.ts
Copy
Ask AI
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: "[email protected]",
}
};
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;
1
Add Dependencies
Add these dependencies to your
Cargo.toml:Copy
Ask AI
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
2
Create a Checkout Session
Create an endpoint that initializes a checkout session:Example usage with actix-web:
server/routes/checkout.rs
Copy
Ask AI
use serde::{Deserialize, Serialize};
use reqwest::header::{HeaderMap, HeaderValue};
#[derive(Serialize)]
struct Price {
total: f64,
currency: String,
}
#[derive(Serialize)]
struct Customer {
email: String,
}
#[derive(Serialize)]
struct CheckoutSession {
reference_id: String,
price: Price,
locale: String,
error_callback_url: String,
success_callback_url: String,
customer: Customer,
}
#[derive(Deserialize)]
struct CheckoutResponse {
redirect_url: String,
}
async fn create_checkout_session(amount: f64, currency: &str) -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let mut headers = HeaderMap::new();
headers.insert("x-ivx-api-key", HeaderValue::from_static("YOUR_API_KEY"));
let session = CheckoutSession {
reference_id: "order_123".to_string(),
price: Price {
total: amount,
currency: currency.to_string(),
},
locale: "en".to_string(),
error_callback_url: "https://example.com/error".to_string(),
success_callback_url: "https://example.com/success".to_string(),
customer: Customer {
email: "[email protected]".to_string(),
},
};
let response = client
.post("https://api.sand.getivy.de/api/service/checkout/session/create")
.headers(headers)
.json(&session)
.send()
.await?
.json::<CheckoutResponse>()
.await?;
Ok(response.redirect_url)
}
Copy
Ask AI
use actix_web::{web, App, HttpResponse, HttpServer};
async fn checkout(data: web::Json<serde_json::Value>) -> HttpResponse {
match create_checkout_session(
data.get("amount").and_then(|v| v.as_f64()).unwrap_or(0.0),
data.get("currency").and_then(|v| v.as_str()).unwrap_or("EUR"),
).await {
Ok(url) => HttpResponse::Ok().json(serde_json::json!({ "url": url })),
Err(e) => HttpResponse::BadRequest().json(serde_json::json!({
"error": format!("Failed to create checkout session: {}", e)
})),
}
}
1
Create a Checkout Session
Create an endpoint that initializes a checkout session:
server/routes/checkout.php
Copy
Ask AI
<?php
function createCheckoutSession($amount, $currency = 'EUR', $locale = 'en') {
$apiKey = 'YOUR_API_KEY';
$url = 'https://api.sand.getivy.de/api/service/checkout/session/create';
$data = [
'reference_id' => 'order_123', // Your internal unique order reference
'price' => [
'total' => $amount,
'currency' => $currency
],
'locale' => $locale,
'error_callback_url' => 'https://example.com/error',
'success_callback_url' => 'https://example.com/success',
'customer' => [
'email' => '[email protected]'
]
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-ivx-api-key: ' . $apiKey
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception('Failed to create checkout session');
}
$result = json_decode($response, true);
return $result['redirect_url'];
}
// Example usage in an endpoint
try {
$amount = $_POST['amount'] ?? 0;
$currency = $_POST['currency'] ?? 'EUR';
$redirectUrl = createCheckoutSession($amount, $currency);
header('Content-Type: application/json');
echo json_encode(['url' => $redirectUrl]);
} catch (Exception $e) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(['error' => $e->getMessage()]);
}
1
Install Dependencies
First, install the required packages:
Copy
Ask AI
pip install fastapi uvicorn requests
2
Create a Checkout Session
Create an endpoint that initializes a checkout session:
server/routes/checkout.py
Copy
Ask AI
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import requests
app = FastAPI()
class CheckoutRequest(BaseModel):
amount: float
currency: str = "EUR"
locale: str = "en"
@app.post("/api/checkout")
async def create_checkout_session(request: CheckoutRequest):
api_key = "YOUR_API_KEY"
url = "https://api.sand.getivy.de/api/service/checkout/session/create"
payload = {
"reference_id": "order_123", # Your internal unique order reference
"price": {
"total": request.amount,
"currency": request.currency
},
"locale": request.locale,
"error_callback_url": "https://example.com/error",
"success_callback_url": "https://example.com/success",
"customer": {
"email": "[email protected]"
}
}
headers = {
"Content-Type": "application/json",
"x-ivx-api-key": api_key
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
result = response.json()
return {"url": result["redirect_url"]}
except requests.exceptions.RequestException as e:
raise HTTPException(status_code=400, detail=f"Failed to create checkout session: {str(e)}")
# Run with: uvicorn checkout:app --reload
3
Run the Server
Start your FastAPI server:
Copy
Ask AI
uvicorn checkout:app --reload
1
Add Dependencies
Add these dependencies to your project:For Maven (For Gradle (
pom.xml):Copy
Ask AI
<dependencies>
<!-- Spring Boot starter (if using Spring) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<!-- JSON processing -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220320</version>
</dependency>
</dependencies>
build.gradle):Copy
Ask AI
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:2.7.0'
implementation 'org.json:json:20220320'
}
2
Create a Checkout Session
Create a service to handle the Ivy API calls:Create a controller to expose the endpoint:
src/main/java/com/example/service/IvyCheckoutService.java
Copy
Ask AI
package com.example.service;
import org.json.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class IvyCheckoutService {
private static final String API_URL = "https://api.sand.getivy.de/api/service/checkout/session/create";
private static final String API_KEY = "YOUR_API_KEY";
public String createCheckoutSession(double amount, String currency, String locale) {
RestTemplate restTemplate = new RestTemplate();
// Set headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("x-ivx-api-key", API_KEY);
// Create request body
JSONObject price = new JSONObject();
price.put("total", amount);
price.put("currency", currency);
JSONObject customer = new JSONObject();
customer.put("email", "[email protected]");
JSONObject requestBody = new JSONObject();
requestBody.put("reference_id", "order_123");
requestBody.put("price", price);
requestBody.put("locale", locale);
requestBody.put("error_callback_url", "https://example.com/error");
requestBody.put("success_callback_url", "https://example.com/success");
requestBody.put("customer", customer);
// Create request entity
HttpEntity<String> request = new HttpEntity<>(requestBody.toString(), headers);
// Make API call
ResponseEntity<String> response = restTemplate.postForEntity(API_URL, request, String.class);
// Parse response
JSONObject responseBody = new JSONObject(response.getBody());
return responseBody.getString("redirect_url");
}
}
src/main/java/com/example/controller/CheckoutController.java
Copy
Ask AI
package com.example.controller;
import com.example.service.IvyCheckoutService;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class CheckoutController {
@Autowired
private IvyCheckoutService ivyCheckoutService;
@PostMapping("/api/checkout")
public ResponseEntity<String> createCheckout(@RequestBody Map<String, Object> request) {
try {
double amount = Double.parseDouble(request.getOrDefault("amount", 0).toString());
String currency = request.getOrDefault("currency", "EUR").toString();
String locale = request.getOrDefault("locale", "en").toString();
String redirectUrl = ivyCheckoutService.createCheckoutSession(amount, currency, locale);
JSONObject response = new JSONObject();
response.put("url", redirectUrl);
return ResponseEntity.ok(response.toString());
} catch (Exception e) {
JSONObject error = new JSONObject();
error.put("error", "Failed to create checkout session: " + e.getMessage());
return ResponseEntity.badRequest().body(error.toString());
}
}
}
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
Desktop Web Implementation
- React
1
Install Dependencies
Copy
Ask AI
npm install @getivy/react-sdk
# or
yarn add @getivy/react-sdk
2
Create IvyCheckout Component
components/IvyCheckout.tsx
Copy
Ask AI
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.3
Add Styling
styles/ivy-checkout.css
Copy
Ask AI
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: 16px;
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: 450px) {
.ivy-modal-content {
padding: 16px 32px 32px;
}
}
@media (max-width: 450px) {
.ivy-modal-content .ivy-modal-iframe-container {
border-radius: 0px;
width: 100% !important;
height: 100% !important;
max-height: none !important;
}
}
Plain HTML
checkout.html
Copy
Ask AI
<!DOCTYPE html>
<html>
<head>
<title>Checkout</title>
<style>
.ivy-checkout {
width: 100%;
max-width: 500px;
margin: 0 auto;
}
.ivy-checkout-frame {
width: 100%;
height: 650px;
border: none;
border-radius: 8px;
}
</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 thepostMessage API.The iframe will send a message to the parent window with the following fields:Always equal to
"ivy"Always equal to
"iframe"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-popupsandallow-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
Mobile Web Implementation
1
Detect Mobile Devices
Copy
Ask AI
// Detect mobile devices
function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
2
Implement Redirect Logic
Copy
Ask AI
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.
3
Configure Return URLs
Set up proper return URLs for mobile redirects:
Copy
Ask AI
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
Copy
Ask AI
// ❌ Don't do this in native apps
<WebView source={{ uri: checkoutUrl }} />
// ✅ Do this instead
Linking.openURL(checkoutUrl);
Mobile Native App Implementation
Mobile Native App Implementation
1
React Native Implementation
Copy
Ask AI
// 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.
2
iOS Swift Implementation
Copy
Ask AI
// 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)
}
3
Android Kotlin Implementation
Copy
Ask AI
// Android Kotlin
fun openCheckoutInBrowser() {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(checkoutUrl))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
4
Configure Deep Linking
Set up deep linking to handle payment completion:
Copy
Ask AI
// 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?
- Contact [email protected]
- Check our API Reference for detailed endpoint documentation