DepartCart API Reference

Overview

DepartCart provides secure, white-label ancillary services for airlines and travel partners. We recommend implementing client-side encryption to generate encrypted URLs directly in your application for optimal performance and security. API endpoints are also available as a fallback option.

Table of Contents

  1. Authentication & Security
  2. Client-Side Encryption (Recommended)
  3. API Endpoints (Fallback)
  4. GTM Integration
  5. Webhook Notifications
  6. Error Handling
  7. Implementation Guide

Authentication & Security

Brand-Specific Encryption

Each partner brand has unique encryption parameters for security isolation:

{
  "brand": "your_brand_name",
  "password": "your-brand-secret-key-2025",
  "salt": "your_brand_salt_2025"
}

Note: Actual credentials are provided during onboarding and differ from examples shown here.


⭐ This is the recommended approach for generating encrypted seatmap URLs. Benefits include:

Data Format

PNR and passenger data is encrypted using this JSON structure:

{
  "pnr": "ABC123",
  "last_name": "SMITH",
  "brand": "your_brand_name"
}

Implementation Examples

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_name, password, salt):
    """Encrypt passenger data for DepartCart API"""

    # Prepare data payload
    data = {
        "pnr": pnr.strip().upper(),
        "last_name": last_name.strip().upper(),
        "brand": brand_name
    }

    # Derive encryption key
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt.encode(),
        iterations=100000,
    )
    key = base64.urlsafe_b64encode(kdf.derive(password.encode()))

    # Encrypt data
    fernet = Fernet(key)
    json_data = json.dumps(data)
    encrypted_data = fernet.encrypt(json_data.encode())

    # Return URL-safe base64 encoded string
    return base64.urlsafe_b64encode(encrypted_data).decode()

# Example usage
encrypted_id = encrypt_passenger_data(
    pnr="ABC123",
    last_name="SMITH", 
    brand_name="your_brand",
    password="your-brand-secret-key-2025",
    salt="your_brand_salt_2025"
)

# Create seatmap URL
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 class DepartCartEncryption
{
    public static string EncryptPassengerData(string pnr, string lastName, 
        string brandName, string password, string salt)
    {
        // Prepare data payload
        var data = new
        {
            pnr = pnr.Trim().ToUpper(),
            last_name = lastName.Trim().ToUpper(),
            brand = brandName
        };

        // Convert to JSON
        string jsonData = JsonSerializer.Serialize(data);
        byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonData);

        // Derive key using PBKDF2
        using (var pbkdf2 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt), 100000))
        {
            byte[] key = pbkdf2.GetBytes(32);

            // Encrypt using AES
            using (var aes = Aes.Create())
            {
                aes.Key = key;
                aes.GenerateIV();

                using (var encryptor = aes.CreateEncryptor())
                {
                    byte[] encrypted = encryptor.TransformFinalBlock(jsonBytes, 0, jsonBytes.Length);

                    // Combine IV + encrypted data
                    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);
                }
            }
        }
    }
}

// Example usage
string encryptedId = DepartCartEncryption.EncryptPassengerData(
    pnr: "ABC123",
    lastName: "SMITH",
    brandName: "your_brand",
    password: "your-brand-secret-key-2025",
    salt: "your_brand_salt_2025"
);

string seatmapUrl = $"https://departcart.com/seatmap?id={encryptedId}&source=api";
const crypto = require('crypto');

function encryptPassengerData(pnr, lastName, brandName, password, salt) {
    // Prepare data payload
    const data = {
        pnr: pnr.trim().toUpperCase(),
        last_name: lastName.trim().toUpperCase(),
        brand: brandName
    };

    // Convert to JSON
    const jsonData = JSON.stringify(data);

    // Derive key using PBKDF2
    const key = crypto.pbkdf2Sync(password, 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');
}

// Example usage
const encryptedId = encryptPassengerData(
    'ABC123',
    'SMITH',
    'your_brand',
    'your-brand-secret-key-2025',
    'your_brand_salt_2025'
);

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 DepartCartEncryption {

    public static String encryptPassengerData(String pnr, String lastName, 
            String brandName, String password, String salt) throws Exception {

        // Prepare data payload
        Map<String, String> data = new HashMap<>();
        data.put("pnr", pnr.trim().toUpperCase());
        data.put("last_name", lastName.trim().toUpperCase());
        data.put("brand", brandName);

        // Convert to JSON
        ObjectMapper mapper = new ObjectMapper();
        String jsonData = mapper.writeValueAsString(data);

        // Derive key using PBKDF2
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.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);
    }

    // Example usage
    public static void main(String[] args) throws Exception {
        String encryptedId = encryptPassengerData(
            "ABC123",
            "SMITH", 
            "your_brand",
            "your-brand-secret-key-2025",
            "your_brand_salt_2025"
        );

        String seatmapUrl = "https://departcart.com/seatmap?id=" + encryptedId + "&source=api";
        System.out.println(seatmapUrl);
    }
}
require 'openssl'
require 'base64'
require 'json'

class DepartCartEncryption
  def self.encrypt_passenger_data(pnr, last_name, brand_name, password, salt)
    # Prepare data payload
    data = {
      pnr: pnr.strip.upcase,
      last_name: last_name.strip.upcase,
      brand: brand_name
    }

    # Convert to JSON
    json_data = data.to_json

    # Derive key using PBKDF2
    key = OpenSSL::PKCS5.pbkdf2_hmac(password, 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

# Example usage
encrypted_id = DepartCartEncryption.encrypt_passenger_data(
  'ABC123',
  'SMITH',
  'your_brand',
  'your-brand-secret-key-2025',
  'your_brand_salt_2025'
)

seatmap_url = "https://departcart.com/seatmap?id=#{encrypted_id}&source=api"
puts seatmap_url

API Endpoints (Fallback)

Generate Encrypted Seatmap URL

Endpoint: POST /api/generate-encrypted-seatmap-url

Description: Server-side encryption for partners who prefer not to implement client-side encryption. Note: Client-side encryption is recommended for better performance and reliability.

Request Body:

{
    "pnr": "ABC123",
    "last_name": "SMITH",
    "brand": "your_brand_name"
}

Response:

{
    "success": true,
    "encrypted_id": "Z0FBQUFBQm9VQTE2TnFpUk1Uc0lfd1lyX3VLcUU2aEN5UndZUnp3RS1ma2UxM1hQZjg4Z3ZILWQtOWNxZVBhTzFING5IaUdyZ2ZmRjhwaDR0WHRVWERZRGVNYVlWbVBXUHJTVVlUMmZsR3VoU3RCdjBVNnRHcWZ3SWJSOEFFM28ycjNCN3BZbXA5Tjd1SkxuSTd2cDhadGN5Sm14OTU1blh3PT0=",
    "seatmap_url": "https://departcart.com/seatmap?id=Z0FBQUFBQm9VQTE2TnFpUk1Uc0lfd1lyX3VLcUU2aEN5UndZUnp3RS1ma2UxM1hQZjg4Z3ZILWQtOWNxZVBhTzFING5IaUdyZ2ZmRjhwaDR0WHRVWERZRGVNYVlWbVBXUHJTVVlUMmZsR3VoU3RCdjBVNnRHcWZ3SWJSOEFFM28ycjNCN3BZbXA5Tjd1SkxuSTd2cDladGN5Sm14OTU1blh3PT0=&source=api"
}

Example Usage:

import requests

response = requests.post('https://departcart.com/api/generate-encrypted-seatmap-url', json={
    'pnr': 'ABC123',
    'last_name': 'SMITH',
    'brand': 'your_brand_name'
})

data = response.json()
seatmap_url = data['seatmap_url']
using System.Net.Http;
using System.Text;
using System.Text.Json;

var client = new HttpClient();
var payload = new
{
    pnr = "ABC123",
    last_name = "SMITH",
    brand = "your_brand_name"
};

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);

var responseData = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<ApiResponse>(responseData);
string seatmapUrl = result.seatmap_url;
const response = await fetch('https://departcart.com/api/generate-encrypted-seatmap-url', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        pnr: 'ABC123',
        last_name: 'SMITH',
        brand: 'your_brand_name'
    })
});

const data = await response.json();
const seatmapUrl = data.seatmap_url;
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 generateSeatmapUrl(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);
        payload.put("last_name", lastName);
        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());

        Map<String, Object> result = mapper.readValue(response.body(), Map.class);
        return (String) result.get("seatmap_url");
    }

    // Example usage
    public static void main(String[] args) throws Exception {
        String seatmapUrl = generateSeatmapUrl("ABC123", "SMITH", "your_brand_name");
        System.out.println("Seat selection: " + seatmapUrl);
    }
}
require 'net/http'
require 'uri'
require 'json'

class DepartCartAPI
  def self.generate_seatmap_url(pnr, last_name, brand_name)
    uri = URI('https://departcart.com/api/generate-encrypted-seatmap-url')

    payload = {
      pnr: pnr,
      last_name: last_name,
      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

# Example usage
seatmap_url = DepartCartAPI.generate_seatmap_url('ABC123', 'SMITH', 'your_brand_name')
puts "Seat selection: #{seatmap_url}" if seatmap_url

Seatmap Display

Endpoint: GET /seatmap

Parameters:

Example:

https://departcart.com/seatmap?id=Z0FBQUFBQm9VQTE2...&source=api

GTM Integration

Lightweight Integration

For a quick, low-maintenance integration, use Google Tag Manager to inject seat selection panels into your confirmation or account pages.

GTM Code Template:

<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>

Benefits:

Setup Steps:

  1. Add the GTM code above to your tag manager
  2. Configure trigger for confirmation/account pages
  3. Ensure your pages have identifiable booking elements
  4. Test with sample bookings

For detailed GTM integration instructions, see: GTM Integration Guide


Webhook Notifications

Real-Time Transaction Updates

DepartCart sends webhook notifications for all transaction events to keep partners updated in real-time.

Webhook URL Setup: Configure your webhook endpoint during onboarding.

Event Types

Webhook Payload

{
  "event_type": "transaction.completed",
  "event_id": "evt_1234567890",
  "timestamp": "2025-06-16T10:30:00Z",
  "brand": "your_brand_name",
  "transaction": {
    "id": "txn_abc123def456",
    "pnr": "ABC123",
    "status": "completed",
    "total_amount": 89.98,
    "currency": "USD",
    "payment": {
      "method": "stripe",
      "gateway_transaction_id": "pi_1234567890",
      "amount_charged": 89.98,
      "processing_fee": 2.70,
      "net_amount": 87.28
    },
    "passenger": {
      "first_name": "John",
      "last_name": "SMITH",
      "email": "john.smith@example.com"
    },
    "flight": {
      "airline_code": "AA",
      "flight_number": "1234",
      "departure_date": "2025-06-20",
      "departure_airport": "JFK",
      "arrival_airport": "LAX",
      "segment_id": "AA1234-20250620-JFKLAX"
    },
    "ancillaries": [
      {
        "type": "seat",
        "description": "Premium Economy Seat 12A",
        "seat_number": "12A",
        "price": 45.00,
        "cost": 15.00,
        "margin": 30.00
      },
      {
        "type": "baggage",
        "description": "Extra Baggage 23kg",
        "weight": "23kg",
        "price": 44.98,
        "cost": 25.00,
        "margin": 19.98
      }
    ],
    "fulfillment": {
      "seat_confirmation": "CONFIRMED",
      "baggage_confirmation": "CONFIRMED",
      "gds_record_locator": "ABC123",
      "updated_at": "2025-06-16T10:29:45Z"
    }
  }
}

Webhook Implementation Examples

from flask import Flask, request, jsonify
import hmac
import hashlib

app = Flask(__name__)

@app.route('/webhook/departcart', methods=['POST'])
def handle_departcart_webhook():
    # Verify webhook signature (recommended)
    signature = request.headers.get('X-DepartCart-Signature')
    payload = request.get_data()

    if not verify_webhook_signature(payload, signature):
        return jsonify({'error': 'Invalid signature'}), 401

    # Process webhook data
    webhook_data = request.get_json()
    event_type = webhook_data['event_type']
    transaction = webhook_data['transaction']

    if event_type == 'transaction.completed':
        # Update your records
        update_booking_ancillaries(
            pnr=transaction['pnr'],
            ancillaries=transaction['ancillaries'],
            payment_info=transaction['payment']
        )

        # Send confirmation email
        send_purchase_confirmation(
            passenger=transaction['passenger'],
            flight=transaction['flight'],
            ancillaries=transaction['ancillaries']
        )

    elif event_type == 'transaction.refunded':
        # Process refund
        process_refund(
            transaction_id=transaction['id'],
            refund_amount=transaction['payment']['amount_charged']
        )

    return jsonify({'status': 'received'}), 200

def verify_webhook_signature(payload, signature):
    # Verify using your webhook secret
    webhook_secret = "your_webhook_secret_key"
    expected_signature = hmac.new(
        webhook_secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(f"sha256={expected_signature}", signature)
using Microsoft.AspNetCore.Mvc;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

[ApiController]
[Route("webhook")]
public class WebhookController : ControllerBase
{
    [HttpPost("departcart")]
    public async Task<IActionResult> HandleDepartCartWebhook()
    {
        // Read webhook payload
        using var reader = new StreamReader(Request.Body);
        var payload = await reader.ReadToEndAsync();

        // Verify signature
        var signature = Request.Headers["X-DepartCart-Signature"].FirstOrDefault();
        if (!VerifyWebhookSignature(payload, signature))
        {
            return Unauthorized("Invalid signature");
        }

        // Parse webhook data
        var webhookData = JsonSerializer.Deserialize<WebhookPayload>(payload);

        switch (webhookData.event_type)
        {
            case "transaction.completed":
                await HandleTransactionCompleted(webhookData.transaction);
                break;

            case "transaction.refunded":
                await HandleTransactionRefunded(webhookData.transaction);
                break;
        }

        return Ok(new { status = "received" });
    }

    private bool VerifyWebhookSignature(string payload, string signature)
    {
        var webhookSecret = "your_webhook_secret_key";
        var keyBytes = Encoding.UTF8.GetBytes(webhookSecret);
        var payloadBytes = Encoding.UTF8.GetBytes(payload);

        using var hmac = new HMACSHA256(keyBytes);
        var computedHash = hmac.ComputeHash(payloadBytes);
        var expectedSignature = $"sha256={Convert.ToHexString(computedHash).ToLower()}";

        return string.Equals(expectedSignature, signature, StringComparison.OrdinalIgnoreCase);
    }
}
const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.raw({ type: 'application/json' }));

app.post('/webhook/departcart', (req, res) => {
    const signature = req.headers['x-departcart-signature'];
    const payload = req.body;

    // Verify webhook signature
    if (!verifyWebhookSignature(payload, signature)) {
        return res.status(401).json({ error: 'Invalid signature' });
    }

    // Parse webhook data
    const webhookData = JSON.parse(payload);
    const { event_type, transaction } = webhookData;

    switch (event_type) {
        case 'transaction.completed':
            handleTransactionCompleted(transaction);
            break;

        case 'transaction.refunded':
            handleTransactionRefunded(transaction);
            break;
    }

    res.json({ status: 'received' });
});

function verifyWebhookSignature(payload, signature) {
    const webhookSecret = 'your_webhook_secret_key';
    const expectedSignature = crypto
        .createHmac('sha256', webhookSecret)
        .update(payload)
        .digest('hex');

    return crypto.timingSafeEqual(
        Buffer.from(`sha256=${expectedSignature}`),
        Buffer.from(signature)
    );
}

async function handleTransactionCompleted(transaction) {
    // Update booking records
    await updateBookingAncillaries(transaction);

    // Send confirmation email
    await sendPurchaseConfirmation(transaction);
}
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import javax.servlet.http.HttpServletRequest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

@RestController
@RequestMapping("/webhook")
public class WebhookController {

    private static final String WEBHOOK_SECRET = "your_webhook_secret_key";

    @PostMapping("/departcart")
    public ResponseEntity<Map<String, String>> handleDepartCartWebhook(
            HttpServletRequest request, @RequestBody String payload) {

        try {
            // Verify signature
            String signature = request.getHeader("X-DepartCart-Signature");
            if (!verifyWebhookSignature(payload, signature)) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(Map.of("error", "Invalid signature"));
            }

            // Parse webhook data
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> webhookData = mapper.readValue(payload, Map.class);
            String eventType = (String) webhookData.get("event_type");
            Map<String, Object> transaction = (Map<String, Object>) webhookData.get("transaction");

            switch (eventType) {
                case "transaction.completed":
                    handleTransactionCompleted(transaction);
                    break;

                case "transaction.refunded":
                    handleTransactionRefunded(transaction);
                    break;
            }

            return ResponseEntity.ok(Map.of("status", "received"));

        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Map.of("error", "Processing error"));
        }
    }

    private boolean verifyWebhookSignature(String payload, String signature) 
            throws NoSuchAlgorithmException, InvalidKeyException {

        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(WEBHOOK_SECRET.getBytes(), "HmacSHA256");
        mac.init(secretKey);

        byte[] hash = mac.doFinal(payload.getBytes());
        StringBuilder expectedSignature = new StringBuilder("sha256=");
        for (byte b : hash) {
            expectedSignature.append(String.format("%02x", b));
        }

        return expectedSignature.toString().equals(signature);
    }

    private void handleTransactionCompleted(Map<String, Object> transaction) {
        // Update booking records and send confirmation email
        System.out.println("Transaction completed: " + transaction.get("id"));
    }

    private void handleTransactionRefunded(Map<String, Object> transaction) {
        // Process refund
        System.out.println("Transaction refunded: " + transaction.get("id"));
    }
}
require 'sinatra'
require 'json'
require 'openssl'

class WebhookController < Sinatra::Base
  WEBHOOK_SECRET = 'your_webhook_secret_key'

  post '/webhook/departcart' do
    # Read payload
    request.body.rewind
    payload = request.body.read

    # Verify signature
    signature = request.env['HTTP_X_DEPARTCART_SIGNATURE']
    unless verify_webhook_signature(payload, signature)
      status 401
      return { error: 'Invalid signature' }.to_json
    end

    # Parse webhook data
    webhook_data = JSON.parse(payload)
    event_type = webhook_data['event_type']
    transaction = webhook_data['transaction']

    case event_type
    when 'transaction.completed'
      handle_transaction_completed(transaction)
    when 'transaction.refunded'
      handle_transaction_refunded(transaction)
    end

    content_type :json
    { status: 'received' }.to_json
  end

  private

  def verify_webhook_signature(payload, signature)
    expected_signature = "sha256=#{OpenSSL::HMAC.hexdigest('SHA256', WEBHOOK_SECRET, payload)}"
    Rack::Utils.secure_compare(expected_signature, signature)
  end

  def handle_transaction_completed(transaction)
    # Update booking records and send confirmation email
    puts "Transaction completed: #{transaction['id']}"
  end

  def handle_transaction_refunded(transaction)
    # Process refund
    puts "Transaction refunded: #{transaction['id']}"
  end
end

Error Handling

HTTP Status Codes

Error Response Format

{
    "success": false,
    "error": {
        "code": "INVALID_PNR",
        "message": "The provided PNR could not be found or is invalid",
        "details": {
            "pnr": "ABC123",
            "brand": "your_brand_name"
        }
    }
}

Common Error Codes


Implementation Guide

πŸš€ Quick Start Checklist

Complete these steps to get your DepartCart integration up and running quickly.


πŸ“‹ Phase 1: Onboarding Requirements

Essential setup items needed before development begins


βš™οΈ Phase 2: Integration Options

Choose your preferred integration approach


πŸ§ͺ Phase 3: Testing

Validate your integration before going live


🎯 Phase 4: Go Live

Final steps for production deployment


⏱️ Estimated Timeline: 4-8 weeks total

Integration Timeline

Phase Duration Activities
Setup 1-2 weeks Onboarding, credentials, branding
Development 2-4 weeks Implement encryption, integrate APIs
Testing 1-2 weeks End-to-end testing, UAT
Launch 1 week Production deployment, monitoring

For detailed implementation steps, see: Partner Onboarding Guide


Support & Resources

Support Contacts

Testing Tools


This documentation is updated regularly. For the latest version, visit: https://departcart.com/docs