Widget Integration
Embed Ivy Checkout directly in your website with our iframe and modal integration
Guide
This guide walks you through embedding Ivy Checkout directly in your website using our iframe and modal integration. You’ll create a seamless checkout experience where customers can complete their payment without leaving your site.
Step 1: Backend Setup
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:
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;
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:
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;
Add Dependencies
Add these dependencies to your Cargo.toml
:
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
Create a Checkout Session
Create an endpoint that initializes a checkout session:
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: "john.doe@example.com".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)
}
Example usage with actix-web:
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)
})),
}
}
Create a Checkout Session
Create an endpoint that initializes a checkout session:
<?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' => 'john.doe@example.com'
]
];
$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()]);
}
Install Dependencies
First, install the required packages:
pip install fastapi uvicorn requests
Create a Checkout Session
Create an endpoint that initializes a checkout session:
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": "john.doe@example.com"
}
}
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
Run the Server
Start your FastAPI server:
uvicorn checkout:app --reload
Add Dependencies
Add these dependencies to your project:
For Maven (pom.xml
):
<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>
For Gradle (build.gradle
):
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:2.7.0'
implementation 'org.json:json:20220320'
}
Create a Checkout Session
Create a service to handle the Ivy API calls:
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", "john.doe@example.com");
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");
}
}
Create a controller to expose the endpoint:
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());
}
}
}
Step 2: Frontend Implementation
Choose your preferred frontend implementation:
Install Dependencies
npm install @getivy/react-sdk
# or
yarn add @getivy/react-sdk
Create IvyCheckout Component
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 });
}}
/>
)
}
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: 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;
}
}
Install Dependencies
npm install @getivy/react-sdk
# or
yarn add @getivy/react-sdk
Create IvyCheckout Component
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 });
}}
/>
)
}
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: 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;
}
}
<!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');
container.innerHTML = `
<iframe
src="${url}&iframe=true"
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>
Reference
React SDK
Check out the React SDK docs here
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:
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 functionallow-same-origin
: Enables secure communicationallow-forms
: Required for payment form inputallow-popups
andallow-popups-to-escape-sandbox
: Required for bank redirectsallow-top-navigation
: Required for completion redirects
Need Help?
- Contact help@getivy.de
- Check our API Reference for detailed endpoint documentation