useLoyalty
Widget

Installation

Add the useLoyalty Widget to your website

This guide walks you through installing the useLoyalty Widget on your website with proper authentication.

Prerequisites

Before installing the widget, ensure you have:

  1. A useLoyalty project with API keys
  2. Server-side code to generate HMAC signatures
  3. A way to identify logged-in users (external ID)

Step 1: Install the SDK

npm install @useloyalty/sdk
# or
pnpm add @useloyalty/sdk

Step 2: Server-Side Signature Generation

The widget requires an HMAC-SHA256 signature generated server-side. The SDK provides a helper function:

import { generateWidgetAuth } from "@useloyalty/sdk";

// Express.js example
app.get("/api/widget-auth", (req, res) => {
  if (!req.user) {
    return res.status(401).json({ error: "Not authenticated" });
  }

  const auth = generateWidgetAuth(
    process.env.USELOYALTY_PUBLIC_KEY!,
    process.env.USELOYALTY_PRIVATE_KEY!,
    req.user.id,
  );

  res.json(auth);
});

Manual Implementation (Node.js)

If you prefer not to use the SDK:

import crypto from "crypto";

interface WidgetAuth {
  publicKey: string;
  externalId: string;
  timestamp: number;
  signature: string;
}

function generateWidgetAuth(
  publicKey: string,
  privateKey: string,
  externalId: string,
): WidgetAuth {
  const timestamp = Date.now();

  // Create signature payload
  const payload = `${publicKey}:${externalId}:${timestamp}`;

  // Generate HMAC-SHA256 signature
  const signature = crypto
    .createHmac("sha256", privateKey)
    .update(payload)
    .digest("hex");

  return {
    publicKey,
    externalId,
    timestamp,
    signature,
  };
}

Python

import hmac
import hashlib
import time

def generate_widget_auth(external_id: str, public_key: str, private_key: str) -> dict:
    timestamp = int(time.time() * 1000)

    # Create signature payload
    payload = f"{public_key}:{external_id}:{timestamp}"

    # Generate HMAC-SHA256 signature
    signature = hmac.new(
        private_key.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return {
        "publicKey": public_key,
        "externalId": external_id,
        "timestamp": timestamp,
        "signature": signature
    }

# Flask example
@app.route('/api/widget-auth')
def widget_auth():
    if not current_user.is_authenticated:
        return jsonify({"error": "Not authenticated"}), 401

    auth = generate_widget_auth(
        str(current_user.id),
        os.environ['USELOYALTY_PUBLIC_KEY'],
        os.environ['USELOYALTY_PRIVATE_KEY']
    )

    return jsonify(auth)

PHP

<?php
function generateWidgetAuth($externalId, $publicKey, $privateKey) {
    $timestamp = round(microtime(true) * 1000);

    // Create signature payload
    $payload = "{$publicKey}:{$externalId}:{$timestamp}";

    // Generate HMAC-SHA256 signature
    $signature = hash_hmac('sha256', $payload, $privateKey);

    return [
        'publicKey' => $publicKey,
        'externalId' => $externalId,
        'timestamp' => $timestamp,
        'signature' => $signature
    ];
}

// Laravel example
Route::get('/api/widget-auth', function () {
    if (!auth()->check()) {
        return response()->json(['error' => 'Not authenticated'], 401);
    }

    $auth = generateWidgetAuth(
        auth()->user()->id,
        config('services.useloyalty.public_key'),
        config('services.useloyalty.private_key')
    );

    return response()->json($auth);
});

Step 2: Add Widget Script

Include the widget script in your HTML:

<!-- Add before </body> -->
<script src="https://cdn.useloyalty.app/widget/useloyalty-widget.iife.js"></script>

Or load it dynamically:

const script = document.createElement("script");
script.src = "https://cdn.useloyalty.app/widget/useloyalty-widget.iife.js";
script.onload = () => initWidget();
document.body.appendChild(script);

Step 3: Initialize Widget

Basic Initialization

<script>
  // Fetch auth from your server
  fetch("/api/widget-auth")
    .then((res) => res.json())
    .then((auth) => {
      UseLoyaltyWidget.init({
        publicKey: auth.publicKey,
        externalId: auth.externalId,
        timestamp: auth.timestamp,
        signature: auth.signature,
      });
    });
</script>

With Member Data

Pass member data to sync with the widget:

UseLoyaltyWidget.init({
  publicKey: auth.publicKey,
  externalId: auth.externalId,
  timestamp: auth.timestamp,
  signature: auth.signature,
  member: {
    email: "user@example.com",
    name: "John Doe",
    avatarUrl: "https://example.com/avatar.jpg",
  },
});

With Callbacks

Handle widget events:

UseLoyaltyWidget.init({
  publicKey: auth.publicKey,
  externalId: auth.externalId,
  timestamp: auth.timestamp,
  signature: auth.signature,
  onReady: () => {
    console.log("Widget loaded successfully");
  },
  onError: (error) => {
    console.error("Widget error:", error);
  },
  onWelcomeBonus: (points) => {
    console.log(`Welcome bonus: ${points} points!`);
  },
});

Framework Integration

For detailed integration guides with full examples:

  • React & Next.js - Components, hooks, and App Router examples
  • Vue.js - See example below

Vue.js Integration

<script setup lang="ts">
import { onMounted, onUnmounted, watch } from "vue";
import { useUser } from "@/composables/useUser";

const { user } = useUser();

onMounted(async () => {
  // Load script
  const script = document.createElement("script");
  script.src = "https://cdn.useloyalty.app/widget/useloyalty-widget.iife.js";
  document.body.appendChild(script);

  await new Promise((resolve) => (script.onload = resolve));

  if (user.value) {
    await initWidget();
  }
});

watch(user, async (newUser) => {
  if (newUser) {
    await initWidget();
  }
});

async function initWidget() {
  const response = await fetch("/api/widget-auth");
  const auth = await response.json();

  window.UseLoyaltyWidget.init({
    ...auth,
    member: {
      email: user.value?.email,
      name: user.value?.name,
    },
  });
}

onUnmounted(() => {
  window.UseLoyaltyWidget?.destroy();
});
</script>

Troubleshooting

Widget Not Appearing

  1. Check browser console for errors
  2. Verify signature is generated correctly
  3. Ensure timestamp is recent (within 5 minutes)
  4. Check API keys are correct

Signature Invalid Error

// Debug signature generation
console.log({
  payload: `${publicKey}:${externalId}:${timestamp}`,
  signature: generatedSignature,
});

CORS Errors

Ensure your API endpoint sets proper CORS headers:

// Express.js
app.use(
  cors({
    origin: "https://your-site.com",
    credentials: true,
  }),
);

Security Checklist

  • Private key stored in environment variables
  • Signature generated server-side only
  • HTTPS enabled in production
  • User authentication verified before generating signature
  • Timestamp validation (reject old timestamps)

On this page