Configuring Webhooks
Passing webhook notification URL via Payment APIs
BoxPay offers payment status notifications through webhook notifications. You can specify your webhook URL in the notifyStatusUrl field when making checkout session and payment request API requests.
Webhook Payload Data Model
Here is a sample webhook payload that you will be receiving –
{
"merchantId": "kljxF2wFYk",
"operationId": "kK9n5Vvpqo",
"transactionId": "kK9n5Vvpqo",
"countryCode": "IN",
"status": {
"operation": "Authorisation",
"status": "Approved",
"reason": "Your payment is successful.",
"reasonCode": "OS_0000"
},
"captureRequired": false,
"legalEntityCode": "phonepe",
"orderId": "wocomtest",
"caller": {
"token": "8b5a6bfb-43ee-46cf-89e4-99e62b1afef4",
"callerType": "CHECKOUT"
},
"pspCode": "PHONEPE",
"pspReference": "T2405281409599607809853",
"timestamp": "2024-05-28T08:40:06.842935921Z",
"money": {
"amount": 1600,
"currencyCode": "INR"
},
"additionalData": {
"first6Digits": "400000",
"accountHolderName": "Test Card",
"last4Digits": "3220"
},
"shopper": {
"firstName": "John",
"lastName": "Doe",
"gender": null,
"phoneNumber": "+918912347898",
"email": "testemail@email.com",
"uniqueReference": "cust-g",
"deliveryAddress": {
"address1": "address line 1",
"address2": "",
"address3": "",
"city": "city",
"state": "state",
"countryCode": "IN",
"postalCode": "122324",
"shopperRef": null,
"addressRef": null,
"labelType": "Other",
"labelName": null,
"name": "receiver",
"email": null,
"phoneNumber": null
}
},
"paymentMethod": {
"type": "Card",
"brand": "VISA"
},
"metadata": {}
}
Processing Webhook notifications
To handle payment webhook notifications, it’s essential to create a secure HTTPS endpoint with a POST method to process the JSON-formatted request payload.
Please ensure that following steps must be taken while processing webhooks to avoid any operational and security issues:
- If IP whitelisting is required to receive the webhooks, you can refer the IP whitelisting page for the list of IPs to be whitelisted.
- Verify the signature of the webhook notification to ensure that notification is indeed coming from BoxPay using following steps or refer webhook signature verification code samples:
- Calculate the signature payload of the webhook notification by concatenating following parameters in the listed order. Null/Undefined/Missing values must be skipped.
- Salt Key – This can be retrieved from Merchant Dashboard by going to Developers -> Webhook page from sidebar. Salt key can be copied from top right of the page. If you don’t have access to merchant dashboard, please reach out to your relationship manager or raise a support ticket.
- Legal Entity Code – can be retrieved from legalEntityCode field of webhook response
- Order Id – can be retrieved from orderId field of webhook response
- Transaction Id – can be retrieved from transactionId field of webhook response
- Operation Id – can be retrieved from operationId field of webhook response
- Event Id – can be retrieved from eventId field of webhook response
- Country Code – can be retrieved from countryCode field of webhook response
- Operation Status – can be retrieved from status.status field of webhook response
- Operation Currency – can be retrieved from money.currencyCode field of webhook response
- Operation Amount – can be retrieved from money.amount field of webhook response
- Generate the SHA256 hash of the signature payload calculated in the previous step
- Match the generated hash from previous step with the value received in X-Signature request header.
- If both values match, please go to next steps else reject the message by returning Non-2xx status code and putting the error details in response.
- Calculate the signature payload of the webhook notification by concatenating following parameters in the listed order. Null/Undefined/Missing values must be skipped.
- Respond with HTTP status 2xx to confirm that you have received the webhook notification successfully. In case, we don’t receive success response, we retry sending that notification later for a total of 5 times with the gap of 30 minutes.
- Avoid any complex processing in the webhook notification processing and respond back as quickly as possible. If you need to do any complex processing, please do that asynchronously by using any messaging systems or any other mechanisms that you employ in your organisation.
- Verify that at least orderId, operationId and currencyCode combinations are correct before you update the payment status. You may also want to verify money.amount depending on the type of operation.
- Handle duplicate webhook notifications by making the processing idempotent. You can use the eventId field to identify if a duplicate notification has been received.
Webhook Signature Verification Code Samples
PHP
$salt_key = // salt key received from BoxPay
$payment_response = json_decode(file_get_contents('php://input'));
$signature_header_value = $_SERVER['HTTP_X_SIGNATURE'];
if ( !isset( $signature_header_value ) ) {
throw new Exception('Rejecting webhook response due to missing webhook signature');
}
$webhook_signature_text = $salt_key.$payment_response->legalEntityCode.$payment_response->orderId.$payment_response->transactionId.$payment_response->operationId.$payment_response->eventId.$payment_response->countryCode.$payment_response->status->status.$payment_response->money->currencyCode.$payment_response->money->amount;
$webhook_signature = hash('sha256', $webhook_signature_text);
if ( $signature_header_value != $webhook_signature ) {
throw new Exception('Rejecting webhook response due to invalid webhook signature');
}
// continue with other checks and then with further processing required at your end
Java
String boxpaySaltKey = // salt key received from BoxPay
PaymentResponse paymentResponse = // Object created from Request body received in webhook
String receivedSignature = // Signature value received in 'X-Signature' request header
String signatureText = new StringBuilder(boxpaySaltKey)
.append(paymentResponse.getLegalEntityCode())
.append(paymentResponse.getOrderId())
.append(paymentResponse.getTransactionId())
.append(paymentResponse.getOperationId())
.append(paymentResponse.getEventId())
.append(paymentResponse.getCountryCode())
.append(paymentResponse.getStatus().getStatus())
.append(paymentResponse.getMoney().getCurrencyCode())
.append(paymentResponse.getMoney().getAmount())
.toString();
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] output = messageDigest.digest(signatureText.getBytes(StandardCharsets.UTF_8));
StringBuilder hashText = new StringBuilder(new BigInteger(1, output).toString(16));
// Add preceding 0s to make it 64 bit
while (hashText.length() < 64) {
hashText.insert(0, "0");
}
String calculatedSignature = hashText.toString();
if (!calculatedSignature.equals(receivedSignature)) {
throw new IllegalArgumentException("Rejecting webhook response due to invalid webhook signature");
}
// continue with other checks and then with further processing required at your end