# Paystack Payment Callback Guide ## Overview The platform now has a complete callback system for handling Paystack payment redirects. This includes both backend processing and frontend display. ## Callback Flow ### 1. Backend Callback Endpoint **GET** `/api/wallet/callback` This is a **public endpoint** (no authentication required) that Paystack redirects to after payment. It: - Receives the payment reference from Paystack query parameters - Verifies the transaction with Paystack API - Credits the user's wallet if successful - Redirects to frontend success/failure page **Query Parameters (from Paystack):** - `reference` or `trxref` - Payment reference **Redirects:** - Success: `${FRONTEND_URL}/payment/success?reference=...&points=...&balance=...` - Failure: `${FRONTEND_URL}/payment/failed?error=...&reference=...` ### 2. Frontend Callback Pages #### Success Page (`/payment/success`) **Query Parameters:** - `reference` - Payment reference - `points` - Points added to wallet - `balance` - New wallet balance - `amount` - Amount paid in Naira - `already_processed` - If payment was already processed #### Failure Page (`/payment/failed`) **Query Parameters:** - `error` - Error message - `reference` - Payment reference - `status` - Payment status from Paystack - `message` - Gateway response message ### 3. Static HTML Callback Page A fallback callback page is available at `/public/payment-callback.html` that: - Shows a loading spinner while processing - Displays success or error messages - Shows payment details (reference, points, balance) - Provides links to navigate back to wallet or retry ## Configuration ### Environment Variables ```env # Backend URL (where Paystack redirects to) BACKEND_URL=http://localhost:3000 # Frontend URL (where users are redirected after processing) FRONTEND_URL=http://localhost:3000 # Optional: Custom callback URL (overrides default) PAYSTACK_CALLBACK_URL=http://localhost:3000/api/wallet/callback ``` ### Paystack Dashboard Setup #### Webhook Configuration (Required) 1. Go to Settings → API Keys & Webhooks 2. Add webhook URL: `https://yourdomain.com/api/wallet/webhook/paystack` 3. Select events: `charge.success` (required) 4. Save configuration See [WEBHOOK_SETUP.md](./WEBHOOK_SETUP.md) for detailed webhook setup instructions. #### Callback URL (Optional) The callback URL is automatically set when initializing payments. You can override it with: - Environment variable: `PAYSTACK_CALLBACK_URL` - Default: `${BACKEND_URL}/api/wallet/callback` ## Callback Scenarios ### Scenario 1: Successful Payment (First Time) 1. User completes payment on Paystack 2. Paystack redirects to `/api/wallet/callback?reference=wallet_...` 3. Backend verifies payment with Paystack API 4. Backend credits wallet 5. Backend redirects to frontend success page with details ### Scenario 2: Successful Payment (Already Processed) 1. User completes payment on Paystack 2. Paystack redirects to `/api/wallet/callback?reference=wallet_...` 3. Backend checks for existing transaction (idempotency) 4. If already processed, redirects to success page with `already_processed=true` 5. No duplicate credit occurs ### Scenario 3: Failed Payment 1. User attempts payment on Paystack 2. Payment fails or is abandoned 3. Paystack redirects to `/api/wallet/callback?reference=...` 4. Backend verifies and finds status != 'success' 5. Backend redirects to frontend failure page with error details ### Scenario 4: Webhook Processing (Parallel) 1. Paystack sends webhook to `/api/wallet/webhook/paystack` 2. Webhook handler verifies signature and processes payment 3. This happens independently of redirect callback 4. Both webhook and callback are idempotent (no double-crediting) ## Frontend Integration ### Option 1: Use Backend Redirect (Recommended) ```javascript // When initializing payment const response = await fetch('/api/wallet/fund', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: 1000 }) }); const data = await response.json(); // Redirect to Paystack window.location.href = data.authorization_url; // Backend callback will handle redirect to your success/failure pages ``` ### Option 2: Use Inline Widget + Manual Verification ```javascript // Use Paystack inline widget const handler = PaystackPop.setup({ key: data.publicKey, email: userEmail, amount: data.payment.amount * 100, ref: data.reference, callback: function(response) { // Verify payment manually verifyPayment(response.reference); } }); async function verifyPayment(reference) { const response = await fetch(`/api/wallet/verify/${reference}`, { headers: { 'Authorization': `Bearer ${token}` } }); const result = await response.json(); if (result.success) { // Redirect to success page window.location.href = `/payment/success?reference=${reference}&points=${result.transaction.amount}&balance=${result.wallet.balance}`; } } ``` ## Testing ### Test Callback Flow 1. Start server: `npm run dev` 2. Initialize payment: `POST /api/wallet/fund` 3. Use test card: `4084084084084081` 4. After payment, Paystack redirects to callback 5. Check redirect URL for success/failure parameters ### Test with cURL ```bash # Simulate callback (after payment) curl "http://localhost:3000/api/wallet/callback?reference=wallet_test_123" ``` ## Security Considerations 1. **Public Endpoint**: The callback endpoint is public (no auth) because Paystack redirects to it 2. **Verification**: Always verify with Paystack API (never trust redirect parameters) 3. **Idempotency**: Both callback and webhook check for existing transactions 4. **Signature Verification**: Webhook uses HMAC signature verification 5. **Error Handling**: All errors redirect to frontend with safe error messages ## Error Handling The callback handler catches all errors and redirects to the failure page with: - Error message (URL encoded) - Reference (if available) - Status (if available) Frontend should display these safely and allow users to retry or contact support.