Skip to content

Channel Messaging

Feature Type

PWA Editor Feature - Real-time messaging from the PWA editor to your application.

The PWA editor sends real-time messages to the parent application at key points during the user journey.

Overview

Channel messages are sent via postMessage API to communicate:

  • Save events during editing
  • Navigation between editor and checkout
  • Progress through checkout steps
  • Order completion

Interactive Demo

Try the Channel Messaging Demo for a generic sample of how channel messages work.

Message Format

All channel messages include basic information:

json
{
  "close": false,
  "checkout": false,
  "action": "save",
  "ui": "mobile",
  "route": {
    "view": "editor",
    "step": 1
  }
}

Base Message Fields

FieldTypeDescription
closebooleanSuggestion whether the view should be closed
checkoutbooleanWhether user has initiated checkout process
actionstringCurrent action/event (e.g., "save", "order", "payment")
uistringUI mode: "mobile" or "desktop"
route.viewstringCurrent page name (e.g., "editor", "checkout")
route.stepintegerStep number for multi-step processes

Additional Data

Depending on the current route, messages include either projectData or checkoutData.

Project Data

Included during editor events:

json
{
  "title": "Family Vacation 2024",
  "preview": "https://pwa-pbai-dev-attachments.s3-accelerate.amazonaws.com/u-abc/jonh123456/projects/p-def/preview.jpeg",
  "link": "https://{frontend}.photobook.ai/editor?id=p-abc123",
  "id": "p-abc123",
  "endUserId": "user-provided-id",
  "product": { /* product details */ },
  "metadata": {
    "currentPages": 24,
    "usedPhotoCount": 18,
    "productName": "Premium Photo Book"
  }
}
FieldTypeDescription
titlestringUser-entered title (or product SKU if not set)
previewstringURL to project preview image
linkstringDirect link to editor for this project
idstringUnique project ID
endUserIdstringUser ID provided by parent client
metadata.currentPagesintegerNumber of pages in project
metadata.usedPhotoCountintegerNumber of photos used (not just uploaded)
metadata.productNamestringName of the product

Checkout Data

Included during checkout events, if your client configuration uses the Editor's internal checkout to process the user's order. Contents vary by checkout step:

Summary Step:

json
{
  "results": [
    {
      "quantity": 2,
      "extraCostPerPage": 0.50,
      "price": 29.99,
      "total": 65.98
    }
  ]
}

Address Step:

json
{
  "results": {
    "firstName": "John",
    "lastName": "Doe",
    "address1": "123 Main St",
    "address2": "Apt 4B",
    "city": "New York",
    "state": "NY",
    "postcode": "10001",
    "country": "United States",
    "isoCode2": "US",
    "isoCode3": "USA",
    "telephone": "+1234567890"
  }
}

Payment Step:

json
{
  "orderId": 12345,
  "voucher": "SAVE10",
  "paymentMethod": "stripe",
  "cartSubTotal": 65.98,
  "shippingCostTotal": 5.99,
  "taxTotal": 3.60,
  "couponTotal": 6.60,
  "grandTotal": 68.97,
  "items": [ /* line items */ ],
  "shippingAddress": { /* address object */ }
}

Message Scenarios

Editor Events

Auto-Save During Editing

Sent periodically as user edits:

json
{
  "title": "Summer Memories",
  "preview": "https://pwa-pbai-dev-attachments.s3-accelerate.amazonaws.com/u-abc/jonh123456/projects/p-def/preview.jpeg",
  "link": "https://{frontend}.photobook.ai/editor?id=p-def",
  "id": "p-def",
  "createdAt": 1774959510786,
  "updatedAt": 1774959594788,
  "expiresAt": 1790770710,
  "product": {
    /* Product configuration including dimensions, specifications, pricing, and PSP details */
  },
  "endUserId": "jonh123456",
  "clientName": "yourclient",
  "metadata": {
    "costPerPage": 0.5,
    "currentPages": 42,
    "basePages": 24,
    "basePrice": 12,
    "usedPhotoCount": 43,
    "productName": "Hard-21-S-190gsm"
  },
  "close": false,
  "checkout": false,
  "action": "save",
  "ui": "desktop",
  "route": {
    "view": "editor",
    "step": 1
  }
}

What to do:

  • Update project status in your database
  • Show save indicator to user
  • Track progress

User Saves and Exits Editor

Sent when user saves and confirms exit:

json
{
  "title": "Summer Memories",
  "preview": "https://pwa-pbai-dev-attachments.s3-accelerate.amazonaws.com/u-abc/jonh123456/projects/p-def/preview.jpeg",
  "link": "https://{frontend}.photobook.ai/editor?id=p-def",
  "id": "p-def",
  "createdAt": 1774959510786,
  "updatedAt": 1774960084901,
  "expiresAt": 1790770710,
  "product": {
    /* Product configuration including dimensions, specifications, pricing, and PSP details */
  },
  "endUserId": "jonh123456",
  "clientName": "yourclient",
  "metadata": {
    "costPerPage": 0.5,
    "currentPages": 42,
    "basePages": 24,
    "basePrice": 12,
    "usedPhotoCount": 43,
    "productName": "Hard-21-S-190gsm"
  },
  "close": true,
  "checkout": false,
  "action": "saveAndExit",
  "ui": "desktop",
  "route": {
    "view": "editor",
    "step": 1
  }
}

What to do:

  • Close the webview/iframe
  • Save project state
  • Bring the user back to the page you want them to land

User Discards Progress and Exits Editor (leaving any uploaded photos intact)

Sent when the user discards progress (sans uploaded photos) and confirms exit:

json
{
  "title": "Summer Memories",
  "preview": "https://pwa-pbai-dev-attachments.s3-accelerate.amazonaws.com/u-abc/jonh123456/projects/p-def/preview.jpeg",
  "link": "https://{frontend}.photobook.ai/editor?id=p-def",
  "id": "p-def",
  "createdAt": 1774959510786,
  "updatedAt": 1774960084901,
  "expiresAt": 1790770710,
  "product": {
    /* Product configuration including dimensions, specifications, pricing, and PSP details */
  },
  "endUserId": "jonh123456",
  "clientName": "yourclient",
  "metadata": {
    "costPerPage": 0.5,
    "currentPages": 42,
    "basePages": 24,
    "basePrice": 12,
    "usedPhotoCount": 43,
    "productName": "Hard-21-S-190gsm"
  },
  "close": true,
  "checkout": false,
  "action": "clearPagebuild",
  "ui": "desktop",
  "route": {
    "view": "editor",
    "step": 1
  }
}

What to do:

  • Close the webview/iframe
  • Save project state (Project is reset to an empty state)
  • Bring the user back to the page you want them to land

User Deletes Project and Exits Editor

Sent when the user deletes the project and confirms exit, usually in the case when a new empty project is generated, and the user chooses to exit before uploading anything:

json
{
  "close": true,
  "checkout": false,
  "action": "deleteProject",
  "ui": "desktop",
  "route": {
    "view": "upload",
    "step": 1
  }
}

What to do:

  • Close the webview/iframe
  • The project is deleted, so there's no need to save it
  • Bring the user back to the page you want them to land

User Initiates Checkout

⚠️ Checkout Flow Fork

When the user clicks checkout/order, there are two possible flows depending on your client configuration:

  • Flow A: Internal Checkout - The Editor's built-in checkout UI handles the entire order process (cart, address, payment, confirmation). See Checkout Events below.

  • Flow B: External Checkout - Control returns to your parent application to handle checkout with your own UI. See External Checkout Flow below.

Flow A: Internal Checkout (Editor handles checkout)

Sent when user clicks checkout/order button and your configuration uses the Editor's internal checkout:

json
{
  "close": false,
  "checkout": true,
  "action": "order",
  "ui": "desktop",
  "route": {
    "view": "editor",
    "step": 1
  }
  // ... projectData fields
}

What to do:

  • Track conversion funnel
  • Keep webview/iframe open (checkout flow begins)
  • The Editor will guide the user through the checkout steps below
Flow B: External Checkout (Your application handles checkout)

Sent when user clicks checkout/order/add to cart button and your configuration uses external checkout:

json
{
  "title": "Summer Memories",
  "preview": "https://pwa-pbai-dev-attachments.s3-accelerate.amazonaws.com/u-abc/jonh123456/projects/p-def/preview.jpeg",
  "link": "https://{frontend}.photobook.ai/editor?id=p-def",
  "id": "p-def",
  "createdAt": 1774959510786,
  "updatedAt": 1774960084901,
  "expiresAt": 1790770710,
  "product": {
    /* Product configuration including dimensions, specifications, pricing, and PSP details */
  },
  "endUserId": "jonh123456",
  "clientName": "yourclient",
  "metadata": {
    "costPerPage": 0.5,
    "currentPages": 42,
    "basePages": 24,
    "basePrice": 12,
    "usedPhotoCount": 43,
    "productName": "Hard-21-S-190gsm"
  },
  "close": true,
  "checkout": true,
  "action": "order",
  "ui": "desktop",
  "route": {
    "view": "editor",
    "step": 1
  }
}

What to do:

  • Close the webview/iframe
  • Use the project and product data to add the item to your cart
  • Navigate to your application's checkout flow
  • Process the order using your own payment and fulfillment system

Checkout Events (Flow A Only)

Applies to Internal Checkout Only

The following events are only sent when using Flow A: Internal Checkout. If you're using Flow B: External Checkout, these events will not occur.

Summary → Address (Step 1 → 2)

json
{
  "close": false,
  "checkout": true,
  "action": "summary",
  "ui": "desktop",
  "route": {
    "view": "checkout",
    "step": 1
  },
  "results": [ /* cart items */ ]
}

Address → Payment (Step 2 → 3)

json
{
  "close": false,
  "checkout": true,
  "action": "address",
  "ui": "desktop",
  "route": {
    "view": "checkout",
    "step": 2
  },
  "results": { /* shipping address */ }
}

Payment → Confirmation (Step 3 → 4)

This message includes complete order details:

json
{
  "close": false,
  "checkout": true,
  "action": "payment",
  "ui": "desktop",
  "route": {
    "view": "checkout",
    "step": 3
  },
  "orderId": 12345,
  "grandTotal": 68.97,
  "items": [ /* order items */ ],
  "shippingAddress": { /* address */ }
  // ... full checkout data
}

What to do:

  • Save order details
  • Track successful conversions
  • Prepare confirmation UI

Order Confirmation Page

Sent when user lands on confirmation page:

json
{
  "close": true,
  "checkout": true,
  "action": "confirmation",
  "ui": "desktop",
  "route": {
    "view": "checkout",
    "step": 4
  }
}

What to do:

  • Close webview/iframe
  • Show order confirmation in your app
  • Display order tracking information

Back Navigation Events

Summary ← Editor

json
{
  "close": false,
  "checkout": true,
  "action": "summaryBack",
  "ui": "desktop",
  "route": {
    "view": "checkout",
    "step": 1
  }
}

Address ← Summary

json
{
  "close": false,
  "checkout": true,
  "action": "addressBack",
  "ui": "desktop",
  "route": {
    "view": "checkout",
    "step": 2
  }
}

Payment ← Address

json
{
  "close": false,
  "checkout": true,
  "action": "paymentBack",
  "ui": "desktop",
  "route": {
    "view": "checkout",
    "step": 3
  }
}

Implementation

Web (iframe)

javascript
// Listen for messages from PWA
window.addEventListener('message', (event) => {
  // Verify origin for security
  if (event.origin !== 'https://{frontend}.photobook.ai') {
    return;
  }
  
  const message = event.data;
  handleChannelMessage(message);
});

function handleChannelMessage(message) {
  const { close, checkout, action, route } = message;
  
  // Handle close suggestion
  if (close) {
    closeWebview();
  }
  
  // Track checkout conversion
  if (checkout && action === 'order') {
    analytics.track('checkout_started');
  }
  
  // Handle order confirmation
  if (action === 'confirmation') {
    showOrderConfirmation();
    closeWebview();
  }
  
  // Save project progress
  if (action === 'save' && message.metadata) {
    saveProjectProgress(message.id, message.metadata);
  }
}

React Native

javascript
import { WebView } from 'react-native-webview';

function EditorWebView({ url }) {
  const handleMessage = (event) => {
    const message = JSON.parse(event.nativeEvent.data);
    
    if (message.close) {
      navigation.goBack();
    }
    
    if (message.action === 'confirmation') {
      navigation.navigate('OrderConfirmation');
    }
  };
  
  return (
    <WebView
      source={{ uri: url }}
      onMessage={handleMessage}
      javaScriptEnabled={true}
    />
  );
}

photobook.ai Developer Documentation