Payment Gateway Integration for system design
· 1. Payment Initiation
∘ Backend Code Example (Python with Flask):
· 2. Payment Processing
∘ Background Job for Status Checking:
· 3. Payment Completion
∘ 1. HTTP Callbacks (Webhooks)
∘ 2. WebSocket Notifications
· 4. Error Handling and Edge Cases
∘ Frontend Error Handling (JavaScript):
· Additional Considerations
In today’s digital economy, integrating payment gateways into web and mobile applications is crucial. This article breaks down the payment flow, providing diagrams and code examples to illustrate the process.
1. Payment Initiation
The payment process begins when a user decides to make a purchase. Here’s how it typically unfolds:
Backend Code Example (Python with Flask):
@app.route('/initiate-payment', methods=['POST'])
def initiate_payment():
order_id = request.json['order_id']
amount = request.json['amount']
# Create a pending transaction in your database
transaction = Transaction(order_id=order_id, amount=amount, status='pending')
db.session.add(transaction)
db.session.commit()
# Request payment URL from gateway
gateway_response = requests.post('https://payment-gateway.com/create-session', json={
'amount': amount,
'currency': 'USD',
'callback_url': 'https://your-app.com/payment-callback',
'transaction_id': transaction.id
})
payment_url = gateway_response.json()['payment_url']
return jsonify({'payment_url': payment_url})
2. Payment Processing
Once the user is redirected to the payment gateway, the actual processing begins:
Background Job for Status Checking:
def check_pending_payments():
pending_transactions = Transaction.query.filter_by(status='pending').all()
for transaction in pending_transactions:
if (datetime.now() - transaction.created_at).total_seconds() > 900: # 15 minutes
# Check with payment gateway
status = check_payment_status(transaction.id)
transaction.status = status
db.session.commit()
def check_payment_status(transaction_id):
response = requests.get(f'https://payment-gateway.com/status/{transaction_id}')
return response.json()['status']
# Run this job every minute
schedule.every(1).minutes.do(check_pending_payments
3. Payment Completion
After processing, the payment gateway informs your application about the transaction status:
Payment gateways typically use one or both of these methods to notify your backend about the payment status:
- HTTP Callbacks (Webhooks)
- WebSocket Notifications
Let’s explore both:
1. HTTP Callbacks (Webhooks)
In this approach:
- The payment gateway sends an HTTP POST request to your predefined callback URL.
- Your backend processes this callback, updates the transaction status, and fulfills the order if successful.
- The user is redirected back to your app, which then checks the payment status and displays the result.
Backend Code (Python with Flask)/Callback Endpoint Implementation:
@app.route('/payment-callback', methods=['POST'])
def payment_callback():
# Verify the callback authenticity (e.g., using a shared secret)
if not verify_callback_authenticity(request):
return 'Invalid callback', 400
transaction_id = request.json['transaction_id']
status = request.json['status']
transaction = Transaction.query.get(transaction_id)
transaction.status = status
db.session.commit()
if status == 'success':
fulfill_order(transaction.order_id)
return '', 200
@app.route('/check-payment-status/<transaction_id>', methods=['GET'])
def check_payment_status(transaction_id):
transaction = Transaction.query.get(transaction_id)
return jsonify({'status': transaction.status})
def fulfill_order(order_id):
# Logic to fulfill the order
pass
Frontend Code (JavaScript):
function checkPaymentStatus(transactionId) {
fetch(`/check-payment-status/${transactionId}`)
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
showSuccessScreen();
} else if (data.status === 'failure') {
showFailureScreen();
} else {
// Payment still processing
setTimeout(() => checkPaymentStatus(transactionId), 5000);
}
});
}
function showSuccessScreen() {
// Update UI to show success message
}
function showFailureScreen() {
// Update UI to show failure message
}
2. WebSocket Notifications
In this approach:
- Your app establishes a WebSocket connection when the user is redirected to the payment gateway.
- The payment gateway still notifies your backend via an HTTP callback.
- Your backend then pushes the status update through the WebSocket connection.
- Your app receives the update in real-time and updates the UI accordingly.
Backend Code (Python with Flask and Flask-SocketIO):
from flask_socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app)
@app.route('/payment-callback', methods=['POST'])
def payment_callback():
# Verify the callback authenticity
if not verify_callback_authenticity(request):
return 'Invalid callback', 400
transaction_id = request.json['transaction_id']
status = request.json['status']
transaction = Transaction.query.get(transaction_id)
transaction.status = status
db.session.commit()
if status == 'success':
fulfill_order(transaction.order_id)
# Emit status update through WebSocket
socketio.emit('payment_status_update', {'transaction_id': transaction_id, 'status': status}, room=transaction_id)
return '', 200
@socketio.on('join')
def on_join(data):
transaction_id = data['transaction_id']
join_room(transaction_id)
Frontend Code (JavaScript with Socket.IO client):
const socket = io();
socket.on('connect', () => {
socket.emit('join', {transaction_id: currentTransactionId});
});
socket.on('payment_status_update', (data) => {
if (data.transaction_id === currentTransactionId) {
if (data.status === 'success') {
showSuccessScreen();
} else if (data.status === 'failure') {
showFailureScreen();
}
}
});
function showSuccessScreen() {
// Update UI to show success message
}
function showFailureScreen() {
// Update UI to show failure message
}
Both methods have their advantages:
- HTTP Callbacks are simpler to implement and more widely supported.
- WebSocket Notifications provide real-time updates and can feel more responsive to the user.
Many applications implement both: they use webhooks as the primary notification method and WebSockets as a way to update the UI in real-time without polling.
Remember to implement proper error handling, authentication, and security measures in both your backend and frontend code when dealing with payment information and status updates.
4. Error Handling and Edge Cases
It’s crucial to handle various scenarios that might occur during the payment process:
Frontend Error Handling (JavaScript):
// Prevent going back
window.history.pushState(null, null, window.location.href);
window.onpopstate = function() {
window.history.pushState(null, null, window.location.href);
alert("Please do not use the back button during payment. If you need to cancel, use the cancel button on the payment page.");
};
// Handle page close
window.onbeforeunload = function() {
return "Are you sure you want to leave? Your payment may not be completed.";
};
Additional Considerations
- Implement proper logging at each stage for debugging and auditing.
- Use secure communication (HTTPS) for all API calls.
- Implement retry logic for failed API calls to the payment gateway.
- Consider implementing a payment expiration time (e.g., 30 minutes) after which the transaction is automatically cancelled.
- Provide clear instructions to users about not closing the browser or navigating away during payment.
- Implement a way for users to check their payment status if they accidentally close the browser (e.g., a “Check Payment Status” feature in your app).
By following this comprehensive flow, you can create a robust payment integration that handles most scenarios in a typical payment process. Remember that specific implementations may vary depending on the payment gateway you’re using and your specific business requirements.