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/false
Payment 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