"Invalid Hash" Error with Changing Prefix in PayNow Zimbabwe Integration


#1

Community,

We’re encountering a critical issue while integrating with PayNow Zimbabwe’s payment gateway. Despite following the official documentation, we keep receiving Invalid Hash errors with constantly changing prefix requirements. Would appreciate any insights from those who’ve solved similar issues.

Problem Description

Every request returns an Invalid Hash error, but the required prefix changes with each attempt:

  1. First error: Hash should start with: 0AC4FE
  2. Subsequent error: Hash should start with: 8BD3B4
  3. Latest error: Hash should start with: 7A2C1F (example)

Our Implementation

1. Hash Generation (Python)

python

import hashlib params = { “id”: “21452”, “reference”: “TEST_ORDER_123”, “amount”: “10.00”, “returnurl”: “https://yourdomain.com/return”, “resulturl”: “https://yourdomain.com/ipn”, “status”: “Message”, “key”: “4e1af19b-86de-4b9d-92b4-f52f5f725f07” } # Concatenation order per documentation data = params[“id”] + params[“reference”] + params[“amount”] + params[“returnurl”] + params[“resulturl”] + params[“status”] + params[“key”] full_hash = hashlib.sha512(data.encode()).hexdigest().upper() paynow_hash = “PREFIX_FROM_ERROR” + full_hash[6:] # Dynamic prefix

2. Sample cURL Request

bash

curl --location ‘https://www.paynow.co.zw/interface/initiatetransaction’ \ --header ‘Content-Type: application/x-www-form-urlencoded’ \ --data-urlencode ‘id=21452’ \ --data-urlencode ‘key=4e1af19b-86de-4b9d-92b4-f52f5f725f07’ \ --data-urlencode ‘reference=TEST_ORDER_123’ \ --data-urlencode ‘amount=10.00’ \ --data-urlencode ‘returnurl=https://yourdomain.com/return’ \ --data-urlencode ‘resulturl=https://yourdomain.com/ipn’ \ --data-urlencode ‘status=Message’ \ --data-urlencode ‘authemail=alinknest123@gmail.com’ \ --data-urlencode ‘hash=7A2C1F9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08’

3. Server Response

http

HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 status=Error&error=Invalid+Hash.++Hash+should+start+with%3a+9F3A2D # New prefix!

What We’ve Tried

  1. Generated hashes using SHA-512 as per documentation
  2. Dynamic prefix updates based on error responses
  3. Verified parameter order and encoding
  4. Tested with/without optional authemail parameter
  5. Used both test mode ( id=2 ) and live credentials

Key Questions

  1. Why does the required hash prefix change with every request?
  2. Is there a hidden parameter (timestamp/nonce) we should include?
  3. Does authemail belong in the hash generation string?
  4. Are there secret salts or key rotations we’re missing?
  5. Has anyone successfully integrated recently with working sample code?

Environment

  • API Endpoint: https://www.paynow.co.zw/interface/initiatetransaction
  • Authentication: Merchant ID + Secret Key
  • Security: SHA-512 hashing

Any help is appreciated! We’ll update this thread with solutions found. For direct collaboration: contact@example.com

Posted in: [PayNow Community Forum] • [Stack Overflow] • [Zimbabwe Devs Network]

Attachments:

  1. [Full request/response logs]
  2. [Documentation screenshot]

Update: According to PayNow support, prefixes are dynamically generated per transaction session. But implementation details remain unclear.


#2

Good morning @alinknest

Please make sure that your integration ID and Key are correct.
I highly recommend that you use our Python SDK.

SDK Source Code

Python Package

If you have to implement the hash yourself, here is an example in python

     body = {
        "resulturl": self.result_url,
        "returnurl": self.return_url,
        "reference": payment.reference,
        "amount": payment.total(),
        "id": self.integration_id,
        "additionalinfo": payment.info(),
        "authemail":  payment.auth_email,
        "phone": phone,
        "method": method,
        "status": "Message"
    }

    for key, value in body.items():
        if key == 'authemail' or key == 'returnurl' or key == 'resulturl':
            continue

        body[key] = quote_plus(str(value))  # Url encode the

    body['hash'] = self.__hash(body, self.integration_key)

    return body

def __hash(self, items, integration_key):
    """Generates a SHA512 hash of the transaction

    Args:
        items (dict): The transaction dictionary to hash
        integration_key (str): Merchant integration key to use during hashing

    Returns:
        str: The hashed transaction
    """
    out = ""
    for key, value in items.items():
        if(str(key).lower() == 'hash'):
            continue

        out += str(value)

    out += integration_key.lower()

    return hashlib.sha512(out.encode('utf-8')).hexdigest().upper()