Webhook Format

JSON

This is the default format. We will send the payload exactly as it appears in the other pages of this documentation, which is a JSON object with a Content-Type of application/json

Schema

JWT (JSON Web Token)

This format is based on JSON Web Tokens and the Security Event Token (SET) standard. It provides a mechanism by which messages can be verified to ensure that only messages from Moneyhub can be accepted by your system. These messages will be encoded like a JWT and delivered with a Content-Type of application/jwt

Given the Payment Completed webhook, the JWT encoded version looks something like the following:

eyJraWQiOiJEOUpBTm1ZZlNHX2tidDJLUnBUS283UUNOSDJfUkstMGE3ODd5am0wN3p3IiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2lkZW50aXR5LWRldi5tb25leWh1Yi5jby51ayIsImF1ZCI6ImQ5ZGM5ZDVlLWQxOTAtNDM4Yy1iNjU4LWE0YmUzNzc5MTAwZCIsImp0aSI6ImM5ZDEzM2UwLWU3MDgtNDAyZC1hNmI3LWRjOGZmOWMyOWExYyIsInN1YiI6IjVmNzVhOWY5NDdmOWJkNzRkOTIxYTZjOSIsImV2ZW50cyI6eyJ1cm46Y29tOm1vbmV5aHViOmV2ZW50czpwYXltZW50LWNvbXBsZXRlZCI6eyJwYXltZW50SWQiOiJkYzk0NDZlZS1hZmMxLTRiNDctYmFiNS0yYjAxYzdkYTA5ZTMiLCJzdGF0dXMiOiJjb21wbGV0ZWQiLCJwYXltZW50U3VibWlzc2lvbklkIjoiMmJlOGNiZTUtZTEzOC00M2M1LWJjNTgtNzYwY2VmYTg5MjQ4Iiwic3VibWl0dGVkQXQiOiIyMDIwLTEwLTAxVDEwOjA1OjQ1LjM3NFoifX0sImlhdCI6MTYwMTU0Njc4MX0.Fld7AM3ywFUXaZYA8uFoGyiWalRhvM8V24a_n0pahYs8o2YGVk-5DCBVNwGGJZT0LMchXiVAo5QeRvauqCv6BsApyycQiGTpAbX0l9V97FANGotB9KRuwstXqZ9tNo9a70-oNJGu8CDsBO2i-mmdNGsCW5QQNGH3z_TtwBq8fX5zYhulc7HMvZr93kfO-zMZUHdeUmUBnqeig0IdCIM-67H1WzAYZ_s86ZMuZLEj5m_VyNas5rbEox4B8GW6aDzQO0YsBbp5jXzfkR0NVNUSip5Q1kTDtK24YbUz9Pt_pmsGof6a_3pSOw_oEAWnHH-C3a4zKQvUBklb9YeGVPv4OQ

Once decoded the payload would look like this:

The header will contain the "kid" which means the key used.

{  
  "kid": "D9JANmYfSG_kbt2KRpTKo7QCNH2_RK-0a787yjm07zw",  
  "alg": "RS256"  
}
{
  "iss": "https://identity.moneyhub.co.uk",
  "aud": "d9dc9d5e-d190-438c-b658-a4be3779100d",
  "jti": "c9d133e0-e708-402d-a6b7-dc8ff9c29a1c",
  "sub": "5f75a9f947f9bd74d921a6c9",
  "events": {
    "urn:com:moneyhub:events:payment-completed": {
      "paymentId": "dc9446ee-afc1-4b47-bab5-2b01c7da09e3",
      "status": "completed",
      "paymentSubmissionId": "2be8cbe5-e138-43c5-bc58-760cefa89248",
      "submittedAt": "2020-10-01T10:05:45.374Z"
    }
  },
  "iat": 1601546781
}

You'll need to use a JWT library to decode and verify the messages

On the provided config on https://identity.moneyhub.co.uk/oidc/.well-known/openid-configuration you can see the public keys on "jwks_uri": "https://identity.moneyhub.co.uk/oidc/certs" you will find something like:

{  
  "keys": [
   	 {
        "kty": "RSA",
        "use": "sig",
        "kid": "D9JANmYfSG_kbt2KRpTKo7QCNH2_RK-0a787yjm07zw",
        "e": "AQAB",
        "n": "g3oKu2O5TgkMoN_lhU8Y5-2sai9y_XPGaoZ3cf1pxdbTFdbsaBjHWjg9tBPsD8xXEEKGMNGIQBLIUVopnrtVFsWe8AI04k5PfUMWIUhto6LHOj64p2ZMRsL6N1CIYXUehmIG7esTHUcgIGmClsto6cno211ZPQjZkkqeOUzmfe0LE7oiGygXbAWtt0jNHlAw-He5tabcup1ezHZ6eAG7hklhIh4-KXICWvpWKNaqxlfX_weEWmHft18mbz1ZFGQfN0gb0VggtzKqe7QBi8SAAIPbiMre_8vEhQdfw0X_EnJnlFu-FBR984vT3eblYGU0sXkQUoD8pAUhNl3UANu0ww"
      },
      {
        "kty": "EC",
        "use": "enc",
        "kid": "T7rxogsi9VUcI_SmoePSJvzif4qIUarPiFAyqTTUuYc",
        "crv": "P-256",
        "x": "GERoSJmHdL5VTyKTv9-XuRiNL4Y4TZnQWeTPewtW8n4",
        "y": "eU5FlDQipbdr96bCSsnH9tTHckOw7gpOZ6-2uavcwnI"
    }
  ]
}

Where:

"kty": This stands for Key Type. It defines the cryptographic algorithm family for the key. In this case, 'RSA' means that this key belongs to the RSA algorithm family.

"use": This describes how the key is meant to be used. 'sig' denotes that the key is used for signing.

"kid": This is the Key ID. It is used to match a specific key. This is just a random string that uniquely identifies the key.

"e": This is the public exponent for a standard RSA public key. It's usually 'AQAB', which is Base64url encoding for the hexadecimal number 0x10001, an RSA public exponent.

"n": This is the modulus for a standard RSA public key. It's a large Base64url-encoded number derived as the product of two prime numbers. It is used together with 'e' to form the public key, and choose and validate the private key in the RSA algorithm.

While you can decode the messages without verifying them, it's better to do both simultaneously. To verify the message, you'll need to retrieve our public signing key

Note: The key is subject to change over time without notice, so don't keep it as a static resource. For your security, the key returned does not display the algorithm. However, once validated, it possesses the capability to utilize it, ensuring a safe and secure experience.

Here is an example written in Node where we retrieve the public signing key to verify the webhook messages:

Packages used:

"got": "^10.5.0",
"node-jose": "^2.2.0",
const got = require("got")
const {JWK} = require("node-jose")

const getPublicMoneyhubKeyAsPEM = async () => {
    try {
        const {jwks_uri} = await got("https://identity.moneyhub.co.uk/oidc/.well-known/openid-configuration").json()
        const jwks = await got(jwks_uri).json()
        let signingKey = await JWK.asKeyStore(jwks)
        signingKey = signingKey.get({use: "sig"})
        console.log(signingKey.toPEM())
    } catch (e) {
        console.error(e)
    }
}

getPublicMoneyhubKeyAsPEM()

This could then be extended to verify and decode the message like so:

const got = require("got")
const {JWS, JWK} = require("node-jose")

const getPublicMoneyhubKeyAsPEM = async (message) => {
    try {
        const {jwks_uri} = await got("https://identity.moneyhub.co.uk/oidc/.well-known/openid-configuration").json()
        const jwks = await got(jwks_uri).json()
        const signingKey = await JWK.asKeyStore(jwks)
        const payload = await JWS.createVerify(signingKey).verify(message)
        console.log(payload)
      
    } catch (e) {
        console.error(e)
    }
}

getPublicMoneyhubKeyAsPEM(message)