Creating a webhook endpoint is no different than creating any page on your website. The endpoint should expect a POST request with JSON data in the body of the request. We recommend using HTTPS for your callback URLs. myPOS will validate the server certificate before sending the notification. myPOS will include the event name in an X-myPOS-Event header as well as a signature validation in an X-myPOS-Signature header. The signature will be generated with the secret provided when creating the webhook. You will need to validate the signature before processing the request to ensure it comes from myPOS.

 

 

Responding to a webhook

To acknowledge receipt of a webhook, the endpoint should return a 2xx HTTP status code. All responses outside this range, including 3xx codes, will indicate to myPOS that the webhook was not received. This means a URL redirection or a “Not Modified” response will be treated as a failure.

 

myPOS will try to deliver webhooks for up to 3 days. Webhooks cannot be manually retried, though information about them can be gathered using the /events endpoint.

 

 

Verifying webhook signature

In order to verify the signature of a webhook, it is required to have the secret used when generating the webhook itself. The signature header contains two parameters:

  1. t – unix timestamp in seconds of when the signature was generated
  2. v1 – signature schema version. Anything below this version should be considered as an invalid request.

myPOS generates the signature using a hash-based message authentication code, or HMAC, with SHA-256 digest algorithm.

 

Extracting timestamp and signature

Split the header value using the  ,  character for separator to get a list of elements. Now split each element, using the  =  character for separator to get a key-value pair.

 

Generate the expected signature

This is achieved by creating an HMAC SHA256 hash of the request body (JSON) using the webhook’s secret as the key.

Please make sure there are no unneeded spaces in the JSON string between the key and value pairs and between the different values.
This will work:
{"amount":100,"tid":"123456789","merchant_name":"TEST MERCHANT"...}
This won't:
{"amount": 100, "tid": "123456789", "merchant_name": "TEST MERCHANT"...}
 

Compare signatures

Compare the expected signature with the one received in the X-myPOS-Signature header. If the signatures match, compute the difference between the current timestamp and the timestamp in the same header, then decide if the difference is within your tolerance. myPOS suggests a tolerance of 5 minutes.

 

If a webhook is marked as failed and retried to be delivered, the timestamp will be different and therefore a new signature will be generated.

 

<?php

// myPOS Header - includes timestamp (t) & signature (v1)
$myPOSHeader = $_SERVER['HTTP_X_MYPOS_SIGNATURE']; 
// the request body (JSON)
$bodyJSON = file_get_contents('php://input'); 

$timestamp = "";
$myposSignature = "";
$verificationStatus = "";

// extract data from the header
$headerParts = $myPOSHeader;
$headerParts = str_replace('t=', "", $headerParts);
$headerParts = str_replace('v1=', "", $headerParts);
$headerParts = explode(",", $headerParts);

// extract the timestamp from the header
$timestamp = $headerParts[0]; 
// extract the signature from the header
$myposSignature = $headerParts[1]; 

// make sure there aren't any empty spaces in the JSON between key/value pairs
$hashParts = $bodyJSON;
$hashParts = str_replace(": ", ":", $hashParts);
$hashParts = str_replace(", ", ",", $hashParts);

// create your signature to match it against the one received in the header
$yourHash = hash_hmac('sha256', $hashParts, '{YOUR_WEBHOOK_SECRET}'); 

// check if the two signatures match
if($yourHash==$myposSignature){
    // VERIFICATION SUCCESS
    $verificationStatus = "SUCCESS";
}else{
    // VERIFICATION FAILED
    $verificationStatus = "FAILED";
}
import json
import hmac
import hashlib

from flask import request

def validate_signature():
    req_data = request.get_json()
    signature_header = request.headers['X-myPOS-Signature']
    expected_signature = signature_header.split(',')[1]
    expected_signature = hmac.new(
        key="myrandomgeneratedsecret".encode('utf-8'),
        msg=json.dumps(req_data).encode('utf-8'),
        digestmod=hashlib.sha256
    ).hexdigest()

    if req_signature != expected_signature:
        print("Signatures don't match")
    else:
        print("Signatures match)
const crypto = require("crypto");

validateSignature: () => {
  const signatureHeader = req.headers["x-mypos-signature"];
  const requestSignature = signatureHeader.split(',')[1];
  const expectedSignature = crypto.createHmac("sha256", "myrandomgeneratedsecret").update(JSON.stringify(req.body)).digest("hex");

  if (requestSignature !== expectedSignature) {
    console.log("Signatures don't match");
  }
  else {
    console.log("Signatures match");
  }
};