# Webhooks

To register the Webhook URL, you will need to contact RocketFuel sales team.

{% hint style="info" %}
The callback URL should respond with status 200 for GET request.
{% endhint %}

## Rocketfuel Webhook and Events <a href="#markdown-header-rocketfuel-webhook-and-events" id="markdown-header-rocketfuel-webhook-and-events"></a>

RocketFuel webhook calls are triggered to update the merchant on the status of the payouts and the payees.

Rocketfuel webhooks support the following events:-

* **PayeeAdded**: This event is triggered when a new payee is successfully added to the system. It confirms that the payee's information has been registered and is ready for further action.
* **PayeeKycStarted**: This webhook is triggered when the Know Your Customer (KYC) process for a payee begins. It signifies that the verification process for the payee's identity has been initiated.
* **PayeeKycStatusChange**: This event is sent whenever there is a change in the payee’s KYC status. It updates the current status of the verification process, such as whether it has been approved, rejected, or is pending further action.
* **PayeeFundAllocated**: This webhook is triggered when funds are successfully allocated to the payee. It signifies that the funds intended for the payout have been reserved.
* **PayoutStarted**: This event is triggered when the payout process begins. It confirms that the payout has been initiated for the payee and the payment is in progress.
* **PayoutStatusChange**: This webhook is sent when there is a change in the status of a payout. It provides updates on the current state of the payout, such as successful completion, failure, or being in progress.

## Securing Callbacks <a href="#markdown-header-securing-callbacks" id="markdown-header-securing-callbacks"></a>

Order callbacks originating from RocketFuel will be signed using our callback signing RSA private key.

If you would like to verify callbacks manually in the language of your choice, the message digest used is SHA256, the message that is signed is the POST body, the padding scheme is PKCS1\_v1\_5, and the signature to be verified is present in the ‘signature’ HTTP data encoded as base64.

**Examples**

{% tabs %}
{% tab title="PHP" %}

```php
public function verifyCallback($body, $signature)
{
    $signature_buffer = base64_decode( $signature );
    return (1 == openssl_verify($body, $signature_buffer, self::getCallbackPublicKey(), OPENSSL_ALGO_SHA256));
}
```

{% endtab %}

{% tab title="Node.js" %}

```
function verifySignature(body, public_key_rsa, signature) {
  const verifier = crypto.createVerify('RSA-SHA256');
  verifier.update(body);
  return verifier.verify(public_key_rsa, signature, 'base64');
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class WebhookVerifier {

    // The public key string (including header/footer)
    private static final String PUBLIC_KEY_STRING =
            "-----BEGIN PUBLIC KEY-----\n" +
            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2e4stIYooUrKHVQmwztC\n" +
            "/l0YktX6uz4bE1iDtA2qu4OaXx+IKkwBWa0hO2mzv6dAoawyzxa2jmN01vrpMkMj\n" +
            "rB+Dxmoq7tRvRTx1hXzZWaKuv37BAYosOIKjom8S8axM1j6zPkX1zpMLE8ys3dUX\n" +
            "FN5Dl/kBfeCTwGRV4PZjP4a+QwgFRzZVVfnpcRI/O6zhfkdlRah8MrAPWYSoGBpG\n" +
            "CPiAjUeHO/4JA5zZ6IdfZuy/DKxbcOlt9H+z14iJwB7eVUByoeCE+Bkw+QE4msKs\n" +
            "aIn4xl9GBoyfDZKajTzL50W/oeoE1UcuvVfaULZ9DWnHOy6idCFH1WbYDxYYIWLi\n" +
            "AQIDAQAB\n" +
            "-----END PUBLIC KEY-----";

    /**
     * Loads a PublicKey from a PEM-formatted string.
     *
     * @param keyStr the PEM public key string
     * @return the PublicKey instance
     * @throws Exception if any error occurs during parsing
     */
    public static PublicKey loadPublicKey(String keyStr) throws Exception {
        // Remove the PEM header and footer, and any whitespace/newlines.
        String publicKeyPEM = keyStr
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s", "");
        // Base64-decode the result
        byte[] decoded = Base64.getDecoder().decode(publicKeyPEM);
        // Generate the public key
        X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePublic(spec);
    }

    /**
     * Verifies the RSA-SHA256 signature for the given payload.
     *
     * @param body      the message to verify (payload)
     * @param signature the Base64-encoded signature string
     * @param publicKey the public key used for verification
     * @return true if the signature is valid; false otherwise
     * @throws Exception if any error occurs during verification
     */
    public static boolean verifySignature(String body, String signature, PublicKey publicKey) throws Exception {
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(publicKey);
        // Update the signature object with the bytes of the body
        sig.update(body.getBytes(StandardCharsets.UTF_8));
        // Decode the signature from Base64
        byte[] sigBytes = Base64.getDecoder().decode(signature);
        // Verify the signature and return the result
        return sig.verify(sigBytes);
    }

    public static void main(String[] args) {
        // Sample payload: the 'data' field from your JSON webhook
        String payloadData = "{\"data\":{\"payeeId\":\"ba2fb7c7-a94f-491a-9538-83a170557748\"," +
                "\"payeeInternalId\":\"\",\"payoutAmount\":0.00008697," +
                "\"payoutCurrency\":\"BTC\",\"payoutId\":\"e4c356dc-8fba-4713-9a00-7845d2c48c35\"," +
                "\"type\":\"crypto\"},\"event\":\"PayoutStarted\"," +
                "\"timestamp\":\"2024-07-16T12:46:30.061Z\"}";
        // The Base64-encoded signature from your webhook
        String signature = "h5EZnyA8v/24knUfnEka4QwgXeUQOb7XE21Duy5W8uV6o1g/7J2sB4gK31NAaXt3cz4TBgW0dA59LNvRogO+VUb6gzj/8jvlDXFtUj5214/cPEBPnuSddW8dy66zuBL3vYviT1qc1it0uNVmXzh2GCjfhfJ2ti3CHDornmiu3AfSROiPf40oAknt1nOpBGqvLafLzLRAcfIHa/6SxsApgdGCP9QW0A9O3WH4+uUNvehdKdGZ2t0Cv9LJGLTekc7Be4k85Tu/SsBbr9l6/laZMeZ/vsQFCzWdvbirHg/O78OjzHeLiHCdeqMrhkwVQKPE2xm1HwDqp8TSPDaX6CiNng==";

        try {
            PublicKey publicKey = loadPublicKey(PUBLIC_KEY_STRING);
            boolean isValid = verifySignature(payloadData, signature, publicKey);
            System.out.println("Signature valid: " + isValid);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

{% endtab %}
{% endtabs %}

### Sample Webhook Payloads <a href="#markdown-header-test-data" id="markdown-header-test-data"></a>

Following is an example for GET & POST payload for the events:

1. **Payee Added**\
   Method: Post

**Body Payload**

```
{
    "type":"rf:webhook",
    "data":"{\"data\":{\"createdAt\":\"2024-07-15T09:38:30.711Z\",\"payeeId\":\"6bcb76d1-4aa9-4a81-9285-728ba42d1813\",\"payeeInternalId\":\"PAYEE101\"},\"event\":\"PayeeAdded\",\"timestamp\":\"2024-07-15T09:38:30.717Z\"}",
    "signature":"UQYgICyhmxxCd5qmKSsJKXrMsmcu66EYobvZISh72xLYeAvBClptlybO5cso+YolnB0oLovT9jA80ZgQ0082yfqVFROmeE2jNsJ3oC9M4XdzBqYrEb8gGGKXBXU2hxEblgjrgVrKqhDKFFDwt6GWMBVss4AWy0Ddb9GA/btu1tw1kYuwyWEk9Ycn2W4NTb+1EyYo+3Z8fCLIrEA9yHsLK91WF87fLUFzdmWw7/kMEfmYykF2ykTNSVNAp2bDsSr73Qu10TTfyifSu3lewLkfQTAUjlereHISMDE9/ZWF/krj0XreQ3y8kof4MnVbtz9myhSSIIxqp5yDBFPp61zBqw=="
}
```

2. **Payee KYC Started**\
   Method: POST

\
**Body Payload**

```
{
  "type": "rf:webhook",
  "data": "{\"data\":{\"payeeId\":\"6bcb76d1-4aa9-4a81-9285-728ba42d1813\",\"payeeInternalId\":\"PAYEE101\"},\"event\":\"PayeeKycStarted\",\"timestamp\":\"2024-07-15T09:40:09.970Z\"}",
  "signature": "0k+4kSI7KWMoE45dqPfUqJmhzUJaKM2SvdoSlDWvF077yC27LDCjpfEcItylWb34o7AyfNJ0cUgXNu/W7ydv5a5PNEf4jW1Ll2hjjhPN/qwYQh6J5r4Mr1CsHjRsFyA7VIRw7y6GfxS6a88Pi4N3D9E+YPz31xcdQ2X5OfzKPuEBFyEO9riWsnPzYIfynbufYSdZDFNPMzjgmrd57XqxHNxKEnW7AQ1Uo1fauCqTgW290DNovunbQlyF29s92Sf7ogHUmM6fvb5nZIY9oGVJ9Kp2JBxVaQ2FNW2WtCVWLYZa37+OqxgmWA3ORmFL1ORQvv75w/1wz4cL6qapPG38Sg=="
}
```

3. **Payee KYC Status Change**\
   Method: POST<br>

   Here’s a description for the two statuses related to KYC:

   1. **manual\_review - Pending Status**:\
      This status indicates that the payee’s KYC process is currently under manual review and pending approval. The verification is yet to be completed, and further checks or actions may be required before the KYC can be finalized.
   2. **completed - KYC is Done**:\
      This status signifies that the KYC process has been completed. The payee has passed all necessary verification checks, and their identity has been confirmed as part of the KYC process.

**Body Payload**

```
{
    "type":"rf:webhook",
    "data":"{\"data\":{\"payeeId\":\"77df710d-26b2-4583-9c56-b0e0d88d2497\",\"payeeInternalId\":\"PAYEE101\",\"status\":\"manual_review\"},\"event\":\"PayeeKycStatusChange\",\"timestamp\":\"2024-07-15T10:24:32.456Z\"}",
    "signature":"s1r80cyhzXva4xr7alLF9G6HBXW9+ZfVsX5QGFZ7xFHF+WBShZ8Gbejl4lNW2BUpDdFnMJdChLsBUud9imUmc1+Ttpz1JVWyHjFTu1zUEgl6Hy1/fkQFNbDaqTKnCKOCUZ8L6YjxSGka/l9zQBI5S6ZXQgshzkDoQQY/aPGfL1ZNSNrJInlFlPILSTJlftC4uTlNNsqryf7wMnCq2XsihaKwnoXeHjLeeIsnqQjvyN5noEQTluP/v/TPfSqIxk2pZmvaoX5Z+gIrOT6Y39SP7Q4FfAHO/oOxlFNR1tDH8wPQdrhVQ1pZ/USqHxILqGSIxyiAKsFLUgIMv2php36pOg=="
}
```

4. **Payee Fund Allocated**\
   Method: POST

**Body Payload**

```
{
  "type": "rf:webhook",
  "data": "{\"data\":{\"amount\":\"10\",\"currency\":\"USD\",\"payeeId\":\"ba2fb7c7-a94f-491a-9538-83a170557748\",\"payeeInternalId\":\"\"},\"event\":\"PayeeFundAllocated\",\"timestamp\":\"2024-07-16T12:44:59.063Z\"}",
  "signature": "PfcMW6BuaZtI1ZO4SgOE1IbL8GePr8RiFZV7HyC9z3hUtqp8JTzAsHX437b6PIkr66SIHAPRZXNCHrxipUrIX1ReN/sOQglueZeJiyanGEnKLXPAm8ozJfHb47DW5A2GwqN/dYpr61JCRlQ2zHLxlirAwZXLgJD6lI7feUPdwJL3xWpwwUai3/ym8RjpMX5RKINLcntrI+WB/gOzG72o9WiOIjX3XgqnhFlU0cgu+AJlepCxstPMYE3iyM3e87WRFW4mVzJdwSAulftR0gzuyzXvhwyQJt69kS6W9FO89VyDx6Weg3j4RYsbeoOagl8IcQeOS095LZ3Xixo+5i4U+g=="
}
```

5. **Payout Started**\
   Method: POST

**Body Payload**

```
{
  "type": "rf:webhook",
  "data": "{\"data\":{\"payeeId\":\"ba2fb7c7-a94f-491a-9538-83a170557748\",\"payeeInternalId\":\"\",\"payoutAmount\":0.00008697,\"payoutCurrency\":\"BTC\",\"payoutId\":\"e4c356dc-8fba-4713-9a00-7845d2c48c35\",\"type\":\"crypto\"},\"event\":\"PayoutStarted\",\"timestamp\":\"2024-07-16T12:46:30.061Z\"}",
  "signature": "h5EZnyA8v/24knUfnEka4QwgXeUQOb7XE21Duy5W8uV6o1g/7J2sB4gK31NAaXt3cz4TBgW0dA59LNvRogO+VUb6gzj/8jvlDXFtUj5214/cPEBPnuSddW8dy66zuBL3vYviT1qc1it0uNVmXzh2GCjfhfJ2ti3CHDornmiu3AfSROiPf40oAknt1nOpBGqvLafLzLRAcfIHa/6SxsApgdGCP9QW0A9O3WH4+uUNvehdKdGZ2t0Cv9LJGLTekc7Be4k85Tu/SsBbr9l6/laZMeZ/vsQFCzWdvbirHg/O78OjzHeLiHCdeqMrhkwVQKPE2xm1HwDqp8TSPDaX6CiNng=="
}
```

6. **Payout Status Change**\
   Method: POST

Here’s the description for the payout statuses:

1. **completed**:\
   The payout has been successfully processed, and the funds have been transferred to the payee’s account.
2. **failed**:\
   The payout process has encountered an issue, and the funds could not be transferred. Further action may be required to resolve the failure.

**Body Payload**

```
{
    "type":"rf:webhook",
    "data":"{\"data\":{\"payeeId\":\"6825a42b-d5e6-4a90-9d50-6c9edbab7b73\",\"payeeInternalId\":\"PAYEE102\",\"payoutId\":\"fb83ba30-ef92-4a5f-9bd9-4a061f5c5fb7\",\"payoutAmount\":0.01105763,\"payoutCurrency\":\"ETH\",\"type\":\"crypto\",\"additionalDetails\":{\"hash\":\"hash_string\"},\"status\":\"completed\"},\"event\":\"PayoutStatusChange\",\"timestamp\":\"2024-07-15T10:34:24.979Z\"}",
    "signature":"LnJDPysCt/eautLgth1OWJn/YDx00ZpiLDZjCn/PNlOP+gcA9XSL95o2ucKsCEvL3ozg04IUKQhjBivrwzY/2wUdj6NgoJRNn+bjSjteuJKwudTRa4tzhYRbo0wcRFTUqDi9ZeWxC+Hh+l75U5JWx/qHD++mwkC3WsMIYnJkQ2xn6sTas7CG33VfoZM6NfVpQwxQxyPEfuOD5Nd7XGfeImqSxD0BIp7lTQQyZTYcRt3bRFdin7NgkADg4llcCFCwksrEdVoeKMS2fb1q2v1d3op2ORXSvkgfMHe7hNhc0w8khbaqXHderSDb+vhMR9FyDJ+NfbvAMa6rSlLETFF/zA=="
}
```

**Authenticity/Verification of request from RKFL**

You can test the authenticity of the request emerging from Rocketfuel by using the following public key to generate the signature. Once the signature is generated, you can tally the signature sent by Rocketfuel.

#### Signature: <a href="#markdown-header-signature" id="markdown-header-signature"></a>

```json
f09HYBeZFqMkeo/ri5kZI0DGnCiSnYSl2KSZLaB3tIL722a1IsnCSsWsfdZRAiv/7e/MdqguXTBmEUdBzKnzR2ATBJF5VRtLeD7LhnNxpSs1+sAAgIwI2JS6nkRj8DTKZbZUzweGSdgARZfxxoVqQaaW4DPb8kXhGPVo/tOG8Rw62Vbyg279ysgWCtNuYltKg05DFxfWy287LtBnvs3kaw0xoTuR5rCnEncFFLRozSCPRSRU0Ebb3kfWNK6surso9OrqVkdbzXLCpLuLkkakxNvNpahzvB3DuT2zZn0NFxP8YGJquAVcWLh2aj0syRPDArHY5An5CtQ6nuiiJB6jTw==
```

#### Public RSA key <a href="#markdown-header-public-rsa-key" id="markdown-header-public-rsa-key"></a>

```
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2e4stIYooUrKHVQmwztC
/l0YktX6uz4bE1iDtA2qu4OaXx+IKkwBWa0hO2mzv6dAoawyzxa2jmN01vrpMkMj
rB+Dxmoq7tRvRTx1hXzZWaKuv37BAYosOIKjom8S8axM1j6zPkX1zpMLE8ys3dUX
FN5Dl/kBfeCTwGRV4PZjP4a+QwgFRzZVVfnpcRI/O6zhfkdlRah8MrAPWYSoGBpG
CPiAjUeHO/4JA5zZ6IdfZuy/DKxbcOlt9H+z14iJwB7eVUByoeCE+Bkw+QE4msKs
aIn4xl9GBoyfDZKajTzL50W/oeoE1UcuvVfaULZ9DWnHOy6idCFH1WbYDxYYIWLi
AQIDAQAB
-----END PUBLIC KEY-----
```
