Perfect for airlines that want seat selection on confirmation pages with minimal development effort.
What you need:
Setup: 1. Add this code to GTM:
<script>
(function() {
// Ultra-minimal GTM injectable - just loads the script, no logic
var script = document.createElement('script');
script.src = 'https://departcart.com/static/loaders-dist/seat_selection_loader.js';
script.setAttribute('data-gtm-source', 'true');
document.head.appendChild(script);
})();
</script>
Result: Automatic seat selection panels appear on confirmation pages, securely encrypted URLs, fully responsive design.
⭐ This is the recommended approach for maximum performance, security, and reliability. Generate encrypted URLs directly in your application without API dependencies.
Benefits: - No API Dependencies: Generate encrypted URLs without calling our servers - Better Performance: No network latency or API rate limits - Higher Reliability: Works even during temporary service outages - Enhanced Security: PNR data is encrypted before leaving your application - Batch Processing: Handle thousands of URLs efficiently
Implementation Steps:
Implementation Steps:
pip install cryptography
dotnet add package Fernet
import json
import base64
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
class DepartCartEncryption:
# Brand-specific credentials (provided during onboarding)
BRAND_CREDENTIALS = {
"your_brand": {
"password": "your-brand-secret-key-2025", # From onboarding
"salt": "your_brand_salt_2025" # From onboarding
}
}
def __init__(self, brand_name):
self.brand_name = brand_name
self.credentials = self.BRAND_CREDENTIALS[brand_name]
self._encryption_key = self._derive_encryption_key()
def _derive_encryption_key(self):
password = self.credentials["password"].encode()
salt = self.credentials["salt"].encode()
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
)
return base64.urlsafe_b64encode(kdf.derive(password))
def encrypt_pnr_data(self, pnr, last_name):
data = {
"pnr": pnr.strip().upper(),
"last_name": last_name.strip().upper(),
"brand": self.brand_name
}
json_data = json.dumps(data)
fernet = Fernet(self._encryption_key)
encrypted_data = fernet.encrypt(json_data.encode())
return base64.urlsafe_b64encode(encrypted_data).decode()
def generate_seatmap_url(self, pnr, last_name, source="cart"):
encrypted_id = self.encrypt_pnr_data(pnr, last_name)
return f"https://departcart.com/seatmap?id={encrypted_id}&source={source}"
# Usage in your booking flow
encryptor = DepartCartEncryption("your_brand")
seatmap_url = encryptor.generate_seatmap_url("ABC123", "SMITH")
print(f"Seat selection: {seatmap_url}")
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using FernetDotNet;
public class DepartCartEncryption
{
// Brand-specific credentials (provided during onboarding)
private static readonly Dictionary<string, BrandCredentials> BrandCredentials = new()
{
{
"your_brand", new BrandCredentials
{
Password = "your-brand-secret-key-2025", // From onboarding
Salt = "your_brand_salt_2025" // From onboarding
}
}
};
private readonly string _brandName;
private readonly BrandCredentials _credentials;
private readonly IFernet _fernet;
public DepartCartEncryption(string brandName)
{
_brandName = brandName;
_credentials = BrandCredentials[brandName];
_fernet = CreateFernetInstance();
}
private IFernet CreateFernetInstance()
{
using var pbkdf2 = new Rfc2898DeriveBytes(
_credentials.Password,
Encoding.UTF8.GetBytes(_credentials.Salt),
100000,
HashAlgorithmName.SHA256
);
var key = pbkdf2.GetBytes(32);
var base64Key = Convert.ToBase64String(key)
.Replace('+', '-')
.Replace('/', '_');
return new Fernet(base64Key);
}
public string EncryptPnrData(string pnr, string lastName)
{
var data = new
{
pnr = pnr.Trim().ToUpperInvariant(),
last_name = lastName.Trim().ToUpperInvariant(),
brand = _brandName
};
var jsonData = JsonSerializer.Serialize(data);
var encryptedBytes = _fernet.Encrypt(Encoding.UTF8.GetBytes(jsonData));
return Convert.ToBase64String(encryptedBytes)
.Replace('+', '-')
.Replace('/', '_')
.TrimEnd('=');
}
public string GenerateSeatmapUrl(string pnr, string lastName, string source = "cart")
{
var encryptedId = EncryptPnrData(pnr, lastName);
return $"https://departcart.com/seatmap?id={encryptedId}&source={source}";
}
}
public class BrandCredentials
{
public string Password { get; set; } = string.Empty;
public string Salt { get; set; } = string.Empty;
}
// Usage in your booking flow
var encryptor = new DepartCartEncryption("your_brand");
var seatmapUrl = encryptor.GenerateSeatmapUrl("ABC123", "SMITH");
Console.WriteLine($"Seat selection: {seatmapUrl}");
If you prefer to use our API to generate encrypted URLs, this option is also available but comes with additional network dependencies.
import requests
def generate_seat_selection_url(pnr, last_name, brand_name):
"""Generate encrypted seatmap URL via DepartCart API"""
response = requests.post('https://departcart.com/api/generate-encrypted-seatmap-url', json={
'pnr': pnr.strip().upper(),
'last_name': last_name.strip().upper(),
'brand': brand_name
})
if response.status_code == 200:
data = response.json()
return data['seatmap_url']
else:
# Handle error
return None
# Usage in your booking flow
seatmap_url = generate_seat_selection_url('ABC123', 'SMITH', 'your_brand_name')
if seatmap_url:
# Redirect user or embed in iframe
print(f"Seat selection: {seatmap_url}")
using System.Net.Http;
using System.Text;
using System.Text.Json;
public async Task<string> GenerateSeatSelectionUrl(string pnr, string lastName, string brandName)
{
var client = new HttpClient();
var payload = new
{
pnr = pnr.Trim().ToUpper(),
last_name = lastName.Trim().ToUpper(),
brand = brandName
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
"https://departcart.com/api/generate-encrypted-seatmap-url",
content);
if (response.IsSuccessStatusCode)
{
var responseData = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<ApiResponse>(responseData);
return result.seatmap_url;
}
return null; // Handle error
}
// Usage in your booking flow
var seatmapUrl = await GenerateSeatSelectionUrl("ABC123", "SMITH", "your_brand_name");
if (seatmapUrl != null)
{
// Redirect user or embed in iframe
Console.WriteLine($"Seat selection: {seatmapUrl}");
}
async function generateSeatSelectionUrl(pnr, lastName, brandName) {
try {
const response = await fetch('https://departcart.com/api/generate-encrypted-seatmap-url', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
pnr: pnr.trim().toUpperCase(),
last_name: lastName.trim().toUpperCase(),
brand: brandName
})
});
if (response.ok) {
const data = await response.json();
return data.seatmap_url;
} else {
// Handle error
return null;
}
} catch (error) {
console.error('Error generating seat selection URL:', error);
return null;
}
}
// Usage in your booking flow
const seatmapUrl = await generateSeatSelectionUrl('ABC123', 'SMITH', 'your_brand_name');
if (seatmapUrl) {
// Redirect user or embed in iframe
console.log(`Seat selection: ${seatmapUrl}`);
}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class DepartCartAPI {
public static String generateSeatSelectionUrl(String pnr, String lastName, String brandName)
throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
// Prepare payload
Map<String, String> payload = new HashMap<>();
payload.put("pnr", pnr.trim().toUpperCase());
payload.put("last_name", lastName.trim().toUpperCase());
payload.put("brand", brandName);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(payload);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://departcart.com/api/generate-encrypted-seatmap-url"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
Map<String, Object> result = mapper.readValue(response.body(), Map.class);
return (String) result.get("seatmap_url");
} else {
return null; // Handle error
}
}
// Usage in your booking flow
public static void main(String[] args) throws Exception {
String seatmapUrl = generateSeatSelectionUrl("ABC123", "SMITH", "your_brand_name");
if (seatmapUrl != null) {
System.out.println("Seat selection: " + seatmapUrl);
}
}
}
require 'net/http'
require 'uri'
require 'json'
class DepartCartAPI
def self.generate_seat_selection_url(pnr, last_name, brand_name)
uri = URI('https://departcart.com/api/generate-encrypted-seatmap-url')
payload = {
pnr: pnr.strip.upcase,
last_name: last_name.strip.upcase,
brand: brand_name
}
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request.body = payload.to_json
response = http.request(request)
if response.code == '200'
result = JSON.parse(response.body)
result['seatmap_url']
else
nil # Handle error
end
end
end
# Usage in your booking flow
seatmap_url = DepartCartAPI.generate_seat_selection_url('ABC123', 'SMITH', 'your_brand_name')
puts "Seat selection: #{seatmap_url}" if seatmap_url
<!-- In your booking confirmation template -->
<div class="flight-summary">
<h3>Flight AA1234 - JFK to LAX</h3>
<p>June 20, 2025 at 8:00 AM</p>
{% if seatmap_url %}
<a href="{{ seatmap_url }}" class="btn btn-primary" target="_blank">
🪑 Select Seats & Add-ons
</a>
{% endif %}
</div>
from flask import Flask, request, jsonify
@app.route('/webhook/departcart', methods=['POST'])
def handle_ancillary_purchase():
"""Receive real-time notifications when passengers buy ancillaries"""
webhook_data = request.get_json()
if webhook_data['event_type'] == 'transaction.completed':
transaction = webhook_data['transaction']
# Update your booking system
update_passenger_ancillaries(
pnr=transaction['pnr'],
seats=transaction['ancillaries']
)
# Send confirmation email
send_ancillary_confirmation_email(transaction)
return jsonify({'status': 'received'})
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
[ApiController]
[Route("webhook")]
public class WebhookController : ControllerBase
{
[HttpPost("departcart")]
public async Task<IActionResult> HandleAncillaryPurchase()
{
using var reader = new StreamReader(Request.Body);
var payload = await reader.ReadToEndAsync();
var webhookData = JsonSerializer.Deserialize<WebhookPayload>(payload);
if (webhookData.event_type == "transaction.completed")
{
var transaction = webhookData.transaction;
// Update your booking system
await UpdatePassengerAncillaries(
transaction.pnr,
transaction.ancillaries
);
// Send confirmation email
await SendAncillaryConfirmationEmail(transaction);
}
return Ok(new { status = "received" });
}
}
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/departcart', async (req, res) => {
try {
const webhookData = req.body;
if (webhookData.event_type === 'transaction.completed') {
const transaction = webhookData.transaction;
// Update your booking system
await updatePassengerAncillaries(
transaction.pnr,
transaction.ancillaries
);
// Send confirmation email
await sendAncillaryConfirmationEmail(transaction);
}
res.json({ status: 'received' });
} catch (error) {
console.error('Webhook error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
@RestController
@RequestMapping("/webhook")
public class WebhookController {
@PostMapping("/departcart")
public ResponseEntity<Map<String, String>> handleAncillaryPurchase(
@RequestBody String payload) {
try {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> webhookData = mapper.readValue(payload, Map.class);
String eventType = (String) webhookData.get("event_type");
if ("transaction.completed".equals(eventType)) {
Map<String, Object> transaction = (Map<String, Object>) webhookData.get("transaction");
// Update your booking system
updatePassengerAncillaries(
(String) transaction.get("pnr"),
(java.util.List<?>) transaction.get("ancillaries")
);
// Send confirmation email
sendAncillaryConfirmationEmail(transaction);
}
return ResponseEntity.ok(Map.of("status", "received"));
} catch (Exception e) {
return ResponseEntity.status(500)
.body(Map.of("error", "Internal server error"));
}
}
private void updatePassengerAncillaries(String pnr, java.util.List<?> ancillaries) {
// Implementation for updating passenger ancillaries
}
private void sendAncillaryConfirmationEmail(Map<String, Object> transaction) {
// Implementation for sending confirmation email
}
}
require 'sinatra'
require 'json'
class WebhookController < Sinatra::Base
post '/webhook/departcart' do
begin
# Read payload
request.body.rewind
payload = request.body.read
webhook_data = JSON.parse(payload)
if webhook_data['event_type'] == 'transaction.completed'
transaction = webhook_data['transaction']
# Update your booking system
update_passenger_ancillaries(
transaction['pnr'],
transaction['ancillaries']
)
# Send confirmation email
send_ancillary_confirmation_email(transaction)
end
content_type :json
{ status: 'received' }.to_json
rescue => e
status 500
{ error: 'Internal server error' }.to_json
end
end
private
def update_passenger_ancillaries(pnr, ancillaries)
# Implementation for updating passenger ancillaries
end
def send_ancillary_confirmation_email(transaction)
# Implementation for sending confirmation email
end
end
| Feature | GTM | Server API | Client Encryption |
import json
import base64
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
def encrypt_passenger_data(pnr, last_name, brand_config):
"""Encrypt passenger data using brand-specific keys"""
# Your brand configuration (provided during onboarding)
password = brand_config['password'] # e.g., "your-brand-secret-key-2025"
salt = brand_config['salt'] # e.g., "your_brand_salt_2025"
# Prepare data
data = {
"pnr": pnr.strip().upper(),
"last_name": last_name.strip().upper(),
"brand": brand_config['name']
}
# Derive key
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt.encode(),
iterations=100000,
)
key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
# Encrypt
fernet = Fernet(key)
json_data = json.dumps(data)
encrypted_data = fernet.encrypt(json_data.encode())
return base64.urlsafe_b64encode(encrypted_data).decode()
# Usage
brand_config = {
'name': 'your_brand_name',
'password': 'your-brand-secret-key-2025', # From onboarding
'salt': 'your_brand_salt_2025' # From onboarding
}
encrypted_id = encrypt_passenger_data('ABC123', 'SMITH', brand_config)
seatmap_url = f"https://departcart.com/seatmap?id={encrypted_id}&source=api"
using System;
using System.Text;
using System.Text.Json;
using System.Security.Cryptography;
public static string EncryptPassengerData(string pnr, string lastName, BrandConfig brandConfig)
{
// Prepare data
var data = new
{
pnr = pnr.Trim().ToUpper(),
last_name = lastName.Trim().ToUpper(),
brand = brandConfig.Name
};
string jsonData = JsonSerializer.Serialize(data);
byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonData);
// Derive key using PBKDF2
using (var pbkdf2 = new Rfc2898DeriveBytes(brandConfig.Password, Encoding.UTF8.GetBytes(brandConfig.Salt), 100000))
{
byte[] key = pbkdf2.GetBytes(32);
using (var aes = Aes.Create())
{
aes.Key = key;
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor())
{
byte[] encrypted = encryptor.TransformFinalBlock(jsonBytes, 0, jsonBytes.Length);
byte[] result = new byte[aes.IV.Length + encrypted.Length];
Array.Copy(aes.IV, 0, result, 0, aes.IV.Length);
Array.Copy(encrypted, 0, result, aes.IV.Length, encrypted.Length);
return Convert.ToBase64String(result);
}
}
}
}
// Usage
var brandConfig = new BrandConfig
{
Name = "your_brand_name",
Password = "your-brand-secret-key-2025", // From onboarding
Salt = "your_brand_salt_2025" // From onboarding
};
string encryptedId = EncryptPassengerData("ABC123", "SMITH", brandConfig);
string seatmapUrl = $"https://departcart.com/seatmap?id={encryptedId}&source=api";
const crypto = require('crypto');
function encryptPassengerData(pnr, lastName, brandConfig) {
// Prepare data
const data = {
pnr: pnr.trim().toUpperCase(),
last_name: lastName.trim().toUpperCase(),
brand: brandConfig.name
};
const jsonData = JSON.stringify(data);
// Derive key using PBKDF2
const key = crypto.pbkdf2Sync(brandConfig.password, brandConfig.salt, 100000, 32, 'sha256');
// Generate IV and encrypt
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(jsonData, 'utf8', 'base64');
encrypted += cipher.final('base64');
// Combine IV + encrypted data
const combined = Buffer.concat([iv, Buffer.from(encrypted, 'base64')]);
return combined.toString('base64');
}
// Usage
const brandConfig = {
name: 'your_brand_name',
password: 'your-brand-secret-key-2025', // From onboarding
salt: 'your_brand_salt_2025' // From onboarding
};
const encryptedId = encryptPassengerData('ABC123', 'SMITH', brandConfig);
const seatmapUrl = `https://departcart.com/seatmap?id=${encryptedId}&source=api`;
import java.util.Base64;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class ClientSideEncryption {
public static String encryptPassengerData(String pnr, String lastName, BrandConfig brandConfig)
throws Exception {
// Prepare data
Map<String, String> data = new HashMap<>();
data.put("pnr", pnr.trim().toUpperCase());
data.put("last_name", lastName.trim().toUpperCase());
data.put("brand", brandConfig.getName());
ObjectMapper mapper = new ObjectMapper();
String jsonData = mapper.writeValueAsString(data);
// Derive key using PBKDF2
PBEKeySpec spec = new PBEKeySpec(
brandConfig.getPassword().toCharArray(),
brandConfig.getSalt().getBytes(),
100000, 256
);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] key = factory.generateSecret(spec).getEncoded();
// Generate IV and encrypt
SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(jsonData.getBytes("UTF-8"));
// Combine IV + encrypted data
byte[] result = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
return Base64.getEncoder().encodeToString(result);
}
// Usage
public static void main(String[] args) throws Exception {
BrandConfig brandConfig = new BrandConfig(
"your_brand_name",
"your-brand-secret-key-2025", // From onboarding
"your_brand_salt_2025" // From onboarding
);
String encryptedId = encryptPassengerData("ABC123", "SMITH", brandConfig);
String seatmapUrl = "https://departcart.com/seatmap?id=" + encryptedId + "&source=api";
System.out.println(seatmapUrl);
}
}
require 'openssl'
require 'base64'
require 'json'
class ClientSideEncryption
def self.encrypt_passenger_data(pnr, last_name, brand_config)
# Prepare data
data = {
pnr: pnr.strip.upcase,
last_name: last_name.strip.upcase,
brand: brand_config[:name]
}
json_data = data.to_json
# Derive key using PBKDF2
key = OpenSSL::PKCS5.pbkdf2_hmac(
brand_config[:password],
brand_config[:salt],
100000, 32,
OpenSSL::Digest::SHA256.new
)
# Generate IV and encrypt
cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.encrypt
cipher.key = key
iv = cipher.random_iv
encrypted = cipher.update(json_data) + cipher.final
# Combine IV + encrypted data
result = iv + encrypted
Base64.strict_encode64(result)
end
end
# Usage
brand_config = {
name: 'your_brand_name',
password: 'your-brand-secret-key-2025', # From onboarding
salt: 'your_brand_salt_2025' # From onboarding
}
encrypted_id = ClientSideEncryption.encrypt_passenger_data('ABC123', 'SMITH', brand_config)
seatmap_url = "https://departcart.com/seatmap?id=#{encrypted_id}&source=api"
puts seatmap_url
| Feature | GTM | Client Encryption ⭐ | API Integration |
|---|---|---|---|
| Setup Time | 1 day | 1-2 days | 1-2 weeks |
| Development | Minimal | Low | Medium |
| Customization | Limited | High | High |
| Security | High | Maximum | High |
| Performance | Good | Excellent | Good |
| API Dependencies | None | None | Yes |
| Maintenance | None | Low | Medium |
For an airline with 10,000 monthly passengers:
Questions? Contact our integration team at integration@departcart.com