# Webhook Documentation This webhook documentation outlines how to integrate and handle payment notifications from PaymentPoint. When a payment is successfully processed, a webhook notification will be sent to your provided URL. This webhook contains information regarding the transaction, including the status, amount, sender, receiver, and other relevant details. ### Webhook Notification Format The webhook will send a **JSON payload** to your endpoint, with the following structure: ```json // Some code { "notification_status": "payment_successful", "transaction_id": "xxx", "amount_paid": 100, "settlement_amount": 99.5, "settlement_fee": 0.5, "transaction_status": "success", "sender": { "name": "AGH ONLINE ACADEMY TUTORS LIMITED", "account_number": "****4290", "bank": "HYDROGEN" }, "receiver": { "name": "ALBARKADATASUB-Abd(Paymentpoint)", "account_number": "6679854996", "bank": "PalmPay" }, "customer": { "name": "Abdulismail", "email": "albarkadatasub@gmail.com", "phone": null, "customer_id": "xxx" }, "description": "Your payment has been successfully processed.", "timestamp": "2024-11-22T13:00:04.256092Z" } ``` #### Webhook Data Breakdown * **notification\_status**: The status of the notification, e.g., "payment\_successful". * **transaction\_id**: Unique identifier for the transaction. * **amount\_paid**: The total amount paid by the customer (in the transaction's currency). * **settlement\_amount**: The actual amount that will be settled after any fees. * **settlement\_fee**: The fee deducted from the payment before settlement. * **transaction\_status**: The status of the payment transaction (e.g., "success"). * **sender**: Information about the sender: * `name`: Name of the sender. * `account_number`: Masked sender's account number. * `bank`: The bank of the sender. * **receiver**: Information about the receiver: * `name`: Name of the receiver. * `account_number`: The account number of the receiver. * `bank`: The bank of the receiver. * **customer**: Information about the customer: * `name`: Customer's name. * `email`: Customer's email address. * `phone`: Customer's phone number (may be null). * `customer_id`: Unique identifier for the customer in your system. * **description**: A message describing the payment status. * **timestamp**: The time the webhook notification was generated, in ISO 8601 format. **Webhook Integration Examples**
{% tabs %} {% tab title="PHP" %} ```php ``` {% endtab %} {% tab title="Python(Django)" %} ```django // Some code import hashlib import hmac from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt import json # Your secret security key (provided by PaymentPoint) security_key = 'xxx' # Replace with your actual security key @csrf_exempt # Disable CSRF validation for the webhook (it's not necessary for webhooks) def webhook(request): if request.method == 'POST': # Get the raw JSON data from the request webhook_data = request.body # Raw bytes, not text # Get the Paymentpoint-Signature from the request headers signature = request.headers.get('Paymentpoint-Signature') # Recreate the hash using the raw body and your security key calculated_signature = hmac.new( security_key.encode('utf-8'), webhook_data, hashlib.sha256 ).hexdigest() # Verify if the calculated signature matches the one in the header if calculated_signature == signature: # Signature is valid, process the webhook data try: # Parse the incoming JSON data data = json.loads(webhook_data) # Extract relevant data transaction_id = data.get('transaction_id') amount_paid = data.get('amount_paid') settlement_amount = data.get('settlement_amount') status = data.get('transaction_status') # Process the data (e.g., store in DB, send email, etc.) print(f"Transaction ID: {transaction_id}") print(f"Amount Paid: {amount_paid}") print(f"Settlement Amount: {settlement_amount}") print(f"Status: {status}") # Respond with a 200 OK status to acknowledge receipt of the webhook return JsonResponse({"status": "success"}, status=200) except json.JSONDecodeError: return JsonResponse({"error": "Invalid JSON format"}, status=400) else: # Invalid signature, reject the request return JsonResponse({"error": "Invalid signature"}, status=400) else: # Method not allowed return JsonResponse({"error": "Invalid method"}, status=405) ``` {% endtab %} {% tab title="Node" %} ```javascript // Get the Paymentpoint-Signature from the request headers const express = require('express'); const crypto = require('crypto'); const bodyParser = require('body-parser'); const app = express(); const port = 3000; // Your secret security key (provided by PaymentPoint) const securityKey = 'xxx'; // Replace with your actual security key // Middleware to parse JSON bodies app.use(bodyParser.json()); app.post('/webhook', (req, res) => { // Get the raw body of the request const webhookData = JSON.stringify(req.body); const signature = req.headers['paymentpoint-signature']; // Recreate the hash using the raw body and your security key const calculatedSignature = crypto .createHmac('sha256', securityKey) .update(webhookData) .digest('hex'); // Verify if the calculated signature matches the one in the header if (calculatedSignature === signature) { // Signature is valid, process the webhook data const data = req.body; // Extract relevant data const transactionId = data.transaction_id; const amountPaid = data.amount_paid; const settlementAmount = data.settlement_amount; const status = data.transaction_status; // Process the data (store in DB, etc.) console.log(`Transaction ID: ${transactionId}`); console.log(`Amount Paid: ${amountPaid}`); console.log(`Settlement Amount: ${settlementAmount}`); console.log(`Status: ${status}`); // Respond with a 200 OK status to acknowledge receipt of the webhook res.status(200).json({ status: 'success' }); } else { // Invalid signature, reject the request res.status(400).json({ error: 'Invalid signature' }); } ``` }); app.listen(port, () => { console.log(`Server is running on port ${port}`); }); {% endtab %} {% endtabs %} To ensure that the incoming webhook is coming from PaymentPoint and hasn't been tampered with, you need to verify the signature by hashing the payload with your secret security key and comparing it with the value in the `Paymentpoint-Signature` header. Here's an updated version of the webhook documentation, including how to implement signature verification in **PHP**, **Python**, and **Node.js**. *** ### Verifying the PaymentPoint Signature #### How the Signature is Generated The signature sent by PaymentPoint (`Paymentpoint-Signature`) is a hash of the raw JSON payload, generated using your security key. To validate that the request is legitimate, you will hash the incoming payload (excluding the signature) with your security key and compare it to the signature sent in the request header. #### Steps to Verify the Signature: 1. **Retrieve the `Paymentpoint-Signature` header** from the request. 2. **Remove the signature from the payload** (this is the `Paymentpoint-Signature` header). 3. **Recreate the hash**: Hash the raw JSON payload using the same hashing algorithm (likely SHA-256) and your security key. 4. **Compare the hashes**: If the generated hash matches the `Paymentpoint-Signature` header, then the request is valid.