Handle Payment Responses and Process Callbacks
A payment handler is a client-side function that processes payment responses from payment gateways after a user completes payment. It handles the critical post-payment flow: verifying payment authenticity, completing platform checkout, updating payment status, and redirecting users to success/failure pages.
In platform extensions, payment handlers are typically attached to payment gateway SDK callbacks (like handler functions in payment popups). They orchestrate the entire post-payment workflow, ensuring payment verification, order creation, and proper user redirection.
The payment handler is attached to the payment gateway popup configuration returned by your Buy Now Endpoint. After a user completes payment in the popup, the payment gateway SDK automatically calls your handler function with the payment response.
The payment handler calls your backend Verify Endpoint to verify the payment and complete checkout. After receiving the verification response, the handler redirects users using the patterns described in Redirection.
Key Concepts
Payment Gateway Callback
Payment gateways provide callback functions that execute after payment completion. These callbacks receive payment response data including:
- Payment ID (unique identifier for the transaction)
- Order ID (payment gateway's order reference)
- Signature (cryptographic proof of payment authenticity)
- Additional metadata
Payment Verification Flow
After receiving payment response, the handler must:
- Verify Payment: Validate the signature to ensure payment is authentic
- Fetch Addresses: Retrieve shipping/billing addresses from payment gateway
- Complete Checkout: Call platform's checkout API with addresses
- Update Payment: Update payment status in platform's order management system
- Handle Response: Process success/failure and redirect accordingly
Platform Integration
Payment handlers integrate with platform systems:
- FPI State: Access user data, cart, configuration from Fynd Platform Interface
- Platform APIs: Call GraphQL mutations like
checkoutCartto create orders - FDK Services: Use platform SDKs to update payment sessions
- State Management: Optionally update frontend state after successful payment
Steps
Payment Gateway Callback
When a user completes payment in the payment gateway popup, the gateway SDK calls your handler function with payment response data. Payment gateways execute the handler function automatically after payment completion. The handler receives a response object with payment details that must be verified before processing.
// Example: Payment gateway callback structure
const paymentGateway = new PaymentGateway({
key: 'public_key',
amount: 10000,
order_id: 'order_123',
handler: async function (response) {
// This is your payment handler
// response contains: payment_id, order_id, signature
await handlePaymentResponse(response);
}
});
Extract Payment Data
Extract and structure payment data from the payment gateway callback response (received in the previous step). Include your internal order ID for tracking. Payment responses contain gateway-specific identifiers. Map these to your internal order tracking system. Always include your internal order ID for database lookups. This payment data is sent to your Verify Endpoint in the next step.
Note: Payment gateway response field names vary by gateway. Common patterns:
payment_gateway_payment_id/razorpay_payment_id/payment_idpayment_gateway_order_id/razorpay_order_id/order_idpayment_gateway_signature/razorpay_signature/signature
// Example: Extract payment data
const paymentHandler = async function (response) {
// Extract payment gateway response
// Field names vary by payment gateway - adjust based on your gateway
const paymentId = response.payment_gateway_payment_id ||
response.razorpay_payment_id ||
response.payment_id;
const gatewayOrderId = response.payment_gateway_order_id ||
response.razorpay_order_id ||
response.order_id;
const signature = response.payment_gateway_signature ||
response.razorpay_signature ||
response.signature;
// Get your internal order ID (from popup config or checkout ID)
const internalOrderId = checkoutId || config.notes?.order_id;
// Structure payment data for verification
const paymentData = {
payment_gateway_payment_id: paymentId,
payment_gateway_order_id: gatewayOrderId,
payment_gateway_signature: signature,
order_id: internalOrderId
};
// Continue to verification...
};
Show Loading State
Display a loading overlay to prevent user interaction during payment processing. Payment processing takes time (network calls, verification, checkout). Show a loader to:
- Prevent duplicate submissions
- Provide user feedback
- Block navigation during critical operations
// Example: Show payment loader
function showPaymentLoader(message = 'Processing payment...') {
// Remove existing loader
hidePaymentLoader();
// Create loader overlay
const loader = document.createElement('div');
loader.id = 'payment-loader';
loader.innerHTML = `
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.7); z-index: 999999;
display: flex; align-items: center; justify-content: center;">
<div style="background: white; padding: 20px; border-radius: 8px;">
<div class="spinner"></div>
<div>${message}</div>
<div style="font-size: 12px; color: #666;">Please do not press back button</div>
</div>
</div>
`;
document.body.appendChild(loader);
}
// In payment handler
showPaymentLoader('Processing payment...');
Call Verification Endpoint
Send payment data to your backend verification endpoint. Include authentication headers from platform state. This endpoint performs signature verification, extracts addresses from the payment gateway, completes platform checkout, and updates payment status. The complete verification flow is documented in the Verify Payment page.
- Use platform state to build dynamic API URLs (multi-tenant support)
- Include authentication headers from FPI state
- Use
credentials: 'include'to send cookies - Handle HTTP errors before parsing JSON
// Example: Call verification endpoint
const paymentHandler = async function (response) {
// Get platform state for API configuration
const state = window.fpi.store.getState();
const baseUrl = `https://${state.custom.appHostName}/ext/your-extension`;
// Build dynamic headers from platform state
const headers = buildDynamicHeaders(state);
try {
// Call verification endpoint
const verifyResponse = await fetch(`${baseUrl}/verify-payment`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers
},
credentials: 'include',
body: JSON.stringify(paymentData)
});
if (!verifyResponse.ok) {
throw new Error(`Verification failed: HTTP ${verifyResponse.status}`);
}
const verification = await verifyResponse.json();
// Process verification result...
} catch (error) {
// Handle errors...
}
};
Build Dynamic Headers
Extract authentication and context headers from platform state (FPI) to include in API requests.
- Platform state (FPI) contains user authentication tokens
- Headers must be forwarded to backend APIs for authentication
- Include cookies for session continuity
- Set origin header for CORS compliance
// Example: Build dynamic headers from platform state
function buildDynamicHeaders(state) {
const headers = {};
// Authorization header (from FPI or state)
if (window.fpi?.authorizationHeader) {
headers['authorization'] = window.fpi.authorizationHeader;
} else if (state.auth?.token) {
headers['authorization'] = `Bearer ${state.auth.token}`;
}
// Cookies for session management
if (document.cookie) {
headers['cookie'] = document.cookie;
}
// Origin for CORS
if (state.configuration?.application?.domain) {
headers['origin'] = `https://${state.configuration.application.domain}`;
}
// Standard headers
Object.assign(headers, {
'accept': '*/*',
'accept-language': navigator.language || 'en-GB,en;q=0.6',
'cache-control': 'no-cache',
'content-type': 'application/json'
});
return headers;
}
Process Verification Response
The verification endpoint (called in the previous step) performs multiple operations and returns results. Extract and validate the response. The response structure is documented in the Verify Payment page. Extract the platform order ID from the response - this is used in the next step for redirection.
- Verification endpoint handles multiple steps: signature verification, address fetching, checkout, payment update
- Response contains results from all operations
- Extract platform order ID for redirection
- Log results for debugging and monitoring
// Example: Process verification response
const verification = await verifyResponse.json();
// Validate verification success
if (!verification.success) {
throw new Error(verification.message || 'Payment verification failed');
}
// Extract results from verification
const gatewayOrderDetails = verification.data?.gateway_order_details;
const platformCheckout = verification.data?.platform_checkout;
const paymentVerification = verification.data?.payment_verification;
// Log results for debugging
console.log('Verification completed', {
has_gateway_order: !!gatewayOrderDetails,
has_platform_checkout: !!platformCheckout,
platform_order_id: platformCheckout?.order_id,
payment_verified: paymentVerification?.success
});
Handle Success Flow
On successful verification and checkout, redirect to success page with order ID. The redirect logic, URL construction, and success condition verification are detailed in the Redirection page. This is the final step in the checkout flow, ensuring users are taken to the order confirmation page with the correct order ID.
- Success requires both verification success AND platform checkout success
- Update platform state if needed (cart clearing, order state)
- Build success URL dynamically from platform state
- Use full redirect (not pushState) to ensure clean state
// Example: Handle success flow
if (verification.success && platformCheckout?.success && platformCheckout?.order_id) {
// Optionally update platform state
try {
const fpi = window.fpi || window.__fpi__;
if (fpi?.store) {
// Clear cart or update order state
// fpi.store.dispatch({ type: 'CLEAR_CART' });
console.log('Platform state updated');
}
} catch (stateError) {
console.warn('Could not update platform state:', stateError);
}
// Build success URL
const state = window.fpi.store.getState();
const storefrontDomain = state.custom?.appHostName ||
state.configuration?.application?.domain ||
window.location.hostname;
// Format: {domain}/cart/order-status?success=true&order_id={order_id}
const successUrl = `https://${storefrontDomain}/cart/order-status?success=true&order_id=${platformCheckout.order_id}`;
// Redirect to success page
window.location.href = successUrl;
}
Handle Failure Flow
On failure, hide loader and show error message without redirecting.
- Failures can occur at verification or checkout stages
- Don't redirect on failure - let user decide next action
- Show clear error messages
- Log failure details for debugging
// Example: Handle failure flow
else {
// Hide loader
hidePaymentLoader();
// Log failure details
console.error('Payment processing failed', {
verification_success: verification.success,
platform_checkout_success: platformCheckout?.success,
error_message: verification.message || platformCheckout?.message
});
// Show error to user
alert(`Payment processing failed: ${verification.message || platformCheckout?.message || 'Unknown error'}`);
// Do NOT redirect - let user retry or go back
}
Error Handling
Wrap entire handler in try-catch to handle network errors and unexpected failures.
- Always wrap async operations in try-catch
- Hide loader in error cases
- Log errors with context for debugging
- Show user-friendly error messages
// Example: Complete error handling
const paymentHandler = async function (response) {
try {
// Show loader
showPaymentLoader('Processing payment...');
// Extract payment data
const paymentData = extractPaymentData(response);
// Verify payment
const verification = await verifyPayment(paymentData);
// Handle success/failure
if (verification.success) {
redirectToSuccess(verification);
} else {
handleFailure(verification);
}
} catch (error) {
// Hide loader on error
hidePaymentLoader();
// Log error
console.error('Payment processing error:', error);
// Show user-friendly error
alert(`Payment processing failed: ${error.message}`);
}
};
Complete Example
Here's a complete example showing how to implement a payment handler:
// Example: Complete payment handler implementation
// 1. Helper: Build dynamic headers
function buildDynamicHeaders(state) {
const headers = {};
if (window.fpi?.authorizationHeader) {
headers['authorization'] = window.fpi.authorizationHeader;
} else if (state.auth?.token) {
headers['authorization'] = `Bearer ${state.auth.token}`;
}
if (document.cookie) headers['cookie'] = document.cookie;
if (state.configuration?.application?.domain) {
headers['origin'] = `https://${state.configuration.application.domain}`;
}
Object.assign(headers, {
'accept': '*/*',
'content-type': 'application/json',
'cache-control': 'no-cache'
});
return headers;
}
// 2. Helper: Show/hide loader
function showPaymentLoader(message = 'Processing payment...') {
hidePaymentLoader();
const loader = document.createElement('div');
loader.id = 'payment-loader';
loader.innerHTML = `<div style="...">${message}</div>`;
document.body.appendChild(loader);
}
function hidePaymentLoader() {
const loader = document.getElementById('payment-loader');
if (loader) loader.remove();
}
// 3. Main payment handler
function createPaymentHandler(config, checkoutId) {
return async function (response) {
console.log('Payment response received:', {
payment_id: response.payment_gateway_payment_id,
order_id: response.payment_gateway_order_id,
has_signature: !!response.payment_gateway_signature
});
// Extract payment data
const orderId = checkoutId || config.notes?.order_id;
const paymentData = {
payment_gateway_payment_id: response.payment_gateway_payment_id,
payment_gateway_order_id: response.payment_gateway_order_id,
payment_gateway_signature: response.payment_gateway_signature,
order_id: orderId
};
// Show loader
showPaymentLoader('Processing payment...');
try {
// Get platform state
const state = window.fpi.store.getState();
const baseUrl = `https://${state.custom.appHostName}/ext/your-extension`;
// Call verification endpoint
const verifyResponse = await fetch(`${baseUrl}/verify-payment`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...buildDynamicHeaders(state)
},
credentials: 'include',
body: JSON.stringify(paymentData)
});
if (!verifyResponse.ok) {
throw new Error(`Verification failed: HTTP ${verifyResponse.status}`);
}
const verification = await verifyResponse.json();
if (!verification.success) {
throw new Error(verification.message || 'Payment verification failed');
}
// Extract results from verification response
// Response structure: verification.data.platform_checkout (or fynd_checkout)
const platformCheckout = verification.data?.platform_checkout ||
verification.data?.fynd_checkout;
const paymentVerification = verification.data?.payment_verification ||
verification.data?.fynd_payment_verification;
// Handle success
// Success requires: verification success AND platform checkout success AND order ID exists
if (verification.success && platformCheckout?.success && platformCheckout?.order_id) {
const storefrontDomain = state.custom?.appHostName ||
state.configuration?.application?.domain ||
window.location.hostname;
const successUrl = `https://${storefrontDomain}/cart/order-status?success=true&order_id=${platformCheckout.order_id}`;
console.log('Payment successful - redirecting to:', successUrl);
window.location.href = successUrl;
} else {
// Handle failure
hidePaymentLoader();
const errorMessage = verification.message ||
platformCheckout?.message ||
'Payment processing failed';
console.error('Payment processing failed', {
verification_success: verification.success,
checkout_success: platformCheckout?.success,
has_order_id: !!platformCheckout?.order_id
});
alert(`Payment processing failed: ${errorMessage}`);
}
} catch (error) {
console.error('Payment processing error:', error);
hidePaymentLoader();
alert(`Payment processing failed: ${error.message}`);
}
};
}
// 4. Usage: Attach handler to payment gateway
function openPaymentPopup(config, checkoutId) {
const paymentGateway = new PaymentGateway({
...config,
handler: createPaymentHandler(config, checkoutId)
});
paymentGateway.open();
}
Next Steps
After implementing the payment handler:
- Implement Verify Endpoint: Your payment handler calls the Verify Endpoint to verify payment signatures, extract addresses, and complete platform checkout
- Implement Redirection: After receiving the verification response, implement Redirection logic to send users to the success page
- Review Controller Patterns: The verify endpoint logic is implemented using patterns from the Controllers page
The payment handler is the bridge between the payment gateway popup (opened in Script Injection) and your backend verification logic.