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 | 
