File size: 6,124 Bytes
4a285d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# 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.