Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ThinkingMan's avatar

Guzzle to POST to external Hosted Payment Page?

I started down a road of using procedural PHP with Laravel to sent a POST to a FirstData Payeezy hosted payment page. (They have sample code here https://support.payeezy.com/hc/en-us/articles/204011429-Sample-Code-Creating-a-Pay-Button-to-use-with-a-Hosted-Payment-Page) But after feedback from this audience (thanks!) and more research I landed on the potential of using Guzzle. So I am wondering if the audience thinks this is a viable approach.

Here is why I need to use the FirstData Payeezy Hosted Page. (1) The client requires that processor (2) the Payeezy gateway handles the recurring payments (3) the gateway supports check processing (4) removes the app from PCI scope. So many reasons to go with this hosted payment page.

The added challenge with my app is that I will have multiple churches connect from the app (i.e. multi tenant) and thus I need to grab the Payeezy credentials from each church and hand those unique credentials over through the POST call. I can make this happen with a standard HTML & PHP setup but I have chosen to use Laravel for all of it's benefits.

So again, would Guzzle be an approach?

0 likes
11 replies
ThinkingMan's avatar

FWIW, this is what the Payeezy hosted page is expecting to receive.

$x_login = ""; // Take from Payment Page ID in Payment Pages interface
$transaction_key = ""; // Take from Payment Pages configuration interface
$x_amount = $_POST["x_amount"];
$x_currency_code = "USD"; // Needs to agree with the currency of the payment page
srand(time()); // initialize random generator for x_fp_sequence
$x_fp_sequence = rand(1000, 100000) + 123456;
$x_fp_timestamp = time(); // needs to be in UTC. Make sure webserver produces UTC

// The values that contribute to x_fp_hash
$hmac_data = $x_login . "^" . $x_fp_sequence . "^" . $x_fp_timestamp . "^" . $x_amount . "^" . $x_currency_code;
$x_fp_hash = hash_hmac('MD5', $hmac_data, $transaction_key);

echo ('<input name="x_login" value="' . $x_login . '" type="hidden">' );
echo ('<input name="x_amount" value="' . $x_amount . '" type="hidden">' );
echo ('<input name="x_fp_sequence" value="' . $x_fp_sequence . '" type="hidden">' );
echo ('<input name="x_fp_timestamp" value="' . $x_fp_timestamp . '" type="hidden">' );
echo ('<input name="x_fp_hash" value="' . $x_fp_hash . '" size="50" type="hidden">' );
echo ('<input name="x_currency_code" value="' . $x_currency_code . '" type="hidden">');

// create parameters input in html
foreach ($_POST as $a => $b) {
    echo "<input type='hidden' name='".htmlentities($a)."' value='".htmlentities($b)."'>";
ThinkingMan's avatar

I know this isn't exactly correct but this is what I am trying right now just to prove out things. Ideas?

public function PostFDMSdata($id)
    {
            $client= new Client();
            $client ->post(
                'https://demo.globalgatewaye4.firstdata.com/pay',
                array(
                    'form_params' => array(
                        '$x_login' => 'JUW-MD-KW-972',
                        '$transaction_key' => 'SomeKeyHere',
                        '$x_amount' => '9.01',
                        '$x_fp_sequence' => 'rand(1000, 100000) + 123456',
                        '$x_fp_timestamp' => 'time()',
                        '$x_fp_hash' => 'ThisHasToBeCalculated',
                        '$x_currency_code' => 'USD',
                        '$x_recurring_billing_amount' => '9.02',
                        '$x_recurring_billing' => 'TRUE',
                        '$x_recurring_billing_id' => 'MB-MD-KW-25-2323',
                        '$x_recurring_billing_start_date' => '2018-09-08',
                        '$x_recurring_billing_end_date' => '2035-01-31'
                    )
                )

                );

        dd($client);
    }
ThinkingMan's avatar

I have updated my Guzzle post to the following just to see what comes out. The method to var dump is wrong but I did it just to see what gets returned. The good news is I am at least trying to post to the First Data hosted page. Can anyone offer the correct step

   public function PostFDMSdata($id)
    {
            $datetime = Carbon::now("UTC");
            $x_login = "JUW-MD-KW-972";
            $transaction_key = "SomeKeyHere";
            $hmac_data = $x_login . "^" . 195701 . "^" . $datetime . "^" . "5.00" . "^" . "USD";
            $x_fp_hash = hash_hmac('MD5', $hmac_data, $transaction_key);

            $client= new Client();
            $response = $client ->post(
                'https://demo.globalgatewaye4.firstdata.com/pay',
                array(
                    'form_params' => array(
                        '$x_login' => $x_login,
                        '$transaction_key' => $transaction_key,
                        '$x_amount' => '9.01',
                        '$x_fp_sequence' => 'rand(1000, 100000) + 123456',
                        '$x_fp_timestamp' => $datetime,
                        '$x_fp_hash' => $x_fp_hash,
                        '$x_currency_code' => 'USD',
                        '$x_recurring_billing_amount' => '9.02',
                        '$x_recurring_billing' => 'TRUE',
                        '$x_recurring_billing_id' => 'MB-MD-KW-25-2323',
                        '$x_recurring_billing_start_date' => '2018-09-08',
                        '$x_recurring_billing_end_date' => '2035-01-31'
                    )
                )

                );

            var_dump($response->getBody()->__toString());

    }

So after this I get an error page inside my own app from the First Data site. If this worked properly then I should have been shown the https://demo.globalgatewaye4.firstdata.com/pay site with the proper info passed over. What are the correct methods?

Cronix's avatar

Why do all of your form_params key names start with a $?

According to your first post, they shouldn't.

echo ('<input name="x_login" value="' . $x_login . '" type="hidden">' );
echo ('<input name="x_amount" value="' . $x_amount . '" type="hidden">' );

Shouldn't they be likex_login instead of $x_login?

Edit: also this line

'$x_fp_sequence' => 'rand(1000, 100000) + 123456',

is wrong. It will literally send 'rand(1000, 100000) + 123456' as the value, since the whole thing is a string. You probably want

'$x_fp_sequence' => rand(1000, 100000) + 123456,

without quotes so it will generate an actual number.

Snapey's avatar

also the amount needs to match between what goes into the hmac and what is in the x_amount field

ThinkingMan's avatar

Gentlemen - First a big thanks for helping the noob. I seem to be getting more detail from the page now. Not sure if I am making the proper handshake. Should I use something other than var_dump? I am getting a "need to enable cookies page" which is a false negative since they are properly enabled on my browser. Thinking it is more with the handoff.

ThinkingMan's avatar

Read up on the Guzzle docs and found this section on Cookies http://docs.guzzlephp.org/en/stable/quickstart.html#cookies. Able to answer that question on my own.

Now I just need to figure out how to get the First Data page to show and not my own. So after the user clicks the pay button they should redirect to the hosted payment page but that is not happening. It still has my app in the URL. I will read Guzzle's redirect section further. It stated that it should redirect by default but that doesn't seem to be the case or possibly I am not understanding the function of a Guzzle redirect.

ThinkingMan's avatar

So I set the redirects = to false and now get a view in my app that states "You are being redirected." with that text being a hyperlink to the correct hosted page, but it never actually redirects to the First Data page. Here is the current function. What is missing?

public function PostFDMSdata($id)
    {
            $datetime = Carbon::now("UTC");
            $x_login = "JUW-MD-KW-972";
            $transaction_key = SomeKeyHere;
            $x_fp_sequence = rand(1000, 100000) + 123456;
            $hmac_data = $x_login . "^" . $x_fp_sequence . "^" . $datetime . "^" . "5.00" . "^" . "USD";
            $x_fp_hash = hash_hmac('MD5', $hmac_data, $transaction_key);

            $client= new Client(['cookies' => true]);
            $response = $client ->post(
                'https://demo.globalgatewaye4.firstdata.com/pay',
                array(
                    'allow_redirects' => false,
                    'form_params' => array(
                        'x_login' => $x_login,
                        'transaction_key' => $transaction_key,
                        'x_amount' => '5.00',
                        'x_fp_sequence' => $x_fp_sequence,
                        'x_fp_timestamp' => $datetime,
                        'x_fp_hash' => $x_fp_hash,
                        'x_currency_code' => 'USD',
                        'x_recurring_billing_amount' => '9.02',
                        'x_recurring_billing' => 'TRUE',
                        'x_recurring_billing_id' => 'MB-MD-KW-25-2323',
                        'x_recurring_billing_start_date' => '2018-09-08',
                        'x_recurring_billing_end_date' => '2035-01-31',
                        'x_show_form' => 'PAYMENT_FORM'
                    )
                )

                );

            var_dump($response->getBody()->__toString());

    }

Snapey's avatar

did it work? are you being redirected because the post worked or because there was an error?

you create an array under form_params is this correct?

ThinkingMan's avatar

Here is the challenge, with Redirects = Yes I see the external form embedded in my view yielding a browser URL with http://donate.devel/churchpay/2 (not what I need).

Instead I would have expected the redirect to send me to the First Data page with the proper post detail ultimately with a URL like this https://demo.globalgatewaye4.firstdata.com/check_cookie?ant=a6372e2161cea20cd2035bf2313b41bc&merchant=HCO-KW-EN-279&order=f04701f1845f3542d593a614243d950c7797b56b535c5c324859f679c2284882&purch=5135798&t=1

The funny thing is that the link above is generated when I have redirects = to false. But the app stops at the view which states "You are being redirected."

ThinkingMan's avatar

@Snapey Do you think the form_params could actually prevent the redirect?

Essentially what I need to do is have the user successfully redirected to the external URL. However, the Guzzle REDIRECT_URL is pointing to an internal blade view of /churchpay/{id}.

I just cannot for the life of me figure out why it won't redirect the user to the external URL despite that URL being specified in the $response = $client ->post('https://demo.globalgatewaye4.firstdata.com/pay',...

I am getting a 200 status code from the FirstData site (which means I passed in the right credentials) and the external site hosted payment page is returned inside my view without any kind of iframe.

Just very perplexed and have tried adjusting routes with no success.

Please or to participate in this conversation.