Why Payment Recovery?

Payments can be interrupted for many reasons. When a payment succeeds on Gammal Tech's side but your callback fails, the user has paid but not received their product. Recovery tools fix this.

📶

Network Failure

Internet drops after payment but before your callback runs.

🔄

Browser Refresh

User refreshes or navigates away during the callback.

💥

JavaScript Error

A bug in your callback code prevents delivery.

🔌

Server Down

Your backend is temporarily unavailable.

⚠️ The Worst Case

User pays → Payment succeeds → Your callback fails → User doesn't get product → User thinks they were scammed. Recovery prevents this nightmare scenario.

Recovery Methods

GammalTech.payment.settlePending() → Promise<Array>

Checks for any pending payments that succeeded but weren't delivered. Returns an array of payments that need delivery. Call this on every page load.

Recommended: Call on page load
// Add this to your main app initialization
async function initApp() {
    // Check for pending payments first
    const pending = await GammalTech.payment.settlePending();
    
    if (pending.length > 0) {
        console.log('Found pending payments:', pending);
        
        for (const payment of pending) {
            // Deliver each pending product
            await deliverProduct(payment);
            await GammalTech.payment.confirmDelivery(payment.id);
        }
    }
    
    // Continue with normal app initialization
    loadDashboard();
}

// Call on page load
initApp();
GammalTech.payment.verifyPayment(paymentId) → Promise<Object>

Verifies the status of a specific payment by ID. Use when you have a payment ID and need to check its current state.

Parameter Type Description
paymentId string The payment ID to verify (e.g., "pay_abc123")
Verify a specific payment
// Verify a payment from a support ticket or webhook
const result = await GammalTech.payment.verifyPayment('pay_abc123xyz');

console.log(result);
// {
//   success: true,
//   payment: {
//     id: "pay_abc123xyz",
//     status: "completed",
//     amount: 100,
//     currency: "EGP",
//     delivered: false
//   }
// }

if (result.success && !result.payment.delivered) {
    // Payment succeeded but not delivered - fix it!
    await deliverProduct(result.payment);
    await GammalTech.payment.confirmDelivery(result.payment.id);
}
GammalTech.payment.confirmDelivery(paymentId) → Promise<Object>

Marks a payment as delivered. This completes the transaction lifecycle and prevents the payment from appearing in settlePending() again.

Confirm delivery
// After successfully delivering the product
async function deliverProduct(payment) {
    // 1. Grant access in your system
    await grantUserAccess(payment.user_id, payment.description);
    
    // 2. Save to your database
    await savePurchase({
        payment_id: payment.id,
        user_id: payment.user_id,
        amount: payment.amount,
        product: payment.description,
        delivered_at: new Date()
    });
    
    // 3. Confirm delivery to Gammal Tech
    await GammalTech.payment.confirmDelivery(payment.id);
    
    // Payment lifecycle complete!
}

Payment Statuses

Status Meaning Action Required
pending Payment initiated but not completed Wait or prompt user to retry
completed Payment succeeded, awaiting delivery Deliver product, call confirmDelivery()
delivered Payment complete and product delivered None - transaction complete
failed Payment failed or was declined Prompt user to retry

Recovery Flow

How settlePending() Works
1

SDK Checks Server

Queries Gammal Tech for payments with status "completed" but not "delivered"

2

Returns Pending Array

Returns array of payment objects that need delivery

3

You Deliver Each

Loop through and deliver each pending product

4

Confirm Delivery

Call confirmDelivery() for each to mark complete

Complete Implementation

Here's a robust payment implementation with recovery:

Robust payment with recovery
<script src="https://api.gammal.tech/sdk-web.js"></script>
<script>
// ============================================
// STEP 1: Check for pending payments on load
// ============================================
async function checkPendingPayments() {
    if (!GammalTech.isLoggedIn()) return;
    
    try {
        const pending = await GammalTech.payment.settlePending();
        
        for (const payment of pending) {
            await handlePaymentDelivery(payment);
        }
    } catch (error) {
        console.error('Error checking pending:', error);
    }
}

// ============================================
// STEP 2: Unified delivery handler
// ============================================
async function handlePaymentDelivery(payment) {
    try {
        // Check if already delivered (idempotency)
        const existing = await checkExistingPurchase(payment.id);
        if (existing) {
            // Already delivered, just confirm
            await GammalTech.payment.confirmDelivery(payment.id);
            return;
        }
        
        // Deliver the product
        await deliverProduct(payment);
        
        // Save to database
        await savePurchase(payment);
        
        // Confirm with Gammal Tech
        await GammalTech.payment.confirmDelivery(payment.id);
        
        // Notify user
        showSuccessMessage('Purchase complete! Enjoy your ' + payment.description);
        
    } catch (error) {
        console.error('Delivery failed:', error);
        // Don't confirm - will retry on next page load
        showErrorMessage('There was an issue. Retrying automatically...');
    }
}

// ============================================
// STEP 3: Payment initiation
// ============================================
async function buyProduct(productId, price) {
    if (!GammalTech.isLoggedIn()) {
        await GammalTech.login();
    }
    
    GammalTech.pay(price, 'Product: ' + productId, handlePaymentDelivery);
}

// ============================================
// STEP 4: Initialize on page load
// ============================================
document.addEventListener('DOMContentLoaded', () => {
    checkPendingPayments();
});
</script>

Best Practices

Always Call settlePending() on Load

Add it to your app initialization. It's a quick check and catches any interrupted payments.

Make Delivery Idempotent

Your deliverProduct() function should check if the product was already delivered before delivering again. Use the payment ID as a unique key.

Don't Confirm Until Delivered

Only call confirmDelivery() after you've successfully delivered the product. If delivery fails, don't confirm — the payment will appear in settlePending() on next load.

Log Everything

Log payment IDs, delivery attempts, and confirmations. This helps debug issues and handle support tickets.

Pro Tip: Backend Webhooks

For mission-critical payments, also implement server-side webhooks (contact Gammal Tech to enable). This provides a backup delivery mechanism independent of the user's browser.

🤖

AI Prompt for Vibe Coding

Payment Recovery

Copy this prompt for help with payment recovery:

I need help implementing Gammal Tech payment recovery. Recovery Methods: - GammalTech.payment.settlePending() → Promise Returns payments that succeeded but weren't delivered - GammalTech.payment.verifyPayment(paymentId) → Promise Check status of specific payment - GammalTech.payment.confirmDelivery(paymentId) → Promise Mark payment as delivered (call after successful delivery) Payment Statuses: - pending: Payment initiated, not completed - completed: Payment succeeded, needs delivery - delivered: Complete transaction - failed: Payment failed Best Practices: 1. Call settlePending() on every page load 2. Make delivery function idempotent (check if already delivered) 3. Only confirmDelivery() after successful delivery 4. If delivery fails, don't confirm (will retry on next load) Please help me: [DESCRIBE YOUR PAYMENT RECOVERY TASK]