Introduction v5.3.1

Powering global payments with secure, enterprise-grade APIs.

This documentation describes the integration procedures and POST protocol usage for e-commerce merchants using the Lipapap Enterprise Payment Platform. Our API implements acquiring payments (purchases) with a robust, secure, and scalable interaction model.

๐Ÿ” Security First: To work with the Lipapap API, merchants are required to comply with the Payment Card Industry Data Security Standards (PCI DSS). We enforce strict encryption and signature verification for all requests.

Key Capabilities

  • Global Payments: Support for 20+ currencies and alternative payment methods (APMs).
  • Recurring & Automation: Tokenization, scheduled payments, and Card2Card transfers.
  • Enterprise Security: 3D-Secure, HMAC-SHA256 signatures, and PCI DSS Level 1 compliance.
  • Developer Friendly: Clear request/response models, webhooks, and sandbox testing.

Integration Process

Get started in four simple steps.

1. Merchant Registration

Before accessing the Payment Platform, provide the following data to your account manager:

DataDescription
IP listList of IP addresses from which requests will be sent.
Callback URLURL for receiving processing result notifications. Max 255 characters.
Contact emailEmail of the person monitoring transactions and refunds.

After registration, you will receive your credentials:

CredentialDescription
CLIENT_KEYUnique UUID to identify your account.
SECRET_KEYPrivate key for signature generation. Keep confidential.
PAYMENT_URLEndpoint URL for API requests.

2. Protocol Mapping

Ensure the S2S CARD protocol is mapped in your account. Payments cannot be processed without proper mapping.

3. API Interaction

Send HTTPS POST requests to PAYMENT_URL with the required parameters. All responses are JSON-encoded.

3D-Secure (3DS) Flow

For 3DS-enabled transactions, the API returns a redirect response. You must redirect the cardholder to the provided redirect_url with the redirect_params using the specified redirect_method (POST/GET). After verification, the cardholder returns to your term_url_3ds.

{
    "action": "SALE",
    "client_key": "9e48fe5c-7653-824c-71a3-ad155d18ad27",
    "order_id": "order-12345",
    "order_amount": "99.99",
    "order_currency": "USD",
    "term_url_3ds": "https://your-site.com/3ds-return",
    "hash": "a1b2c3d4..."
}
{
    "action": "SALE",
    "result": "REDIRECT",
    "status": "3DS",
    "trans_id": "txn-67890",
    "redirect_url": "https://acs.bank.com/3ds",
    "redirect_method": "POST",
    "redirect_params": { "PaReq": "...", "MD": "..." }
}

4. Possible Actions

ActionDescription
SALECreates a SALE or AUTH transaction.
CAPTURECaptures a previously authorized transaction.
CREDITVOIDCreates a REFUND or REVERSAL.
RECURRING_SALECreates a SALE using stored cardholder data.
CARD2CARDTransfers funds between two credit cards.
GET_TRANS_STATUSRetrieves the status of a transaction.

Sale / Auth / Capture

Single Message (SMS) and Dual Message (DMS) transactions.

SALE Request

Use auth=Y for AUTH transactions (DMS). For sub-accounts, specify channel_id.

โš ๏ธ Important: In cascading mode, only the final attempt callback is sent unless a redirection is required.

Request Parameters

ParameterDescriptionRequired
actionSALEYes
client_keyYour unique UUIDYes
order_idYour transaction ID (max 255 chars)Yes
order_amountAmount (e.g., 100.99)Yes
order_currency3-letter currency code (e.g., USD)Yes
card_numberCredit card numberYes*
card_exp_monthExpiry month (MM)Yes*
card_exp_yearExpiry year (YYYY)Yes*
card_cvv2CVV/CVC2Yes
payer_emailCustomer emailYes
payer_ipCustomer IP addressYes
term_url_3ds3DS return URLYes
authY for AUTH onlyNo
hashSignature (Formula 1)Yes

* Optional if card_token is provided.

Response (Successful Sale)

{
    "action": "SALE",
    "result": "SUCCESS",
    "status": "SETTLED",
    "order_id": "order-12345",
    "trans_id": "txn-67890",
    "amount": "99.99",
    "currency": "USD",
    "descriptor": "LIPAPAP*Merchant"
}
{
    "action": "SALE",
    "result": "DECLINED",
    "status": "DECLINED",
    "order_id": "order-12345",
    "trans_id": "txn-67890",
    "decline_reason": "Insufficient funds"
}

CAPTURE Request

Captures a previously authorized transaction (auth=Y).

ParameterDescriptionRequired
actionCAPTUREYes
client_keyYour unique UUIDYes
trans_idPlatform transaction ID from the AUTHYes
amountCapture amount (optional, defaults to full amount)No
hashSignature (Formula 2)Yes

Alternative Payments (APM)

Mobile money, e-wallets, and local payment methods.

MOMOAPM MOBILE MONEY

Processes payments using mobile money and alternative payment methods. Supports 20+ currencies.

๐Ÿ“ฑ Pay attention: Ensure payer_phone is valid for the selected country and mobile money provider.

Request Parameters

ParameterDescriptionRequired
actionMOMOAPMYes
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmount (e.g., 113.00)Yes
order_descriptionTransaction descriptionYes
payer_phoneCustomer phone (e.g., +2547XXXXXXXX)Yes
payer_emailCustomer emailYes
payer_country2-letter country code (e.g., KE)Yes
term_url_3dsReturn URL after paymentYes
timestampUnix timestamp (milliseconds)Yes
hashSignature (Formula 1)Yes

Sample Request

{
    "action": "MOMOAPM",
    "client_key": "fe5cad15-5763-4c82-a371-9e488ad275d1",
    "order_id": "pay-1643-11001",
    "order_amount": "113.00",
    "order_description": "Payment for Invoice #INV-001",
    "payer_phone": "254712345678",
    "payer_email": "customer@example.com",
    "payer_country": "KE",
    "term_url_3ds": "https://example.com/callback",
    "timestamp": "1741027351153",
    "hash": "6517839f60ca23c382d02e9dbcfa82f8"
}
{
    "action": "MOMOAPM",
    "result": "REDIRECT",
    "status": "REDIRECT",
    "order_id": "pay-1643-11001",
    "trans_id": "pay-1643-11001",
    "amount": "113.00",
    "currency": "PHP",
    "txMsg": "Transaction successful",
    "redirect_url": "https://lipapap.net/pgate/refer.php?token=...",
    "redirect_method": "GET"
}
C2B_SIMULATE SANDBOX

Simulates a Customer to Business (C2B) payment transaction. This is intended for sandbox/testing environments only and allows developers to verify the C2B payment flow without using real funds.

๐Ÿงช Test Only: This action is available exclusively in sandbox mode. Production requests for C2B_SIMULATE will be rejected.

Request Parameters

ParameterDescriptionRequired
actionC2B_SIMULATEYes
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmount in KES (e.g., 10.00)Yes
order_descriptionTransaction descriptionYes
payer_phoneCustomer phone (format: 2547XXXXXXXX)Yes
payer_emailCustomer emailYes
payer_country2-letter country code (e.g., KE)Yes
timestampUnix timestamp (milliseconds)Yes
hashSignature (Formula 1)Yes

Sample Request

{
    "action": "C2B_SIMULATE",
    "client_key": "fe5cad15-5763-4c82-a371-9e488ad275d1",
    "order_id": "SIM-2026-001234",
    "order_amount": "10.00",
    "order_description": "C2B Simulation Test",
    "payer_phone": "254712345678",
    "payer_email": "simulate@example.com",
    "payer_country": "KE",
    "timestamp": "1741027351153",
    "hash": "6517839f60ca23c382d02e9dbcfa82f8"
}
{
    "action": "C2B_SIMULATE",
    "status": "success",
    "environment": "sandbox",
    "order_id": "SIM-2026-001234",
    "message": "Success. Request accepted for processing",
    "ResponseCode": "0",
    "ResponseDescription": "Success",
    "TransactionID": "SIM123456789"
}
STK_PUSH MOBILE PROMPT

Initiates an STK (Sim Toolkit) Push payment request. The customer receives a prompt on their mobile phone to enter their M-PESA PIN and complete the transaction.

๐Ÿ“ฒ Mobile Prompt: The STK Push flow requires the customer to have a registered M-PESA account. The customer receives a push notification on their registered phone number.

Request Parameters

ParameterDescriptionRequired
actionSTK_PUSHYes
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmount in KES (e.g., 100.00)Yes
order_descriptionTransaction descriptionYes
payer_phoneCustomer phone (format: 2547XXXXXXXX)Yes
payer_emailCustomer emailYes
payer_country2-letter country code (e.g., KE)Yes
term_url_3dsCallback URL for transaction resultYes
timestampUnix timestamp (milliseconds)Yes
hashSignature (Formula 1)Yes

Sample Request

{
    "action": "STK_PUSH",
    "client_key": "fe5cad15-5763-4c82-a371-9e488ad275d1",
    "order_id": "Mpesa-5643-12358",
    "order_amount": "100.00",
    "order_description": "Deposit From M-PESA",
    "payer_phone": "254722905699",
    "payer_email": "customer@example.com",
    "payer_country": "KE",
    "term_url_3ds": "https://example.com/callback",
    "timestamp": "1741027351153",
    "hash": "6517839f60ca23c382d02e9dbcfa82f8"
}
{
    "action": "STK_PUSH",
    "status": "success",
    "environment": "production",
    "order_id": "Mpesa-5643-12358",
    "CheckoutRequestID": "ws_CO_123456789",
    "MerchantRequestID": "MR-12345-67890",
    "ResponseCode": "0",
    "ResponseDescription": "Success. Request accepted for processing",
    "message": "STK Push sent successfully. Awaiting customer confirmation."
}

STK Push Flow

  1. Your system sends the STK_PUSH request to Lipapap.
  2. Lipapap forwards the request to the M-PESA API.
  3. The customer receives a pop-up on their phone asking them to confirm the payment.
  4. After the customer enters their PIN, M-PESA processes the transaction.
  5. A callback is sent to your term_url_3ds with the final transaction result.
C2B_CONFIRMATION WEBHOOK

This action is used to handle incoming confirmation callbacks from the M-PESA API for C2B transactions. This endpoint is called by Lipapap/M-PESA and should be implemented by your system to receive and process C2B transaction confirmations.

๐Ÿ”” Webhook Endpoint: You must provide a publicly accessible URL to receive these callbacks. The URL should be configured with your account manager or via the merchant portal.

Callback Payload (Received from Lipapap)

FieldDescription
TransIDM-PESA transaction ID
TransactionTypeType of transaction (e.g., PayBill, BuyGoods)
TransTimeTransaction timestamp
TransAmountTransaction amount
BusinessShortCodeYour Paybill/Till number
BillRefNumberBill reference number
MSISDNCustomer phone number
FirstNameCustomer first name
LastNameCustomer last name

Sample Callback Payload

{
    "TransID": "TSC123456789",
    "TransactionType": "PayBill",
    "TransTime": "20260128153045",
    "TransAmount": "100.00",
    "BusinessShortCode": "123456",
    "BillRefNumber": "INV-2026-001",
    "InvoiceNumber": "INV-2026-001",
    "OrgAccountBalance": "50000.00",
    "ThirdPartyTransID": "TP-123456",
    "MSISDN": "254712345678",
    "FirstName": "James",
    "MiddleName": "M",
    "LastName": "Kilali"
}
{
    "ResultCode": "0",
    "ResultDesc": "Success"
}

Webhook Implementation Example (Server-Side)

<?php
// Your webhook endpoint to receive C2B confirmations
// Example: https://your-domain.com/mpesa/c2b/callback

// Get the raw POST data from M-PESA
$rawInput = file_get_contents('php://input');
$data = json_decode($rawInput, true);

// Validate the callback
if (!$data) {
    http_response_code(400);
    echo json_encode(['ResultCode' => '1', 'ResultDesc' => 'Invalid payload']);
    exit;
}

// Extract transaction data
$transactionId = $data['TransID'] ?? null;
$amount = $data['TransAmount'] ?? 0;
$phone = $data['MSISDN'] ?? '';
$billRef = $data['BillRefNumber'] ?? '';
$shortcode = $data['BusinessShortCode'] ?? '';

// Process the transaction in your system
// 1. Validate the bill reference
// 2. Update order status
// 3. Send confirmation to customer

// Respond to M-PESA with success
http_response_code(200);
echo json_encode([
    'ResultCode' => '0',
    'ResultDesc' => 'Success'
]);
?>
๐Ÿ“‹ Important: Your webhook endpoint must respond with ResultCode: 0 within 5 seconds, otherwise M-PESA will retry the callback.

Refund / Reverse

CreditVOID transactions for full or partial refunds.

CREDITVOID Request

Completes a REFUND (full/partial) or REVERSAL transaction.

ParameterDescriptionRequired
actionCREDITVOIDYes
client_keyYour unique UUIDYes
trans_idPlatform transaction ID to refundYes
amountRefund amount (optional, defaults to full)No
hashSignature (Formula 2)Yes

Response

{
    "action": "CREDITVOID",
    "result": "ACCEPTED",
    "order_id": "order-12345",
    "trans_id": "txn-67890"
}
{
    "action": "CREDITVOID",
    "result": "SUCCESS",
    "status": "REFUND",
    "order_id": "order-12345",
    "trans_id": "txn-67890",
    "amount": "99.99",
    "hash": "a1b2c3d4..."
}

Void / Debit / Credit2Card

Cancel, debit, or transfer funds.

VOID Request

Cancels a previously authorized transaction.

ParameterDescriptionRequired
actionVOIDYes
client_keyYour unique UUIDYes
trans_idPlatform transaction ID to voidYes
hashSignature (Formula 2)Yes

DEBIT Request

Transfers funds from a credit card to a merchant's account.

ParameterDescriptionRequired
actionDEBITYes
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmount (e.g., 500.00)Yes
card_numberCredit card numberYes
card_exp_monthExpiry monthYes
card_exp_yearExpiry yearYes
payer_ipCustomer IP addressYes
hashSignature (Formula 1)Yes

CREDIT2CARD Request

Transfers funds from a merchant's account to a credit card.

ParameterDescriptionRequired
actionCREDIT2CARDYes
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmount (e.g., 250.00)Yes
card_numberReceiver's credit card numberYes
card_exp_monthReceiver's expiry monthYes
card_exp_yearReceiver's expiry yearYes
payer_ipCustomer IP addressYes
hashSignature (Formula 1)Yes

Recurring Sale & Card2Card

Automated payments and card-to-card transfers.

RECURRING_SALE Request

Implements SALE or AUTH transactions using a stored recurring_token from a previous transaction with recurring_init=Y.

๐Ÿ”„ Important: In cascading mode, only the final attempt callback is sent.
ParameterDescriptionRequired
actionRECURRING_SALEYes
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmountYes
recurring_tokenToken from a previous transactionYes
payer_emailCustomer emailYes
payer_ipCustomer IP addressYes
authY for AUTH onlyNo
hashSignature (Formula 1)Yes

CARD2CARD Request

Transfers money between two credit cards.

ParameterDescriptionRequired
actionCARD2CARDYes
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmountYes
sender_card_numberSender's card numberYes
sender_card_exp_monthSender's expiry monthYes
sender_card_exp_yearSender's expiry yearYes
sender_card_cvv2Sender's CVVYes
receiver_card_numberReceiver's card numberYes
payer_ipCustomer IP addressYes
term_url_3ds3DS return URLYes
hashSignature (Formula 1)Yes

Recurring Schedules

CREATE, PAUSE, RUN, DELETE, and INFO operations.

CREATE_SCHEDULE Request

Creates a recurring payment schedule.

ParameterDescriptionRequired
actionCREATE_SCHEDULEYes
client_keyYour unique UUIDYes
schedule_idUnique schedule IDYes
recurring_tokenToken from a previous transactionYes
amountAmount per paymentYes
currency3-letter currency codeYes
start_dateSchedule start date (yyyy-MM-dd)Yes
intervalDAILY, WEEKLY, MONTHLY, YEARLYYes
interval_countNumber of intervals between paymentsYes
hashSignature (Formula 1)Yes

Schedule Management

ActionDescriptionRequired Params
PAUSE_SCHEDULESuspends a scheduleschedule_id
RUN_SCHEDULEResumes a paused scheduleschedule_id
DELETE_SCHEDULEDeletes a scheduleschedule_id
SCHEDULE_INFORetrieves schedule detailsschedule_id
DESCHEDULETerminates schedule and removes tokenschedule_id

All management actions require client_key, action, schedule_id, and hash (Formula 2).

Transaction Status

Query transaction and order details.

GET_TRANS_STATUS

Retrieves the status of a specific transaction.

ParameterDescriptionRequired
actionGET_TRANS_STATUSYes
client_keyYour unique UUIDYes
trans_idPlatform transaction IDYes
hashSignature (Formula 2)Yes

GET_TRANS_DETAILS

Retrieves full order details including all associated transactions.

ParameterDescriptionRequired
actionGET_TRANS_DETAILSYes
client_keyYour unique UUIDYes
order_idMerchant transaction IDYes
hashSignature (Formula 2)Yes

Status Codes

StatusDescription
SETTLEDTransaction completed successfully.
PENDINGAwaiting capture (AUTH transaction).
DECLINEDTransaction was declined.
3DSAwaiting 3D-Secure validation.
REFUNDTransaction has been refunded.
REVERSALTransaction has been reversed.
VOIDTransaction has been voided.

Appendix & Error Codes

Signature formulas, error codes, and testing.

Formula 1: Request Signature

Signature = HMAC-SHA256(
    CONCAT(
        client_key,
        order_id,
        order_amount,
        order_currency,
        order_description,
        card_number,
        card_exp_month,
        card_exp_year,
        payer_email,
        payer_phone,
        payer_ip,
        secret_key
    ),
    secret_key
)
                

Non-required parameters not provided should be replaced with an empty string.

Formula 2: Response/Callback Signature

Signature = HMAC-SHA256(
    CONCAT(
        action,
        result,
        status,
        order_id,
        trans_id,
        trans_date,
        amount,
        currency,
        decline_reason,
        secret_key
    ),
    secret_key
)
                

If decline_reason is not present, use an empty string.

Error Codes

CodeDescription
100Invalid client_key
101Invalid signature
102Missing required parameter
200Transaction not found
202Transaction declined by issuer
300Invalid card details
302Insufficient funds
500Token not found
600System error โ€” contact support

Testing

Card NumberExpiryResult
4111 1111 1111 111101/2026Successful SALE โ†’ SETTLED
4000 0000 0000 000201/2026Declined โ†’ DECLINED
5555 5555 5555 444401/20263DS Required โ†’ REDIRECT

iFrame Integration v2.0

Embed the Lipapap payment form directly in your website.

๐Ÿ–ฅ๏ธ iFrame ready: The iFrame approach allows you to host the payment form within your own page, giving you full control over the UI while keeping payment data secure.

How It Works

You can embed the Lipapap payment interface in an iFrame by constructing the proper URL with query parameters. The iFrame will handle the payment flow (including 3DS) and communicate results via postMessage or redirects.

1. Build the iFrame URL

Use the following base URL and append the required parameters. All parameters are URL-encoded.

ParameterDescriptionRequired
client_keyYour unique UUIDYes
order_idYour transaction IDYes
order_amountAmount (e.g., 99.99)Yes
order_currency3-letter currency codeYes
payer_emailCustomer emailYes
payer_ipCustomer IP addressYes
term_url_3ds3DS return URL (must be same origin as parent)Yes
hashSignature (Formula 1)Yes
iframe_modeSet to 1 to enable iFrame modeYes
redirect_methodPOST or GET (default POST)No

Example iFrame URL:

https://api.lipapap.com/payment/iframe?
client_key=9e48fe5c-7653-824c-71a3-ad155d18ad27
&order_id=order-12345
&order_amount=99.99
&order_currency=USD
&payer_email=customer@example.com
&payer_ip=192.168.1.100
&term_url_3ds=https://your-site.com/3ds-return
&hash=a1b2c3d4e5f6...
&iframe_mode=1
<iframe src="https://api.lipapap.com/payment/iframe?
client_key=9e48fe5c-7653-824c-71a3-ad155d18ad27
&order_id=order-12345
&order_amount=99.99
&order_currency=USD
&payer_email=customer@example.com
&payer_ip=192.168.1.100
&term_url_3ds=https://your-site.com/3ds-return
&hash=a1b2c3d4e5f6...
&iframe_mode=1"
        width="100%" 
        height="600" 
        frameborder="0"
        allow="payment"
        sandbox="allow-scripts allow-same-origin allow-forms">
</iframe>

2. Handling 3DS in iFrame

When 3DS is required, the iFrame will redirect to the bank's ACS page. After verification, the cardholder is sent back to your term_url_3ds. For iFrame mode, we recommend that term_url_3ds sends a postMessage back to the parent window with the transaction result.

// In your parent page, listen for messages from the iFrame
window.addEventListener('message', function(event) {
    // Verify origin for security
    if (event.origin !== 'https://api.lipapap.com') return;

    const data = event.data;
    if (data.type === 'payment_result') {
        // Handle success, decline, or error
        console.log('Payment result:', data);
        if (data.result === 'SUCCESS') {
            // Redirect to success page or update UI
        } else {
            // Handle decline
        }
    }
});
// On your term_url_3ds page (inside iFrame):
const result = {
    type: 'payment_result',
    result: 'SUCCESS', // or 'DECLINED'
    order_id: 'order-12345',
    trans_id: 'txn-67890',
    amount: '99.99'
};

// Send result to parent window
window.parent.postMessage(result, 'https://your-site.com');

3. iFrame Styling & Responsiveness

You can style the container and iFrame to fit your page design. We recommend using a flexible layout:

/* Container */
.payment-iframe-container {
    width: 100%;
    max-width: 480px;
    margin: 0 auto;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    background: #ffffff;
}

.payment-iframe-container iframe {
    width: 100%;
    height: 520px;
    border: none;
    display: block;
}

/* Responsive */
@media (max-width: 600px) {
    .payment-iframe-container iframe {
        height: 480px;
    }
}

4. Full Integration Example

Below is a complete, working example that combines the iFrame, postMessage listener, and a simple UI.

<!DOCTYPE html>
<html>
<head>
    <title>Lipapap iFrame Payment</title>
    <style>
        body { font-family: Inter, sans-serif; background: #f8fafc; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; padding: 20px; }
        .container { width: 100%; max-width: 480px; }
        .card { background: white; border-radius: 16px; padding: 24px; box-shadow: 0 4px 24px rgba(0,0,0,0.06); }
        .card h2 { margin-top: 0; font-weight: 600; }
        .amount { font-size: 1.5rem; font-weight: 700; color: #0f172a; }
        .iframe-wrapper { margin: 20px 0; border-radius: 12px; overflow: hidden; border: 1px solid #e2e8f0; }
        .iframe-wrapper iframe { width: 100%; height: 480px; border: none; display: block; }
        .status { padding: 12px 16px; border-radius: 8px; margin-top: 16px; display: none; }
        .status.success { background: #dcfce7; color: #166534; display: block; }
        .status.declined { background: #fee2e2; color: #991b1b; display: block; }
        .status.error { background: #fef9c3; color: #854d0e; display: block; }
    </style>
</head>
<body>
    <div class="container">
        <div class="card">
            <h2>Complete Your Payment</h2>
            <p>Order: <strong>order-12345</strong></p>
            <p class="amount">$99.99 USD</p>

            <div class="iframe-wrapper">
                <iframe id="paymentIframe"
                    src="https://api.lipapap.com/payment/iframe?client_key=9e48fe5c-7653-824c-71a3-ad155d18ad27&order_id=order-12345&order_amount=99.99&order_currency=USD&payer_email=customer@example.com&payer_ip=192.168.1.100&term_url_3ds=https://your-site.com/3ds-return&hash=a1b2c3d4e5f6...&iframe_mode=1"
                    allow="payment"
                    sandbox="allow-scripts allow-same-origin allow-forms"
                ></iframe>
            </div>

            <div id="paymentStatus" class="status"></div>
        </div>
    </div>

    <script>
        window.addEventListener('message', function(event) {
            // Always verify origin in production
            if (event.origin !== 'https://api.lipapap.com') return;

            const data = event.data;
            if (data.type === 'payment_result') {
                const statusDiv = document.getElementById('paymentStatus');
                if (data.result === 'SUCCESS') {
                    statusDiv.className = 'status success';
                    statusDiv.textContent = 'โœ… Payment successful! Transaction ID: ' + data.trans_id;
                } else if (data.result === 'DECLINED') {
                    statusDiv.className = 'status declined';
                    statusDiv.textContent = 'โŒ Payment declined: ' + (data.decline_reason || 'Please try again.');
                } else {
                    statusDiv.className = 'status error';
                    statusDiv.textContent = 'โš ๏ธ Payment error: ' + (data.message || 'Unknown error');
                }
            }
        });
    </script>
</body>
</html>
๐Ÿ”’ Security Note: Always verify the event.origin in your postMessage listener to prevent malicious data injection. Also ensure your term_url_3ds page sends the payment result only after proper validation.