/generate-encrypted-url API endpoint/generate-encrypted-url endpoint to generate encrypted URLsThis document provides complete implementation code for Python and C# to generate encrypted URLs directly in your application using the client-side approach.
DepartCart supports encrypted PNR and last name data in seatmap URLs to protect sensitive passenger information. Partners can encrypt this data client-side using brand-specific encryption keys, eliminating the need to call our /generate-encrypted-url API endpoint. This document provides complete implementation code for Python and C# to generate encrypted URLs directly in your application.
https://departcart.com/seatmap?id=Z0FBQUFBQm9VQTE2TnFpUk1Uc0lfd1lyX3VLcUU2aEN5UndZUnp3RS1ma2UxM1hQZjg4Z3ZILWQtOWNxZVBhTzFING5IaUdyZ2ZmRjhwaDR0WHRVWERZRGVNYVlWbVBXUHJTVVlUMmZsR3VoU3RCdjBVNnRHcWZ3SWJSOFFFM28ycjNCN3BZbXA5Tjd1SkxuSTd2cDhadGN5Sm14OTU1blh3PT0=&source=cart
Each partner brand has its own encryption parameters for security isolation:
| Brand | Password | Salt |
|---|---|---|
| demo | demo-secret-key-2025 |
demo_salt_2025 |
| auntbetty | auntbetty-secret-key-2025 |
auntbetty_salt_2025 |
| byojet | byojet-secret-key-2025 |
byojet_salt_2025 |
| flightcentre | flightcentre-secret-key-2025 |
flightcentre_salt_2025 |
By implementing encryption in your application, you gain several advantages:
/generate-encrypted-url endpoint"""
DepartCart Partner Encryption Library
Complete standalone implementation for generating encrypted PNR URLs
"""
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:
"""
Partner encryption class for DepartCart seatmap URLs
"""
# Brand-specific encryption credentials
BRAND_CREDENTIALS = {
"demo": {
"password": "demo-secret-key-2025",
"salt": "demo_salt_2025"
},
"auntbetty": {
"password": "auntbetty-secret-key-2025",
"salt": "auntbetty_salt_2025"
},
"byojet": {
"password": "byojet-secret-key-2025",
"salt": "byojet_salt_2025"
},
"flightcentre": {
"password": "flightcentre-secret-key-2025",
"salt": "flightcentre_salt_2025"
}
}
def __init__(self, brand_name):
"""
Initialize encryption for a specific brand
Args:
brand_name (str): Brand name (demo, auntbetty, byojet, flightcentre)
"""
if brand_name not in self.BRAND_CREDENTIALS:
raise ValueError(f"Unknown brand: {brand_name}. Supported brands: {list(self.BRAND_CREDENTIALS.keys())}")
self.brand_name = brand_name
self.credentials = self.BRAND_CREDENTIALS[brand_name]
self._encryption_key = self._derive_encryption_key()
def _derive_encryption_key(self):
"""Derive Fernet encryption key from brand password and salt"""
password = self.credentials["password"].encode()
salt = self.credentials["salt"].encode()
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
return key
def encrypt_pnr_data(self, pnr, last_name):
"""
Encrypt PNR and last_name data for DepartCart
Args:
pnr (str): The PNR/booking reference
last_name (str): Passenger's last name
Returns:
str: Base64-encoded encrypted ID for use in URLs
"""
try:
# Create data payload (exact format expected by DepartCart)
data = {
"pnr": pnr.strip().upper(),
"last_name": last_name.strip().upper(),
"brand": self.brand_name
}
# Convert to JSON
json_data = json.dumps(data)
# Encrypt using Fernet
fernet = Fernet(self._encryption_key)
encrypted_data = fernet.encrypt(json_data.encode())
# Return base64-encoded encrypted data for URL safety
return base64.urlsafe_b64encode(encrypted_data).decode()
except Exception as e:
raise ValueError(f"Encryption failed: {str(e)}")
def generate_seatmap_url(self, pnr, last_name, source="cart", base_url="https://departcart.com"):
"""
Generate complete encrypted seatmap URL
Args:
pnr (str): The PNR/booking reference
last_name (str): Passenger's last name
source (str): Traffic source identifier (default: "cart")
base_url (str): DepartCart base URL
Returns:
str: Complete encrypted seatmap URL
"""
encrypted_id = self.encrypt_pnr_data(pnr, last_name)
return f"{base_url}/seatmap?id={encrypted_id}&source={source}"
# Convenience functions for quick usage
def encrypt_for_brand(pnr, last_name, brand_name):
"""
Quick function to encrypt PNR data for a specific brand
Args:
pnr (str): The PNR/booking reference
last_name (str): Passenger's last name
brand_name (str): Brand name
Returns:
str: Encrypted ID
"""
encryptor = DepartCartEncryption(brand_name)
return encryptor.encrypt_pnr_data(pnr, last_name)
def generate_seatmap_url(pnr, last_name, brand_name, source="cart"):
"""
Quick function to generate complete seatmap URL
Args:
pnr (str): The PNR/booking reference
last_name (str): Passenger's last name
brand_name (str): Brand name
source (str): Traffic source identifier
Returns:
str: Complete encrypted seatmap URL
"""
encryptor = DepartCartEncryption(brand_name)
return encryptor.generate_seatmap_url(pnr, last_name, source)
# Example usage
if __name__ == "__main__":
# Example 1: Using the class
demo_encryptor = DepartCartEncryption("demo")
encrypted_id = demo_encryptor.encrypt_pnr_data("WPCOEN", "APPLESEED")
seatmap_url = demo_encryptor.generate_seatmap_url("WPCOEN", "APPLESEED")
print(f"Encrypted ID: {encrypted_id}")
print(f"Seatmap URL: {seatmap_url}")
# Example 2: Using convenience functions
quick_encrypted_id = encrypt_for_brand("WPCOEN", "APPLESEED", "demo")
quick_url = generate_seatmap_url("WPCOEN", "APPLESEED", "demo", "website")
print(f"Quick Encrypted ID: {quick_encrypted_id}")
print(f"Quick URL: {quick_url}")
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using FernetDotNet; // Install-Package Fernet
/// <summary>
/// DepartCart Partner Encryption Library for C#
/// Complete standalone implementation for generating encrypted PNR URLs
///
/// Requirements:
/// - Install NuGet package: Fernet (dotnet add package Fernet)
/// - Or use Package Manager: Install-Package Fernet
/// </summary>
public class DepartCartEncryption
{
// Brand-specific encryption credentials
private static readonly Dictionary<string, BrandCredentials> BrandCredentials = new()
{
{
"demo", new BrandCredentials
{
Password = "demo-secret-key-2025",
Salt = "demo_salt_2025"
}
},
{
"auntbetty", new BrandCredentials
{
Password = "auntbetty-secret-key-2025",
Salt = "auntbetty_salt_2025"
}
},
{
"byojet", new BrandCredentials
{
Password = "byojet-secret-key-2025",
Salt = "byojet_salt_2025"
}
},
{
"flightcentre", new BrandCredentials
{
Password = "flightcentre-secret-key-2025",
Salt = "flightcentre_salt_2025"
}
}
};
private readonly string _brandName;
private readonly BrandCredentials _credentials;
private readonly IFernet _fernet;
public DepartCartEncryption(string brandName)
{
if (!BrandCredentials.ContainsKey(brandName))
{
throw new ArgumentException($"Unknown brand: {brandName}. Supported brands: {string.Join(", ", BrandCredentials.Keys)}");
}
_brandName = brandName;
_credentials = BrandCredentials[brandName];
_fernet = CreateFernetInstance();
}
/// <summary>
/// Create Fernet instance with brand-specific key derivation
/// </summary>
private IFernet CreateFernetInstance()
{
// Derive key using PBKDF2 (same as Python implementation)
using var pbkdf2 = new Rfc2898DeriveBytes(
_credentials.Password,
Encoding.UTF8.GetBytes(_credentials.Salt),
100000,
HashAlgorithmName.SHA256
);
var key = pbkdf2.GetBytes(32); // 32 bytes for Fernet
var base64Key = Convert.ToBase64String(key)
.Replace('+', '-')
.Replace('/', '_'); // URL-safe base64
return new Fernet(base64Key);
}
/// <summary>
/// Encrypt PNR and last name data for DepartCart
/// </summary>
/// <param name="pnr">The PNR/booking reference</param>
/// <param name="lastName">Passenger's last name</param>
/// <returns>Base64-encoded encrypted ID for use in URLs</returns>
public string EncryptPnrData(string pnr, string lastName)
{
try
{
// Create data payload (exact format expected by DepartCart)
var data = new
{
pnr = pnr.Trim().ToUpperInvariant(),
last_name = lastName.Trim().ToUpperInvariant(),
brand = _brandName
};
// Convert to JSON
var jsonData = JsonSerializer.Serialize(data);
// Encrypt using Fernet (matches Python implementation)
var encryptedBytes = _fernet.Encrypt(Encoding.UTF8.GetBytes(jsonData));
// Return URL-safe base64-encoded encrypted data
return Convert.ToBase64String(encryptedBytes)
.Replace('+', '-')
.Replace('/', '_')
.TrimEnd('=');
}
catch (Exception ex)
{
throw new InvalidOperationException($"Encryption failed: {ex.Message}", ex);
}
}
/// <summary>
/// Generate complete encrypted seatmap URL
/// </summary>
/// <param name="pnr">The PNR/booking reference</param>
/// <param name="lastName">Passenger's last name</param>
/// <param name="source">Traffic source identifier (default: "cart")</param>
/// <param name="baseUrl">DepartCart base URL</param>
/// <returns>Complete encrypted seatmap URL</returns>
public string GenerateSeatmapUrl(string pnr, string lastName, string source = "cart",
string baseUrl = "https://departcart.com")
{
var encryptedId = EncryptPnrData(pnr, lastName);
return $"{baseUrl}/seatmap?id={encryptedId}&source={source}";
}
/// <summary>
/// Quick static method to encrypt PNR data for a specific brand
/// </summary>
public static string EncryptForBrand(string pnr, string lastName, string brandName)
{
var encryptor = new DepartCartEncryption(brandName);
return encryptor.EncryptPnrData(pnr, lastName);
}
/// <summary>
/// Quick static method to generate complete seatmap URL
/// </summary>
public static string GenerateSeatmapUrl(string pnr, string lastName, string brandName, string source = "cart")
{
var encryptor = new DepartCartEncryption(brandName);
return encryptor.GenerateSeatmapUrl(pnr, lastName, source);
}
}
/// <summary>
/// Brand credentials container
/// </summary>
public class BrandCredentials
{
public string Password { get; set; } = string.Empty;
public string Salt { get; set; } = string.Empty;
}
// Example usage
class Program
{
static void Main()
{
try
{
// Example 1: Using the class
var demoEncryptor = new DepartCartEncryption("demo");
var encryptedId = demoEncryptor.EncryptPnrData("WPCOEN", "APPLESEED");
var seatmapUrl = demoEncryptor.GenerateSeatmapUrl("WPCOEN", "APPLESEED");
Console.WriteLine($"Encrypted ID: {encryptedId}");
Console.WriteLine($"Seatmap URL: {seatmapUrl}");
// Example 2: Using static convenience methods
var quickEncryptedId = DepartCartEncryption.EncryptForBrand("WPCOEN", "APPLESEED", "demo");
var quickUrl = DepartCartEncryption.GenerateSeatmapUrl("WPCOEN", "APPLESEED", "demo", "website");
Console.WriteLine($"Quick Encrypted ID: {quickEncryptedId}");
Console.WriteLine($"Quick URL: {quickUrl}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
// Project file dependencies (.csproj):
/*
<PackageReference Include="Fernet" Version="1.0.0" />
<PackageReference Include="System.Text.Json" Version="7.0.0" />
*/
Installation Instructions for C#:
1. Create a new C# project: dotnet new console
2. Install the Fernet package: dotnet add package Fernet
3. Copy the code above into your Program.cs file
4. Run: dotnet run
const crypto = require('crypto');
function deriveKeyForBrand(brandName, password, salt) {
const key = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
return key.toString('base64url');
}
function encryptPnrData(pnr, lastName, brandName, password, salt) {
// Create data payload
const data = {
pnr: pnr.trim().toUpperCase(),
last_name: lastName.trim().toUpperCase(),
brand: brandName
};
// Convert to JSON
const jsonData = JSON.stringify(data);
// Create cipher
const key = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher('aes-256-cbc', key);
// Encrypt
let encrypted = cipher.update(jsonData, 'utf8', 'base64');
encrypted += cipher.final('base64');
// Return base64-encoded for URL safety
return Buffer.from(encrypted).toString('base64url');
}
// Example usage
const pnr = "WPCOEN";
const lastName = "APPLESEED";
const brand = "demo";
const password = "demo-secret-key-2025";
const salt = "demo_salt_2025";
const encryptedId = encryptPnrData(pnr, lastName, brand, password, salt);
console.log(`Encrypted ID: ${encryptedId}`);
// Create final URL
const seatmapUrl = `https://departcart.com/seatmap?id=${encryptedId}&source=cart`;
console.log(`Seatmap URL: ${seatmapUrl}`);
<?php
function deriveKeyForBrand($brandName, $password, $salt) {
return base64_encode(hash_pbkdf2('sha256', $password, $salt, 100000, 32, true));
}
function encryptPnrData($pnr, $lastName, $brandName, $password, $salt) {
// Create data payload
$data = [
'pnr' => strtoupper(trim($pnr)),
'last_name' => strtoupper(trim($lastName)),
'brand' => $brandName
];
// Convert to JSON
$jsonData = json_encode($data);
// Derive key
$key = hash_pbkdf2('sha256', $password, $salt, 100000, 32, true);
// Encrypt using AES-256-CBC
$iv = random_bytes(16);
$encrypted = openssl_encrypt($jsonData, 'aes-256-cbc', $key, 0, $iv);
// Combine IV and encrypted data
$combined = base64_encode($iv . base64_decode($encrypted));
return $combined;
}
// Example usage
$pnr = "WPCOEN";
$lastName = "APPLESEED";
$brand = "demo";
$password = "demo-secret-key-2025";
$salt = "demo_salt_2025";
$encryptedId = encryptPnrData($pnr, $lastName, $brand, $password, $salt);
echo "Encrypted ID: " . $encryptedId . "\n";
// Create final URL
$seatmapUrl = "https://departcart.com/seatmap?id=" . urlencode($encryptedId) . "&source=cart";
echo "Seatmap URL: " . $seatmapUrl . "\n";
?>
Python Example:
from departcart_encryption import DepartCartEncryption
def generate_seatmap_link_for_email(booking_pnr, passenger_last_name, brand):
"""Generate seatmap link for booking confirmation emails"""
try:
encryptor = DepartCartEncryption(brand)
seatmap_url = encryptor.generate_seatmap_url(
pnr=booking_pnr,
last_name=passenger_last_name,
source="email"
)
return seatmap_url
except Exception as e:
print(f"Failed to generate seatmap link: {e}")
return None
# Usage in email template
booking_pnr = "ABC123"
passenger_last_name = "Smith"
brand = "demo"
seatmap_link = generate_seatmap_link_for_email(booking_pnr, passenger_last_name, brand)
if seatmap_link:
print(f"Include this link in email: {seatmap_link}")
C# Example:
public class BookingEmailService
{
public string GenerateSeatmapLinkForEmail(string bookingPnr, string passengerLastName, string brand)
{
try
{
var encryptor = new DepartCartEncryption(brand);
return encryptor.GenerateSeatmapUrl(bookingPnr, passengerLastName, "email");
}
catch (Exception ex)
{
Console.WriteLine($"Failed to generate seatmap link: {ex.Message}");
return null;
}
}
}
// Usage
var emailService = new BookingEmailService();
var seatmapLink = emailService.GenerateSeatmapLinkForEmail("ABC123", "Smith", "demo");
if (seatmapLink != null)
{
Console.WriteLine($"Include this link in email: {seatmapLink}");
}
Python Flask Example:
from flask import Flask, render_template, request
from departcart_encryption import DepartCartEncryption
app = Flask(__name__)
@app.route('/manage-booking', methods=['GET', 'POST'])
def manage_booking():
if request.method == 'POST':
pnr = request.form.get('pnr')
last_name = request.form.get('last_name')
brand = 'demo' # Your brand
try:
encryptor = DepartCartEncryption(brand)
seatmap_url = encryptor.generate_seatmap_url(pnr, last_name, "website")
# Redirect to DepartCart or show link
return render_template('booking_found.html', seatmap_url=seatmap_url)
except Exception as e:
return render_template('booking_error.html', error=str(e))
return render_template('manage_booking_form.html')
C# ASP.NET Core Example:
[ApiController]
[Route("api/[controller]")]
public class BookingController : ControllerBase
{
private readonly string _brandName = "demo"; // Your brand
[HttpPost("seatmap-url")]
public IActionResult GenerateSeatmapUrl([FromBody] BookingRequest request)
{
try
{
var seatmapUrl = DepartCartEncryption.GenerateSeatmapUrl(
request.Pnr,
request.LastName,
_brandName,
"website"
);
return Ok(new { seatmapUrl });
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
}
public class BookingRequest
{
public string Pnr { get; set; }
public string LastName { get; set; }
}
Python Example:
import csv
from departcart_encryption import DepartCartEncryption
def process_booking_batch(csv_file_path, brand):
"""Process multiple bookings and generate encrypted URLs"""
encryptor = DepartCartEncryption(brand)
results = []
with open(csv_file_path, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
try:
encrypted_url = encryptor.generate_seatmap_url(
pnr=row['pnr'],
last_name=row['last_name'],
source="batch"
)
results.append({
'pnr': row['pnr'],
'last_name': row['last_name'],
'seatmap_url': encrypted_url,
'status': 'success'
})
except Exception as e:
results.append({
'pnr': row['pnr'],
'last_name': row['last_name'],
'error': str(e),
'status': 'failed'
})
return results
# Usage
results = process_booking_batch('bookings.csv', 'demo')
for result in results:
if result['status'] == 'success':
print(f"PNR {result['pnr']}: {result['seatmap_url']}")
else:
print(f"PNR {result['pnr']} failed: {result['error']}")
C# Example:
public class BatchProcessor
{
public List<BookingResult> ProcessBookingBatch(string csvFilePath, string brand)
{
var results = new List<BookingResult>();
var encryptor = new DepartCartEncryption(brand);
var lines = File.ReadAllLines(csvFilePath);
var header = lines[0].Split(',');
for (int i = 1; i < lines.Length; i++)
{
var values = lines[i].Split(',');
var pnr = values[0];
var lastName = values[1];
try
{
var seatmapUrl = encryptor.GenerateSeatmapUrl(pnr, lastName, "batch");
results.Add(new BookingResult
{
Pnr = pnr,
LastName = lastName,
SeatmapUrl = seatmapUrl,
Status = "success"
});
}
catch (Exception ex)
{
results.Add(new BookingResult
{
Pnr = pnr,
LastName = lastName,
Error = ex.Message,
Status = "failed"
});
}
}
return results;
}
}
public class BookingResult
{
public string Pnr { get; set; }
public string LastName { get; set; }
public string SeatmapUrl { get; set; }
public string Error { get; set; }
public string Status { get; set; }
}
Python Example:
import os
from departcart_encryption import DepartCartEncryption
class EnvironmentConfig:
def __init__(self):
self.brand = os.getenv('DEPARTCART_BRAND', 'demo')
self.base_url = os.getenv('DEPARTCART_BASE_URL', 'https://departcart.com')
self.default_source = os.getenv('DEPARTCART_SOURCE', 'website')
def get_encryptor(self):
return DepartCartEncryption(self.brand)
def generate_url(self, pnr, last_name, source=None):
encryptor = self.get_encryptor()
return encryptor.generate_seatmap_url(
pnr,
last_name,
source or self.default_source,
self.base_url
)
# Usage
config = EnvironmentConfig()
url = config.generate_url("WPCOEN", "APPLESEED")
print(f"Generated URL: {url}")
C# Example:
public class EnvironmentConfig
{
public string Brand { get; }
public string BaseUrl { get; }
public string DefaultSource { get; }
public EnvironmentConfig()
{
Brand = Environment.GetEnvironmentVariable("DEPARTCART_BRAND") ?? "demo";
BaseUrl = Environment.GetEnvironmentVariable("DEPARTCART_BASE_URL") ?? "https://departcart.com";
DefaultSource = Environment.GetEnvironmentVariable("DEPARTCART_SOURCE") ?? "website";
}
public DepartCartEncryption GetEncryptor()
{
return new DepartCartEncryption(Brand);
}
public string GenerateUrl(string pnr, string lastName, string source = null)
{
var encryptor = GetEncryptor();
return encryptor.GenerateSeatmapUrl(pnr, lastName, source ?? DefaultSource, BaseUrl);
}
}
// Usage
var config = new EnvironmentConfig();
var url = config.GenerateUrl("WPCOEN", "APPLESEED");
Console.WriteLine($"Generated URL: {url}");
Visit: https://departcart.com/generate-encrypted-url
- Enter your PNR and last name
- Generate encrypted URL
- Test both encrypted and legacy URLs
Visit: https://departcart.com/test-encryption
- Confirms encryption/decryption is working
- Returns sample encrypted ID for testing
https://departcart.com/seatmap?id={encrypted_id}For implementation assistance or to receive your brand's encryption parameters, contact: - Technical Support: [support@departcart.com] - Integration Team: [integration@departcart.com]
Q: Should I use client-side encryption or API-based encryption? A: Client-side encryption is recommended for better performance, reliability, and reduced API dependencies.
Q: How long are encrypted URLs valid? A: Encrypted URLs don't expire, but they're tied to specific brand keys.
Q: What if decryption fails? A: Users will see an "Invalid or expired link" error message.
Q: Can I use the same encryption for multiple brands? A: No, each brand must use its own specific encryption parameters.