Webhooks
Code samples to generate and verify signature hash for the data received via Payment Status webhooks sent from PortOne servers.
- Golang
- PHP
- NodeJS
- C#
- Java
- Python
package main
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
    "net/url"
    "strconv"
)
type WebhookResponse struct {
    Amount           float64
    ChannelKey       string
    ChannelOrderRef  string
    CountryCode      string
    Currency         string
    MerchantOrderRef string
    MethodName       string
    OrderRef         string
    SignatureHash    string
    Status           string
}
func VerifySignature(webhookResponse WebhookResponse, secretKey string) bool {
    // Create a url.Values map and add the necessary parameters
    params := make(url.Values)
    params.Add("amount", strconv.FormatFloat(webhookResponse.Amount, 'f', -1, 64))
    params.Add("channel_key", webhookResponse.ChannelKey)
    params.Add("channel_order_ref", webhookResponse.ChannelOrderRef)
    params.Add("country_code", webhookResponse.CountryCode)
    params.Add("currency", webhookResponse.Currency)
    params.Add("merchant_order_ref", webhookResponse.MerchantOrderRef)
    params.Add("method_name", webhookResponse.MethodName)
    params.Add("order_ref", webhookResponse.OrderRef)
    params.Add("status", webhookResponse.Status)
    // Encode the parameters
    data := params.Encode()
    // Create the HMAC hash using SHA-256
    secret := []byte(secretKey)
    message := []byte(data)
    hash := hmac.New(sha256.New, secret)
    hash.Write(message)
    // Convert the hash to a base64 string
    hashValue := base64.StdEncoding.EncodeToString(hash.Sum(nil))
    // Compare the computed hash with the signature received in the payment response
    if hashValue != webhookResponse.SignatureHash {
        fmt.Println("Hash verification failed, not from valid source")
        return false
    } else {
        fmt.Println("Hash verification succeeded")
        return true
    }
}
func main() {
    portOneSecret := "PORTONE_SECRET"
    // Define the webhook response struct below with the respective params received in webhook
    var amount float64
    amount = 100.25
    webhookResponse := WebhookResponse{
        Amount:           amount,
        ChannelKey:       "channel_key",
        ChannelOrderRef:  "channel_order_ref",
        CountryCode:      "country_code",
        Currency:         "currency",
        MerchantOrderRef: "merchant_order_ref",
        MethodName:       "method_name",
        OrderRef:         "order_ref",
        SignatureHash:    "signature_hash",
        Status:           "status",
    }
    // Verify the signature
    isValid := VerifySignature(webhookResponse, portOneSecret)
    // Output the result of the verification
    if isValid {
        fmt.Println("Webhook response is valid.")
    } else {
        fmt.Println("Webhook response is invalid.")
    }
}
<?php
function VerifySignature($webhookResponseObj, $secretKey) {
    // Extract the received signature hash from the webhookResponseObj object
    $signature_hash = $webhookResponseObj->signature_hash;
    // Create an array with the necessary parameters
    $data = array(
        'amount' => $webhookResponseObj->amount,
        'channel_key' => $webhookResponseObj->channel_key,
        'channel_order_ref' => $webhookResponseObj->channel_order_ref,
        'country_code' => $webhookResponseObj->country_code,
        'currency' => $webhookResponseObj->currency,
        'merchant_order_ref' => $webhookResponseObj->merchant_order_ref,
        'method_name' => $webhookResponseObj->method_name,
        'order_ref' => $webhookResponseObj->order_ref,
        'status' => $webhookResponseObj->status,
    );
    // Sort the array by keys
    ksort($data);
    // Build the query string
    $message = http_build_query($data);
    // Generate the HMAC hash using SHA-256 and encode it in base64
    $hash_value = base64_encode(hash_hmac('sha256', $message, $secretKey, true));
    // Compare the generated hash with the received signature hash
    if ($hash_value !== $signature_hash) {
        echo "Hash verification failed, not from valid source\n";
        return false;
    } else {
        echo "Hash verification succeeded\n";
        return true;
    }
}
// Define the main function
function main() {
    $secret_key = "PORTONE_SECRET";
    // Define the webhook response struct below with the respective params received in webhook
    $webhookResponseObj = (object) [
        'amount' => 100.25,
        'channel_key' => 'channel_key',
        'channel_order_ref' => 'channel_order_ref',
        'country_code' => 'country_code',
        'currency' => 'currency',
        'merchant_order_ref' => 'merchant_order_ref',
        'method_name' => 'method_name',
        'order_ref' => 'order_ref',
        'status' => 'status',
        'signature_hash' => 'signature_hash',
    ];;
    // Verify the signature
    $isValid = VerifySignature($webhookResponseObj, $secret_key);
    // Print the result of the verification
    if ($isValid) {
        echo "Webhook response is valid.\n";
    } else {
        echo "Webhook response is invalid.\n";
    }
}
// Call the main function
main();
?>
const crypto = require('crypto');
const { URLSearchParams } = require('url');
function VerifySignature(webhookResponseObj, secretKey) {
    const params = new URLSearchParams();
    params.append('currency', webhookResponseObj.currency)
    params.append('amount', webhookResponseObj.amount)
    params.append('order_ref', webhookResponseObj.order_ref)
    params.append('merchant_order_ref', webhookResponseObj.merchant_order_ref)
    params.append('channel_order_ref', webhookResponseObj.channel_order_ref)
    params.append('country_code', webhookResponseObj.country_code)
    params.append('status', webhookResponseObj.status)
    params.append('channel_key', webhookResponseObj.channel_key)
    params.append('method_name', webhookResponseObj.method_name)
    params.sort();
    const message = params.toString()
    const hash_value =  crypto.createHmac('sha256', secretKey).update(message).digest('base64');
    if(hash_value !==  webhookResponseObj.signature_hash){
      console.log("Hash verification failed, not from valid source")
      return false;
    } else{
      console.log("Hash verification succeded")
      return true;
    }
}
// Main function to demonstrate calling VerifySignature
function main() {
    const secretKey = 'PORTONE_SECRET';
    // Example webhookResponseObj object
    const webhookResponseObj = {
        amount: 100.25,
        channel_key: 'channel_key',
        channel_order_ref: 'channel_order_ref',
        country_code: 'country_code',
        currency: 'currency',
        merchant_order_ref: 'merchant_order_ref',
        method_name: 'method_name',
        order_ref: 'order_ref',
        status: 'status',
        signature_hash: 'signature_hash'
    };
    // Verify the signature
    const isValid = VerifySignature(webhookResponseObj, secretKey);
    // Print the result of the verification
    if (isValid) {
        console.log("Webhook response is valid.");
    } else {
        console.log("Webhook response is invalid.");
    }
}
// Call the main function
main();
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApp
{
    class WebhookResponse
    {
        public double amount;
        public string currency;
        public string order_ref;
        public string merchant_order_ref;
        public string country_code;
        public string channel_order_ref;
        public string status;
        public string channel_key;
        public string method_name;
        public string signature_hash;
    }
    class ApiSecurityExample
    {
        public static bool VerifySignature(WebhookResponse webhookResponse, string secret)
        {
            var map = new SortedDictionary<string, string>()
            {
                { "amount", RemoveTrailingZeros(webhookResponse.amount) },
                { "channel_key", webhookResponse.channel_key },
                { "channel_order_ref", webhookResponse.channel_order_ref },
                { "country_code", webhookResponse.country_code },
                { "currency", webhookResponse.currency },
                { "merchant_order_ref", webhookResponse.merchant_order_ref },
                { "method_name", webhookResponse.method_name },
                { "order_ref", webhookResponse.order_ref },
                { "status", webhookResponse.status },
            };
            var stringBuilder = new StringBuilder();
            foreach (var key in map.Keys)
            {
                if (stringBuilder.Length > 0)
                {
                    stringBuilder.Append("&");
                }
                var value = map[key];
                try
                {
                    stringBuilder.Append((key != null ? Uri.EscapeDataString(key) : ""));
                    stringBuilder.Append("=");
                    stringBuilder.Append(value != null ? Uri.EscapeDataString(value) : "");
                }
                catch (ArgumentNullException e)
                {
                    throw new Exception("The key or value is null.", e);
                }
                catch (UriFormatException e)
                {
                    throw new Exception("Invalid format for key or value.", e);
                }
            }
            var message = stringBuilder.ToString();
            // Console.WriteLine("message: " + message);
            var encoding = new ASCIIEncoding();
            byte[] keyByte = encoding.GetBytes(secret);
            byte[] messageBytes = encoding.GetBytes(message);
            var hmacsha256 = new HMACSHA256(keyByte);
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            string hash_value = Convert.ToBase64String(hashmessage);
            if (hash_value != webhookResponse.signature_hash) {
                Console.WriteLine("Hash verification failed, not from valid source");
                return false;
              } else {
                Console.WriteLine("Hash verification succeded");
                return true;
              }
        }
        private static string RemoveTrailingZeros(double amount)
        {
            return amount.ToString("0.###################");
        }
    }
class Program
    {
      static void Main(string[] args)
        {
            string secret = "PORTONE_SECRET";
            WebhookResponse webhookResponse = new WebhookResponse()
            {
                amount = 100.25,
                channel_key = "channel_key",
                channel_order_ref = "channel_order_ref",
                country_code = "country_code",
                currency = "currency",
                merchant_order_ref = "merchant_order_ref",
                method_name = "method_name",
                order_ref = "order_ref",
                status = "status",
                signature_hash = "signature_hash"
            };
            bool isValid = ApiSecurityExample.VerifySignature(webhookResponse, secret);
            // Print the result
            if (isValid) {
                Console.WriteLine("Webhook response is valid.");
            } else {
                Console.WriteLine("Webhook response is invalid.");
            }
        }
    }
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
public class Main {
    public static void main(String[] args) {
        String secret = "PORTONE_SECRET";
        // Create WebhookResponse object using setter methods
        WebhookResponse webhookResponse = new WebhookResponse();
        webhookResponse.setAmount(100.25);
        webhookResponse.setChannelKey("channel_key");
        webhookResponse.setChannelOrderRef("channel_order_ref");
        webhookResponse.setCountryCode("country_code");
        webhookResponse.setCurrency("currency");
        webhookResponse.setMerchantOrderRef("merchant_order_ref");
        webhookResponse.setMethodName("method_name");
        webhookResponse.setOrderRef("order_ref");
        webhookResponse.setSignature("signature");
        webhookResponse.setStatus("status");
        boolean isValid = verifySignature(webhookResponse, secret);
        // Print the result
        if (isValid) {
            System.out.println("Webhook response is valid.");
        } else {
            System.out.println("Webhook response is invalid.");
        }
    }
    public static boolean verifySignature(WebhookResponse webhookResponse, String secretKey) {
        try {
            Map<String, String> params = new TreeMap<>();
            params.put("amount", webhookResponse.getAmount());
            params.put("channel_key", webhookResponse.getChannelKey());
            params.put("channel_order_ref", webhookResponse.getChannelOrderRef());
            params.put("country_code", webhookResponse.getCountryCode());
            params.put("currency", webhookResponse.getCurrency());
            params.put("merchant_order_ref", webhookResponse.getMerchantOrderRef());
            params.put("method_name", webhookResponse.getMethodName());
            params.put("order_ref", webhookResponse.getOrderRef());
            params.put("status", webhookResponse.getStatus());
            // Encode the parameters
            String data = encodeParams(params);
            byte[] secret = secretKey.getBytes();
            byte[] message = data.getBytes();
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(secret, "HmacSHA256");
            sha256_HMAC.init(secretKeySpec);
            byte[] hash = sha256_HMAC.doFinal(message);
            String computedSignature = Base64.getEncoder().encodeToString(hash);
            if (!computedSignature.equals(webhookResponse.getSignature())) {
                System.out.println("Hash verification failed, not from a valid source");
                return false;
            } else {
                System.out.println("Hash verification succeeded");
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    public static String encodeParams(Map<String, String> params) {
        StringBuilder encodedParams = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (encodedParams.length() > 0) {
                encodedParams.append("&");
            }
            encodedParams.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
                         .append("=")
                         .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        return encodedParams.toString();
    }
}
class WebhookResponse {
    private Double amount;
    private String channelKey;
    private String channelOrderRef;
    private String countryCode;
    private String currency;
    private String merchantOrderRef;
    private String methodName;
    private String orderRef;
    private String signature;
    private String status;
    // Default constructor
    public WebhookResponse() {
    }
    // Setter methods
    public void setOrderRef(String orderRef) {
        this.orderRef = orderRef;
    }
    public void setChannelOrderRef(String channelOrderRef) {
        this.channelOrderRef = channelOrderRef;
    }
    public void setMerchantOrderRef(String merchantOrderRef) {
        this.merchantOrderRef = merchantOrderRef;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public void setSignature(String signature) {
        this.signature = signature;
    }
    public void setAmount(Double amount) {
        this.amount = amount;
    }
    public void setChannelKey(String channelKey) {
        this.channelKey = channelKey;
    }
    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }
    public void setCurrency(String currency) {
        this.currency = currency;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    // Getter methods
    public String getCurrency() {
        return currency;
    }
    public String getAmount() {
        DecimalFormat df = new DecimalFormat("0.##");
        return df.format(amount);
    }
    public String getOrderRef() {
        return orderRef;
    }
    public String getMerchantOrderRef() {
        return merchantOrderRef;
    }
    public String getCountryCode() {
        return countryCode;
    }
    public String getChannelOrderRef() {
        return channelOrderRef;
    }
    public String getStatus() {
        return status;
    }
    public String getChannelKey() {
        return channelKey;
    }
    public String getMethodName() {
        return methodName;
    }
    public String getSignature() {
        return signature;
    }
}
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib.parse
import hashlib
import hmac
import base64
class WebhookResponseObj:
    def __init__(self, currency, amount, order_ref, merchant_order_ref, channel_order_ref, country_code, status, channel_key, method_name, signature_hash):
        # Instance Variables
        self.amount = amount
        self.channel_key = channel_key
        self.channel_order_ref = channel_order_ref
        self.country_code = country_code
        self.currency = currency
        self.merchant_order_ref = merchant_order_ref
        self.method_name = method_name
        self.order_ref = order_ref
        self.signature_hash = signature_hash
        self.status = status
def VerifySignature(webhookResponseObj, secretKey):
    f = {
        'amount': f"{webhookResponseObj.amount:.2f}".rstrip('0').rstrip('.'),
        'channel_key': webhookResponseObj.channel_key,
        'channel_order_ref': webhookResponseObj.channel_order_ref,
        'country_code': webhookResponseObj.country_code,
        'currency': webhookResponseObj.currency,
        'merchant_order_ref': webhookResponseObj.merchant_order_ref,
        'method_name': webhookResponseObj.method_name,
        'order_ref': webhookResponseObj.order_ref,
        'status': webhookResponseObj.status,
    }
    f = dict(sorted(f.items()))
    message1 = urllib.parse.urlencode(f)
    message = message1.encode('utf-8')
    secret = secretKey.encode('utf-8')
    signature = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest()).decode('utf-8')
    if signature != webhookResponseObj.signature_hash:
        print("Hash verification failed, not from valid source\n")
        return False
    else:
        print("Hash verification succeeded\n")
        return True
# Define constants
secret = 'PORTONE_SECRET'
# Create an instance of WebhookResponseObj
webhookResponseObj = WebhookResponseObj(
    amount=100.25,
    channel_key='channel_key',
    channel_order_ref='channel_order_ref',
    country_code='country_code',
    currency='currency',
    merchant_order_ref='merchant_order_ref',
    method_name='method_name',
    order_ref='order_ref',
    signature_hash='signature_hash',
    status='status'
)
# Call VerifySignature
isValid = VerifySignature(webhookResponseObj, secret)
if isValid:
    print("Webhook response is valid.\n")
else:
    print("Webhook response is invalid.\n")
Code samples to generate and verify signature hash for the data received via Payment Link Status webhooks sent from PortOne servers.
- Golang
- PHP
- NodeJS
- C#
- Java
- Python
package main
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
    "net/url"
    "strconv"
)
type PLWebhookResponse struct {
    Amount           float64
    CountryCode      string
    Currency         string
    LinkRef          string
    MerchantOrderRef string
    SignatureHash    string
    Status           string
}
func VerifySignature(plWebhookResponse PLWebhookResponse, secretKey string) bool {
    // Create a url.Values map and add the necessary parameters
    params := make(url.Values)
    params.Add("amount", strconv.FormatFloat(plWebhookResponse.Amount, 'f', -1, 64))
    params.Add("country_code", plWebhookResponse.CountryCode)
    params.Add("currency", plWebhookResponse.Currency)
    params.Add("link_ref", plWebhookResponse.LinkRef)
    params.Add("merchant_order_ref", plWebhookResponse.MerchantOrderRef)
    params.Add("status", plWebhookResponse.Status)
    // Encode the parameters
    data := params.Encode()
    // Create the HMAC hash using SHA-256
    secret := []byte(secretKey)
    message := []byte(data)
    hash := hmac.New(sha256.New, secret)
    hash.Write(message)
    // Convert the hash to a base64 string
    hashValue := base64.StdEncoding.EncodeToString(hash.Sum(nil))
    // Compare the computed hash with the signature received in the payment response
    if hashValue != plWebhookResponse.SignatureHash {
        fmt.Println("Hash verification failed, not from valid source")
        return false
    } else {
        fmt.Println("Hash verification succeeded")
        return true
    }
}
func main() {
    portOneSecret := "PORTONE_SECRET"
    // Define the webhook response struct below with the respective params received in webhook
    var amount float64
    amount = 100.25
    plWebhookResponse := PLWebhookResponse{
        Amount:           amount,
        CountryCode:      "country_code",
        Currency:         "currency",
        MerchantOrderRef: "merchant_order_ref",
        LinkRef:          "link_ref",
        SignatureHash:    "signature_hash",
        Status:           "status",
    }
    // Verify the signature
    isValid := VerifySignature(plWebhookResponse, portOneSecret)
    // Output the result of the verification
    if isValid {
        fmt.Println("Payment Link Webhook response is valid.")
    } else {
        fmt.Println("Payment Link Webhook response is invalid.")
    }
}
<?php
function VerifySignature($plWebhookResponseObj, $secretKey) {
    // Extract the received signature hash from the plWebhookResponseObj object
    $signature_hash = $plWebhookResponseObj->signature_hash;
    // Create an array with the necessary parameters
    $data = array(
        'amount' => $plWebhookResponseObj->amount,
        'link_ref' => $plWebhookResponseObj->link_ref,
        'country_code' => $plWebhookResponseObj->country_code,
        'currency' => $plWebhookResponseObj->currency,
        'merchant_order_ref' => $plWebhookResponseObj->merchant_order_ref,
        'status' => $plWebhookResponseObj->status,
    );
    // Sort the array by keys
    ksort($data);
    // Build the query string
    $message = http_build_query($data);
    // Generate the HMAC hash using SHA-256 and encode it in base64
    $hash_value = base64_encode(hash_hmac('sha256', $message, $secretKey, true));
    // Compare the generated hash with the received signature hash
    if ($hash_value !== $signature_hash) {
        echo "Hash verification failed, not from valid source\n";
        return false;
    } else {
        echo "Hash verification succeeded\n";
        return true;
    }
}
// Define the main function
function main() {
    $secret_key = "PORTONE_SECRET";
    // Define the webhook response struct below with the respective params received in webhook
    $plWebhookResponseObj = (object) [
        'amount' => 100.25,
        'link_ref' => 'link_ref',
        'country_code' => 'country_code',
        'currency' => 'currency',
        'merchant_order_ref' => 'merchant_order_ref',
        'status' => 'status',
        'signature_hash' => 'signature_hash',
    ];;
    // Verify the signature
    $isValid = VerifySignature($plWebhookResponseObj, $secret_key);
    // Print the result of the verification
    if ($isValid) {
        echo "Payment Link Webhook response is valid.\n";
    } else {
        echo "Payment Link Webhook response is invalid.\n";
    }
}
// Call the main function
main();
?>
const crypto = require('crypto');
const { URLSearchParams } = require('url');
function VerifySignature(plWebhookResponseObj, secretKey) {
    const params = new URLSearchParams();
    params.append('currency', plWebhookResponseObj.currency)
    params.append('amount', plWebhookResponseObj.amount)
    params.append('link_ref', plWebhookResponseObj.link_ref)
    params.append('merchant_order_ref', plWebhookResponseObj.merchant_order_ref)
    params.append('country_code', plWebhookResponseObj.country_code)
    params.append('status', plWebhookResponseObj.status)
    params.sort();
    const message = params.toString()
    const hash_value =  crypto.createHmac('sha256', secretKey).update(message).digest('base64');
    if(hash_value !==  plWebhookResponseObj.signature_hash){
      console.log("Hash verification failed, not from valid source")
      return false;
    } else{
      console.log("Hash verification succeded")
      return true;
    }
}
// Main function to demonstrate calling VerifySignature
function main() {
    const secretKey = 'PORTONE_SECRET';
    // Example plWebhookResponseObj object
    const plWebhookResponseObj = {
        amount: 100.25,
        link_ref: 'link_ref',
        country_code: 'country_code',
        currency: 'currency',
        merchant_order_ref: 'merchant_order_ref',
        status: 'status',
        signature_hash: 'signature_hash'
    };
    // Verify the signature
    const isValid = VerifySignature(plWebhookResponseObj, secretKey);
    // Print the result of the verification
    if (isValid) {
        console.log("Payment Link Webhook response is valid.");
    } else {
        console.log("Payment Link Webhook response is invalid.");
    }
}
// Call the main function
main();
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApp
{
    class PLWebhookResponse
    {
        public double amount;
        public string currency;
        public string link_ref;
        public string merchant_order_ref;
        public string country_code;
        public string status;
        public string signature_hash;
    }
    class ApiSecurityExample
    {
        public static bool VerifySignature(PLWebhookResponse plWebhookResponse, string secret)
        {
            var map = new SortedDictionary<string, string>()
            {
                { "amount", RemoveTrailingZeros(plWebhookResponse.amount) },
                { "link_ref", plWebhookResponse.link_ref },
                { "country_code", plWebhookResponse.country_code },
                { "currency", plWebhookResponse.currency },
                { "merchant_order_ref", plWebhookResponse.merchant_order_ref },
                { "status", plWebhookResponse.status },
            };
            var stringBuilder = new StringBuilder();
            foreach (var key in map.Keys)
            {
                if (stringBuilder.Length > 0)
                {
                    stringBuilder.Append("&");
                }
                var value = map[key];
                try
                {
                    stringBuilder.Append((key != null ? Uri.EscapeDataString(key) : ""));
                    stringBuilder.Append("=");
                    stringBuilder.Append(value != null ? Uri.EscapeDataString(value) : "");
                }
                catch (ArgumentNullException e)
                {
                    throw new Exception("The key or value is null.", e);
                }
                catch (UriFormatException e)
                {
                    throw new Exception("Invalid format for key or value.", e);
                }
            }
            var message = stringBuilder.ToString();
            // Console.WriteLine("message: " + message);
            var encoding = new ASCIIEncoding();
            byte[] keyByte = encoding.GetBytes(secret);
            byte[] messageBytes = encoding.GetBytes(message);
            var hmacsha256 = new HMACSHA256(keyByte);
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            string hash_value = Convert.ToBase64String(hashmessage);
            if (hash_value != plWebhookResponse.signature_hash) {
                Console.WriteLine("Hash verification failed, not from valid source");
                return false;
              } else {
                Console.WriteLine("Hash verification succeded");
                return true;
              }
        }
        private static string RemoveTrailingZeros(double amount)
        {
            return amount.ToString("0.###################");
        }
    }
class Program
    {
      static void Main(string[] args)
        {
            string secret = "PORTONE_SECRET";
            PLWebhookResponse plWebhookResponse = new PLWebhookResponse()
            {
                amount = 100.25,
                link_ref = "link_ref",
                country_code = "country_code",
                currency = "currency",
                merchant_order_ref = "merchant_order_ref",
                status = "status",
                signature_hash = "signature_hash"
            };
            bool isValid = ApiSecurityExample.VerifySignature(plWebhookResponse, secret);
            // Print the result
            if (isValid) {
                Console.WriteLine("Payment Link Webhook response is valid.");
            } else {
                Console.WriteLine("Payment Link Webhook response is invalid.");
            }
        }
    }
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
public class Main {
    public static void main(String[] args) {
        String secret = "PORTONE_SECRET";
        // Create PLWebhookResponse object using setter methods
        PLWebhookResponse plWebhookResponse = new PLWebhookResponse();
        plWebhookResponse.setAmount(100.25);
        plWebhookResponse.setLinkRef("link_ref");
        plWebhookResponse.setCountryCode("country_code");
        plWebhookResponse.setCurrency("currency");
        plWebhookResponse.setMerchantOrderRef("merchant_order_ref");
        plWebhookResponse.setSignature("signature");
        plWebhookResponse.setStatus("status");
        boolean isValid = verifySignature(plWebhookResponse, secret);
        // Print the result
        if (isValid) {
            System.out.println("Payment Link Webhook response is valid.");
        } else {
            System.out.println("Payment Link Webhook response is invalid.");
        }
    }
    public static boolean verifySignature(PLWebhookResponse plWebhookResponse, String secretKey) {
        try {
            Map<String, String> params = new TreeMap<>();
            params.put("amount", plWebhookResponse.getAmount());
            params.put("link_ref", plWebhookResponse.getLinkRef());
            params.put("country_code", plWebhookResponse.getCountryCode());
            params.put("currency", plWebhookResponse.getCurrency());
            params.put("merchant_order_ref", plWebhookResponse.getMerchantOrderRef());
            params.put("status", plWebhookResponse.getStatus());
            // Encode the parameters
            String data = encodeParams(params);
            byte[] secret = secretKey.getBytes();
            byte[] message = data.getBytes();
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(secret, "HmacSHA256");
            sha256_HMAC.init(secretKeySpec);
            byte[] hash = sha256_HMAC.doFinal(message);
            String computedSignature = Base64.getEncoder().encodeToString(hash);
            if (!computedSignature.equals(plWebhookResponse.getSignature())) {
                System.out.println("Hash verification failed, not from a valid source");
                return false;
            } else {
                System.out.println("Hash verification succeeded");
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    public static String encodeParams(Map<String, String> params) {
        StringBuilder encodedParams = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (encodedParams.length() > 0) {
                encodedParams.append("&");
            }
            encodedParams.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
                         .append("=")
                         .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        return encodedParams.toString();
    }
}
class PLWebhookResponse {
    private Double amount;
    private String linkRef;
    private String countryCode;
    private String currency;
    private String merchantOrderRef;
    private String signature;
    private String status;
    // Default constructor
    public PLWebhookResponse() {
    }
    // Setter methods
    public void setLinkRef(String linkRef) {
        this.linkRef = linkRef;
    }
    public void setMerchantOrderRef(String merchantOrderRef) {
        this.merchantOrderRef = merchantOrderRef;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public void setSignature(String signature) {
        this.signature = signature;
    }
    public void setAmount(Double amount) {
        this.amount = amount;
    }
    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }
    public void setCurrency(String currency) {
        this.currency = currency;
    }
    // Getter methods
    public String getCurrency() {
        return currency;
    }
    public String getAmount() {
        DecimalFormat df = new DecimalFormat("0.##");
        return df.format(amount);
    }
    public String getLinkRef() {
        return linkRef;
    }
    public String getMerchantOrderRef() {
        return merchantOrderRef;
    }
    public String getCountryCode() {
        return countryCode;
    }
    public String getStatus() {
        return status;
    }
    public String getSignature() {
        return signature;
    }
}
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib.parse
import hashlib
import hmac
import base64
class PLWebhookResponseObj:
    def __init__(self, currency, amount, link_ref, merchant_order_ref, country_code, status, signature_hash):
        # Instance Variables
        self.amount = amount
        self.country_code = country_code
        self.currency = currency
        self.link_ref = link_ref
        self.merchant_order_ref = merchant_order_ref
        self.signature_hash = signature_hash
        self.status = status
def VerifySignature(plWebhookResponseObj, secretKey):
    f = {
        'amount': f"{plWebhookResponseObj.amount:.2f}".rstrip('0').rstrip('.'),
        'country_code': plWebhookResponseObj.country_code,
        'currency': plWebhookResponseObj.currency,
        'link_ref': plWebhookResponseObj.link_ref,
        'merchant_order_ref': plWebhookResponseObj.merchant_order_ref,
        'status': plWebhookResponseObj.status,
    }
    f = dict(sorted(f.items()))
    message1 = urllib.parse.urlencode(f)
    message = message1.encode('utf-8')
    secret = secretKey.encode('utf-8')
    signature = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest()).decode('utf-8')
    if signature != plWebhookResponseObj.signature_hash:
        print("Hash verification failed, not from valid source\n")
        return False
    else:
        print("Hash verification succeeded\n")
        return True
# Define constants
secret = 'PORTONE_SECRET'
# Create an instance of PLWebhookResponseObj
plWebhookResponseObj = PLWebhookResponseObj(
    amount=100.25,
    country_code='country_code',
    currency='currency',
    link_ref='link_ref',
    merchant_order_ref='merchant_order_ref',
    signature_hash='signature_hash',
    status='status'
)
# Call VerifySignature
isValid = VerifySignature(plWebhookResponseObj, secret)
if isValid:
    print("Payment Link Webhook response is valid.\n")
else:
    print("Payment Link Webhook response is invalid.\n")
Code samples to generate and verify signature hash for the data received via Subscription Link Status webhooks sent from PortOne servers.
- Golang
- PHP
- NodeJS
- C#
- Java
- Python
package main
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
    "net/url"
)
type SLWebhookResponse struct {
    Currency         string
    OrderRef         string
    MerchantOrderRef string
    SignatureHash    string
    Status           string
}
func VerifySignature(slWebhookResponse SLWebhookResponse, secretKey string) bool {
    // Create a url.Values map and add the necessary parameters
    params := make(url.Values)
    params.Add("currency", slWebhookResponse.Currency)
    params.Add("order_ref", slWebhookResponse.OrderRef)
    params.Add("merchant_order_ref", slWebhookResponse.MerchantOrderRef)
    params.Add("status", slWebhookResponse.Status)
    // Encode the parameters
    data := params.Encode()
    // Create the HMAC hash using SHA-256
    secret := []byte(secretKey)
    message := []byte(data)
    hash := hmac.New(sha256.New, secret)
    hash.Write(message)
    // Convert the hash to a base64 string
    hashValue := base64.StdEncoding.EncodeToString(hash.Sum(nil))
    // Compare the computed hash with the signature received in the payment response
    if hashValue != slWebhookResponse.SignatureHash {
        fmt.Println("Hash verification failed, not from valid source")
        return false
    } else {
        fmt.Println("Hash verification succeeded")
        return true
    }
}
func main() {
    portOneSecret := "PORTONE_SECRET"
    // Define the webhook response struct below with the respective params received in webhook
    slWebhookResponse := SLWebhookResponse{
        Currency:         "currency",
        MerchantOrderRef: "merchant_order_ref",
        OrderRef:         "order_ref",
        SignatureHash:    "signature_hash",
        Status:           "status",
    }
    // Verify the signature
    isValid := VerifySignature(slWebhookResponse, portOneSecret)
    // Output the result of the verification
    if isValid {
        fmt.Println("Subscription Link Webhook response is valid.")
    } else {
        fmt.Println("Subscription Link Webhook response is invalid.")
    }
}
<?php
function VerifySignature($slWebhookResponseObj, $secretKey) {
    // Extract the received signature hash from the slWebhookResponseObj object
    $signature_hash = $slWebhookResponseObj->signature_hash;
    // Create an array with the necessary parameters
    $data = array(
        'order_ref' => $slWebhookResponseObj->order_ref,
        'currency' => $slWebhookResponseObj->currency,
        'merchant_order_ref' => $slWebhookResponseObj->merchant_order_ref,
        'status' => $slWebhookResponseObj->status,
    );
    // Sort the array by keys
    ksort($data);
    // Build the query string
    $message = http_build_query($data);
    // Generate the HMAC hash using SHA-256 and encode it in base64
    $hash_value = base64_encode(hash_hmac('sha256', $message, $secretKey, true));
    // Compare the generated hash with the received signature hash
    if ($hash_value !== $signature_hash) {
        echo "Hash verification failed, not from valid source\n";
        return false;
    } else {
        echo "Hash verification succeeded\n";
        return true;
    }
}
// Define the main function
function main() {
    $secret_key = "PORTONE_SECRET";
    // Define the webhook response struct below with the respective params received in webhook
    $slWebhookResponseObj = (object) [
        'order_ref' => 'order_ref',
        'currency' => 'currency',
        'merchant_order_ref' => 'merchant_order_ref',
        'status' => 'status',
        'signature_hash' => 'signature_hash',
    ];;
    // Verify the signature
    $isValid = VerifySignature($slWebhookResponseObj, $secret_key);
    // Print the result of the verification
    if ($isValid) {
        echo "Subscription Link Webhook response is valid.\n";
    } else {
        echo "Subscription Link Webhook response is invalid.\n";
    }
}
// Call the main function
main();
?>
const crypto = require('crypto');
const { URLSearchParams } = require('url');
function VerifySignature(slWebhookResponseObj, secretKey) {
    const params = new URLSearchParams();
    params.append('currency', slWebhookResponseObj.currency)
    params.append('order_ref', slWebhookResponseObj.order_ref)
    params.append('merchant_order_ref', slWebhookResponseObj.merchant_order_ref)
    params.append('status', slWebhookResponseObj.status)
    params.sort();
    const message = params.toString()
    const hash_value =  crypto.createHmac('sha256', secretKey).update(message).digest('base64');
    if(hash_value !==  slWebhookResponseObj.signature_hash){
      console.log("Hash verification failed, not from valid source")
      return false;
    } else{
      console.log("Hash verification succeded")
      return true;
    }
}
// Main function to demonstrate calling VerifySignature
function main() {
    const secretKey = 'PORTONE_SECRET';
    // Example slWebhookResponseObj object
    const slWebhookResponseObj = {
        order_ref: 'order_ref',
        currency: 'currency',
        merchant_order_ref: 'merchant_order_ref',
        status: 'status',
        signature_hash: 'signature_hash'
    };
    // Verify the signature
    const isValid = VerifySignature(slWebhookResponseObj, secretKey);
    // Print the result of the verification
    if (isValid) {
        console.log("Subscription Link Webhook response is valid.");
    } else {
        console.log("Subscription Link Webhook response is invalid.");
    }
}
// Call the main function
main();
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApp
{
    class SLWebhookResponse
    {
        public string currency;
        public string order_ref;
        public string merchant_order_ref;
        public string status;
        public string signature_hash;
    }
    class ApiSecurityExample
    {
        public static bool VerifySignature(SLWebhookResponse slWebhookResponse, string secret)
        {
            var map = new SortedDictionary<string, string>()
            {
                { "order_ref", slWebhookResponse.order_ref },
                { "currency", slWebhookResponse.currency },
                { "merchant_order_ref", slWebhookResponse.merchant_order_ref },
                { "status", slWebhookResponse.status },
            };
            var stringBuilder = new StringBuilder();
            foreach (var key in map.Keys)
            {
                if (stringBuilder.Length > 0)
                {
                    stringBuilder.Append("&");
                }
                var value = map[key];
                try
                {
                    stringBuilder.Append((key != null ? Uri.EscapeDataString(key) : ""));
                    stringBuilder.Append("=");
                    stringBuilder.Append(value != null ? Uri.EscapeDataString(value) : "");
                }
                catch (ArgumentNullException e)
                {
                    throw new Exception("The key or value is null.", e);
                }
                catch (UriFormatException e)
                {
                    throw new Exception("Invalid format for key or value.", e);
                }
            }
            var message = stringBuilder.ToString();
            // Console.WriteLine("message: " + message);
            var encoding = new ASCIIEncoding();
            byte[] keyByte = encoding.GetBytes(secret);
            byte[] messageBytes = encoding.GetBytes(message);
            var hmacsha256 = new HMACSHA256(keyByte);
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            string hash_value = Convert.ToBase64String(hashmessage);
            if (hash_value != slWebhookResponse.signature_hash) {
                Console.WriteLine("Hash verification failed, not from valid source");
                return false;
              } else {
                Console.WriteLine("Hash verification succeded");
                return true;
              }
        }
    }
class Program
    {
      static void Main(string[] args)
        {
            string secret = "PORTONE_SECRET";
            SLWebhookResponse slWebhookResponse = new SLWebhookResponse()
            {
                order_ref = "order_ref",
                currency = "currency",
                merchant_order_ref = "merchant_order_ref",
                status = "status",
                signature_hash = "signature_hash"
            };
            bool isValid = ApiSecurityExample.VerifySignature(slWebhookResponse, secret);
            // Print the result
            if (isValid) {
                Console.WriteLine("Subscription Link Webhook response is valid.");
            } else {
                Console.WriteLine("Subscription Link Webhook response is invalid.");
            }
        }
    }
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
public class Main {
    public static void main(String[] args) {
        String secret = "PORTONE_SECRET";
        // Create SLWebhookResponse object using setter methods
        SLWebhookResponse slWebhookResponse = new SLWebhookResponse();
        slWebhookResponse.setOrderRef("order_ref");
        slWebhookResponse.setCurrency("currency");
        slWebhookResponse.setMerchantOrderRef("merchant_order_ref");
        slWebhookResponse.setSignature("signature");
        slWebhookResponse.setStatus("status");
        boolean isValid = verifySignature(slWebhookResponse, secret);
        // Print the result
        if (isValid) {
            System.out.println("Subscription Link Webhook response is valid.");
        } else {
            System.out.println("Subscription Link Webhook response is invalid.");
        }
    }
    public static boolean verifySignature(SLWebhookResponse slWebhookResponse, String secretKey) {
        try {
            Map<String, String> params = new TreeMap<>();
            params.put("order_ref", slWebhookResponse.getOrderRef());
            params.put("currency", slWebhookResponse.getCurrency());
            params.put("merchant_order_ref", slWebhookResponse.getMerchantOrderRef());
            params.put("status", slWebhookResponse.getStatus());
            // Encode the parameters
            String data = encodeParams(params);
            byte[] secret = secretKey.getBytes();
            byte[] message = data.getBytes();
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(secret, "HmacSHA256");
            sha256_HMAC.init(secretKeySpec);
            byte[] hash = sha256_HMAC.doFinal(message);
            String computedSignature = Base64.getEncoder().encodeToString(hash);
            if (!computedSignature.equals(slWebhookResponse.getSignature())) {
                System.out.println("Hash verification failed, not from a valid source");
                return false;
            } else {
                System.out.println("Hash verification succeeded");
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    public static String encodeParams(Map<String, String> params) {
        StringBuilder encodedParams = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (encodedParams.length() > 0) {
                encodedParams.append("&");
            }
            encodedParams.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
                         .append("=")
                         .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        return encodedParams.toString();
    }
}
class SLWebhookResponse {
    private String orderRef;
    private String currency;
    private String merchantOrderRef;
    private String signature;
    private String status;
    // Default constructor
    public SLWebhookResponse() {
    }
    // Setter methods
    public void setOrderRef(String orderRef) {
        this.orderRef = orderRef;
    }
    public void setMerchantOrderRef(String merchantOrderRef) {
        this.merchantOrderRef = merchantOrderRef;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public void setSignature(String signature) {
        this.signature = signature;
    }
    public void setCurrency(String currency) {
        this.currency = currency;
    }
    // Getter methods
    public String getCurrency() {
        return currency;
    }
    public String getOrderRef() {
        return orderRef;
    }
    public String getMerchantOrderRef() {
        return merchantOrderRef;
    }
    public String getStatus() {
        return status;
    }
    public String getSignature() {
        return signature;
    }
}
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib.parse
import hashlib
import hmac
import base64
class SLWebhookResponseObj:
    def __init__(self, currency, order_ref, merchant_order_ref, status, signature_hash):
        # Instance Variables
        self.currency = currency
        self.order_ref = order_ref
        self.merchant_order_ref = merchant_order_ref
        self.signature_hash = signature_hash
        self.status = status
def VerifySignature(slWebhookResponseObj, secretKey):
    f = {
        'currency': slWebhookResponseObj.currency,
        'order_ref': slWebhookResponseObj.order_ref,
        'merchant_order_ref': slWebhookResponseObj.merchant_order_ref,
        'status': slWebhookResponseObj.status,
    }
    f = dict(sorted(f.items()))
    message1 = urllib.parse.urlencode(f)
    message = message1.encode('utf-8')
    secret = secretKey.encode('utf-8')
    signature = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest()).decode('utf-8')
    if signature != slWebhookResponseObj.signature_hash:
        print("Hash verification failed, not from valid source\n")
        return False
    else:
        print("Hash verification succeeded\n")
        return True
# Define constants
secret = 'PORTONE_SECRET'
# Create an instance of SLWebhookResponseObj
slWebhookResponseObj = SLWebhookResponseObj(
    currency='currency',
    order_ref='order_ref',
    merchant_order_ref='merchant_order_ref',
    signature_hash='signature_hash',
    status='status'
)
# Call VerifySignature
isValid = VerifySignature(slWebhookResponseObj, secret)
if isValid:
    print("Subscription Link Webhook response is valid.\n")
else:
    print("Subscription Link Webhook response is invalid.\n")