Popular Integrations
Shopify

Shopify Integration

Keep your customers informed throughout their shopping journey with automated SMS notifications. Send order confirmations, shipping updates, and delivery notifications directly from your Shopify store.

Use Case: Increase customer satisfaction and reduce support inquiries with real-time SMS updates for orders.

How It Works

Shopify webhooks trigger SMS notifications for:

  • Order creation and confirmation
  • Payment received
  • Order fulfillment started
  • Shipment tracking updates
  • Delivery confirmation
  • Order cancellations and refunds
  • Abandoned cart reminders

Prerequisites

Before you begin, ensure you have:

  • An active SMSLeopard account with API credentials (Get started here)
  • Shopify store with admin access
  • A webhook endpoint to receive Shopify notifications
  • Shopify API credentials (for private apps) or access token

Integration Steps

Step 1: Create Webhook Endpoint

Set up an endpoint to handle Shopify webhooks:

const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
const app = express();
 
app.use(express.json());
 
// Shopify webhook endpoint
app.post('/shopify/webhook/:topic', async (req, res) => {
try {
// Verify webhook from Shopify
if (!verifyShopifyWebhook(req)) {
return res.status(401).json({ error: 'Unauthorized' });
}
 
    const topic = req.params.topic;
    const order = req.body;
 
    console.log('Shopify webhook received:', topic);
 
    // Handle different webhook topics
    switch (topic) {
      case 'orders-create':
        await handleOrderCreated(order);
        break;
      case 'orders-fulfilled':
        await handleOrderFulfilled(order);
        break;
      case 'orders-cancelled':
        await handleOrderCancelled(order);
        break;
      case 'checkouts-create':
        // Store abandoned cart for later reminder
        await storeAbandonedCart(order);
        break;
      default:
        console.log('Unhandled webhook topic:', topic);
    }
 
    res.status(200).send('OK');
 
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: error.message });
}
});
 
// Verify Shopify webhook
function verifyShopifyWebhook(req) {
const hmacHeader = req.headers['x-shopify-hmac-sha256'];
const shopifySecret = process.env.SHOPIFY_WEBHOOK_SECRET;
 
if (!hmacHeader || !shopifySecret) {
return false;
}
 
const hash = crypto
.createHmac('sha256', shopifySecret)
.update(req.rawBody)
.digest('base64');
 
return hash === hmacHeader;
}
 
// Handle order created
async function handleOrderCreated(order) {
const customer = order.customer;
const phoneNumber = customer.phone || order.shipping_address?.phone;
 
if (!phoneNumber) {
console.log('No phone number found for order:', order.order_number);
return;
}
 
const orderTotal = parseFloat(order.total_price);
const currency = order.currency;
 
const message = `Thank you for your order #${order.order_number}! Total: ${currency} ${orderTotal.toFixed(2)}. We'll notify you when it ships.`;
 
await sendSMS(phoneNumber, message);
}
 
// Handle order fulfilled
async function handleOrderFulfilled(order) {
const customer = order.customer;
const phoneNumber = customer.phone || order.shipping_address?.phone;
 
if (!phoneNumber) {
return;
}
 
const fulfillment = order.fulfillments[0];
const trackingNumber = fulfillment?.tracking_number;
const trackingUrl = fulfillment?.tracking_url;
 
let message = `Great news! Your order #${order.order_number} has been shipped.`;
 
if (trackingNumber) {
message += ` Tracking: ${trackingNumber}`;
}
 
if (trackingUrl) {
message += ` Track it here: ${trackingUrl}`;
}
 
await sendSMS(phoneNumber, message);
}
 
// Handle order cancelled
async function handleOrderCancelled(order) {
const customer = order.customer;
const phoneNumber = customer.phone || order.shipping_address?.phone;
 
if (!phoneNumber) {
return;
}
 
const message = `Your order #${order.order_number} has been cancelled. If you have questions, please contact our support team.`;
 
await sendSMS(phoneNumber, message);
}
 
// Store abandoned cart for reminder
async function storeAbandonedCart(checkout) {
// Store in database for later processing
await db.abandonedCarts.create({
checkoutId: checkout.id,
phone: checkout.phone || checkout.shipping_address?.phone,
email: checkout.email,
cartValue: checkout.total_price,
createdAt: new Date(checkout.created_at)
});
}
 
// Send abandoned cart reminders (run as scheduled job)
async function sendAbandonedCartReminders() {
const oneHourAgo = new Date(Date.now() - 60 _ 60 _ 1000);
const threeDaysAgo = new Date(Date.now() - 3 _ 24 _ 60 _ 60 _ 1000);
 
const abandonedCarts = await db.abandonedCarts.find({
createdAt: { $gte: threeDaysAgo, $lte: oneHourAgo },
reminderSent: false
});
 
for (const cart of abandonedCarts) {
if (!cart.phone) continue;
 
    const message = `You left items in your cart! Complete your purchase now and get 10% off with code COMEBACK10. Shop here: ${cart.checkoutUrl}`;
 
    await sendSMS(cart.phone, message);
    await db.abandonedCarts.updateOne(
      { _id: cart._id },
      { $set: { reminderSent: true } }
    );
 
}
}
 
// Send SMS using SMSLeopard API
async function sendSMS(phoneNumber, message) {
const apiKey = process.env.SMSLEOPARD_API_KEY;
const apiSecret = process.env.SMSLEOPARD_API_SECRET;
const sourceAddress = process.env.SENDER_ID;
 
const formattedPhone = formatPhoneNumber(phoneNumber);
 
try {
const response = await axios.post(
'https://api.smsleopard.com/v1/sms/send',
{
source: sourceAddress,
destination: [formattedPhone],
message: message
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}:${apiSecret}`
}
}
);
 
    console.log('SMS sent successfully:', response.data);
    return response.data;
 
} catch (error) {
console.error('Error sending SMS:', error.response?.data || error.message);
throw error;
}
}
 
function formatPhoneNumber(phone) {
if (!phone) return null;
 
phone = phone.replace(/[\s\-\(\)]/g, '');
 
if (phone.startsWith('0')) {
return '254' + phone.substring(1);
} else if (phone.startsWith('+')) {
return phone.substring(1);
}
 
return phone;
}
 
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Shopify webhook server running on port ${PORT}`);
});
 

Step 2: Register Webhooks in Shopify

Option A: Using Shopify Admin

  1. Go to Settings > Notifications
  2. Scroll to Webhooks
  3. Click Create webhook
  4. Select event (e.g., "Order creation")
  5. Enter webhook URL with topic: https://yourdomain.com/shopify/webhook/orders-create
  6. Format: JSON
  7. Click Save

Option B: Using Shopify API

const axios = require('axios');
 
async function createShopifyWebhooks() {
  const shopifyDomain = process.env.SHOPIFY_DOMAIN;
  const accessToken = process.env.SHOPIFY_ACCESS_TOKEN;
  const webhookUrl = process.env.WEBHOOK_BASE_URL;
 
  const topics = [
    'orders/create',
    'orders/fulfilled',
    'orders/cancelled',
    'checkouts/create'
  ];
 
  for (const topic of topics) {
    try {
      await axios.post(
        `https://${shopifyDomain}/admin/api/2024-01/webhooks.json`,
        {
          webhook: {
            topic: topic,
            address: `${webhookUrl}/shopify/webhook/${topic.replace('/', '-')}`,
            format: 'json'
          }
        },
        {
          headers: {
            'X-Shopify-Access-Token': accessToken,
            'Content-Type': 'application/json'
          }
        }
      );
 
      console.log(`Webhook created for ${topic}`);
    } catch (error) {
      console.error(`Error creating webhook for ${topic}:`, error.response?.data);
    }
  }
}
 
createShopifyWebhooks();

Message Templates

const orderTemplates = {
  confirmation: (orderNumber, total, currency) =>
    `Thank you! Order #${orderNumber} confirmed. Total: ${currency} ${total}. Track your order in your account.`,
 
  shipped: (orderNumber, carrier, tracking) =>
    `Order #${orderNumber} shipped via ${carrier}! Tracking: ${tracking}`,
 
  outForDelivery: (orderNumber) =>
    `Great news! Order #${orderNumber} is out for delivery today. Someone should be home to receive it.`,
 
  delivered: (orderNumber) =>
    `Order #${orderNumber} has been delivered! Enjoy your purchase and let us know if you need anything.`,
 
  cancelled: (orderNumber) =>
    `Order #${orderNumber} cancelled. Refund processed within 3-5 business days.`,
 
  abandonedCart: (items, discount) =>
    `You left ${items} in your cart! Use code ${discount} for 10% off. Complete your purchase now!`,
};

Advanced Features

Order Status Updates

Track and notify customers about detailed order status:

async function sendOrderStatusUpdate(orderId, status) {
  const order = await getOrderFromShopify(orderId);
  const phone = order.customer.phone;
 
  const statusMessages = {
    pending: "Your order is being processed.",
    processing: "Your order is being prepared for shipment.",
    shipped: "Your order has been shipped!",
    out_for_delivery: "Your order is out for delivery today.",
    delivered: "Your order has been delivered. Enjoy!",
    failed_delivery: "Delivery attempt failed. Please contact us.",
  };
 
  const message = `Order #${order.order_number}: ${statusMessages[status]}`;
  await sendSMS(phone, message);
}

Personalized Recommendations

Send product recommendations after delivery:

async function sendPostDeliveryRecommendations(order) {
  // Wait 3 days after delivery
  setTimeout(async () => {
    const recommendations = await getProductRecommendations(order.line_items);
 
    const message = `Hope you're enjoying your purchase! Check out these items you might like: ${recommendations.url}`;
 
    await sendSMS(order.customer.phone, message);
  }, 3 * 24 * 60 * 60 * 1000);
}

Review Requests

Request reviews after successful delivery:

async function requestReview(order) {
  const reviewUrl = `https://yourstore.com/reviews?order=${order.id}`;
 
  const message = `Hi ${order.customer.first_name}! How was your recent purchase? Share your feedback: ${reviewUrl}`;
 
  await sendSMS(order.customer.phone, message);
}

Best Practices

  • Always verify webhooks using HMAC signature
  • Handle duplicate webhooks gracefully (Shopify may send duplicates)
  • Store phone numbers in E.164 format in customer records
  • Respect customer preferences - allow opt-out from SMS
  • Keep messages concise and informative
  • Include tracking links in shipping notifications
  • Test in development store before going live
  • Monitor webhook failures and implement retry logic

Monitoring

// Track webhook performance
const webhookMetrics = {
  received: 0,
  processed: 0,
  failed: 0,
  smsSent: 0,
  smsFailed: 0,
};
 
app.post("/shopify/webhook/:topic", async (req, res) => {
  webhookMetrics.received++;
 
  try {
    // Process webhook
    webhookMetrics.processed++;
 
    // Send SMS
    await sendSMS(phone, message);
    webhookMetrics.smsSent++;
 
    res.status(200).send("OK");
  } catch (error) {
    webhookMetrics.failed++;
    res.status(500).send("Error");
  }
});
 
// Endpoint to view metrics
app.get("/metrics", (req, res) => {
  res.json(webhookMetrics);
});

Refer to Shopify Webhook Documentation (opens in a new tab) for more information.