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
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
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 |
Choose Your Integration Method
📋 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.
- Go to GETMONDO.CO
- Login → Developer → Apple Pay → Add Apple Pay Domain
🔄 Payment Flow
On your website
Native Apple Pay interface
Your JS calls MONDO's validation endpoint
Face ID / Touch ID
POST to /payment/ with apple_pay_token
User redirected to success/failure URL
🔗 API Endpoint
Merchant Validation Endpoint
Merchant Validation Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| validationURL | string | ✓ Yes | The URL provided by Apple in the onvalidatemerchant event |
| company_account_id | string | ✓ Yes | Your MONDO Partner Account ID |
| gateway_secret_key | string | ✓ Yes | Your MONDO Partner Secret Key |
| domain_name | string | ○ Conditional | Your 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. |
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)
| Parameter | Type | Description |
|---|---|---|
| company_account_id | string | Your MONDO Partner Account ID |
| gateway_secret_key | string | Your MONDO Partner Secret Key |
| apple_pay_token | object | Encrypted token from event.payment.token — pass directly to MONDO as-is (do not decrypt) |
| transaction_amount | decimal | Payment amount (e.g., 99.99) |
| transaction_currency_iso3 | string | Currency code (EUR, USD, GBP) |
| cardholder_email_address | string | Customer's email |
Optional Parameters
| Parameter | Description |
|---|---|
| partner_return_url_completed | URL for successful payment redirect |
| partner_return_url_rejected | URL for rejected payment redirect |
| partner_webhook_url | URL for webhook notifications |
| live_or_sandbox | live 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
| Field | Description |
|---|---|
| status | success or error |
| transaction_status | COMPLETED or REJECTED |
| gateway_session_id | Unique session identifier for this transaction |
| transaction_id | Unique transaction identifier - use this to correlate with callbacks |
| gateway_message | Human-readable status message |
| redirect_url | URL to redirect user after payment (optional - for UX only) |
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.
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
What Happens
- ✅ Token structure is validated
- ✅ All required fields are checked
- ✅ Real cryptographic decryption
- ✅ Real CardCorp API processing
- ✅ Real money is charged
🎯 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.
⚙️ 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:
| Parameter | Value | Result |
|---|---|---|
| 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.
"***") 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.version | ✓ | Always EC_v1 |
| paymentData.data | ✓ | Base64 encrypted payment data from Apple |
| paymentData.signature | ✓ | Base64 signature for verification |
| paymentData.header.ephemeralPublicKey | ✓ | Base64 ephemeral public key |
| paymentData.header.publicKeyHash | ✓ | Base64 hash of merchant's public key |
| paymentData.header.transactionId | ✓ | Unique transaction identifier |
| paymentMethod.network | ○ | Card network (Visa, Mastercard, Amex) |
| paymentMethod.displayName | ○ | Display 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:
Intermediate Status
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?
- 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:
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.