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
- Go to Settings > Notifications
- Scroll to Webhooks
- Click Create webhook
- Select event (e.g., "Order creation")
- Enter webhook URL with topic:
https://yourdomain.com/shopify/webhook/orders-create - Format: JSON
- 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.