🔧 Payment Recovery
Handle interrupted payments, verify transaction status, and ensure no payment is lost due to network issues or browser crashes.
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.
User pays → Payment succeeds → Your callback fails → User doesn't get product → User thinks they were scammed. Recovery prevents this nightmare scenario.
Recovery Methods
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.
// 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();
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 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);
}
Marks a payment as delivered. This completes the transaction lifecycle and prevents the payment from appearing in settlePending() again.
// 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
SDK Checks Server
Queries Gammal Tech for payments with status "completed" but not "delivered"
Returns Pending Array
Returns array of payment objects that need delivery
You Deliver Each
Loop through and deliver each pending product
Confirm Delivery
Call confirmDelivery() for each to mark complete
Complete Implementation
Here's a robust payment implementation 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.
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 RecoveryCopy this prompt for help with payment recovery: