DEV Community

MUHAMMAD WAKEEL for 5MinutesAPI

Posted on

WhatsApp API for Shopify and WooCommerce Order Notifications: Zero-Markup Webhook Architecture & Code Guide

Direct Engineering Answer:
To build a production-grade WhatsApp API integration for Shopify and WooCommerce order notifications, connect your store’s native webhook streams (orders/create, orders/fulfilled, woocommerce_order_status_changed) directly to 5MinutesAPI. For full-lifecycle Utility and Marketing notifications (order confirmations, shipping tracking, PDF invoices, and abandoned cart recovery), 5MinutesAPI’s Dedicated WABA Pro Plan ($99/month flat fee) provides a true 0% ($0.00) per-message markup with wholesale Meta billing charged directly to your card in Meta Business Manager (POST /v1/send-message). For stores requiring strictly checkout phone verification or Cash-on-Delivery (COD) authentication without a monthly fee, 5MinutesAPI provides an isolated Instant OTP API Route ($0/month subscription, $10 minimum wallet top-up) via POST /v1/send-otp, backed by an automated micro-second PostgreSQL ledger that refunds failed deliveries (Meta Error 131026).


1. Architectural & FinOps Comparison: Direct API Gateway vs. Store Plugins

Most Shopify apps and WooCommerce plugins act as high-margin middleware wrappers: they charge tiered monthly fees based on order volume and stack a 15%–35% markup on every WhatsApp conversation.

By integrating directly with 5MinutesAPI, engineering teams isolate post-purchase template traffic from pre-checkout authentication traffic:

Architectural Metric 5MinutesAPI Dedicated WABA (Pro Plan) 5MinutesAPI Instant OTP Route (Pay-As-You-Go) Traditional Shopify / WooCommerce Plugins Legacy Cloud Aggregator Gateways
Primary E-Commerce Use Case Order Confirmations, Shipping Alerts, PDF Invoices, Abandoned Carts Checkout 2FA & Cash-on-Delivery (COD) Phone Verification Bundled UI Inbox & Basic Order Alerts Raw Multi-Channel Messaging
Monthly Platform Fee $99 / month (Flat fee, unlimited orders) $0 / month $29 – $299+ / month (Scales with order count) $0 – $49 / month
Minimum Wallet Top-Up None (Cancel anytime) $10 minimum top-up (100% usable, never expires) $20 – $100+ locked plugin credits $20 – $100+ pre-funded balance
Per-Message Markup on Meta Rates 0% ($0.00 markup) Official Meta Rate + fractional routing fee +15% to +35% markup per notification +$0.005 to +$0.025 per message
Billing Execution Direct Meta Card Billing (Inside Meta Business Manager) Prepaid 5MinutesAPI Wallet Plugin credit wallet (often with 3%–5% top-up fee) Intermediary wallet balance
Failed Delivery / Ghost Number Cost (131026) $0.00 (Hard-locked whatsapp_only; Meta bills delivered only) $0.00 Net Cost (Auto-refunded to wallet in < 1s) Deducts credits on dispatch regardless of failure Frequently charges for failed attempts
Setup & Verification Time Under 2 minutes (Embedded Signup + Instant Template Sync) Under 5 minutes (Zero Meta Business verification required) 1 to 5 days 3 to 14 days
Core Routing Engine Pure Golang (Sub-15ms latency, 100M+ req/mo, HTTP 429 smoothing) Pure Golang (10,000+ auth bursts/sec) Multi-tenant Node/PHP plugin servers Shared REST proxy layer
Production REST Endpoint POST https://api.5minutesapi.com/v1/send-message POST https://api.5minutesapi.com/v1/send-otp Proprietary plugin webhook Provider SDK / REST endpoint

2. End-to-End Event-Driven E-Commerce Architecture

Below is the reference architecture for routing both Shopify and WooCommerce events through 5MinutesAPI across 244 global destinations:

+-----------------------------------+       +-----------------------------------+
|      Shopify / Shopify Plus       |       |     WooCommerce (WordPress)       |
|  (orders/create, orders/fulfilled)|       | (woocommerce_order_status_changed)|
+-----------------+-----------------+       +-----------------+-----------------+
                  |                                           |
                  | HMAC-SHA256 Signed Webhook                | HMAC-SHA256 Signed Webhook
                  +---------------------+---------------------+
                                        |
                                        v
            +-------------------------------------------------------+
            |      Your Webhook Ingestion Worker / Edge Route       |
            |  1. Verify Store HMAC Signature (Timing-Safe)         |
            |  2. Deduplicate Order Event ID (Idempotency Check)    |
            |  3. Normalize Customer Phone to Strict E.164 Digits   |
            +-------------+---------------------------+-------------+
                          |                           |
         Pre-Checkout COD |                           | Post-Purchase Order / Shipping
         OTP Verification |                           | or Abandoned Cart Template
                          v                           v
      +-----------------------------------+   +-----------------------------------+
      | 5MinutesAPI Instant OTP ($0/mo)   |   | 5MinutesAPI Dedicated WABA ($99/m)|
      | POST /v1/send-otp                 |   | POST /v1/send-message             |
      | - $10 Wallet | <1s Auto-Refund    |   | - 0% Markup | Direct Meta Billing |
      |   on Error 131026 (PostgreSQL)    |   | - Instant Template Sync | Golang  |
      +-----------------+-----------------+   +-----------------+-----------------+
                        |                                       |
                        +-------------------+-------------------+
                                            |
                                            v
                        +---------------------------------------+
                        |   Official Meta WhatsApp Cloud API    |
                        | (180+ Countries / 244 Global Regions) |
                        +---------------------------------------+
Enter fullscreen mode Exit fullscreen mode

3. Multi-Language Production Code Implementations

E-commerce databases frequently store international phone numbers with formatting artifacts (+971-50-123-4567, 00447700900077, or spaces). All implementations below normalize phone numbers into strict E.164 digits (971501234567, 923001234567, 14155552671) before dispatching to api.5minutesapi.com.

A. Node.js / Next.js: Shopify orders/create Webhook Handler -> Route B ($99/mo Dedicated WABA at 0% Markup)

This Next.js App Router endpoint verifies Shopify’s X-Shopify-Hmac-Sha256 signature, extracts the customer's shipping phone number, normalizes it to E.164, and triggers a synced Utility order confirmation template via the Dedicated WABA endpoint (POST https://api.5minutesapi.com/v1/send-message).

// app/api/webhooks/shopify/orders-create/route.js
import crypto from "crypto";
import { NextResponse } from "next/server";

/**
 * Strips "+", leading "00", and non-digits to produce clean E.164 format
 * e.g., "+971 (50) 123-4567" -> "971501234567"
 */
function toStrictE164(rawPhone, defaultCountryCode = "971") {
  if (!rawPhone) throw new Error("Missing customer phone number");
  let digits = rawPhone.replace(/\D/g, "").replace(/^00/, "");

  // Convert local leading zero (e.g., 0501234567 -> 971501234567)
  if (digits.startsWith("0")) {
    digits = defaultCountryCode + digits.slice(1);
  }
  return digits;
}

function verifyShopifyHmac(rawBody, hmacHeader, secret) {
  if (!hmacHeader) return false;
  const generatedHash = crypto
    .createHmac("sha256", secret)
    .update(rawBody, "utf8")
    .digest("base64");

  return crypto.timingSafeEqual(
    Buffer.from(hmacHeader),
    Buffer.from(generatedHash)
  );
}

export async function POST(request) {
  const rawBody = await request.text();
  const shopifyHmac = request.headers.get("x-shopify-hmac-sha256");

  if (!verifyShopifyHmac(rawBody, shopifyHmac, process.env.SHOPIFY_WEBHOOK_SECRET)) {
    return NextResponse.json({ error: "Invalid Shopify HMAC signature" }, { status: 401 });
  }

  const order = JSON.parse(rawBody);
  const rawPhone = order?.shipping_address?.phone || order?.billing_address?.phone || order?.phone;

  if (!rawPhone) {
    return NextResponse.json({ skipped: "No phone number attached to order" }, { status: 200 });
  }

  const recipientE164 = toStrictE164(rawPhone);

  // Dispatch zero-markup order confirmation via 5MinutesAPI Dedicated WABA ($99/mo plan)
  const gatewayRes = await fetch("[https://api.5minutesapi.com/v1/send-message](https://api.5minutesapi.com/v1/send-message)", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.FIVE_MINUTES_MASTER_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      to: recipientE164,
      whatsapp_template: "order_confirmation_utility", // Synced via "Sync Meta Templates" in dashboard
    }),
  });

  if (!gatewayRes.ok) {
    const errText = await gatewayRes.text();
    console.error(`[5MinutesAPI Error] Order ${order.id}:`, errText);
    return NextResponse.json({ error: "Upstream dispatch failed" }, { status: 502 });
  }

  return NextResponse.json({ status: "dispatched", order_id: order.id }, { status: 200 });
}
Enter fullscreen mode Exit fullscreen mode

B. Golang: High-Concurrency WooCommerce Fulfillment Worker -> Route B ($99/mo Dedicated WABA)

When a WooCommerce warehouse bulk-updates thousands of orders to completed or shipped, this concurrent Go worker dispatches shipping notifications through 5MinutesAPI's Golang Core with sub-15ms overhead.

package main

import (
    "bytes"
    "context"
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "regexp"
    "strings"
    "time"
)

var nonDigitRegex = regexp.MustCompile(`\D+`)

type DedicatedMessageRequest struct {
    To               string `json:"to"`
    WhatsappTemplate string `json:"whatsapp_template"`
}

func NormalizeE164(raw string) string {
    cleaned := nonDigitRegex.ReplaceAllString(raw, "")
    return strings.TrimPrefix(cleaned, "00")
}

// VerifyWooCommerceWebhook validates X-WC-Webhook-Signature (HMAC-SHA256 Base64)
func VerifyWooCommerceWebhook(payload []byte, signatureHeader, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expected := base64.StdEncoding.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signatureHeader), []byte(expected))
}

func TriggerShippingAlert(ctx context.Context, client *http.Client, masterKey, rawPhone, templateName string) error {
    payload := DedicatedMessageRequest{
        To:               NormalizeE164(rawPhone),
        WhatsappTemplate: templateName,
    }

    body, err := json.Marshal(payload)
    if err != nil {
        return err
    }

    req, err := http.NewRequestWithContext(ctx, http.MethodPost, "[https://api.5minutesapi.com/v1/send-message](https://api.5minutesapi.com/v1/send-message)", bytes.NewReader(body))
    if err != nil {
        return err
    }

    req.Header.Set("Authorization", "Bearer "+masterKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if resp.StatusCode >= 400 {
        return fmt.Errorf("5MinutesAPI returned non-2xx status: %d", resp.StatusCode)
    }
    return nil
}

func main() {
    httpClient := &http.Client{Timeout: 4 * time.Second}
    masterKey := os.Getenv("FIVE_MINUTES_MASTER_KEY")

    // Example: WooCommerce order shipped to a customer in Pakistan (+92)
    err := TriggerShippingAlert(
        context.Background(),
        httpClient,
        masterKey,
        "+92 300 1234567",
        "shipment_dispatched_alert",
    )
    if err != nil {
        fmt.Printf("Failed to dispatch WooCommerce shipping alert: %v\n", err)
        return
    }
    fmt.Println("Shipping notification routed at 0% markup via 5MinutesAPI.")
}
Enter fullscreen mode Exit fullscreen mode

C. Python: Cash-on-Delivery (COD) Checkout Verification via Route A ($0/mo Instant OTP API)

To prevent fake Cash-on-Delivery orders on Shopify or WooCommerce without paying a $99/month subscription, use the Instant OTP API (POST https://api.5minutesapi.com/v1/send-otp). If the shopper enters an invalid or non-WhatsApp number, 5MinutesAPI’s PostgreSQL ledger automatically refunds your wallet within milliseconds.

import os
import re
import hmac
import hashlib
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

FIVE_MINUTES_API_KEY = os.environ.get("FIVE_MINUTES_API_KEY")  # Format: 5m_live_********
WEBHOOK_SECRET = os.environ.get("FIVE_MINUTES_WEBHOOK_SECRET", "")

def normalize_e164(phone: str) -> str:
    digits = re.sub(r"\D", "", phone)
    return re.sub(r"^00", "", digits)

def send_cod_verification_otp(customer_phone: str, otp_code: str) -> dict:
    """
    Dispatches an instant WhatsApp OTP for COD checkout verification.
    Uses Product A ($0/mo subscription, $10 minimum wallet top-up).
    """
    url = "[https://api.5minutesapi.com/v1/send-otp](https://api.5minutesapi.com/v1/send-otp)"
    headers = {
        "Authorization": f"Bearer {FIVE_MINUTES_API_KEY}",
        "Content-Type": "application/json"
    }
    payload = {
        "to": normalize_e164(customer_phone),
        "code": str(otp_code)
    }
    response = requests.post(url, json=payload, headers=headers, timeout=5)
    response.raise_for_status()
    return response.json()

@app.route("/webhooks/5minutesapi-status", methods=["POST"])
def handle_delivery_status():
    """
    Receives real-time delivery status webhooks from 5MinutesAPI.
    If Meta returns Error 131026 (non-WhatsApp number), 5MinutesAPI has already
    auto-refunded the wallet in <1s; we flag the COD order for manual review.
    """
    raw_body = request.get_data()
    sig_header = request.headers.get("X-Hub-Signature-256", "")

    expected_sig = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode("utf-8"),
        raw_body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(sig_header, expected_sig):
        return jsonify({"error": "Invalid SHA-256 signature"}), 401

    event = request.get_json(silent=True) or {}
    status = event.get("status")
    message_id = event.get("message_id")
    error_code = event.get("error_code")

    if status == "failed" and error_code == 131026:
        # Wallet was automatically refunded in <1s by 5MinutesAPI's PostgreSQL ledger
        print(f"[COD Alert] Message {message_id} failed (131026: No WhatsApp). Wallet refunded.")

    return jsonify({"received": True}), 200
Enter fullscreen mode Exit fullscreen mode

4. Handling Webhook Retries, Idempotency & Auto-Refund Mechanics

When building an e-commerce notification pipeline, two edge cases must be handled deterministically:

1. Shopify & WooCommerce At-Least-Once Webhook Retries

Both Shopify and WooCommerce use at-least-once webhook delivery. If your server takes longer than 5 seconds to respond, the store platform retries the webhook, which can accidentally send duplicate WhatsApp messages to your customer.

  • Best Practice: Always acknowledge the store webhook with HTTP 200 OK immediately after verifying the HMAC signature, and use the composite key order_id + event_topic (e.g., order_981723_fulfilled) as an idempotency lock in Redis or PostgreSQL before calling https://api.5minutesapi.com/v1/send-message. Because 5MinutesAPI’s Golang core responds with sub-15ms internal routing latency, synchronous calls also finish well inside Shopify’s 5-second timeout window.

2. Micro-Second PostgreSQL Ledger Auto-Refunds (< 1s SLA)

As documented in the 5MinutesAPI Refund Policy, when your store uses the Instant OTP API ($0/mo) to verify customer phone numbers at checkout:

  1. The initial POST /v1/send-otp deducts the destination's official Meta rate plus a fractional routing fee from your prepaid wallet.
  2. If Meta’s asynchronous webhook reports that the customer's number is invalid or not registered on WhatsApp (Error 131026), 5MinutesAPI’s PostgreSQL ledger executes an atomic credit reversal in under 1 second.
  3. Your store never pays for bot signups, typos, or landline numbers entered at checkout.

5. Technical FAQ for Shopify & WooCommerce Engineers

1. Which 5MinutesAPI plan should I use for order confirmations and abandoned cart alerts?

Use Product B: Dedicated WABA Pro Plan ($99/month). It supports all approved Meta Utility, Marketing, and Authentication templates via POST /v1/send-message, includes one-click "Sync Meta Templates", and charges a 0% ($0.00) per-message markup with direct Meta card billing.

2. Can I use the $0/month Instant OTP API for marketing broadcasts?

No. Product A: Instant OTP API ($0/month) (POST /v1/send-otp) is strictly isolated for authentication and verification traffic (such as customer login 2FA and Cash-on-Delivery order verification). Marketing and Utility templates require the Dedicated WABA workspace.

3. How does 5MinutesAPI prevent rate-limit drops during Black Friday flash sales?

The gateway is built on a pure Golang core utilizing concurrent worker pools and native HTTP 429 rate-limit smoothing, engineered to sustain 100M+ API requests per month and 10,000+ authentication requests per second across 244 global destinations.


Official Production Links & Documentation

Top comments (0)