Webhooks, remastered.

Greetings Revolut community :wave:,

We are thrilled to announce the release of our latest improvements in our Business API webhook version, Webhooks v2.

This upgrade brings exciting enhancements and improvements, enabling you to automate your business operations and all things money.

Exciting improvements include:

:one: Set up to 10 webhook URL endpoints: We upgraded from 1 webhook url, to 10.

:two: Security and signature verification: Safeguard your webhooks with our new signing secret, ensuring authenticity and avoiding fraud.

:three: Webhook URL update and edition: You can now edit your webhook url.

:four: Timestamp validation: Each request now contains a Timestamp, to ensure the exact moment in which it was sent and avoid replay conditions.

Explore our brand new API reference!

And we’ve revamped our documentation with comprehensive guides, practical examples, improved navigation, and invaluable security tips.

Get started with our webhooks and let us know your thoughts!

5 Likes

Hello, I am developing an integration with Merchant API and i need some help to understand how can I correlate a TransactionCreated event with a Revolut Order, since my system is able to create Revolut Order and track a feedback so we have the revolut order ID and then we have a webhook for listen all kind of events, but according to documentation I can´t find a way to correlate the event with the order, can anyone help me understand how? Thanks in advance

2 Likes

Hi @jgvaldes
This is currently not supported. In order to track the lifecycle of a Merchant order, you should instead set up Webhooks for the Merchant account. You have more info on it here.

2 Likes

totally right, my bad, i was confused by another section about payment link webhooks that are totally different, documentation about Revolut Orders is accurate thanks a lot for respond but i have another issue in my end about verify signature, I hope doesn’t bother with 1 more problem:
According to doc here should be easy to check signature, I made the exact use of this example just updating with my values and there is no way i get a valid signature, do you have any real example in any public repo to check the implementation?

1 Like

Hi. I tried to use webhooks for Merchant API. I created the order, the payment works, everything works perfectly, the problem occurs with the weebhooks integration. I didn’t receive any feedback after adding the order, although the status is completed.

1 Like

hi there, the webhooks only hits your callback url for events you subscribe for, check the doc events options for subscribe to callbacks here I have feedbacks all good, my problem is related with verify signature :frowning: I used the same example and it’s basically impossible to get the same signature they send

3 Likes

Hi @jgvaldes
Can you share the code snippet that calculates the signature? also can you let me know how do you get the values (timestamp, order payload)

2 Likes

How do I use / find the Merchant Web guides for Angular Typescript developers? its all in React! :frowning:

1 Like

hi @Mohamed.Laghzali sorry the delay, I was on vacations. Yes i can share the code, i tried a lot of options:
First we have and endpoint for listen webhook events and we capture data and pass to a function:

# Endpoint action:
    def create(self, request, *args, **kwargs):
        validate_revolut_endpoints_services_active()  # validate endpoint and some services we have

        request_ip = get_client_ip(request)
        success, revolut_webhook_order_event, error_message = process_webhook_order_event(
            request_headers=request.headers, event_data=request.data, request_ip=request_ip
        )
        # event_data should be the payload in raw json format sent by revolut

Inside process_webhook_order_event we extract the timestamp header and create an internal object for save all data, event type, payload, signature, etc and we call to this function to validate the signature where:
revolut_webhook_order.raw_timestamp: is the raw timestamp we extract from headers
revolut_webhook_order.revolut_payload: is the json data payload we capture in the endpoint
revolut_webhook_order.revolut_webhook.signing_secret: is the webhook signing secret we save from our webhook subscription

def verify_webhook_integrity_by_signature(revolut_webhook_order, signatures) -> tuple:
    """
    Validate if the provided request timestamp is within the allowed time range.

    :param RevolutWebhookOrderEvent revolut_webhook_order: instance with all required data
    :param str signatures: signatures captured in request headers
    :return: Tuple with format (valid, matched_signature, b2b_signature)
    :rtype: Tuple[bool, str, str]
    """
    valid, matched_signature, b2b_signature = False, None, None
    if signatures:
        # process each possible signature
        for revolut_signature in signatures.split(","):
            matched_signature = revolut_signature.strip()
            version, signature = matched_signature.split("=")

            payload_to_sign = (
                version
                + "."
                + str(revolut_webhook_order.raw_timestamp)
                + "."
                + json.dumps(revolut_webhook_order.revolut_payload)
                #  + str(revolut_webhook_order.revolut_payload)  # also tried this way
            )

            b2b_signature = (
                version
                + "="
                + hmac.new(
                    bytes(revolut_webhook_order.revolut_webhook.signing_secret, "utf-8"),
                    msg=bytes(payload_to_sign, "utf-8"),
                    digestmod=hashlib.sha256,
                ).hexdigest()
            )

            if b2b_signature == matched_signature:
                valid = True
                break

    return valid, matched_signature, b2b_signature
1 Like

Finally we could figured out the solution, we need to replace all spaces from payload body before process like this line:
json.dumps(revolut_webhook_order.revolut_payload).replace(" ", "")

I think it should be nice to mention that in the official documentation. Thanks for the help

1 Like

Hi @jgvaldes i would avoid using json.dumps() at all when dealing with signature verifications. as it may alter the original data. its best to capture the payload in raw format (bytes) and process it in the same format throughout the whole process.

2 Likes

Hi @HughP

Unfortunately there is no specific guides for Angular. the code examples are written in raw JS. however the docs provide general information that can be used to integrate with every frameworks.

1 Like

That’s quite annoying.

To note though, examples and nuget packages, etc are not written in raw JS but ReactJS, totally different things.

The API examples are good, written for many languages but not the Card Capture payment functions, which is the Primary function of a Payment Gateway.

1 Like

thanks for the advice, I will remember that if we found any issue in the future since this was the only way we got success verification

1 Like