Link to home
Start Free TrialLog in
Avatar of Marco Gasi
Marco GasiFlag for Spain

asked on

How to get HTTP_STRIPE_SIGNATURE?

Hi everybody.
I'm fighting with Stripe. My current issue is that Stripe suggests to use following code to check Stripe-signature using PaymentIntent (see https://stripe.com/docs/webhooks/signatures#verify-official-libraries):
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];

Open in new window

I never heard about that and in effect it gives Unefined index Notice if used.
Their support tells that they put signature in header but even using following code I can't see it:
$headers = apache_request_headers();
foreach ($headers as $header => $value) {
    echo "$header: $value <br />\n";
}

Open in new window

Has somebody any idea about this?
Thank you so much for any help
Avatar of David Favor
David Favor
Flag of United States of America image

The HTTP_STRIPE_SIGNATURE header only exists during a Stripe API call.

Tip: When working with Payment Gateways always begin with their sample code, then modify the code after you have the sample working.

For example, pull down the PHP code + verify it works, then remove the comment *// Do something with $event* + add in your actual code.
Avatar of Marco Gasi

ASKER

Hi David. Thank you for your answer.
I do an API call. I use the example code found in their documentation to use PaymentIntent. And the code works: I click on my 'Pay' button, a test popup shoes up to ask authorization (3D Secure) and then in my console log I see the PaymentIntent data. Here arise the issue: I have setup a webhook in my Dashboard and its works too. What I have understood is that, once I get PaymentIntent response I should redirect the user to the endpoint and here I get Undefined index.
So I guess I misunderstood something, but I need help to understand what I misunderstood.

And yes, I do exactly what you suggest in your tip :)
By endpoint, I'm guessing you mean a thank you page.

If this is true, then the HTTP_STRIPE_SIGNATURE server header will be gone, because now you're on a new page.

This isn't really a problem, because HTTP_STRIPE_SIGNATURE will only be used during the actual transaction + has no use on the thank you page.

Clarifying what you mean by redirect the user to the endpoint will help.
And congrats on getting this much of Stripe to work.

You're in the home stretch!
Hi David. First, thank you for congrats :)

Okay, I understand your point: when I check for the signature it has already gone because its life is short... Let's think at signature as a butterfly ;)

And I understand you say it's not so important, but...
Why Stripe docs suggest to check for the signature (https://stripe.com/docs/webhooks/signatures#verify-official-libraries)? When I should do it? Or I am stupid or Stripe docs is not so great as they say around the web, because I really can't understand this point. In the same tima, I can't really understand why I should setup a webhook. You look patient, so I'm going to describe where I am.

1) Setup a checkout.php page. You can see it in action here: http://emgie.webintenerife.com/auth/sign-in.php (User these credentials:
username: Giorgio Prot
password: sasa
), then open Cart menu in the top navigation and click View cart. Finally, click on Finalizar compra and you'll be brought to checkout.php page;
here you have to use Giorgio Prot as card holder name and 4000 0000 0000 3063 as card number (other data, as you know, can be the ones you prefer)
2) This is the source code of checkout.php
<?php
require_once 'inc_common.php';
if (!isset($_SESSION['loggedUser'])) {
    header('Location: auth/sign-in.php');
    exit;
}
require __DIR__ . '/vendor/autoload.php';
require_once 'admin/php/db.php';

$cartAmount = $cart->calcRawTotal();

$cartResume = $cart->getItemsInCart();

\Stripe\Stripe::setApiKey("SECRET_KEY");

$intent = \Stripe\PaymentIntent::create([
        "amount" => $cartAmount * 100,
        "currency" => "eur",
        "payment_method_types" => ["card"],
    ]);
?>
<!DOCTYPE html>
<html lang="en">
    <head>

        <?php include 'inc_head.php' ?>

    </head>

    <body id="page-top">

        <?php include 'inc_nav.php' ?>

        <section class="features" id="features">
            <div class="container">
                <div class="section-heading text-center">
                    <h2>Página de pago</h2>
                    <p class="text-muted">Por favor rellena el formulario con tus datos</p>
                    <hr>
                </div>
                <div class="row">
                    <div class="col-lg-12 my-auto">
                        <div id="checkoutForm">    
                            <!--<form action="./charge.php" method="post" id="payment-form">-->
                            <form action="#" method="post" id="payment-form">
                                <div class="form-row">
                                    <label for="cardholder-name">Nombre en la tarjeta</label>
                                    <input type="text" name="cardholder-name" id="cardholder-name" />
                                </div>
                                <div class="form-row">
                                    <label for="card-element">Tarjeta de crédito o débito</label>
                                    <div id="card-element">
                                        <!-- a Stripe Element will be inserted here. -->
                                    </div>
                                    <!-- Used to display form errors -->
                                    <div id="card-errors"></div>
                                    <input type="hidden" name="cartAmount" value="<?php echo $cartAmount ?>" />
                                    <button id="card-button" data-secret="<?= $intent->client_secret ?>">Pagar</button>
                                </div>

                            </form>  
                            <div id="result"></div>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <?php include 'inc_footer.php' ?>

        <script>
            var stripe = Stripe('pk_test_ZqS0jryOJjww1tdbV6wC1Sbp', {
                betas: ['payment_intent_beta_3']
            });

            var elements = stripe.elements();
            var cardElement = elements.create('card');
            cardElement.mount('#card-element');
            var cardholderName = document.getElementById('cardholder-name');
            var cardButton = document.getElementById('card-button');
            var clientSecret = cardButton.dataset.secret;

//            cardButton.addEventListener('click', function (ev) {
            $(cardButton).on('click', function (e) {
                e.preventDefault();
                stripe.handleCardPayment(
                        clientSecret,
                        cardElement,
                        {
                            source_data: {
                                owner: {
                                    name: cardholderName.value
                                }
                            }
                        }
                ).then(function (result) {
                    if (result.error) {
                        console.log(result);
                        // Display error.message in your UI.
                    } else {
                        console.log(result);
                        window.location.href = 'php/stripe_payment.php';
                    }
                });


//                stripe.confirmPaymentIntent(
//                        clientSecret,
//                        cardElement,
//                        {
//                            source_data: {
//                                owner: {
//                                    name: cardholderName.value
//                                }
//                            },
//                            return_url: 'http://emgie.webintenerife.com/php/stripe-payment.php'
//                        }
//                ).then(function (result) {
//                    console.log(result);
//                    var intent = result.paymentIntent;
//                    var action = intent.next_action;
//                    if (action && action.type === 'redirect_to_url') {
//                        window.location = action.redirect_to_url.url;
//                    }
//                });
//

            });

        </script>

    </body>

</html>

Open in new window


Here id the code of inc_common.php:
<?php

error_reporting(E_ALL);
ini_set('display_errors', 'On');
session_start();
header('Content-Type: text/html; charset=utf-8');

define('WEBROOT', 'http://emgie.webintenerife.com/');
define("EMGIEROOT", $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR);

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

require EMGIEROOT . 'vendor/autoload.php';
require_once EMGIEROOT . 'admin/php/db.php';
require_once EMGIEROOT . 'admin/ChromePhp.php';

use emgieCart\Cart;

setlocale(LC_ALL, 'es_ES.UTF-8');

$loggedUser = isset($_SESSION['loggedUser']) ? 1 : 0;

$excludedPages = array(
    '/email-confirmation.php',
    '/auth/email-confirmation.php',
    '/auth/email-sent.php',
    '/auth/login-error.php',
    '/auth/login.php',
    '/auth/logout.php',
    '/auth/register-new-account.php',
    '/auth/register-data-missing.php',
    '/auth/registration-error.php',
    '/auth/sign-in.php',
    '/auth/wrong-token.php',
);
if (!in_array($_SERVER['SCRIPT_NAME'], $excludedPages)) {
    $_SESSION['prevPage'] = $_SERVER['SCRIPT_NAME'];
} else {
    $_SESSION['prevPage'] = '/index.php';
}
if (!isset($_SESSION['loggedUser']) && !isset($_SESSION['userId']) && !isset($_SESSION['tempUserId'])) {
    $_SESSION['tempUserId'] = uniqid('', true);
}
$userId = isset($_SESSION['userId']) ? $_SESSION['userId'] : $_SESSION['tempUserId'];

$emptyCart = false;
$cart = new Cart($userId, $loggedUser);
$cartCounter = $cart->totalCartItemCount();
if ($cartCounter == 0) {
    $emptyCart = true;
}

Open in new window


At line 102 of the first snippet you see my redirect: I thought that, once sent PaymentIntent to Stripe I should have to redirect user to a confirmation page or something like this and in this page use the endpoint code.

Okay, let me honest: I don't know why and how I have to use endpoint pages and I really need explanations on this... Even if I disable the webhooks, in my e-commerc test site nothing changes: it works fine. Can you help me on this? I don't understand the logic and I don't find any guideline over the Internet: it looks like nobody gets into issues with this (this is the reason why I really appreciated your congrats :D).

Why setup a webhook? How to use it? Btw, this is the code Stripe suggests for the endpoint and is the one I get in my stripe-payment.php page:
<?php

error_reporting(E_ALL);
ini_set('display_errors', 'On');
// You can find your endpoint's secret in your webhook settings
require_once '../vendor/autoload.php';
$endpoint_secret = 'whsec_YJGvfO8ozUwq2j9tEoa5ua3JhLnqAbrK';
$payload = @file_get_contents('php://input');

$headers = apache_request_headers();
foreach ($headers as $header => $value) {
    echo "$header: $value <br />\n";
}
//echo "<pre>";
//var_dump($_SERVER);
//echo "</pre>";
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = null;

try {
    $event = \Stripe\Webhook::constructEvent(
            $payload, $sig_header, $endpoint_secret
    );
} catch (\UnexpectedValueException $e) {
    // Invalid payload
    http_response_code(400); // PHP 5.4 or greater
    exit();
} catch (\Stripe\Error\SignatureVerification $e) {
    // Invalid signature
    http_response_code(400); // PHP 5.4 or greater
    exit();
}

if ($event->type == "payment_intent.succeeded") {
    $intent = $event->data->object;
    printf("Succeeded: %s", $intent->id);
    http_response_code(200);
    exit();
} elseif ($event->type == "payment_intent.payment_failed") {
    $intent = $event->data->object;
    $error_message = $intent->last_payment_error ? $intent->last_payment_error->message : "";
    printf("Failed: %s, %s", $intent->id, $error_message);
    http_response_code(200);
    exit();
}

Open in new window


Thank you!
ASKER CERTIFIED SOLUTION
Avatar of David Favor
David Favor
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
So, you're saying that HTTP_STRIPE_SIGNATURE is automatically handled by handleCardPayment() function of Stripe.js. Then it's really simple: I have just complicated my life for nothing :)
Thank you so much.
Cheers