Click here to Skip to main content
15,886,963 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
Hi Team

I have an error when trying to use payfast payment integration, its complaining about 404 Bad request " [signature: Generated signature does not match submitted signature]". I have used what is on the documentation. How do i fix this in order for me test my application?


What I have tried:

<?php
session_start();
// PayFast Integration Logic

// Get the order details from the checkout process
//$orderID = $_POST['order_id'];
//$products = $_POST['products'];
//$grandTotal = $_POST['grand_total'];
$amount = $_POST['amount'] = 100;
//$signature = $_POST['signature'];
$allItems = $_SESSION['allItems'];
$grand_total = $_SESSION['grand_total'];
//$name = $_POST['name'];
//$email = $_POST['email'];
//$phone = $_POST['phone'];
//$address = $_POST['address'];

// Set your PayFast merchant details
$merchantID = '10010868'; // Replace with your actual merchant ID
$merchantKey = 'exnibu0q9zmuz'; // Replace with your actual merchant key

// Set the PayFast URL based on your environment (testing or production)
$isTestingMode = true; // Set to true for testing environment or false for production environment
$payfastURL = $isTestingMode ? 'https://sandbox.payfast.co.za/eng/process' : 'https://www.payfast.co.za/eng/process';

$merchantID = '10010868'; // Replace with your actual merchant ID
$merchantKey = 'exnibu0q9zmuz'; // Replace with your actual merchant key
$passphrase = ''; // Replace with your actual passphrase (if applicable)

$data = array(
    'merchant_id' => $merchantID,
    'merchant_key' => $merchantKey,
    'return_url' => 'http://example.com/payment_success.php',
    'cancel_url' => 'http://example.com/payment_cancel.php',
    'notify_url' => 'http://example.com/payment_notify.php',
    'amount' => $amount,
    'item_name' => 'Cell Phone',
    'item_description' => 'Electronics',
    'email_address' => 'gcira2023@outlook.com',
    'name_first' => 'John',
    'name_last' => 'Doe',
);

// Generate the signature
$passphrase = ''; // Replace with your actual passphrase, if applicable
$signature = generateSignature($data, $passphrase);

// Add the signature to the payment data
$data['signature'] = $signature;

// If in testing mode, use the sandbox URL, otherwise use the production URL
$testingMode = true;
$pfHost = $testingMode ? 'sandbox.payfast.co.za' : 'www.payfast.co.za';

// Create the HTML form
$htmlForm = '<form action="https://' . $pfHost . '/eng/process" method="post">';
foreach ($data as $name => $value) {
    $htmlForm .= '<input name="' . $name . '" type="hidden" value=\'' . $value . '\' />';
}
$htmlForm .= '<input type="submit" value="Pay Now" /></form>';

echo $htmlForm;

// Function to generate the signature
function generateSignature($data, $merchantKey)
{
    ksort($data);
    $signature = '';
    foreach ($data as $key => $value) {
        if ($key !== 'signature') {
            $signature .= $key . '=' . $value . '&';
        }
    }
    $signature .= $merchantKey;
    $signature = md5($signature);

    return $signature;
}
?>
Posted
Updated 15-Jun-23 1:52am
Comments
Richard Deeming 15-Jun-23 3:39am    
If the code they provide doesn't work, then talk to their support.

If their support can't (or won't) help, then find a better payment provider.

As per the documentation found at - Step 2: Create security signature[^] your code is incorrect. You have cleared '$passphrase' as an empty string, documentation gave it a value.
Your 'generateSignature' function also differ from theirs. Use the link above to re-code your function and HTML as per their requirements (this includes the order of the returned data array) and your error will disappear -

1. Concatenation of the name value pairs of all the non-blank variables with '&' used as a separator
Quote:
Variable order: The pairs must be listed in the order in which they appear in the attributes description. eg. name_first=John&name_last=Doe​&email_address=…
* Do not use the API signature format, which uses alphabetical ordering!


2. Add your passphrase (Important part right here, an empty string will not do)
The passphrase is an extra security feature, used as a ‘salt’, and is set by the Merchant in the Settings section of their Payfast Dashboard.
Add the passphrase to the end of the below string.
E.g. name_first=John&name_last=Doe​&email_address=…&passphrase=... The resultant URL encoding must be in upper case (eg. http%3A%2F%2F), and spaces encoded as ‘+’.
3. MD5 the parameter string and pass it as a hidden input named “signature”.

Security Signature holder -
<input type="hidden" name="signature" value="f103e22c0418655fb03991538c51bfd5"> 


Signature function generation -
/**
 * @param array $data
 * @param null $passPhrase
 * @return string
 */
function generateSignature($data, $passPhrase = null) {
    // Create parameter string
    $pfOutput = '';
    foreach( $data as $key => $val ) {
        if($val !== '') {
            $pfOutput .= $key .'='. urlencode( trim( $val ) ) .'&';
        }
    }
    // Remove last ampersand
    $getString = substr( $pfOutput, 0, -1 );
    if( $passPhrase !== null ) {
        $getString .= '&passphrase='. urlencode( trim( $passPhrase ) );
    }
    return md5( $getString );
} 


Full form implementation -
/ Construct variables
$cartTotal = 10.00; // This amount needs to be sourced from your application
$passphrase = 'jt7NOE43FZPn';
$data = array(
    // Merchant details
    'merchant_id' => '10000100',
    'merchant_key' => '46f0cd694581a',
    'return_url' => 'http://www.yourdomain.co.za/return.php',
    'cancel_url' => 'http://www.yourdomain.co.za/cancel.php',
    'notify_url' => 'http://www.yourdomain.co.za/notify.php',
    // Buyer details
    'name_first' => 'First Name',
    'name_last'  => 'Last Name',
    'email_address'=> 'test@test.com',
    // Transaction details
    'm_payment_id' => '1234', //Unique payment ID to pass through to notify_url
    'amount' => number_format( sprintf( '%.2f', $cartTotal ), 2, '.', '' ),
    'item_name' => 'Order#123'
);

$signature = generateSignature($data, $passphrase);
$data['signature'] = $signature;

// If in testing mode make use of either sandbox.payfast.co.za or www.payfast.co.za
$testingMode = true;
$pfHost = $testingMode ? 'sandbox.payfast.co.za' : 'www.payfast.co.za';
$htmlForm = '<form action="https://'.$pfHost.'/eng/process" method="post">';
foreach($data as $name=> $value)
{
    $htmlForm .= '<input name="'.$name.'" type="hidden" value=\''.$value.'\' />';
}
$htmlForm .= '<input type="submit" value="Pay Now" /></form>';
echo $htmlForm; 
 
Share this answer
 
v2
I think maybe there is an issue with your generateSignature function. If I remove the call to md5, and print the result I get ...
amount=100&cancel_url=http://example.com/payment_cancel.php&email_address=gcira2023@outlook.com&item_description=Electronics&item_name=Cell Phone&merchant_id=10010868&merchant_key=exnibu0q9zmuz&name_first=John&name_last=Doe¬ify_url=http://example.com/payment_notify.php&return_url=http://example.com/payment_success.php&

... which looks a bit strange to me.
The code that does this is the lines at:
PHP
foreach ($data as $key => $value) {
    if ($key !== 'signature') {
        $signature .= $key . '=' . $value . '&';
    }
}

Is the signature really to be constructed from every key and its value?
 
Share this answer
 
Comments
Gcobani Mkontwana 16-Jun-23 21:45pm    
@Richard MacCutchan, yes it must
Richard MacCutchan 17-Jun-23 3:59am    
OK, so now you need to check what is different between your calculated value and the one stored previously. That is not something that anyone here can do for you.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900