Appearance
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
| Field | Type | Description |
|---|---|---|
close | boolean | Suggestion whether the view should be closed |
checkout | boolean | Whether user has initiated checkout process |
action | string | Current action/event (e.g., "save", "order", "payment") |
ui | string | UI mode: "mobile" or "desktop" |
route.view | string | Current page name (e.g., "editor", "checkout") |
route.step | integer | Step 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"
}
}| Field | Type | Description |
|---|---|---|
title | string | User-entered title (or product SKU if not set) |
preview | string | URL to project preview image |
link | string | Direct link to editor for this project |
id | string | Unique project ID |
endUserId | string | User ID provided by parent client |
metadata.currentPages | integer | Number of pages in project |
metadata.usedPhotoCount | integer | Number of photos used (not just uploaded) |
metadata.productName | string | Name 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}
/>
);
}