Webhooks
Getting started with Webhooks
Webhooks
are an essential part of your payment integration. They allow Revio to notify you about events on your account, such as a successful payment or a failed transaction.
A webhook
URL is an endpoint on your server where you can receive notifications about such events. When an event occurs, we'll make a POST request to that endpoint, with a JSON body containing the event's details, including the event's type and the associated data.
When to use Webhooks
Webhooks
are supported for all kinds of payment methods, but they're especially useful for methods and events that happen outside your application's control, such as:
- getting paid via Card, Instant EFT or Debit Order
- a customer being charged for their subscription (recurring payments)
- a pending payment transitions to successful
These are all asynchronous actions—your application does not control them, so you won't know when they are completed unless we notify you or check later.
Setting up a webhook
allows us to notify you when these payments are completed. Within your webhook endpoint, you can then:
- Update a customer's membership records in your database when a subscription payment succeeds.
- Email a customer when a subscription payment fails.
- Update your order records when a pending payment status is updated to successful.
Webhook Retry logic
Our callbacks
have a retry mechanism that uses exponential backoff.
The last retry attempt is made at +36h of the first webhook.
Webhook structure
{
"id": "dd1d0995-1105-4086-ab81-40da2d831325",
"due": 1663329928,
"type": "purchase",
"client": {
"cc": [],
"bcc": [],
"city": "",
"email": "[email protected]",
"phone": "",
"country": "",
"zip_code": "",
"bank_code": "",
"full_name": "",
"brand_name": "",
"legal_name": "",
"tax_number": "",
"client_type": null,
"bank_account": "",
"personal_code": "",
"shipping_city": "",
"street_address": "",
"shipping_country": "",
"shipping_zip_code": "",
"registration_number": "",
"shipping_street_address": ""
},
"issued": "2022-09-16",
"status": "paid",
"is_test": true,
"payment": {
"amount": 100,
"paid_on": 1663326625,
"currency": "ZAR",
"fee_amount": 0,
"net_amount": 90,
"description": "",
"is_outgoing": false,
"payment_type": "purchase",
"pending_amount": 10,
"remote_paid_on": 1663326625,
"owned_bank_code": null,
"owned_bank_account": null,
"pending_unfreeze_on": 1663585825,
"owned_bank_account_id": null
},
"product": "purchases",
"user_id": null,
"brand_id": "f880ebd4-04fb-4b88-8531-7ca148c93f2e",
"order_id": null,
"platform": "api",
"purchase": {
"debt": 0,
"notes": "",
"total": 100,
"currency": "ZAR",
"language": "en",
"products": [
{
"name": "test",
"price": 100,
"category": "",
"discount": 0,
"quantity": "1.0000",
"tax_percent": "0.00"
}
],
"timezone": "UTC",
"due_strict": false,
"email_message": "",
"total_override": null,
"shipping_options": [],
"subtotal_override": null,
"total_tax_override": null,
"payment_method_details": {},
"request_client_details": [],
"total_discount_override": null
},
"client_id": null,
"reference": "",
"viewed_on": 1663326561,
"company_id": "492c861c-a64f-424f-8edf-9165667dc526",
"created_on": 1663326328,
"event_type": "purchase.paid",
"updated_on": 1663326625,
"invoice_url": null,
"checkout_url": "https://gate.revio.co.za/p/dd1d0995-1105-4086-ab81-40da2d831325/invoice/",
"send_receipt": false,
"skip_capture": false,
"creator_agent": "",
"issuer_details": {
"website": "",
"brand_name": "ACME Insurance",
"legal_city": "",
"legal_name": "ACME Insurance",
"tax_number": "",
"bank_accounts": [
{
"bank_code": "",
"bank_account": ""
}
],
"legal_country": "ZA",
"legal_zip_code": "",
"registration_number": "",
"legal_street_address": ""
},
"marked_as_paid": false,
"status_history": [
{
"status": "created",
"timestamp": 1663326328
},
{
"status": "viewed",
"timestamp": 1663326561
},
{
"status": "pending_execute",
"timestamp": 1663326621
},
{
"status": "paid",
"timestamp": 1663326625
}
],
"cancel_redirect": "",
"created_from_ip": "102.65.48.194",
"direct_post_url": null,
"force_recurring": false,
"recurring_token": null,
"failure_redirect": "",
"success_callback": "",
"success_redirect": "",
"transaction_data": {
"flow": "payform",
"extra": {
"card_type": "debit",
"card_brand": "mastercard",
"masked_pan": "555555******4444",
"card_issuer": "ciagroup",
"expiry_year": 22,
"expiry_month": 12,
"cardholder_name": "Test",
"card_issuer_country": "BR"
},
"country": "BR",
"attempts": [
{
"flow": "payform",
"type": "execute",
"error": null,
"extra": {
"card_type": "debit",
"card_brand": "mastercard",
"masked_pan": "555555******4444",
"card_issuer": "ciagroup",
"expiry_year": 22,
"expiry_month": 12,
"cardholder_name": "Test",
"card_issuer_country": "BR"
},
"country": "BR",
"client_ip": "102.65.48.194",
"fee_amount": 10,
"successful": true,
"payment_method": "mastercard",
"processing_time": 1663326625
}
],
"payment_method": "mastercard"
},
"refundable_amount": 100,
"is_recurring_token": false,
"billing_template_id": null,
"currency_conversion": null,
"reference_generated": "RV2",
"refund_availability": "all",
"payment_method_whitelist": null
}
{
"id": "3d6ae45d-1525-4b03-9206-0b6ff82bf6f1",
"due": 1663327973,
"type": "purchase",
"client": {
"cc": [],
"bcc": [],
"city": "",
"email": "[email protected]",
"phone": "",
"country": "",
"zip_code": "",
"bank_code": "",
"full_name": "",
"brand_name": "",
"legal_name": "",
"tax_number": "",
"client_type": null,
"bank_account": "",
"personal_code": "",
"shipping_city": "",
"street_address": "",
"shipping_country": "",
"shipping_zip_code": "",
"registration_number": "",
"shipping_street_address": ""
},
"issued": "2022-09-16",
"status": "error",
"is_test": true,
"payment": null,
"product": "purchases",
"user_id": null,
"brand_id": "f880ebd4-04fb-4b88-8531-7ca148c93f2e",
"order_id": null,
"platform": "api",
"purchase": {
"debt": 0,
"notes": "",
"total": 100,
"currency": "ZAR",
"language": "en",
"products": [
{
"name": "test",
"price": 100,
"category": "",
"discount": 0,
"quantity": "1.0000",
"tax_percent": "0.00"
}
],
"timezone": "UTC",
"due_strict": false,
"email_message": "",
"total_override": null,
"shipping_options": [],
"subtotal_override": null,
"total_tax_override": null,
"payment_method_details": {},
"request_client_details": [],
"total_discount_override": null
},
"client_id": null,
"reference": "",
"viewed_on": 1663326689,
"company_id": "492c861c-a64f-424f-8edf-9165667dc526",
"created_on": 1663324373,
"event_type": "purchase.payment_failure",
"updated_on": 1663326852,
"invoice_url": null,
"checkout_url": "https://payments.revio.co.za/p/3d6ae45d-1525-4b03-9206-0b6ff82bf6f1/",
"send_receipt": false,
"skip_capture": false,
"creator_agent": "",
"issuer_details": {
"website": "",
"brand_name": "ACME Insurance",
"legal_city": "",
"legal_name": "ACME Insurance",
"tax_number": "",
"bank_accounts": [
{
"bank_code": "",
"bank_account": ""
}
],
"legal_country": "ZA",
"legal_zip_code": "",
"registration_number": "",
"legal_street_address": ""
},
"marked_as_paid": false,
"status_history": [
{
"status": "created",
"timestamp": 1663324373
},
{
"status": "viewed",
"timestamp": 1663326689
},
{
"status": "pending_execute",
"timestamp": 1663326850
},
{
"status": "error",
"timestamp": 1663326852
}
],
"cancel_redirect": "",
"created_from_ip": "102.65.48.194",
"direct_post_url": null,
"force_recurring": false,
"recurring_token": null,
"failure_redirect": "",
"success_callback": "",
"success_redirect": "",
"transaction_data": {
"flow": "payform",
"extra": {
"card_type": "debit",
"card_brand": "mastercard",
"card_issuer": "ciagroup",
"card_issuer_country": "BR"
},
"country": "",
"attempts": [
{
"flow": "payform",
"type": "execute",
"error": {
"code": "general_transaction_error",
"message": "Unrecognized transaction error"
},
"extra": {
"card_type": "debit",
"card_brand": "mastercard",
"masked_pan": "555555******4444",
"card_issuer": "ciagroup",
"expiry_year": 22,
"expiry_month": 11,
"cardholder_name": "test",
"card_issuer_country": "BR"
},
"country": "BR",
"client_ip": "102.65.48.194",
"fee_amount": 20,
"successful": false,
"payment_method": "mastercard",
"processing_time": 1663326852
}
],
"payment_method": ""
},
"refundable_amount": 0,
"is_recurring_token": false,
"billing_template_id": null,
"currency_conversion": null,
"reference_generated": "RV1",
"refund_availability": "none",
"payment_method_whitelist": null
}
{
"id": "d1b8e94e-6908-44e0-9724-2a3d6240bd0b",
"due": 1663931735,
"type": "purchase",
"client": {
"cc": [],
"bcc": [],
"city": "",
"email": "[email protected]",
"phone": "",
"country": "",
"zip_code": "",
"bank_code": "",
"full_name": "Test",
"brand_name": "",
"legal_name": "",
"tax_number": "",
"client_type": null,
"bank_account": "",
"personal_code": "",
"shipping_city": "",
"street_address": "",
"shipping_country": "",
"shipping_zip_code": "",
"registration_number": "",
"shipping_street_address": ""
},
"issued": "2022-09-16",
"status": "created",
"is_test": true,
"payment": null,
"product": "billing_invoices",
"user_id": "44744012-b260-4cf1-a910-f692c98e893f",
"brand_id": "f880ebd4-04fb-4b88-8531-7ca148c93f2e",
"order_id": null,
"platform": "web",
"purchase": {
"debt": 0,
"notes": "",
"total": 1000,
"currency": "ZAR",
"language": "en",
"products": [
{
"name": "Test",
"price": 1000,
"category": "",
"discount": 0,
"quantity": "1.0000",
"tax_percent": "0.00"
}
],
"timezone": "UTC",
"due_strict": false,
"email_message": "",
"total_override": null,
"shipping_options": [],
"subtotal_override": null,
"total_tax_override": null,
"payment_method_details": {},
"request_client_details": [],
"total_discount_override": null
},
"client_id": "b9ffc7ab-4eb9-4ec8-b300-0bc50b6ec894",
"reference": "",
"viewed_on": null,
"company_id": "492c861c-a64f-424f-8edf-9165667dc526",
"created_on": 1663327006,
"event_type": "purchase.created",
"updated_on": 1663327006,
"invoice_url": "https://gate.revio.co.za/p/d1b8e94e-6908-44e0-9724-2a3d6240bd0b/invoice/",
"checkout_url": "https://payments.revio.co.za/p/d1b8e94e-6908-44e0-9724-2a3d6240bd0b/",
"send_receipt": true,
"skip_capture": false,
"creator_agent": "",
"issuer_details": {
"website": "",
"brand_name": "ACME Insurance",
"legal_city": "",
"legal_name": "ACME Insurance",
"tax_number": "",
"bank_accounts": [
{
"bank_code": "",
"bank_account": ""
}
],
"legal_country": "ZA",
"legal_zip_code": "",
"registration_number": "",
"legal_street_address": ""
},
"marked_as_paid": false,
"status_history": [
{
"status": "created",
"timestamp": 1663327006
}
],
"cancel_redirect": "",
"created_from_ip": "102.65.48.194",
"direct_post_url": null,
"force_recurring": false,
"recurring_token": null,
"failure_redirect": "",
"success_callback": "",
"success_redirect": "",
"transaction_data": {
"flow": "payform",
"extra": {},
"country": "",
"attempts": [],
"payment_method": ""
},
"refundable_amount": 0,
"is_recurring_token": false,
"billing_template_id": "4af4427f-ec97-4830-bc3a-6fcb1e0ab69a",
"currency_conversion": null,
"reference_generated": "RV3",
"refund_availability": "none",
"payment_method_whitelist": null
}
Webhook authentication
Payloads are signed using asymmetric A.K.A. public-key cryptography to guarantee the authenticity of delivered callbacks. Each callback delivery request includes an X-Signature header field. This field contains a base64-encoded RSA PKCS#1 v1.5 signature of the SHA256 digest of the request body buffer. The signature is generated by using the entire webhook JSON body.
You can obtain the public key for Webhook
authentication from Webhook.public_key
of the corresponding Webhook.
You can obtain the public key for success callback authentication from Webhook public key.
Webhook
callback payloads are signed using a dedicated key pair. You can obtain the public key from Webhook.public_key
.
You can see how to verify the signature in C# below:
using var rsa = new RSACryptoServiceProvider();
// Import the public key
rsa.ImportFromPem(publicKey.ToCharArray());
// Read signature from the header value and convert to bytes
var signatureBytes = Convert.FromBase64String(signature);
// Verify the signature
success = rsa.VerifyData(requestObject, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Please note that Revio is not responsible for any financial losses incurred due to not implementing payload signature verification.
Creating a webhook
Webhooks
can be created via the Revio dashboard or by API.
For creating and modifying Webhooks
, see the Webhook CRUD API specification.
To create a webhook on the Revio dashboard:
- Log in to your dashboard and click on Developers
- Navigate to Webhooks to create a new
Webhook
- Remember to specify the
events
You want to listen to
Revio allows up to 8 webhooks shared between live and test modes.
Pro tip
When testing, you can get an instant webhook URL by visiting webhook.site. This will allow you to inspect the received payload without having to write any code or set up a server.
Updated 10 months ago