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:
- A useLoyalty project with API keys
- Server-side code to generate HMAC signatures
- A way to identify logged-in users (external ID)
Step 1: Install the SDK
npm install @useloyalty/sdk
# or
pnpm add @useloyalty/sdkStep 2: Server-Side Signature Generation
The widget requires an HMAC-SHA256 signature generated server-side. The SDK provides a helper function:
Using SDK (Recommended)
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
- Check browser console for errors
- Verify signature is generated correctly
- Ensure timestamp is recent (within 5 minutes)
- 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)