| # 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. | |