Waffy JavaScript SDK Guide
Complete JavaScript SDK for integrating Waffy payments into your web applications. Supports payment links, custom UI, user tokens, and comprehensive event handling.
Key Features
Custom popup UI or external Waffy iframe views
Automatic customer authentication and token management
Native custom elements for easy integration
Installation & Setup
Option 1: CDN (Recommended)
<!DOCTYPE html> <html> <head> <title>Waffy Payment Integration</title> <!-- Include Waffy SDK --> <script src="https://cdn.waffy.com/sdk/v2/waffy.min.js"></script> </head> <body> <!-- Your content --> </body> </html>
Option 2: NPM Package
# Install via npm npm install @waffy/payment-sdk # Or with yarn yarn add @waffy/payment-sdk
// ES6 Modules
import WaffyPayment from '@waffy/payment-sdk';
// CommonJS
const WaffyPayment = require('@waffy/payment-sdk');Quick Start - 3 Lines Integration
Get payments working in under 5 minutes
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.waffy.com/sdk/v2/waffy.min.js"></script>
</head>
<body>
<h1>Buy Premium Plan - 99 SAR</h1>
<!-- Step 1: Add button container -->
<div id="waffy-payment-button"></div>
<script>
// Step 2: Initialize with minimal config
WaffyPayment.init({
merchantId: 'YOUR_MERCHANT_ID',
amount: 99,
currency: 'SAR'
});
</script>
</body>
</html>- • Creates a payment button automatically
- • Detects available payment methods
- • Handles the entire payment flow
- • Shows success/error messages
SDK Components
Three main components for different integration approaches
1. WaffyLinkGenerator
Creates payment links through backend API integration.
const linkGenerator = new WaffyLinkGenerator({
backendUrl: 'http://localhost:5001/api/waffy',
debug: true,
timeout: 30000
});
// Create simple payment
const result = await linkGenerator.createSimplePayment({
amount: 1000,
customerPhone: '+966563335555',
providerPhone: '+966557174411',
title: 'Service Payment',
description: 'Website development project',
redirectUrl: 'https://yoursite.com/success'
});
console.log('Payment URL:', result.payment_url);2. WaffyPaymentPopup
Displays payments in a custom popup interface.
const popup = new WaffyPaymentPopup({
theme: 'light',
debug: true,
onSuccess: (result) => {
console.log('Payment successful:', result);
window.location.href = '/success';
},
onCancel: () => {
console.log('Payment cancelled');
}
});
popup.open(paymentUrl, {
amount: '1000',
title: 'Payment Title',
description: 'Payment description'
});3. WaffyPaymentButton (Web Component)
Native web component for external payment views.
<waffy-payment-button
payment-url="https://waffyapp.com/payment/abc123"
text="Pay Now"
size="large"
theme="light"
debug="true">
</waffy-payment-button>
<script>
// Handle events
document.querySelector('waffy-payment-button')
.addEventListener('payment-success', (e) => {
console.log('Payment completed!', e.detail);
});
</script>Payment Link Generation
Different types of payment links for various use cases
Simple Payment
const result = await linkGenerator.createSimplePayment({
amount: 2500, // Amount in SAR
customerPhone: '+966563335555', // Customer phone
providerPhone: '+966557174411', // Provider phone
title: 'Website Development', // Payment title
description: 'Full-stack development', // Description
redirectUrl: 'https://mysite.com/success'
});
// Result contains:
// - contract_id: Contract ID
// - milestone_id: Milestone ID
// - payment_url: Payment URL with user token
// - success: true/falsePayment with Platform Fee
const result = await linkGenerator.createPaymentWithBroker({
amount: 2500,
customerPhone: '+966563335555',
providerPhone: '+966557174411',
brokerPhone: '+966507274790', // Platform phone
brokerFee: 125, // Platform fee (5%)
title: 'Service with Platform Fee',
description: 'Payment with 5% platform fee',
redirectUrl: 'https://mysite.com/success'
});Service Contract Payment
const result = await linkGenerator.createServicePayment({
amount: 5000,
customerPhone: '+966563335555',
providerPhone: '+966557174411',
projectTitle: 'Mobile App Development', // Project name
milestoneTitle: 'Initial Development Phase', // Milestone name
projectDescription: 'iOS and Android app', // Project details
milestoneDescription: 'UI/UX and backend', // Milestone details
redirectUrl: 'https://mysite.com/success'
});Event Handling
Comprehensive event system for all payment states
Payment Button Events
const button = document.querySelector('waffy-payment-button');
// Payment successful
button.addEventListener('payment-success', (e) => {
console.log('Payment completed:', e.detail);
// Redirect or update UI
window.location.href = '/success?id=' + e.detail.transactionId;
});
// Payment cancelled
button.addEventListener('payment-cancelled', (e) => {
console.log('Payment cancelled:', e.detail);
// Handle cancellation
});
// Modal opened
button.addEventListener('modal-open', (e) => {
console.log('Payment modal opened');
// Track analytics event
gtag('event', 'payment_modal_open');
});
// Payment error
button.addEventListener('payment-error', (e) => {
console.error('Payment error:', e.detail);
// Show user-friendly error message
showErrorMessage(e.detail.message);
});Payment Popup Events
const popup = new WaffyPaymentPopup({
onSuccess: (result) => {
console.log('Payment successful:', result);
// result.method - payment method used
// result.transactionId - transaction ID
// result.status - payment status
// Process successful payment
processSuccessfulPayment(result);
},
onError: (error) => {
console.error('Payment error:', error);
// error.message - error description
// error.code - error code (if available)
// Handle payment error
handlePaymentError(error);
},
onCancel: () => {
console.log('Payment cancelled by user');
// Handle cancellation
},
onClose: () => {
console.log('Popup closed');
// Cleanup logic here
cleanupPaymentState();
}
});Framework Integration
React Integration
import React, { useEffect, useState } from 'react';
const PaymentComponent = ({ amount, customer }) => {
const [paymentUrl, setPaymentUrl] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
const linkGenerator = new window.WaffyLinkGenerator({
backendUrl: process.env.REACT_APP_BACKEND_URL,
debug: process.env.NODE_ENV === 'development'
});
const createPayment = async () => {
setLoading(true);
try {
const result = await linkGenerator.createSimplePayment({
amount,
customerPhone: customer.phone,
providerPhone: process.env.REACT_APP_PROVIDER_PHONE,
title: 'React Payment',
redirectUrl: window.location.origin + '/success'
});
setPaymentUrl(result.payment_url);
} catch (error) {
console.error('Payment link creation failed:', error);
} finally {
setLoading(false);
}
};
createPayment();
}, [amount, customer]);
const handlePaymentSuccess = (event) => {
console.log('Payment completed:', event.detail);
// Handle success
};
if (loading) {
return <div>Creating payment...</div>;
}
return (
<div>
{paymentUrl && (
<waffy-payment-button
payment-url={paymentUrl}
text={`Pay ${amount} SAR`}
size="large"
onPaymentSuccess={handlePaymentSuccess}
/>
)}
</div>
);
};Vue.js Integration
<template>
<div>
<waffy-payment-button
v-if="paymentUrl"
:payment-url="paymentUrl"
:text="`Pay ${amount} SAR`"
size="large"
@payment-success="handlePaymentSuccess"
@payment-error="handlePaymentError"
/>
<div v-else>Creating payment...</div>
</div>
</template>
<script>
export default {
data() {
return {
paymentUrl: '',
linkGenerator: null
};
},
async mounted() {
this.linkGenerator = new window.WaffyLinkGenerator({
backendUrl: process.env.VUE_APP_BACKEND_URL,
debug: process.env.NODE_ENV === 'development'
});
await this.createPaymentLink();
},
methods: {
async createPaymentLink() {
try {
const result = await this.linkGenerator.createSimplePayment({
amount: this.amount,
customerPhone: this.customer.phone,
providerPhone: process.env.VUE_APP_PROVIDER_PHONE,
title: 'Vue Payment',
redirectUrl: window.location.origin + '/success'
});
this.paymentUrl = result.payment_url;
} catch (error) {
console.error('Payment creation failed:', error);
}
},
handlePaymentSuccess(event) {
console.log('Payment successful:', event.detail);
this.$emit('payment-completed', event.detail);
}
}
};
</script>Security & Best Practices
Environment Configuration
// Never expose secret keys in frontend code
const config = {
development: {
merchantId: process.env.WAFFY_TEST_MERCHANT_ID,
testMode: true,
backendUrl: 'http://localhost:5001/api/waffy'
},
production: {
merchantId: process.env.WAFFY_LIVE_MERCHANT_ID,
testMode: false,
backendUrl: 'https://api.yoursite.com/waffy'
}
};
// Validate configuration
function validateConfig(env) {
const cfg = config[env];
if (!cfg.merchantId || !cfg.backendUrl) {
throw new Error(`Missing Waffy configuration for ${env}`);
}
return cfg;
}
const waffyConfig = validateConfig(process.env.NODE_ENV);Input Validation
class PaymentValidator {
static validateAmount(amount) {
const numAmount = parseFloat(amount);
if (isNaN(numAmount) || numAmount <= 0) {
throw new Error('Invalid amount');
}
return Math.round(numAmount * 100) / 100; // Round to 2 decimals
}
static validatePhone(phone) {
const cleaned = phone.replace(/\D/g, '');
if (!/^(966|0)?5[0-9]{8}$/.test(cleaned)) {
throw new Error('Invalid Saudi phone number');
}
return '+966' + cleaned.slice(-9);
}
static sanitizeString(input) {
return input.replace(/[<>"'&]/g, '').trim().slice(0, 255);
}
}
// Usage before creating payment
const validatedData = {
amount: PaymentValidator.validateAmount(userAmount),
customerPhone: PaymentValidator.validatePhone(userPhone),
title: PaymentValidator.sanitizeString(userTitle)
};Error Handling
// Comprehensive error handling
async function createPaymentWithRetry(paymentData, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await linkGenerator.createSimplePayment(paymentData);
} catch (error) {
console.error(`Payment attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
// All retries failed
throw new Error('Payment creation failed after multiple attempts');
}
// Wait before retry (exponential backoff)
await new Promise(resolve =>
setTimeout(resolve, 1000 * attempt)
);
}
}
}
// Usage with user feedback
try {
showLoadingState('Creating payment...');
const result = await createPaymentWithRetry(paymentData);
hideLoadingState();
displayPaymentButton(result.payment_url);
} catch (error) {
hideLoadingState();
showErrorMessage('Unable to create payment. Please try again.');
console.error('Payment creation failed:', error);
}Troubleshooting Common Issues
SDK not loading
Check:
- Script src URL is correct
- Network connectivity
- Content Security Policy allows cdn.waffy.com
- Browser console for loading errors
Payment button not appearing
Solutions:
- Verify container element exists
- Check merchant ID is valid
- Ensure payment URL is properly formatted
- Enable debug mode for detailed logs
Backend connection failed
Check:
- Backend server is running
- Backend URL is correct
- CORS is properly configured
- API endpoints are accessible
Complete Integration Guide
Step-by-step payment integration tutorial
API Reference
Detailed API documentation and parameters