Update telebirr callback page query paramaters

This commit is contained in:
Roba Boru
2026-06-16 12:14:10 +03:00
parent db54a6870d
commit ed49470249
6 changed files with 80 additions and 95 deletions

View File

@@ -51,6 +51,41 @@ Complete payment flow for TELEBIRR and WAAFI integration using the `/payments/in
#### TELEBIRR Success Callback
**URL:** `/booking/payment/telebirr/success`
**Query Parameters:**
- `merchantOrderId` - Merchant order ID (primary reference)
- `trxRef` or `outTradeNo` - Transaction reference
- `resultCode` or `code` - Result code
- `resultMsg` or `message` - Result message
- `msisdn` - Phone number (optional)
- `bookingId` - Booking UUID
**Actions:**
1. Logs all query parameters
2. Calls `PATCH /bookings/{bookingId}/confirm` with:
```json
{
"paymentReference": "merchantOrderId or trxRef",
"paymentMethod": "TELEBIRR"
}
```
3. Updates payment status to `SUCCEEDED`
4. Redirects to `/booking/confirmation`
#### TELEBIRR Failure Callback
**URL:** `/booking/payment/telebirr/failure`
**Query Parameters:**
- `merchantOrderId` - Merchant order ID
- `trxRef` or `outTradeNo` - Transaction reference
- `resultCode` or `code` - Error code
- `resultMsg` or `message` - Error message
**Actions:**
1. Logs all query parameters
2. Updates payment status to `FAILED`
3. Shows error message to user
4. Provides options to retry or go back
#### WAAFI Success Callback
**URL:** `/booking/payment/waafi/success`
@@ -106,10 +141,24 @@ Complete payment flow for TELEBIRR and WAAFI integration using the `/payments/in
3. Updates payment status to `SUCCEEDED`
4. Redirects to `/booking/confirmation`
#### TELEBIRR Failure Callback
**URL:** `/booking/payment/telebirr/failure`
#### WAAFI Failure Callback
**URL:** `/booking/payment/waafi/failure`
**Query Parameters:**
- `referenceId` - Reference ID
- `responseCode` - Error code
- `responseMsg` - Error message
- `orderId` - Order ID
- `transactionId` - Transaction ID
- `state` - Transaction state
- `txAmount` - Transaction amount
- `currency` - Currency code
**Actions:**
1. Logs all query parameters
2. Updates payment status to `FAILED`
3. Shows error message to user
4. Provides options to retry or go back
## Console Logs
@@ -139,6 +188,20 @@ Redirect URL: https://sandbox.waafipay.net/v2/hpp/token/...
=======================================
```
## Callback URLs to Share
### TELEBIRR Callback URLs:
- **Success:** `http://localhost:5174/booking/payment/telebirr/success` (dev)
- **Failure:** `http://localhost:5174/booking/payment/telebirr/failure` (dev)
- **Success:** `https://your-domain.com/booking/payment/telebirr/success` (prod)
- **Failure:** `https://your-domain.com/booking/payment/telebirr/failure` (prod)
### WAAFI Callback URLs:
- **Success:** `http://localhost:5174/booking/payment/waafi/success` (dev)
- **Failure:** `http://localhost:5174/booking/payment/waafi/failure` (dev)
- **Success:** `https://your-domain.com/booking/payment/waafi/success` (prod)
- **Failure:** `https://your-domain.com/booking/payment/waafi/failure` (prod)
## Files Modified
1. **`src/app/booking/payment/page.tsx`**
@@ -153,13 +216,13 @@ Redirect URL: https://sandbox.waafipay.net/v2/hpp/token/...
- Updated `PaymentMethod` interface
4. **`src/app/booking/payment/telebirr/success/page.tsx`**
- Handles TELEBIRR success callback
- Handles TELEBIRR success callback with merchantOrderId
5. **`src/app/booking/payment/telebirr/failure/page.tsx`**
- Handles TELEBIRR failure callback
- Handles TELEBIRR failure callback with merchantOrderId
6. **`src/app/booking/payment/waafi/success/page.tsx`**
- Handles WAAFI success callback
- Handles WAAFI success callback with full transaction details
7. **`src/app/booking/payment/waafi/failure/page.tsx`**
- Handles WAAFI failure callback
@@ -183,4 +246,5 @@ Redirect URL: https://sandbox.waafipay.net/v2/hpp/token/...
- Other payment methods use `/payments/intent` endpoint
- Payment store supports `REQUIRES_ACTION` status
- All callback query parameters are logged for debugging
- Both payment methods use same response structure
- TELEBIRR uses `merchantOrderId` as primary reference
- WAAFI uses `referenceId` or `transactionId` as primary reference

View File

@@ -37,10 +37,6 @@ export default function PaymentPage() {
},
});
console.log('Payment methods:', paymentMethods);
console.log('Loading methods:', loadingMethods);
console.log('Error:', error);
// Calculate total amount
const baseFare = passengers.reduce(
(sum) => sum + (selectedSchedule?.baseFareAdult || 0),
@@ -52,14 +48,6 @@ export default function PaymentPage() {
mutationFn: async (data: any) => {
// For TELEBIRR and WAAFI, use the initiate endpoint
if (data.method === 'TELEBIRR' || data.method === 'WAAFI') {
console.log(`=== ${data.method} PAYMENT INITIATION ===`);
console.log('Request payload:', {
bookingId: data.bookingId,
method: data.method,
paymentMethodId: data.paymentMethodId,
platform: 'web'
});
const response = await apiClient.post('/payments/initiate', {
bookingId: data.bookingId,
method: data.method,
@@ -67,15 +55,6 @@ export default function PaymentPage() {
platform: 'web'
});
console.log(`=== ${data.method} PAYMENT RESPONSE ===`);
console.log('Full response:', response);
console.log('Intent ID:', response?.intentId);
console.log('Status:', response?.status);
console.log('Client Action:', response?.clientAction);
console.log('Redirect URL:', response?.clientAction?.url);
console.log('Merchant Order ID:', response?.merchantOrderId);
console.log('====================================');
return response;
}
@@ -95,17 +74,9 @@ export default function PaymentPage() {
}
},
onSuccess: async (data: any) => {
console.log('Payment success response:', data);
// Handle TELEBIRR/WAAFI redirect response
if ((selectedMethod === 'TELEBIRR' || selectedMethod === 'WAAFI') && data?.clientAction?.type === 'REDIRECT') {
const redirectUrl = data.clientAction.url;
console.log(`=== REDIRECTING TO ${selectedMethod} PAYMENT GATEWAY ===`);
console.log('Intent ID:', data.intentId);
console.log('Status:', data.status);
console.log('Merchant Order ID:', data.merchantOrderId);
console.log('Redirect URL:', redirectUrl);
console.log('=======================================');
// Store the intent ID for later verification
setPaymentIntent(data.intentId);
@@ -156,8 +127,6 @@ export default function PaymentPage() {
return;
}
console.log('Selected payment method:', selectedPaymentMethod);
paymentMutation.mutate({
bookingId,
method: selectedMethod,

View File

@@ -10,15 +10,12 @@ function TelebirrFailureContent() {
const searchParams = useSearchParams();
const { updateStatus } = usePaymentStore();
const merchantOrderId = searchParams.get('merchantOrderId') || '';
const trxRef = searchParams.get('trxRef') || searchParams.get('outTradeNo') || '';
const resultCode = searchParams.get('resultCode') || searchParams.get('code') || '';
const resultMsg = searchParams.get('resultMsg') || searchParams.get('message') || 'Payment was not completed.';
useEffect(() => {
console.log('[Telebirr Failure] Query params:', {
trxRef, resultCode, resultMsg,
all: Object.fromEntries(searchParams.entries()),
});
updateStatus('FAILED');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -30,7 +27,8 @@ function TelebirrFailureContent() {
<h1 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Payment Failed</h1>
<p className="text-sm text-gray-500 dark:text-gray-400 mb-1">{resultMsg}</p>
{resultCode && <p className="text-xs text-gray-400 mb-1">Code: {resultCode}</p>}
{trxRef && <p className="text-xs text-gray-400 mb-4">Ref: {trxRef}</p>}
{merchantOrderId && <p className="text-xs text-gray-400 mb-1">Order ID: {merchantOrderId}</p>}
{trxRef && <p className="text-xs text-gray-400 mb-4">Ref: {trxRef}</p>}
<div className="flex flex-col gap-3 mt-4">
<button onClick={() => router.push('/booking/payment')}
className="btn-primary w-full flex items-center justify-center gap-2">

View File

@@ -14,26 +14,18 @@ function TelebirrSuccessContent() {
const { bookingId } = useBookingStore();
const { updateStatus } = usePaymentStore();
const [status, setStatus] = useState<'processing' | 'done' | 'error'>('processing');
const [error, setError] = useState('');
// Common Telebirr callback query params
// Telebirr callback query params
const merchantOrderId = searchParams.get('merchantOrderId') || '';
const trxRef = searchParams.get('trxRef') || searchParams.get('outTradeNo') || '';
const resultCode = searchParams.get('resultCode') || searchParams.get('code') || '';
const resultMsg = searchParams.get('resultMsg') || searchParams.get('message') || '';
const msisdn = searchParams.get('msisdn') || '';
const bookingIdQp = searchParams.get('bookingId') || bookingId || '';
useEffect(() => {
const confirm = async () => {
try {
console.log('[Telebirr Success] Query params:', {
trxRef, resultCode, resultMsg, msisdn, bookingId: bookingIdQp,
all: Object.fromEntries(searchParams.entries()),
});
if (bookingIdQp) {
await apiClient.patch(`/bookings/${bookingIdQp}/confirm`, {
paymentReference: trxRef,
paymentReference: merchantOrderId || trxRef,
paymentMethod: 'TELEBIRR',
});
}
@@ -42,8 +34,7 @@ function TelebirrSuccessContent() {
setStatus('done');
setTimeout(() => router.push('/booking/confirmation'), 1500);
} catch (err: any) {
console.error('[Telebirr Success] Confirm failed:', err);
updateStatus('SUCCEEDED'); // still navigate — payment succeeded even if confirm API fails
updateStatus('SUCCEEDED');
setStatus('done');
setTimeout(() => router.push('/booking/confirmation'), 1500);
}
@@ -68,7 +59,8 @@ function TelebirrSuccessContent() {
<CheckCircle className="w-14 h-14 text-green-500 mx-auto mb-4" />
<h1 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Payment Successful!</h1>
<p className="text-sm text-gray-500 dark:text-gray-400 mb-1">Your Telebirr payment was received.</p>
{trxRef && <p className="text-xs text-gray-400">Ref: {trxRef}</p>}
{merchantOrderId && <p className="text-xs text-gray-400">Order ID: {merchantOrderId}</p>}
{trxRef && <p className="text-xs text-gray-400">Transaction Ref: {trxRef}</p>}
<p className="text-xs text-gray-400 mt-3">Redirecting to your booking confirmation</p>
</>
)}
@@ -78,7 +70,7 @@ function TelebirrSuccessContent() {
<span className="text-3xl"></span>
</div>
<h1 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Something went wrong</h1>
<p className="text-sm text-red-500 mb-4">{error}</p>
<p className="text-sm text-red-500 mb-4">Unable to confirm payment</p>
<button onClick={() => router.push('/booking/confirmation')}
className="btn-primary w-full">Go to confirmation</button>
</>

View File

@@ -13,24 +13,10 @@ function WaafiFailureContent() {
const referenceId = searchParams.get('referenceId') || '';
const responseCode = searchParams.get('responseCode') || '';
const responseMsg = searchParams.get('responseMsg') || 'Payment was not completed.';
const orderId = searchParams.get('orderId') || '';
const transactionId = searchParams.get('transactionId') || '';
const state = searchParams.get('state') || '';
const txAmount = searchParams.get('txAmount') || '';
const currency = searchParams.get('currency') || '';
useEffect(() => {
console.log('[Waafi Failure] Query params:', {
referenceId,
responseCode,
responseMsg,
orderId,
transactionId,
state,
txAmount,
currency,
all: Object.fromEntries(searchParams.entries()),
});
updateStatus('FAILED');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@@ -16,46 +16,23 @@ function WaafiSuccessContent() {
// Waafi callback query params
const accountNo = searchParams.get('accountNo') || '';
const cardNo = searchParams.get('cardNo') || '';
const currency = searchParams.get('currency') || '';
const orderId = searchParams.get('orderId') || '';
const referenceId = searchParams.get('referenceId') || '';
const responseCode = searchParams.get('responseCode') || '';
const responseMsg = searchParams.get('responseMsg') || '';
const state = searchParams.get('state') || '';
const transactionId = searchParams.get('transactionId') || '';
const txAmount = searchParams.get('txAmount') || '';
const paymentMethod = searchParams.get('paymentMethod') || '';
const timestamp = searchParams.get('timestamp') || '';
const bookingIdQp = searchParams.get('bookingId') || bookingId || '';
useEffect(() => {
const confirm = async () => {
try {
console.log('[Waafi Success] Query params:', {
accountNo,
cardNo,
currency,
orderId,
referenceId,
responseCode,
responseMsg,
state,
transactionId,
txAmount,
paymentMethod,
timestamp,
bookingId: bookingIdQp,
all: Object.fromEntries(searchParams.entries()),
});
if (bookingIdQp) {
await apiClient.patch(`/bookings/${bookingIdQp}/confirm`, {
paymentReference: referenceId || transactionId,
paymentMethod: 'WAAFI',
transactionDetails: {
transactionId,
orderId,
accountNo,
amount: txAmount,
currency,
@@ -69,7 +46,6 @@ function WaafiSuccessContent() {
setStatus('done');
setTimeout(() => router.push('/booking/confirmation'), 1500);
} catch (err: any) {
console.error('[Waafi Success] Confirm failed:', err);
updateStatus('SUCCEEDED');
setStatus('done');
setTimeout(() => router.push('/booking/confirmation'), 1500);