Hum Widget Hook Notifications
The Hum widget provides a universal notification system that works across web browsers, iOS WebViews, and Android WebViews. All hooks use four communication methods for maximum compatibility:- postMessage - Works in web iframes, iOS WKWebView, and Android WebView
- iOS WKWebView message handler - Direct communication with iOS native code
- Android WebView interface - Direct communication with Android native code
- DOM events - Fallback for same-document scenarios
Available Hooks
The widget emits three types of notification hooks:Order Completion
Triggered when an order is successfully submitted or fails
Plan Saved
Triggered when a user saves an internet plan for later
Plan Unsaved
Triggered when a user removes a saved plan
Order Completion Hook
Triggered when an order is successfully submitted or fails.Event Data Structure
Copy
interface OrderCompletedEventDetail {
orderId: string; // Unique order identifier
success: boolean; // Whether the order was successful
message?: string; // Success/error message
orderData?: { // Complete order information (only on success)
selectedPlan: SelectedPlan;
selectedInternetAddons: SelectedInternetAddon[];
selectedTvProducts: SelectedTvProduct[];
selectedTvAddons: SelectedTvProduct[];
customerData: CustomerData;
scheduleData: ScheduleData;
totalAmount?: {
amount_cents: number;
amount_currency: string;
};
};
timestamp: string; // ISO timestamp of when the order was completed
}
Web Integration (JavaScript)
- postMessage (Recommended)
- DOM Event (Fallback)
Copy
window.addEventListener('message', (event) => {
if (event.data.type === 'humOrderCompleted') {
const { orderId, success, orderData, message, timestamp } = event.data.data;
if (success) {
console.log(`✅ Order ${orderId} completed at ${timestamp}`);
console.log('Customer:', orderData.customerData);
console.log('Selected Plan:', orderData.selectedPlan);
console.log('Total Amount:', orderData.totalAmount);
// Track conversion in Google Analytics
gtag('event', 'purchase', {
transaction_id: orderId,
value: orderData.totalAmount.amount_cents / 100,
currency: orderData.totalAmount.amount_currency
});
// Redirect to thank you page
window.location.href = `/thank-you?orderId=${orderId}`;
} else {
console.error(`❌ Order failed: ${message}`);
alert(`Order submission failed: ${message}`);
}
}
});
Use the postMessage method as your primary integration method. It works across all platforms including iframes and WebViews.
Common Use Cases
Track Conversion in Analytics
Track Conversion in Analytics
Copy
if (event.data.type === 'humOrderCompleted' && event.data.data.success) {
const { orderId, orderData } = event.data.data;
// Google Analytics 4
gtag('event', 'purchase', {
transaction_id: orderId,
value: orderData.totalAmount.amount_cents / 100,
currency: orderData.totalAmount.amount_currency,
items: [{
item_name: orderData.selectedPlan.name,
item_category: 'Internet Service',
price: orderData.selectedPlan.price
}]
});
// Facebook Pixel
fbq('track', 'Purchase', {
value: orderData.totalAmount.amount_cents / 100,
currency: orderData.totalAmount.amount_currency
});
}
Send Data to Backend/CRM
Send Data to Backend/CRM
Copy
if (event.data.type === 'humOrderCompleted' && event.data.data.success) {
const { orderId, orderData } = event.data.data;
// Send to your backend
fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orderId,
customer: orderData.customerData,
plan: orderData.selectedPlan,
timestamp: event.data.data.timestamp
})
})
.then(response => response.json())
.then(data => console.log('Order synced to backend:', data))
.catch(error => console.error('Failed to sync order:', error));
}
Update Application State
Update Application State
Copy
// React example
if (event.data.type === 'humOrderCompleted' && event.data.data.success) {
const { orderId, orderData } = event.data.data;
// Update state
setOrderStatus('completed');
setOrderId(orderId);
setCustomerData(orderData.customerData);
// Navigate to confirmation page
navigate(`/order-confirmation/${orderId}`);
}
iOS Integration (Swift)
1
Configure WKWebView with Message Handler
Copy
import WebKit
class WebViewController: UIViewController, WKScriptMessageHandler {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Configure message handler
let contentController = WKUserContentController()
contentController.add(self, name: "humWidget")
let config = WKWebViewConfiguration()
config.userContentController = contentController
// Initialize WebView
webView = WKWebView(frame: view.bounds, configuration: config)
view.addSubview(webView)
// Load widget page
if let url = URL(string: "https://your-widget-url.com") {
webView.load(URLRequest(url: url))
}
}
}
2
Handle Widget Messages
Copy
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
guard message.name == "humWidget",
let messageData = message.body as? [String: Any],
let type = messageData["type"] as? String else {
return
}
switch type {
case "humOrderCompleted":
handleOrderCompleted(messageData: messageData)
default:
break
}
}
3
Process Order Completion
Copy
private func handleOrderCompleted(messageData: [String: Any]) {
guard let data = messageData["data"] as? [String: Any],
let orderId = data["orderId"] as? String,
let success = data["success"] as? Bool else {
return
}
if success {
print("✅ Order \(orderId) completed successfully!")
// Access order data
if let orderData = data["orderData"] as? [String: Any] {
let customerData = orderData["customerData"] as? [String: Any]
let firstName = customerData?["firstName"] as? String
let lastName = customerData?["lastName"] as? String
let email = customerData?["email"] as? String
print("Customer: \(firstName ?? "") \(lastName ?? "")")
print("Email: \(email ?? "")")
// Save to Core Data, update UI, track in analytics
DispatchQueue.main.async {
self.showOrderConfirmation(orderId: orderId)
}
}
if let totalAmount = (data["orderData"] as? [String: Any])?["totalAmount"] as? [String: Any] {
let cents = totalAmount["amount_cents"] as? Int ?? 0
let currency = totalAmount["amount_currency"] as? String ?? "USD"
let dollars = Double(cents) / 100.0
print("Total: \(dollars) \(currency)")
}
} else {
let errorMessage = data["message"] as? String ?? "Unknown error"
print("❌ Order failed: \(errorMessage)")
DispatchQueue.main.async {
self.showError(message: errorMessage)
}
}
}
private func showOrderConfirmation(orderId: String) {
let alert = UIAlertController(
title: "Order Completed",
message: "Your order \(orderId) has been submitted successfully!",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
Android Integration (Kotlin)
1
Define Data Classes
Copy
import com.google.gson.annotations.SerializedName
data class OrderCompletedMessage(
val type: String,
val data: OrderCompletedData
)
data class OrderCompletedData(
val orderId: String,
val success: Boolean,
val message: String?,
val orderData: OrderData?,
val timestamp: String
)
data class OrderData(
val selectedPlan: SelectedPlan,
val customerData: CustomerData,
val totalAmount: TotalAmount?
)
data class SelectedPlan(
val id: String,
val name: String,
val providerName: String,
val price: Double
)
data class CustomerData(
val firstName: String,
val lastName: String,
val email: String,
val phoneNumber: String
)
data class TotalAmount(
@SerializedName("amount_cents") val amountCents: Int,
@SerializedName("amount_currency") val amountCurrency: String
)
2
Setup WebView with JavaScript Interface
Copy
import android.webkit.JavascriptInterface
import android.webkit.WebView
import com.google.gson.Gson
class WebViewActivity : AppCompatActivity() {
private lateinit var webView: WebView
private val gson = Gson()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_webview)
webView = findViewById(R.id.webview)
setupWebView()
}
private fun setupWebView() {
webView.settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
}
// Add JavaScript interface for widget communication
webView.addJavascriptInterface(
WebAppInterface(this),
"AndroidInterface"
)
// Load widget page
webView.loadUrl("https://your-widget-url.com")
}
inner class WebAppInterface(private val context: WebViewActivity) {
@JavascriptInterface
fun onOrderCompleted(jsonMessage: String) {
try {
val message = gson.fromJson(jsonMessage, OrderCompletedMessage::class.java)
runOnUiThread {
handleOrderCompleted(message)
}
} catch (e: Exception) {
Log.e("WebAppInterface", "Error parsing order completion message", e)
}
}
}
}
3
Handle Order Completion
Copy
private fun handleOrderCompleted(message: OrderCompletedMessage) {
val data = message.data
if (data.success) {
Log.i("HumWidget", "✅ Order ${data.orderId} completed at ${data.timestamp}")
// Access order data
data.orderData?.let { orderData ->
val customer = orderData.customerData
val plan = orderData.selectedPlan
Log.i("HumWidget", "Customer: ${customer.firstName} ${customer.lastName}")
Log.i("HumWidget", "Email: ${customer.email}")
Log.i("HumWidget", "Plan: ${plan.name} from ${plan.providerName}")
orderData.totalAmount?.let { amount ->
val dollars = amount.amountCents / 100.0
Log.i("HumWidget", "Total: $$dollars ${amount.amountCurrency}")
}
// Save to Room database, track in Firebase, update UI
showOrderConfirmation(data.orderId, orderData)
// Track in Firebase Analytics
val bundle = Bundle().apply {
putString("order_id", data.orderId)
putString("provider", plan.providerName)
putDouble("value", orderData.totalAmount?.amountCents?.div(100.0) ?: 0.0)
}
FirebaseAnalytics.getInstance(this)
.logEvent("hum_order_completed", bundle)
}
} else {
val errorMessage = data.message ?: "Unknown error"
Log.e("HumWidget", "❌ Order failed: $errorMessage")
showError(errorMessage)
}
}
private fun showOrderConfirmation(orderId: String, orderData: OrderData) {
AlertDialog.Builder(this)
.setTitle("Order Completed")
.setMessage(
"Your order $orderId has been submitted successfully!\n\n" +
"Plan: ${orderData.selectedPlan.name}\n" +
"Provider: ${orderData.selectedPlan.providerName}"
)
.setPositiveButton("OK") { dialog, _ ->
dialog.dismiss()
}
.show()
}
Always handle both success and failure cases in the order completion hook. Network issues or validation errors can cause order submission to fail.
Plan Save Hook
Triggered when a user saves an internet plan for later.Event Data Structure
Copy
interface SavePlanEventDetail {
planId: string; // Unique plan identifier
success: boolean; // Whether the save was successful
message?: string; // Success/error message
timestamp: string; // ISO timestamp of when the plan was saved
}
Web Integration (JavaScript)
- postMessage (Recommended)
- DOM Event (Fallback)
Copy
window.addEventListener('message', (event) => {
if (event.data.type === 'humPlanSaved') {
const { planId, success, message, timestamp } = event.data.data;
if (success) {
console.log(`✅ Plan ${planId} saved at ${timestamp}`);
// Store in localStorage
const savedPlans = JSON.parse(localStorage.getItem('savedPlans') || '[]');
savedPlans.push({
planId,
savedAt: timestamp
});
localStorage.setItem('savedPlans', JSON.stringify(savedPlans));
// Send to backend
fetch('/api/saved-plans', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ planId, userId: getCurrentUserId() })
});
// Update UI
showNotification('Plan saved successfully!', 'success');
} else {
console.error(`❌ Failed to save plan: ${message}`);
showNotification(`Failed to save plan: ${message}`, 'error');
}
}
});
Store saved plan IDs in your database or local storage to enable features like “My Saved Plans” or filtering widget results to show only saved plans.
iOS Integration (Swift)
Copy
if type == "humPlanSaved" {
handlePlanSaved(messageData: messageData)
}
private func handlePlanSaved(messageData: [String: Any]) {
guard let data = messageData["data"] as? [String: Any],
let planId = data["planId"] as? String,
let success = data["success"] as? Bool,
let timestamp = data["timestamp"] as? String else {
return
}
if success {
print("✅ Plan \(planId) saved at \(timestamp)")
// Save to Core Data or UserDefaults
savePlanToStorage(planId: planId, timestamp: timestamp)
// Track in analytics
Analytics.logEvent("plan_saved", parameters: [
"plan_id": planId,
"timestamp": timestamp
])
// Update UI
DispatchQueue.main.async {
self.showSaveConfirmation(planId: planId)
self.updateSavedPlansUI()
}
} else {
let errorMessage = data["message"] as? String ?? "Unknown error"
print("❌ Failed to save plan: \(errorMessage)")
DispatchQueue.main.async {
self.showError(message: "Failed to save plan: \(errorMessage)")
}
}
}
private func savePlanToStorage(planId: String, timestamp: String) {
var savedPlans = UserDefaults.standard.stringArray(forKey: "savedPlans") ?? []
if !savedPlans.contains(planId) {
savedPlans.append(planId)
UserDefaults.standard.set(savedPlans, forKey: "savedPlans")
}
}
private func showSaveConfirmation(planId: String) {
let alert = UIAlertController(
title: "Plan Saved",
message: "Plan has been saved to your favorites!",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
Android Integration (Kotlin)
Copy
data class PlanSavedMessage(
val type: String,
val data: PlanSavedData
)
data class PlanSavedData(
val planId: String,
val success: Boolean,
val message: String?,
val timestamp: String
)
inner class WebAppInterface(private val context: WebViewActivity) {
@JavascriptInterface
fun onPlanSaved(jsonMessage: String) {
try {
val message = gson.fromJson(jsonMessage, PlanSavedMessage::class.java)
runOnUiThread {
handlePlanSaved(message)
}
} catch (e: Exception) {
Log.e("WebAppInterface", "Error parsing plan save message", e)
}
}
}
private fun handlePlanSaved(message: PlanSavedMessage) {
val data = message.data
if (data.success) {
Log.i("HumWidget", "✅ Plan ${data.planId} saved at ${data.timestamp}")
// Save to SharedPreferences or Room database
savePlanToStorage(data.planId, data.timestamp)
// Track in Firebase Analytics
val bundle = Bundle().apply {
putString("plan_id", data.planId)
putString("timestamp", data.timestamp)
}
FirebaseAnalytics.getInstance(this)
.logEvent("plan_saved", bundle)
// Update UI
showSaveConfirmation(data.planId)
updateSavedPlansUI()
} else {
val errorMessage = data.message ?: "Unknown error"
Log.e("HumWidget", "❌ Failed to save plan: $errorMessage")
showError("Failed to save plan: $errorMessage")
}
}
private fun savePlanToStorage(planId: String, timestamp: String) {
val sharedPrefs = getSharedPreferences("HumWidget", Context.MODE_PRIVATE)
val savedPlans = sharedPreefs.getStringSet("savedPlans", mutableSetOf())?.toMutableSet() ?: mutableSetOf()
savedPlans.add(planId)
sharedPrefs.edit()
.putStringSet("savedPlans", savedPlans)
.apply()
}
private fun showSaveConfirmation(planId: String) {
Snackbar.make(
findViewById(R.id.root),
"Plan saved to your favorites!",
Snackbar.LENGTH_SHORT
).show()
}
Plan Unsave Hook
Triggered when a user removes a saved plan.Event Data Structure
Copy
interface UnsavePlanEventDetail {
planId: string; // Unique plan identifier
success: boolean; // Whether the unsave was successful
message?: string; // Success/error message
timestamp: string; // ISO timestamp of when the plan was unsaved
}
Web Integration (JavaScript)
- postMessage (Recommended)
- DOM Event (Fallback)
Copy
window.addEventListener('message', (event) => {
if (event.data.type === 'humPlanUnsaved') {
const { planId, success, message, timestamp } = event.data.data;
if (success) {
console.log(`✅ Plan ${planId} unsaved at ${timestamp}`);
// Remove from localStorage
const savedPlans = JSON.parse(localStorage.getItem('savedPlans') || '[]');
const updatedPlans = savedPlans.filter(p => p.planId !== planId);
localStorage.setItem('savedPlans', JSON.stringify(updatedPlans));
// Send to backend
fetch(`/api/saved-plans/${planId}`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: getCurrentUserId() })
});
// Update UI
showNotification('Plan removed from saved plans', 'info');
} else {
console.error(`❌ Failed to unsave plan: ${message}`);
showNotification(`Failed to unsave plan: ${message}`, 'error');
}
}
});
iOS Integration (Swift)
Copy
if type == "humPlanUnsaved" {
handlePlanUnsaved(messageData: messageData)
}
private func handlePlanUnsaved(messageData: [String: Any]) {
guard let data = messageData["data"] as? [String: Any],
let planId = data["planId"] as? String,
let success = data["success"] as? Bool else {
return
}
if success {
print("✅ Plan \(planId) unsaved")
// Remove from storage
removePlanFromStorage(planId: planId)
// Track in analytics
Analytics.logEvent("plan_unsaved", parameters: ["plan_id": planId])
// Update UI
DispatchQueue.main.async {
self.updateSavedPlansUI()
self.showNotification(message: "Plan removed from favorites")
}
}
}
private func removePlanFromStorage(planId: String) {
var savedPlans = UserDefaults.standard.stringArray(forKey: "savedPlans") ?? []
savedPlans.removeAll { $0 == planId }
UserDefaults.standard.set(savedPlans, forKey: "savedPlans")
}
Android Integration (Kotlin)
Copy
@JavascriptInterface
fun onPlanUnsaved(jsonMessage: String) {
try {
val message = gson.fromJson(jsonMessage, PlanUnsavedMessage::class.java)
runOnUiThread {
handlePlanUnsaved(message)
}
} catch (e: Exception) {
Log.e("WebAppInterface", "Error parsing plan unsave message", e)
}
}
private fun handlePlanUnsaved(message: PlanUnsavedMessage) {
val data = message.data
if (data.success) {
Log.i("HumWidget", "✅ Plan ${data.planId} unsaved")
// Remove from storage
removePlanFromStorage(data.planId)
// Track in Firebase
val bundle = Bundle().apply {
putString("plan_id", data.planId)
}
FirebaseAnalytics.getInstance(this)
.logEvent("plan_unsaved", bundle)
// Update UI
updateSavedPlansUI()
showNotification("Plan removed from favorites")
}
}
private fun removePlanFromStorage(planId: String) {
val sharedPrefs = getSharedPreferences("HumWidget", Context.MODE_PRIVATE)
val savedPlans = sharedPrefs.getStringSet("savedPlans", mutableSetOf())?.toMutableSet()
savedPlans?.remove(planId)
sharedPrefs.edit()
.putStringSet("savedPlans", savedPlans)
.apply()
}
React Native Integration
For React Native applications using WebView:Copy
import { WebView } from 'react-native-webview';
import AsyncStorage from '@react-native-async-storage/async-storage';
function HumWidgetScreen() {
const handleMessage = (event) => {
try {
const message = JSON.parse(event.nativeEvent.data);
switch (message.type) {
case 'humOrderCompleted':
handleOrderCompleted(message.data);
break;
case 'humPlanSaved':
handlePlanSaved(message.data);
break;
case 'humPlanUnsaved':
handlePlanUnsaved(message.data);
break;
}
} catch (error) {
console.error('Failed to parse widget message:', error);
}
};
const handleOrderCompleted = (data) => {
const { orderId, success, orderData } = data;
if (success) {
console.log(`✅ Order ${orderId} completed`);
// Navigate to confirmation screen
navigation.navigate('OrderConfirmation', { orderId, orderData });
// Track in analytics
analytics().logEvent('purchase', {
transaction_id: orderId,
value: orderData.totalAmount.amount_cents / 100,
currency: orderData.totalAmount.amount_currency
});
} else {
Alert.alert('Order Failed', data.message);
}
};
const handlePlanSaved = async (data) => {
if (data.success) {
console.log(`✅ Plan ${data.planId} saved`);
// Save to AsyncStorage
const savedPlans = await AsyncStorage.getItem('savedPlans');
const plans = savedPlans ? JSON.parse(savedPlans) : [];
plans.push(data.planId);
await AsyncStorage.setItem('savedPlans', JSON.stringify(plans));
// Show toast
Toast.show('Plan saved to favorites!');
}
};
const handlePlanUnsaved = async (data) => {
if (data.success) {
console.log(`✅ Plan ${data.planId} unsaved`);
// Remove from AsyncStorage
const savedPlans = await AsyncStorage.getItem('savedPlans');
const plans = savedPlans ? JSON.parse(savedPlans) : [];
const updated = plans.filter(id => id !== data.planId);
await AsyncStorage.setItem('savedPlans', JSON.stringify(updated));
Toast.show('Plan removed from favorites');
}
};
return (
<WebView
source={{ uri: 'https://your-widget-url.com' }}
onMessage={handleMessage}
javaScriptEnabled={true}
/>
);
}
React Native’s WebView component automatically handles postMessage communication between the web content and native code.
Browser Compatibility
All hooks work in:- ✅ Chrome, Firefox, Safari, Edge (all modern versions)
- ✅ iOS WKWebView (iOS 11+)
- ✅ Android WebView (Android 5.0+)
- ✅ React Native WebView
- ✅ Cordova/PhoneGap WebView
Summary
All hooks use the same four communication methods for universal compatibility across platforms. Choose the integration method that best fits your application architecture.
| Hook | Event Type | Trigger | Key Data |
|---|---|---|---|
| Order Completed | humOrderCompleted | Order submission success/failure | orderId, success, orderData, timestamp |
| Plan Saved | humPlanSaved | User saves a plan | planId, success, timestamp |
| Plan Unsaved | humPlanUnsaved | User removes saved plan | planId, success, timestamp |
