Skip to main content
Version: 4.0.2

PostMessage Integration

The Charge SDK uses PostMessage API for secure cross-origin communication between the merchant's frontend application and the payment processing iframe/popup window. This enables real-time payment status updates without requiring page refreshes or polling.

PostMessage Communication Flow

Message Types

1. SDK_LOADED

Sent when the payment SDK has successfully loaded and is ready for user interaction.

Message Structure:

{
type: 'SDK_LOADED',
timestamp: '2024-01-15T10:30:00.000Z'
}

When to expect: Immediately after the payment window opens and the SDK initializes.

Merchant Action: Update loading status to indicate the payment form is ready.

2. PAYMENT_COMPLETED / PAYMENT_SUCCESS

Sent when a payment has been successfully processed and completed.

Message Structure:

{
type: 'PAYMENT_COMPLETED', // or 'PAYMENT_SUCCESS'
chargeId: 'c4767bf0-83a0-4b53-aece-23565cddff90',
orderId: 'dba445e8-a780-4b38-8527-507f4429676a',
amount: 1200000, // Amount in lowest denominator
currency: 'IDR',
status: 'completed',
timestamp: '2024-01-15T10:35:00.000Z'
}

When to expect: After successful payment processing.

Merchant Action:

  • Close payment window
  • Redirect to success/confirmation page
  • Update order status in your system
  • Display success message to user

3. PAYMENT_CANCELLED

Sent when the user cancels the payment process.

Message Structure:

{
type: 'PAYMENT_CANCELLED',
reason: 'user_cancelled', // or 'timeout', 'error'
timestamp: '2024-01-15T10:32:00.000Z'
}

When to expect: When user closes payment window or clicks cancel.

Merchant Action:

  • Close payment window
  • Return user to checkout page
  • Display appropriate message

4. PAYMENT_FAILED

Sent when a payment attempt fails due to processing errors.

Message Structure:

{
type: 'PAYMENT_FAILED',
error: {
code: 'INSUFFICIENT_FUNDS',
message: 'Insufficient funds in account'
},
timestamp: '2024-01-15T10:33:00.000Z'
}

When to expect: When payment processing encounters an error.

Merchant Action:

  • Display error message
  • Allow user to retry or choose different payment method
  • Log error for debugging

Implementation Guide

Step 1: Set Up PostMessage Listener

Add a message event listener to your merchant frontend application:

window.addEventListener('message', function(event) {
console.log('Received postMessage:', event);

// Verify origin for security.
// The full list of origins for your configuration is provided during onboarding.
const allowedOrigins = [
'http://localhost:3000',
'https://dmg-ecommerce-backend.ecomm-stg.dmgsecure.io',
window.location.origin
];

if (!allowedOrigins.includes(event.origin)) {
console.warn('Received message from unauthorized origin:', event.origin);
return;
}

const data = event.data;
handlePaymentMessage(data);
});

Step 2: Handle Different Message Types

Create a message handler function:

function handlePaymentMessage(data) {
console.log('Processing message type:', data.type);

switch (data.type) {
case 'SDK_LOADED':
handleSDKLoaded(data);
break;

case 'PAYMENT_COMPLETED':
case 'PAYMENT_SUCCESS':
handlePaymentSuccess(data);
break;

case 'PAYMENT_CANCELLED':
handlePaymentCancelled(data);
break;

case 'PAYMENT_FAILED':
handlePaymentFailed(data);
break;

default:
console.log('Unknown message type:', data.type);
}
}

Step 3: Implement Message Handlers

function handleSDKLoaded(data) {
console.log('SDK has loaded successfully');

// Clear any loading timeouts
if (sdkLoadTimeout) {
clearTimeout(sdkLoadTimeout);
sdkLoadTimeout = null;
}

// Update UI to show payment is ready
updatePaymentStatus('Waiting for payment...', 'loading');
}

function handlePaymentSuccess(data) {
console.log('Payment completed successfully:', data);

// Update UI to show success
updatePaymentStatus('Payment completed successfully!', 'success');

// Close payment window and redirect after delay
setTimeout(() => {
closePaymentWindow();
window.location.href = `payment-confirmation.html?transaction_id=${data.chargeId}&amount=${data.amount}`;
}, 2000);
}

function handlePaymentCancelled(data) {
console.log('Payment was cancelled:', data);

// Update UI to show cancellation
updatePaymentStatus('Payment was cancelled', 'error');

// Close payment window after delay
setTimeout(() => {
closePaymentWindow();
}, 2000);
}

function handlePaymentFailed(data) {
console.log('Payment failed:', data);

// Update UI to show error
updatePaymentStatus(`Payment failed: ${data.error?.message || 'Unknown error'}`, 'error');

// Close payment window after delay
setTimeout(() => {
closePaymentWindow();
}, 2000);
}

Step 4: Payment Window Management

let paymentWindow = null;
let paymentWindowCheckInterval = null;

function openPaymentWindow(paymentLink) {
// Open payment window with specific dimensions
paymentWindow = popupPayment({
url: paymentLink,
target: 'paymentWindow',
w: 500,
h: 630
});

// Start monitoring the payment window
startPaymentWindowMonitoring();
}

function popupPayment({url, target, w, h}) {
const top = Math.max(0, (window.screen.height - h) / 2);
const left = Math.max(0, (window.screen.width - w) / 2);

const newWindow = window.open(url, target, `
scrollbars=yes,
width=${w},
height=${h},
top=${top},
left=${left}
`);

if (window.focus) {
newWindow.focus();
}

return newWindow;
}

function startPaymentWindowMonitoring() {
// Check if payment window is closed every 1 second
paymentWindowCheckInterval = setInterval(() => {
if (paymentWindow && paymentWindow.closed) {
console.log('Payment window was closed by user');
updatePaymentStatus('Payment window was closed', 'error');

// Clean up and hide loader
setTimeout(() => {
closePaymentWindow();
}, 2000);
}
}, 1000);
}

function closePaymentWindow() {
// Clean up intervals
if (paymentWindowCheckInterval) {
clearInterval(paymentWindowCheckInterval);
paymentWindowCheckInterval = null;
}

// Close payment window if still open
if (paymentWindow && !paymentWindow.closed) {
paymentWindow.close();
paymentWindow = null;
}

// Hide any loading overlays
hidePaymentLoader();
}

Security Considerations

Origin Validation

Always validate the origin of incoming messages to prevent security vulnerabilities:

const allowedOrigins = [
'https://your-payment-domain.com',
'https://staging-payment-domain.com',
window.location.origin // For same-origin scenarios
];

if (!allowedOrigins.includes(event.origin)) {
console.warn('Received message from unauthorized origin:', event.origin);
return;
}

Message Validation

Validate message structure and content:

function isValidMessage(data) {
// Check if message has required type field
if (!data || typeof data.type !== 'string') {
return false;
}

// Validate specific message types
switch (data.type) {
case 'PAYMENT_COMPLETED':
return data.chargeId && data.orderId && typeof data.amount === 'number';
case 'PAYMENT_FAILED':
return data.error && data.error.code;
default:
return true;
}
}

Timeout Handling

Implement timeouts to handle scenarios where messages might not be received:

let sdkLoadTimeout = setTimeout(() => {
console.warn('SDK load timeout - no SDK_LOADED message received');
updatePaymentStatus('Payment page loading timeout', 'warning');
}, 10000); // 10 second timeout

Troubleshooting

Common Issues

1. Messages Not Received

Problem: PostMessage events are not being received.

Solutions:

  • Verify origin validation is not blocking legitimate messages
  • Check browser console for JavaScript errors
  • Ensure event listener is added before opening payment window
  • Verify payment window is opening correctly

2. Origin Validation Errors

Problem: Messages are being blocked due to origin mismatch.

Solutions:

  • Add all legitimate payment domain origins to allowedOrigins array
  • Check for protocol mismatches (http vs https)
  • Verify subdomain configurations

3. Payment Window Closes Immediately

Problem: Payment window closes before user can complete payment.

Solutions:

  • Check for popup blockers
  • Verify payment URL is valid and accessible
  • Ensure window dimensions are appropriate
  • Check for JavaScript errors in payment window

4. Timeout Issues

Problem: SDK_LOADED message never received.

Solutions:

  • Increase timeout duration
  • Check network connectivity
  • Verify payment service is running
  • Check for CORS issues

Debug Mode

Enable debug logging for troubleshooting to make sure the orgin whitelisting is correct and nothing else is blocking the postmessaging from the application side:

const DEBUG_MODE = true; // Set to false in production

function debugLog(message, data = null) {
if (DEBUG_MODE) {
console.log(`[Payment Debug] ${message}`, data);
}
}

// Use in message handler
window.addEventListener('message', function(event) {
debugLog('Received message', {
origin: event.origin,
type: event.data?.type,
data: event.data
});

// ... rest of handler
});