OPay Callback Notification Signature

Whenever the status of any of your transaction has been updated, e.g. a reference code payment has been paid, OPay will keep you informed at your designated callBackUrl. Since anyone can get hold of your endpoint and attempt to send you phony event objects for malicious purposes (e.g. to see if they can mark their subscription to your product as renewed just in case you aren't running any verifications on the transaction reference), it is important to verify that callbacks originate from OPay.

You can do any or both of the below to verify callbacks from OPay:

  • Watch the IPs and accept callbacks only from our IPs.
  • Validate the Signature as described in the section that follows.

In this page, you will learn:

  1. How to calculate the signature of the received callback.
  2. How to validate OPay callback signature.

Callback Signature Calculator

-HMac-SHA3-512 signature of the callback payload

Valid callbacks are raised with sha512 value, which is essentially a HMAC-SHA3-512 signature of the callback payload signed using your Private Key.

The following section helps you generate a signature from the HMAC computation of the private key and payload.

Enter your OPay Private Key
Enter OPay Callback Payload

Callback Signature Validation

-HMac-SHA3-512 signature of the callback payload

Valid callbacks are raised with Hmac sha3-512 value, which is essentially a HMac-SHA3-512 signature of the callback payload. Yes, signed using your Private Key.

-Example signature payload params:

There are two values for Refunded, when payload.refunded is true, it is t, otherwise it is f.

                        
                            {
                               "payload":{
                                  "amount":"49160",
                                  "channel":"Web",
                                  "country":"NG",
                                  "currency":"NGN",
                                  "displayedFailure":"",
                                  "fee":"737",
                                  "feeCurrency":"NGN",
                                  "instrumentType":"BankCard",
                                  "reference":"10023",
                                  "refunded":false,
                                  "status":"SUCCESS",
                                  "timestamp":"2022-05-07T06:20:46Z",
                                  "token":"220507145660712931829",
                                  "transactionId":"220507145660712931829",
                                  "updated_at":"2022-05-07T07:20:46Z"
                               },
                               "sha512":"9f605d69f04e94172875dc156537071cead060bbcaeaca94a7b8805af9f89611e2fdf6836713c9c90b028ca7e4470b1356e996975f2abc862315aaa9b7f2ae2d",
                               "type":"transaction-status"
                            }
                        
                

-Code Example:

                                
                                    class NiCallBackAutoController
                                    {
                                        private $secretkey;
                                        private $merchantId;

                                        public function __construct() {
                                            $this->merchantId = '256621051120756';
                                            $this->secretkey = 'OPAYPRV**************************98453';
                                        }

                                        public function test(Request $request)
                                        {
                                            $input = $request->all();
                                            if(empty($input) || !isset($input['payload'])){
                                                return 'Parameter error';
                                            }
                                            if (!isset($input['payload']['reference']) && !isset($input['payload']['transactionId']) && !isset($input['payload']['status']) && !isset($input['payload']['amount']) && !isset($input['payload']['currency']) && !isset($input['payload']['refunded']) && !isset($input['payload']['timestamp']) && !isset($input['payload']['token']) && !isset($input['payload']['transactionId']) && !isset($input['sha512'])) {
                                                return 'Parameter error';
                                            }
                                            $reference = $input['payload']['reference'];
                                            $status = $input['payload']['status'];
                                            $amount = $input['payload']['amount'];
                                            $currency = $input['payload']['currency'];
                                            $refunded = $input['payload']['refunded'];
                                            $timestamp = $input['payload']['timestamp'];
                                            $token = $input['payload']['token'];
                                            $transactionId = $input['payload']['transactionId'];

                                            $authJson = sprintf("{Amount:\"%s\",Currency:\"%s\",Reference:\"%s\",Refunded:%s,Status:\"%s\",Timestamp:\"%s\",Token:\"%s\",TransactionID:\"%s\"}", $amount, $currency, $reference, $refunded?"t":"f", $status, $timestamp, $token, $transactionId);
                                            $result = $this->auth($authJson);
                                            return $result;
                                        }

                                        public function auth ( $authJson ) {
                                            $secretKey = $this->secretkey;
                                            $auth = hash_hmac('sha3-512', $authJson, $secretKey);
                                            return $auth;
                                        }
                                    }
                                
                            
                                
                                    package com.opay.sdk.utils;

                                    import com.fasterxml.jackson.databind.ObjectMapper;
                                    import org.bouncycastle.jce.provider.BouncyCastleProvider;

                                    import javax.crypto.Mac;
                                    import javax.crypto.spec.SecretKeySpec;
                                    import java.security.InvalidKeyException;
                                    import java.security.NoSuchAlgorithmException;
                                    import java.security.NoSuchProviderException;
                                    import java.security.Security;
                                    import java.util.Map;

                                    public class SignatureUtils {

                                        static{
                                            if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null){
                                                Security.addProvider(new BouncyCastleProvider());
                                            }
                                        }

                                        public static void main(String[] args) throws Exception{
                                            String requestBody = "{\"payload\":{\"amount\":\"49160\",\"channel\":\"Web\",\"country\":\"NG\",\"currency\":\"NGN\",\"displayedFailure\":\"\",\"fee\":\"737\",\"feeCurrency\":\"NGN\",\"instrumentType\":\"BankCard\",\"reference\":\"10023\",\"refunded\":false,\"status\":\"SUCCESS\",\"timestamp\":\"2022-05-07T06:20:46Z\",\"token\":\"220507145660712931829\",\"transactionId\":\"220507145660712931829\",\"updated_at\":\"2022-05-07T07:20:46Z\"},\"sha512\":\"9f605d69f04e94172875dc156537071cead060bbcaeaca94a7b8805af9f89611e2fdf6836713c9c90b028ca7e4470b1356e996975f2abc862315aaa9b7f2ae2d\",\"type\":\"transaction-status\"}";
                                            ObjectMapper objectMapper = new ObjectMapper();
                                            PaymentStatusCallBackRequest callBack = objectMapper.readValue(requestBody,PaymentStatusCallBackRequest.class);
                                            System.out.println(verifyPaymentNotifySignature(callBack,"OPAYPRV16498196872570.13953388019021462"));
                                        }

                                        public static boolean verifyPaymentNotifySignature(PaymentStatusCallBackRequest callback, String privateKey){
                                            String format = "{Amount:\"%s\",Currency:\"%s\",Reference:\"%s\",Refunded:%s,Status:\"%s\",Timestamp:\"%s\",Token:\"%s\",TransactionID:\"%s\"}";
                                            String signContent = String.format(format,
                                            callback.payload.amount,
                                            callback.payload.currency,
                                            callback.payload.reference,
                                            callback.payload.refunded ? "t" : "f",
                                            callback.payload.status,
                                            callback.payload.timestamp,
                                            callback.payload.token == null ? "" : callback.payload.token,
                                            callback.payload.transactionId
                                            );

                                            String sign = buildHmacSha3_512(signContent, privateKey);

                                            return callback.sha512.equalsIgnoreCase(sign);
                                        }

                                        public static String buildHmacSha3_512(String value, String secret){
                                            byte[] keyBytes = secret.getBytes();
                                            SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HMac-SHA3-512");
                                            Mac mac = null;
                                            try {
                                            mac = Mac.getInstance("HMac-SHA3-512", "BC");
                                            mac.init(signingKey);
                                            } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException e) {
                                            throw new RuntimeException(e);
                                            }
                                            byte[] rawHmac = mac.doFinal(value.getBytes());
                                            return byte2hex(rawHmac);
                                        }

                                        public static String byte2hex(byte[] b) {
                                            String hs = "";
                                            String stmp = "";
                                            for (int n = 0; n < b.length; n++) {
                                            stmp = (Integer.toHexString(b[n] & 0XFF));
                                            if (stmp.length() == 1) {
                                            hs = hs + "0" + stmp;
                                            } else {
                                            hs = hs + stmp ;
                                            }
                                            }
                                            return hs.toLowerCase();
                                        }

                                        public static class PaymentStatusCallBackRequest {
                                            private Payload payload;
                                            private String sha512;
                                            private String type;

                                            public Payload getPayload() {
                                            return payload;
                                            }

                                            public void setPayload(Payload payload) {
                                            this.payload = payload;
                                            }

                                            public String getSha512() {
                                            return sha512;
                                            }

                                            public void setSha512(String sha512) {
                                            this.sha512 = sha512;
                                            }

                                            public String getType() {
                                            return type;
                                            }

                                            public void setType(String type) {
                                            this.type = type;
                                            }
                                        }

                                        public static class Payload{
                                            private String amount;
                                            private String channel;
                                            private String country;
                                            private String currency;
                                            private String displayedFailure;
                                            private String fee;
                                            private String feeCurrency;
                                            private String instrumentType;
                                            private String reference;
                                            private boolean refunded;
                                            private String status;
                                            private String timestamp;
                                            private String token;
                                            private String transactionId;
                                            private String updated_at;
                                            private Map extra;

                                            public String getAmount() {
                                            return amount;
                                            }

                                            public void setAmount(String amount) {
                                            this.amount = amount;
                                            }

                                            public String getChannel() {
                                            return channel;
                                            }

                                            public void setChannel(String channel) {
                                            this.channel = channel;
                                            }

                                            public String getCountry() {
                                            return country;
                                            }

                                            public void setCountry(String country) {
                                            this.country = country;
                                            }

                                            public String getCurrency() {
                                            return currency;
                                            }

                                            public void setCurrency(String currency) {
                                            this.currency = currency;
                                            }

                                            public String getDisplayedFailure() {
                                            return displayedFailure;
                                            }

                                            public void setDisplayedFailure(String displayedFailure) {
                                            this.displayedFailure = displayedFailure;
                                            }

                                            public String getFee() {
                                            return fee;
                                            }

                                            public void setFee(String fee) {
                                            this.fee = fee;
                                            }

                                            public String getFeeCurrency() {
                                            return feeCurrency;
                                            }

                                            public void setFeeCurrency(String feeCurrency) {
                                            this.feeCurrency = feeCurrency;
                                            }

                                            public String getInstrumentType() {
                                            return instrumentType;
                                            }

                                            public void setInstrumentType(String instrumentType) {
                                            this.instrumentType = instrumentType;
                                            }

                                            public String getReference() {
                                            return reference;
                                            }

                                            public void setReference(String reference) {
                                            this.reference = reference;
                                            }

                                            public boolean isRefunded() {
                                            return refunded;
                                            }

                                            public void setRefunded(boolean refunded) {
                                            this.refunded = refunded;
                                            }

                                            public String getStatus() {
                                            return status;
                                            }

                                            public void setStatus(String status) {
                                            this.status = status;
                                            }

                                            public String getTimestamp() {
                                            return timestamp;
                                            }

                                            public void setTimestamp(String timestamp) {
                                            this.timestamp = timestamp;
                                            }

                                            public String getToken() {
                                            return token;
                                            }

                                            public void setToken(String token) {
                                            this.token = token;
                                            }

                                            public String getTransactionId() {
                                            return transactionId;
                                            }

                                            public void setTransactionId(String transactionId) {
                                            this.transactionId = transactionId;
                                            }

                                            public String getUpdated_at() {
                                            return updated_at;
                                            }

                                            public void setUpdated_at(String updated_at) {
                                            this.updated_at = updated_at;
                                            }

                                            public Map getExtra() {
                                            return extra;
                                            }

                                            public void setExtra(Map extra) {
                                            this.extra = extra;
                                            }
                                        }
                                    }
                                
                            

See Also

User Profile 12 messages

James Jones
Application Developer
Recent Notifications
Another purpose persuade Due in 2 Days
+28%
Would be to people Due in 2 Days
+50%
-27%
The best product Due in 2 Days
+8%