Marco Gasi
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):
Their support tells that they put signature in header but even using following code I can't see it:
Thank you so much for any help
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'];
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";
}
Has somebody any idea about this?Thank you so much for any help
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 :)
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.
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!
You're in the home stretch!
ASKER
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
Here id the code of inc_common.php:
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:
Thank you!
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>
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;
}
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();
}
Thank you!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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 :)
ASKER
Thank you so much.
Cheers
Cheers
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.