Integration Guides
Last Updated: 2026-03-09 Covers: Tawk.to Live Chat, PayTabs Payment Gateway, GTM Cross-Domain Tracking, Daftra ERP Related:
.claude/CLAUDE.md(project context),public/_headers(CSP),src/layouts/Layout.astro(script placement)
Table of Contents
1. Tawk.to Live Chat
1.1 Account Creation
- Go to https://www.tawk.to/signup
- Register with the company email (info@urwhats.com or sales@urwave.com)
- Verify email address
1.2 Property Setup
- Property Name: urWhats
- Site URL: https://urwhats.com
- Default Language: English
- Secondary Language: Arabic
- Time Zone: Asia/Riyadh (UTC+3)
1.3 Widget Customization
| Setting | Value |
|---|---|
| Theme Color | #45b33d (primary green) |
| Widget Title (EN) | Chat with us |
| Widget Title (AR) | تحدث معنا |
| Position (LTR/EN) | Bottom-right |
| Position (RTL/AR) | Bottom-left |
WhatsApp button overlap note: The site already has a WhatsApp floating button fixed at bottom-[30px] end-[30px] (see src/layouts/Layout.astro line 46-55). The Tawk.to widget defaults to bottom-right as well. Options to avoid overlap:
- Option A (recommended): Configure Tawk.to widget offset to sit ~80px above the WhatsApp button. Use the Tawk.to Advanced settings → Widget Position → Custom bottom margin of
100px. - Option B: Move the WhatsApp button to the opposite side of the Tawk.to widget. This requires editing
Layout.astroclasses (end-[30px]→start-[30px]), but breaks the established UX pattern. - Option C: Hide the WhatsApp floating button entirely and rely on Tawk.to with a WhatsApp channel connected. Tawk.to supports WhatsApp as a channel under Channels → WhatsApp.
For RTL (Arabic) pages, the end-[30px] class on the WhatsApp button resolves to bottom-left. Tawk.to's bottom-left position for RTL means they will overlap on Arabic pages too. Apply the same offset strategy for both directions.
1.4 Greeting Messages
Configure under Triggers → Add Trigger → Site Visitor:
English greeting:
Hi! :wave: How can we help you today? We typically respond in minutes.
Arabic greeting:
:wave: كيف يمكننا مساعدتك اليوم؟ نرد عادة خلال دقائق.
Trigger conditions:
- Time on page: 15 seconds (avoid interrupting users immediately)
- Pages visited: >= 1
- Don't show if chat is already open
1.5 Operating Hours
| Day | Hours (AST / UTC+3) |
|---|---|
| Sunday - Thursday | 9:00 AM - 6:00 PM |
| Friday - Saturday | Offline |
Enable Offline Form for outside business hours:
- Collect: Name, Email, Message
- Offline message (EN): "We're currently offline. Leave a message and we'll get back to you during business hours (Sun-Thu, 9AM-6PM AST)."
- Offline message (AR): "نحن غير متصلين حالياً. اترك رسالة وسنعود إليك خلال ساعات العمل (الأحد-الخميس، 9 صباحاً - 6 مساءً بتوقيت السعودية)."
1.6 Team Setup
- Go to Administration → Team Members
- Add agents with appropriate roles:
- Admin: Full access to settings, billing, and analytics
- Agent: Can respond to chats, view visitor info, no settings access
- Each agent should download the mobile app:
- Enable push notifications for new chats on mobile
1.7 CSP Header Changes
The current CSP in public/_headers (line 6) needs these additions:
| Directive | Add |
|---|---|
script-src |
https://embed.tawk.to |
connect-src |
https://va.tawk.to wss://va.tawk.to |
frame-src |
https://tawk.to |
img-src |
Already covered by https: glob |
Updated CSP script-src segment (add after https://connect.facebook.net):
https://embed.tawk.to
Updated CSP connect-src segment (add after https://connect.facebook.net):
https://va.tawk.to wss://va.tawk.to
Updated CSP frame-src segment (add after https://www.facebook.com):
https://tawk.to
1.8 Layout.astro Script Placement
Add the following script block in src/layouts/Layout.astro before the closing </body> tag, after the Footer component and before the existing <script> blocks (around line 58):
<!-- Tawk.to Live Chat -->
<script is:inline>
var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
(function(){
var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
s1.async=true;
s1.src='https://embed.tawk.to/PROPERTY_ID/WIDGET_ID';
s1.charset='UTF-8';
s1.setAttribute('crossorigin','*');
s0.parentNode.insertBefore(s1,s0);
})();
</script>
Replace PROPERTY_ID and WIDGET_ID with the values from the Tawk.to dashboard (Administration → Channels → Chat Widget → Direct Chat Link). The URL format is https://embed.tawk.to/{propertyId}/{widgetId}.
Use is:inline because Astro would otherwise bundle and transform the script, breaking the Tawk.to loader pattern that relies on synchronous global variable initialization.
1.9 Testing Checklist
- Widget appears on English pages (bottom-right, offset above WhatsApp button)
- Widget appears on Arabic pages (bottom-left, offset above WhatsApp button)
- Chat greeting fires after 15 seconds on page
- Agent receives notification when visitor sends a message
- Offline form appears outside business hours
- Widget theme color matches site green (#45b33d)
- Mobile: widget is usable, does not overlap with WhatsApp button
- CSP: no console errors related to tawk.to resources being blocked
2. PayTabs Payment Gateway
2.1 Why PayTabs
| Criteria | PayTabs | Tap | Stripe | HyperPay |
|---|---|---|---|---|
| Daftra native integration | Yes | Yes | No | No |
| Mada (local debit) support | Yes | Yes | No | Yes |
| Saudi entity | Yes (Riyadh HQ) | Yes (Bahrain/KSA) | No (US) | Yes (Riyadh) |
| Recurring billing API | Yes | Yes | Yes | Yes |
| Apple Pay | Yes | Yes | Yes | Yes |
| STC Pay | Yes | No | No | Yes |
| Monthly fee | Varies by plan | None | None | Varies |
| Per-transaction fee | ~2.25% + 1 SAR | ~2.75% | 2.9% + 1 SAR | ~2.5% |
| Setup time | 3-5 business days | 2-3 business days | 1-2 weeks (KYC) | 3-5 business days |
Decision: PayTabs is recommended because:
- Native Daftra integration (Settings → Payment Gateways → PayTabs) -- no custom code needed
- Saudi-headquartered company, understands local compliance
- Supports all Saudi payment methods including Mada and STC Pay
- Recurring billing API for SaaS subscription model
Alternative: Tap (tap.company) is also Daftra-native and has no monthly fee. Consider Tap if monthly gateway fees are a concern during early growth. Tap lacks STC Pay support.
HyperPay is NOT supported by Daftra natively -- would require custom webhook integration and is not recommended for this setup.
2.2 Account Creation
- Go to https://www.paytabs.com
- Click Merchant Signup → select Saudi Arabia
- Fill in business details:
- Legal Entity Name: urWave Company
- Trade Name: urWhats
- Commercial Registration: 7052775355
- VAT Number: 300075277550003
- Business Type: Technology / SaaS
- Website: https://urwhats.com
2.3 Required Documents
Prepare these before starting the application:
- Commercial Registration certificate (CR: 7052775355) -- from Ministry of Commerce (mc.gov.sa)
- National ID (or Iqama) of authorized signatory
- Saudi bank account details (IBAN, bank name, account holder)
- Company letterhead (optional, may be requested for verification)
- Website URL with active content (https://urwhats.com)
Approval typically takes 3-5 business days after document submission.
2.4 Daftra Connection Steps
Once PayTabs merchant account is approved:
Log into PayTabs Merchant Dashboard (merchant.paytabs.com)
Navigate to Developers → Key Management
Copy these three values:
- Profile ID (numeric)
- Server Key (starts with
S...) - Client Key (starts with
C...)
Log into Daftra (app.daftra.com)
Go to Settings → Payment Gateways
Select PayTabs from the list
Enter:
- Profile ID
- Server Key
- Client Key
- Currency: SAR
Enable desired payment methods:
- Mada (Saudi local debit -- most customers will use this)
- Visa
- Mastercard
- Apple Pay
- STC Pay
Save configuration
2.5 Sandbox Testing
Before going live:
- In PayTabs dashboard, switch to Sandbox/Test Mode
- In Daftra, use the sandbox credentials (separate Profile ID / Server Key / Client Key)
- Create a test invoice in Daftra
- Pay using test card numbers:
- Visa:
4111 1111 1111 1111, any future expiry, CVV123 - Mada: Use PayTabs sandbox Mada test numbers (check their docs)
- Visa:
- Verify:
- Payment page loads correctly with SAR currency
- Payment completes and Daftra marks invoice as paid
- Customer receives payment confirmation email from Daftra
- Switch to Live Mode once testing is complete
2.6 Supported Payment Methods
| Method | Type | Typical Usage |
|---|---|---|
| Mada | Local debit card | ~70% of Saudi online payments |
| Visa / Mastercard | International credit/debit | ~20% of payments |
| Apple Pay | Mobile wallet | Growing adoption |
| STC Pay | Mobile wallet | Popular with STC subscribers |
2.7 Fee Structure
Fees vary by PayTabs plan. Approximate rates for Saudi merchants:
| Component | Rate |
|---|---|
| Per transaction | ~2.25% + 1 SAR |
| Monthly fee | Varies (0-500 SAR depending on plan) |
| Refund fee | Original transaction fee is not returned |
| Chargeback fee | 75 SAR per chargeback |
| Settlement | T+2 business days to Saudi bank account |
Negotiate rates based on expected transaction volume. PayTabs offers custom pricing for SaaS businesses with recurring billing.
2.8 Recurring Billing
For monthly SaaS subscriptions, use the PayTabs Recurring Payments API:
- On first payment, tokenize the card via PayTabs hosted payment page
- Store the
tran_ref(transaction reference) andtokenreturned by PayTabs - For subsequent months, call the PayTabs recurring API with the stored token
- Handle failures: retry logic, card expiry notifications, grace periods
Daftra's recurring invoice feature handles the invoice side. The payment execution requires either:
- Manual: Customer pays each invoice via the Daftra payment link (PayTabs hosted page)
- Automated: Custom integration using PayTabs recurring API + Daftra webhook (see Section 4.5)
For initial launch, manual payment via Daftra invoice links is simpler and sufficient.
3. GTM Cross-Domain Tracking
3.1 Overview
| Property | Value |
|---|---|
| GTM Container | GTM-KBKH4G46 |
| Marketing site | urwhats.com |
| App (registration) | app.urwhats.com |
| Meta Pixel | 1367382874855915 (separate, not covered here) |
Problem: Users click "Start Free Trial" on urwhats.com and land on app.urwhats.com/register. Without cross-domain tracking, this navigation creates a new session in Google Analytics, losing all attribution data (UTM parameters, landing page, referral source).
Solution: GTM cross-domain linker tag that passes the _gl parameter between domains, maintaining session continuity.
3.2 Step 1: Configure Cross-Domain Linker
In GTM (tagmanager.google.com → Container GTM-KBKH4G46):
- Open your GA4 Configuration Tag (or create one if using gtag.js directly)
- Under Fields to Set, add:
| Field Name | Value |
|---|---|
linker |
{"domains": ["urwhats.com", "app.urwhats.com"], "accept_incoming": true, "decorate_forms": true} |
Alternatively, if using the GA4 Configuration tag type:
- Go to Tags → GA4 Configuration Tag
- Click Configure Tag Settings → More Settings → Cross Domain Measurement
- Add domains:
urwhats.comapp.urwhats.com
- Check Accept incoming linker parameters
- Check Decorate forms (for any forms that submit cross-domain)
- Trigger: All Pages
- Save
3.3 Step 2: Custom dataLayer Events
Define these events for tracking key user actions. The marketing site (urwhats.com) pushes events via GTM. The app (app.urwhats.com) must also push events to the same GTM container.
Event: plan_selected
Fired when a user clicks a "Choose Plan" or "Start Free Trial" button on the pricing page.
dataLayer.push({
event: 'plan_selected',
plan_name: 'growth', // 'growth', 'business', or 'enterprise'
plan_id: 1, // numeric plan ID
plan_price: 349, // price in SAR
plan_currency: 'SAR' // 'SAR' or 'USD'
});
Implementation: Add onclick handlers to the pricing CTA buttons in src/components/DynamicPlans.astro.
Event: registration_complete
Fired on app.urwhats.com after a user successfully completes registration. This is the primary conversion event.
dataLayer.push({
event: 'registration_complete',
plan_name: 'growth',
plan_id: 1,
method: 'email' // 'email', 'google', 'phone'
});
Implementation: Must be added to the app.urwhats.com codebase (separate repo). Coordinate with the app development team.
Event: language_switch
Fired when a user clicks the language switcher.
dataLayer.push({
event: 'language_switch',
from_language: 'en',
to_language: 'ar'
});
Implementation: Add to the language switcher click handler in src/components/Navigation.astro.
3.4 Step 3: Google Ads Conversion Tracking
In Google Ads (ads.google.com):
- Go to Tools → Conversions → New Conversion Action
- Choose Website
- Name: "Registration Complete"
- Category: Sign-up
- Value: Set to plan price or estimated LTV (e.g., 349 SAR for Growth plan)
- Count: One conversion per click
- Click-through window: 30 days
- View-through window: 1 day
In GTM, create a new tag:
- Tag type: Google Ads Conversion Tracking
- Conversion ID: (from Google Ads)
- Conversion Label: (from Google Ads)
- Conversion Value:
{{plan_price}}(create a Data Layer Variable forplan_price) - Currency Code:
{{plan_currency}} - Trigger: Custom Event →
registration_complete
Create GTM Variables (Data Layer type) for:
plan_name→ Variable name:DLV - plan_nameplan_id→ Variable name:DLV - plan_idplan_price→ Variable name:DLV - plan_priceplan_currency→ Variable name:DLV - plan_currency
3.5 Step 4: Testing with GTM Preview Mode
- Open GTM → Preview (top right button)
- Enter URL:
https://urwhats.com - Navigate to the pricing page
- Click a "Choose Plan" button
- Verify in the Preview panel:
-
plan_selectedevent fires with correct data - GA4 Configuration tag fires on all pages
- Cross-domain linker is active (check tag details)
-
- Follow the link to
app.urwhats.com/register - Check the URL contains the
_glparameter (e.g.,?planid=1&_gl=1*abc123*...) - In the Preview panel for app.urwhats.com:
- GA4 tag fires and recognizes the incoming linker parameter
- Session is continuous (same Client ID as urwhats.com)
Common issues:
_glparameter not appearing: Check that the link uses a standard<a>tag (not JavaScript navigation). Ensuredecorate_formsis enabled.- Session breaks: Verify both domains are listed in GA4 Data Streams → Configure Tag Settings → Cross-domain measurement.
- GTM not loading on app.urwhats.com: The same GTM container (GTM-KBKH4G46) must be installed on both domains.
3.6 Meta Pixel Note
The Meta Pixel (1367382874855915) is already installed on urwhats.com via src/components/MetaPixel.astro. Meta Pixel handles cross-domain tracking differently (via fbclid parameter and first-party cookies). No additional GTM configuration is needed for Meta -- manage pixel events directly through Meta Events Manager or the Meta Pixel code.
4. Daftra ERP Configuration
4.1 Invoice Template Setup
Configure the default invoice template in Daftra to match urWave/urWhats branding:
- Go to Settings → Invoice Settings → Invoice Template
- Configure:
| Setting | Value |
|---|---|
| Company Name | urWave Company |
| Trade Name | urWhats |
| Logo | Upload urWhats logo (use public/assets/images/logos/urWhats-logo.svg) |
| Default Currency | SAR (Saudi Riyal) |
| Default Language | Arabic (primary for Saudi clients) |
| Secondary Language | English (for bilingual invoices) |
| Tax Label | VAT / ضريبة القيمة المضافة |
| Tax Rate | 15% (Saudi VAT) |
| Tax Number (VAT) | 300075277550003 |
| CR Number | 7052775355 |
| Address | Riyadh, Kingdom of Saudi Arabia |
| Phone | +966 508 777 669 |
| sales@urwave.com |
- Enable QR Code on invoices (required by ZATCA for B2C invoices)
- Save and preview a sample invoice
4.2 Payment Gateway Connection
Connect PayTabs as the payment gateway (see Section 2.4 for detailed steps):
- Settings → Payment Gateways → PayTabs
- Enter Profile ID, Server Key, Client Key
- Set currency to SAR
- Enable: Mada, Visa, Mastercard, Apple Pay, STC Pay
- Test with a sandbox invoice before going live
4.3 Recurring Invoice Templates
Create subscription billing templates for each plan:
| Plan | Monthly Price (SAR) | Plan ID |
|---|---|---|
| Free | 0 (no invoice) | - |
| Starter | 349 | 1 |
| Growth | 999 | 2 |
| Pro | 1,999 | 3 |
| Enterprise | Custom quote | 4+ |
For each paid plan:
- Go to Invoices → Recurring Invoices → New Template
- Configure:
- Template Name:
urWhats - {Plan Name} Monthly - Recurrence: Monthly
- Start Date: Customer subscription start date
- Item Description:
urWhats {Plan Name} Plan - Monthly Subscription - Amount: Plan price (before VAT)
- Tax: 15% VAT (auto-calculated)
- Total: Price + VAT (e.g., Starter: 349 + 52.35 = 401.35 SAR)
- Payment Terms: Due on receipt
- Auto-send: Enable (sends invoice email automatically on creation date)
- Template Name:
- Attach PayTabs payment link (Daftra auto-generates this when PayTabs is connected)
4.4 ZATCA E-Invoicing Compliance
Saudi Arabia's Zakat, Tax and Customs Authority (ZATCA) mandates electronic invoicing in two phases:
Phase 1 (Generation) -- Already required:
- Generate invoices electronically (Daftra handles this natively)
- Include QR code on all invoices
- Include seller VAT number, buyer VAT number (if B2B), invoice date, total with VAT
Phase 2 (Integration) -- Rolling enforcement by taxpayer group:
- Invoices must be reported to ZATCA's Fatoora platform in near-real-time
- Requires API integration between Daftra and ZATCA
- Daftra has built-in ZATCA Phase 2 compliance (check Settings → ZATCA Integration)
Steps to enable:
- Go to Settings → ZATCA Integration
- Enter the Onboarding CSIDs (Compliance and Production) from the ZATCA Fatoora portal
- Configure:
- Solution Name: Daftra
- Organization Unit: urWave Company
- VAT Number: 300075277550003
- CR Number: 7052775355
- Run the compliance check (Daftra validates invoice format against ZATCA requirements)
- Enable Production Mode once compliance check passes
Note: Check ZATCA's current enforcement wave schedule at zatca.gov.sa to confirm whether urWave Company's revenue bracket is currently required to comply with Phase 2. Smaller businesses may have later deadlines.
4.5 Webhook: Payment Received Notification
Set up a webhook to notify app.urwhats.com when a payment is received, enabling automatic plan activation or renewal.
Daftra Webhook Configuration:
- Go to Settings → Webhooks (or API Settings → Webhooks)
- Create a new webhook:
| Setting | Value |
|---|---|
| Event | payment.created (or invoice.paid) |
| URL | https://app.urwhats.com/api/webhooks/daftra |
| Method | POST |
| Content-Type | application/json |
| Active | Yes |
Expected Payload:
{
"event": "payment.created",
"data": {
"invoice_id": 12345,
"invoice_number": "INV-2026-0042",
"amount": 401.35,
"amount_before_tax": 349.00,
"tax_amount": 52.35,
"currency": "SAR",
"status": "paid",
"payment_method": "mada",
"customer_id": 678,
"customer_email": "client@example.com",
"paid_at": "2026-03-09T14:30:00+03:00",
"plan_name": "Starter",
"recurring_invoice_id": 99
}
}
Security: HMAC Signature Verification
Daftra signs webhook payloads with an HMAC-SHA256 signature. The receiving endpoint on app.urwhats.com must verify this:
- Daftra sends the signature in the
X-Daftra-Signatureheader (or similar -- check Daftra's API docs for exact header name) - The webhook secret is available in Daftra's webhook settings
- On app.urwhats.com, verify the signature before processing:
// Pseudocode for the app.urwhats.com webhook handler
const crypto = require('crypto');
function verifyDaftraWebhook(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
On successful verification, the app should:
- Look up the customer by
customer_emailorcustomer_id - Activate or renew the corresponding plan
- Log the payment event
- Return HTTP 200
- Look up the customer by
On failure (invalid signature, unknown customer):
- Return HTTP 400 or 401
- Log the error for investigation
- Do NOT activate any plan
Implementation note: The webhook endpoint (app.urwhats.com/api/webhooks/daftra) must be built in the app.urwhats.com codebase (separate repository). Store the webhook secret as an environment variable (DAFTRA_WEBHOOK_SECRET), never hardcode it.
4.6 Customer Management
Sync customer records between Daftra and app.urwhats.com:
Manual approach (initial launch):
- When a new user registers on app.urwhats.com, manually create a matching customer in Daftra
- Use the same email address as the unique identifier across both systems
- Include: company name, contact name, email, phone, VAT number (if B2B)
Automated approach (future):
- Use the Daftra REST API to auto-create customers when users register on app.urwhats.com
- Endpoint:
POST /api/customers(see Daftra API docs) - Trigger:
registration_completeevent on app.urwhats.com - Store the Daftra
customer_idin the app.urwhats.com user record for future invoice lookups
Customer fields to sync:
| Field | Source | Notes |
|---|---|---|
| app.urwhats.com | Primary identifier | |
| Company Name | Registration form | Required for B2B invoicing |
| Phone | Registration form | Format: +966XXXXXXXXX |
| VAT Number | Optional field | Required for B2B tax invoices |
| Plan | app.urwhats.com | Maps to recurring invoice template |
| Language | app.urwhats.com | AR or EN, for invoice language |
Appendix: Implementation Priority
| Integration | Priority | Effort | Dependency |
|---|---|---|---|
| GTM Cross-Domain | High | 2-3 hours | GTM access, app.urwhats.com GTM install |
| PayTabs + Daftra | High | 1-2 days | Document submission, PayTabs approval (3-5 days) |
| Tawk.to Live Chat | Medium | 1-2 hours | CSP header update, account setup |
| Daftra Recurring Billing | Medium | 3-4 hours | PayTabs connected, plan templates defined |
| Daftra Webhook → App | Low (future) | 4-6 hours | App endpoint built, webhook secret configured |
| Customer Sync (automated) | Low (future) | 6-8 hours | Daftra API access, app integration |
Start with GTM Cross-Domain (quick win, high impact on attribution) and PayTabs account application (long lead time due to document verification). Tawk.to can be added independently at any time. Daftra recurring billing and webhooks depend on PayTabs being live.