Overview

What It Is

Accept Apple Pay payments through MONDO

What You Need

MONDO Account ID + Secret Key

What You Send

Apple Pay token + amount + currency

What You Get

COMPLETED or REJECTED status

How It Works

Customer
Apple Pay
You
MONDO
Done

Option A: Self-Hosted

You display Apple Pay button on your site and send us the token

Option B: MONDO-Hosted

We handle everything - just redirect your customer to us

Quick Start (Self-Hosted)

POST https://server-to-server.getmondo.co/payment/

{ "company_account_id": "your_id",
"gateway_secret_key": "your_key",
"apple_pay_token": { /* from Apple Pay JS */ },
"transaction_amount": "99.99",
"transaction_currency_iso3": "EUR",
"live_or_sandbox": "sandbox"
}

About Apple Pay

Apple Pay provides a fast, secure, and private way for customers to pay. With MONDO as your Payment Service Provider (PSP), you can accept Apple Pay without needing your own Apple Developer account.

MONDO as Your PSP

  • MONDO owns the Apple Merchant ID
  • MONDO manages Apple Pay certificates
  • MONDO handles merchant validation
  • MONDO decrypts and processes payment tokens
What You DON'T Need: No Apple Developer Account, no Apple Merchant ID, no certificates to manage.

Token Handling — Who Does What?

Apple Pay uses end-to-end encryption to protect cardholder data. Here's how the responsibilities are divided:

Apple (Customer's Device) Generates an encrypted payment token containing card data, cryptogram, and authentication details
You (Merchant) Pass the encrypted token directly to MONDO via the apple_pay_token parameter — do NOT attempt to decrypt
MONDO (PSP) Decrypts the token using our Apple Pay Payment Processing Certificate and processes the payment with CardCorp
Important: You never see or handle raw card numbers. The token you receive from Apple is encrypted and can only be decrypted by MONDO using our merchant certificate.

Choose Your Integration Method

Best for: Full control over the Apple Pay button appearance and user experience on your own checkout page.

📋 Prerequisites

  • Partner Account ID - Your unique MONDO account identifier
  • Partner Secret Key - Your API authentication key
  • HTTPS - Your website must use HTTPS

⚠️ Domain Verification Required

Your domain must be registered with Apple through MONDO. This is a one-time setup.

  1. Go to GETMONDO.CO
  2. LoginDeveloperApple PayAdd Apple Pay Domain

🔄 Payment Flow

1
User Clicks Apple Pay Button
On your website
2
Apple Pay Sheet Opens
Native Apple Pay interface
3
Merchant Validation
Your JS calls MONDO's validation endpoint
4
User Authenticates
Face ID / Touch ID
5
Token Sent to MONDO
POST to /payment/ with apple_pay_token
6
3D Secure & Redirect
User redirected to success/failure URL

🔗 API Endpoint

POST https://server-to-server.getmondo.co/payment/

Merchant Validation Endpoint

POST https://server-to-server.getmondo.co/payment/apple_pay_validate_merchant.php

Merchant Validation Parameters

ParameterTypeRequiredDescription
validationURLstring✓ YesThe URL provided by Apple in the onvalidatemerchant event
company_account_idstring✓ YesYour MONDO Partner Account ID
gateway_secret_keystring✓ YesYour MONDO Partner Secret Key
domain_namestring○ ConditionalYour Apple Pay domain (e.g. paynow.yoursite.com). Required for server-to-server calls where the browser Origin header is not available. Not needed if calling directly from the browser.
Server-to-Server Integration Note:
If your backend proxies the merchant validation call (i.e. your server calls MONDO instead of the browser calling directly), you must include domain_name set to the domain where your Apple Pay button is displayed. This ensures the Apple Pay session matches your payment page domain.

📝 Required Parameters (Payment Processing)

ParameterTypeDescription
company_account_idstringYour MONDO Partner Account ID
gateway_secret_keystringYour MONDO Partner Secret Key
apple_pay_tokenobjectEncrypted token from event.payment.token — pass directly to MONDO as-is (do not decrypt)
transaction_amountdecimalPayment amount (e.g., 99.99)
transaction_currency_iso3stringCurrency code (EUR, USD, GBP)
cardholder_email_addressstringCustomer's email

Optional Parameters

ParameterDescription
partner_return_url_completedURL for successful payment redirect
partner_return_url_rejectedURL for rejected payment redirect
partner_webhook_urlURL for webhook notifications
live_or_sandboxlive or sandbox

📤 API Response

Apple Pay payments are processed immediately. The response contains the final transaction status:

{
    "status": "success",
    "transaction_status": "COMPLETED",
    "gateway_session_id": "73d68b79579eeaa4a1f35f667509ba19",
    "transaction_id": "37819a4707f3736b6fb42148d98f05ab",
    "gateway_message": "Transaction succeeded",
    "redirect_url": "https://yoursite.com/success?gateway_session_id=...&payment_status=COMPLETED"
}

Response Fields

FieldDescription
statussuccess or error
transaction_statusCOMPLETED or REJECTED
gateway_session_idUnique session identifier for this transaction
transaction_idUnique transaction identifier - use this to correlate with callbacks
gateway_messageHuman-readable status message
redirect_urlURL to redirect user after payment (optional - for UX only)
Instant Processing
Unlike card payments that require 3D Secure redirect, Apple Pay transactions are processed instantly. The payment is already complete when you receive the response.

🍎 Apple Pay JS Integration

Step 1: Add Apple Pay Button

<apple-pay-button buttonstyle="black" type="plain" locale="en" onclick="startApplePayPayment()"></apple-pay-button>

Step 2: Check Availability

if (window.ApplePaySession && ApplePaySession.canMakePayments()) {
    document.getElementById('apple-pay-button').style.display = 'block';
}

Step 3: Complete Implementation

async function startApplePayPayment() {
    const paymentRequest = {
        countryCode: 'US',
        currencyCode: 'EUR',
        merchantCapabilities: ['supports3DS'],
        supportedNetworks: ['visa', 'masterCard'],
        total: { label: 'Your Company', amount: '99.99', type: 'final' }
    };
    
    const session = new ApplePaySession(3, paymentRequest);
    
    // Merchant Validation
    session.onvalidatemerchant = async (event) => {
        const resp = await fetch('https://server-to-server.getmondo.co/payment/apple_pay_validate_merchant.php', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                validationURL: event.validationURL,
                company_account_id: 'YOUR_ACCOUNT_ID',
                gateway_secret_key: 'YOUR_SECRET_KEY',
                domain_name: 'your-domain.com'  // Required for server-to-server; optional if browser calls directly
            })
        });
        session.completeMerchantValidation(await resp.json());
    };
    
    // Payment Authorization - Apple provides ENCRYPTED token in event.payment.token
    // IMPORTANT: Pass the token directly to MONDO - do NOT attempt to decrypt it
    session.onpaymentauthorized = async (event) => {
        const resp = await fetch('https://server-to-server.getmondo.co/payment/', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                company_account_id: 'YOUR_ACCOUNT_ID',
                gateway_secret_key: 'YOUR_SECRET_KEY',
                apple_pay_token: event.payment.token,  // Encrypted token - MONDO decrypts
                transaction_amount: '99.99',
                transaction_currency_iso3: 'EUR',
                cardholder_email_address: 'customer@example.com',
                partner_return_url_completed: 'https://yoursite.com/success',
                partner_return_url_rejected: 'https://yoursite.com/failed',
                live_or_sandbox: 'live'
            })
        });
        
        const result = await resp.json();
        if (result.transaction_status === 'COMPLETED') {
            session.completePayment(ApplePaySession.STATUS_SUCCESS);
            // Payment already processed - redirect is optional for user experience
            if (result.redirect_url) {
                window.location.href = result.redirect_url;
            }
        } else {
            session.completePayment(ApplePaySession.STATUS_FAILURE);
            if (result.redirect_url) {
                window.location.href = result.redirect_url;
            }
        }
    };
    
    session.begin();
}

Testing Modes

MONDO supports two testing modes. Understanding the difference is crucial for successful integration.

SANDBOX For Testing

What Happens

  • ✅ Token structure is validated
  • ✅ All required fields are checked
  • ⏭️ Decryption is skipped (mock data returned)
  • ⏭️ CardCorp API call is skipped
  • ✅ Full payment flow is executed
💡
Key Point: You must send a properly structured token - the same format required for LIVE mode.
LIVE For Production

What Happens

  • ✅ Token structure is validated
  • ✅ All required fields are checked
  • ✅ Real cryptographic decryption
  • ✅ Real CardCorp API processing
  • ✅ Real money is charged
🔒
Key Point: Only use LIVE mode with real Apple Pay tokens from actual devices.

🎯 Why SANDBOX Requires the Same Token Structure

This design ensures that when you switch from SANDBOX to LIVE, your integration works immediately without code changes.

🧪
SANDBOX
Test with valid structure
Validated
Structure confirmed
🚀
LIVE
Works immediately

⚙️ Setting the Mode

Include the live_or_sandbox parameter in your API request:

{
    "company_account_id": "your_account_id",
    "gateway_secret_key": "your_secret_key",
    "apple_pay_token": { ... },
    "transaction_amount": "99.99",
    "transaction_currency_iso3": "EUR",
    "live_or_sandbox": "sandbox"  // or "live" for production
}

🎭 Sandbox: Force Transaction Result

In SANDBOX mode, you can force a specific transaction outcome for testing:

ParameterValueResult
sandbox_force_transaction_status COMPLETED ✅ Payment approved
sandbox_force_transaction_status REJECTED ❌ Payment declined

Apple Pay Token Structure

The Apple Pay token must have the following structure. This is the same for both SANDBOX and LIVE modes.

Important: Sending an invalid token structure (like just "***") will result in an error, even in SANDBOX mode.

Required Token Structure

{
    "paymentData": {
        "version": "EC_v1",
        "data": "BASE64_ENCRYPTED_PAYMENT_DATA",
        "signature": "BASE64_SIGNATURE",
        "header": {
            "ephemeralPublicKey": "BASE64_EPHEMERAL_KEY",
            "publicKeyHash": "BASE64_PUBLIC_KEY_HASH",
            "transactionId": "UNIQUE_TRANSACTION_ID"
        }
    },
    "paymentMethod": {
        "displayName": "Visa 1234",
        "network": "Visa",
        "type": "debit"
    },
    "transactionIdentifier": "APPLE_TRANSACTION_ID"
}

Field Descriptions

Field Required Description
paymentData.versionAlways EC_v1
paymentData.dataBase64 encrypted payment data from Apple
paymentData.signatureBase64 signature for verification
paymentData.header.ephemeralPublicKeyBase64 ephemeral public key
paymentData.header.publicKeyHashBase64 hash of merchant's public key
paymentData.header.transactionIdUnique transaction identifier
paymentMethod.networkCard network (Visa, Mastercard, Amex)
paymentMethod.displayNameDisplay name from Apple Wallet

= Required  |  = Recommended

Sandbox Test Token

Copy this token for SANDBOX testing. The encrypted values don't need to be real - only the structure is validated.

{
    "paymentData": {
        "version": "EC_v1",
        "data": "c2FuZGJveF9lbmNyeXB0ZWRfZGF0YV8xNzAyMjU2MDAwMDAw",
        "signature": "c2FuZGJveF9zaWduYXR1cmVfMTcwMjI1NjAwMDAwMA==",
        "header": {
            "ephemeralPublicKey": "c2FuZGJveF9lcGhlbWVyYWxfa2V5XzE3MDIyNTYwMDAwMDA=",
            "publicKeyHash": "c2FuZGJveF9wdWJsaWNfa2V5X2hhc2g=",
            "transactionId": "SANDBOX_TXN_1702256000000_a1b2c3d4e5f6"
        }
    },
    "paymentMethod": {
        "displayName": "Visa 4242",
        "network": "Visa",
        "type": "debit"
    },
    "transactionIdentifier": "SANDBOX_TXN_1702256000000_a1b2c3d4e5f6"
}

Common Token Errors

Error Cause Solution
Invalid token format Token is not a JSON object Send token as object, not string
Missing paymentData Token doesn't have paymentData property Include full token from Apple Pay JS
Missing header fields ephemeralPublicKey, publicKeyHash, or transactionId missing Ensure all header fields are present

Where Does the Token Come From?

In LIVE mode, the token comes from Apple Pay JS when the user authorizes payment:

session.onpaymentauthorized = (event) => {
    // This is the token you send to MONDO
    const token = event.payment.token;
    
    // Send to MONDO API
    fetch('/payment/', {
        body: JSON.stringify({
            apple_pay_token: token,  // Pass the entire token object
            ...
        })
    });
};

Transaction Status

Understanding transaction statuses and how to query them is essential for a complete integration.

Final Transaction Statuses

These are the possible final statuses you will receive via webhook or when querying the /details endpoint:

COMPLETED
Payment successful
REJECTED
Payment declined by processor
CANCELED
User canceled payment
FAILED
Technical error occurred

Intermediate Status

INITIATED

The transaction has been created but is still being processed. This can happen when:

  • User is completing 3D Secure authentication
  • Payment is being processed by the card network
  • User abandoned the payment flow

When Should You Query Transaction Status?

Best Practice: Always rely on webhooks as your primary notification method. Only query the /details endpoint as a fallback if:
  • You didn't receive a webhook within expected time (e.g., 5 minutes)
  • You need to verify a transaction status manually
  • Your webhook endpoint was temporarily unavailable

Query Transaction Status

To check the status of a transaction, make a POST request to the details endpoint:

POST https://server-to-server.getmondo.co/details/

Request Parameters

Parameter Required Description
company_account_id Your MONDO Partner Account ID
gateway_secret_key Your MONDO Partner Secret Key
gateway_session_id The session ID returned from the initial payment request

Example Request

curl -X POST https://server-to-server.getmondo.co/details/ \
  -H "Content-Type: application/json" \
  -d '{
    "company_account_id": "your_account_id",
    "gateway_secret_key": "your_secret_key",
    "gateway_session_id": "sess_abc123xyz"
  }'

Example Response

{
    "success": true,
    "transaction_status": "COMPLETED",
    "gateway_session_id": "sess_abc123xyz",
    "transaction_amount": "99.99",
    "transaction_currency_iso3": "EUR",
    "card_last_4_digits": "4242",
    "card_brand": "VISA",
    "gateway_result_code": "000.000.000",
    "gateway_result_description": "Transaction succeeded",
    "created_at": "2024-12-10T22:30:00Z",
    "completed_at": "2024-12-10T22:31:15Z"
}

Receiving Status Updates

There are two ways to receive the final transaction status:

Method How It Works When to Use
Webhook Recommended MONDO sends a POST request to your partner_webhook_url Primary method - automatic, real-time
Redirect URL User is redirected to your partner_return_url_completed or partner_return_url_rejected For user-facing confirmation
Query /details You make a POST request to check status Fallback if webhook not received

If Transaction Stays INITIATED

If a transaction remains in INITIATED status for more than 15 minutes, it likely means:

  • The user abandoned the 3D Secure challenge
  • The user closed their browser during payment
  • There was a network issue during processing

Action: Query the /details endpoint to get the current status. These transactions will eventually timeout and move to FAILED or CANCELED status.