Symfony Stripe Disconnect after Payment - php

in my application Symfony 5.4 and PHP 8.0, i'm using Stripe Payment Checkout https://stripe.com/docs/payments/checkout, after payment success or error my user need to be redirect.
'success_url' => $YOUR_DOMAIN.'/domain/projet/{CHECKOUT_SESSION_ID}/domain/succes',
'cancel_url' => $YOUR_DOMAIN.'/domain/projet/{CHECKOUT_SESSION_ID}/error',
But user is disconnect and i don't want user disconnect, sometime if we are lucky user is not disconnect after payment.
#[Route('/espace-client/payer/project/{idBilling}/{idProject}', name: 'final')]
public function stripePayment(int $idBilling, int $idProject)
{
$project = $this->projectRepository->findOneById($idProject);
$billing = $this->billingRepository->findOneById($idBilling);
$YOUR_DOMAIN = 'https://127.0.0.1:8000';
$productStripe[] = [
'price_data' => [
'currency' => 'eur',
'unit_amount' => $billing->getPrice(),
'product_data' => [
'name' => $billing->getName(),
'images' => null,
],
],
'quantity' => 1,
];
Stripe::setApiKey($this->publicKey);
$checkout_session = Session::create(
[
'line_items' => [[
$productStripe,
]],
'payment_method_types' => [
'card',
],
'mode' => 'payment',
'success_url' => $YOUR_DOMAIN.'/espace-client/projet/{CHECKOUT_SESSION_ID}/paiement/succes',
'cancel_url' => $YOUR_DOMAIN.'/espace-client/projet/{CHECKOUT_SESSION_ID}/erreur',
]
);
$billing->setStripeSessionId($checkout_session->id);
$this->entityManager->flush();
return $this->redirect($checkout_session->url);
}
I tryied everyting but user is disconnected always and i don't know why.
Payment, checkout all is ok

If your users get disconnected after the Stripe redirect, it usually means that one or more of the user’s cookies (such as their login or session cookie) are lost during the redirect process. And there are two common causes for this:
A mismatch between HTTPS and HTTP protocols after the redirect. For example if the user is browsing your website on HTTPS, but then gets redirected to a HTTP URL. To fix this, make sure all URLs involved use the same protocol.
The SameSite attribute is set to strict on the cookies used for login or session. If that’s the case, those cookies will not be included when the user is redirected back to your website because that redirect is considered a cross-site request. To fix this, change the SameSite attribute from strict to either lax or none. More info here.

Related

How to fix CORS Error using Livewire Stripe Prebuilt Checkout [duplicate]

This question already has an answer here:
Stripe Checkout example running into CORS error from localhost
(1 answer)
Closed 1 year ago.
I'm using a simple Stripe Prebuilt Checkout (HTML/PHP) within a Livewire component.
It works fine on local but not in production.
When I view the Network I see I get a Status: CORS Error then a Status: 403 error.
See image:
Here is my code:
Component:
<form wire:submit.prevent="checkout">
<x-assets.checkout-button type="submit" id="checkout-button">
Buy Now
</x-assets.checkout-button>
</form>
#push('stripe')
<script src="https://polyfill.io/v3/polyfill.min.js?version=3.52.1&features=fetch"></script>
<script src="https://js.stripe.com/v3/"></script>
#endpush
Then in the Server Side:
public function checkout()
{
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
header('Content-Type: application/json');
$YOUR_DOMAIN = config('app.url');
$checkout_session = \Stripe\Checkout\Session::create([
'billing_address_collection' => 'required',
'shipping_address_collection' => [
'allowed_countries' => ['US'],
],
'line_items' => [[
// Provide the exact Price ID (e.g. pr_1234) of the product you want to sell
'price' => $this->product->stripe_plan,
'quantity' => 1,
]],
'mode' => 'payment',
'allow_promotion_codes' => true,
'success_url' => url($YOUR_DOMAIN . '/product/thank-you'),
'cancel_url' => $YOUR_DOMAIN . '/product',
'automatic_tax' => [
'enabled' => false,
],
]);
header('HTTP/1.1 303 See Other');
header('Location: ' . $checkout_session->url);
return redirect($checkout_session->url);
}
These are the directions I'm following: https://stripe.com/docs/checkout/quickstart
I have a form element and I'm not using #CSRF because I'm using Livewire.
In my VerifyCsrfToken.php file I have the following for Livewire:
protected $except = [
'stripe/webhook',
'shippo/webhook',
'discord/webhook',
'livewire/*',
];
Is it something to do with the Headers in the Server side code? header('Content-Type: application/json');
You cannot send a redirect response to a fetch / XMLHttpRequest. You need to either change your client request pattern (eg, a form submit), or send the session url back to the client as data and redirect client-side instead.
See this previous answer for an example of this: https://stackoverflow.com/a/68630525

Cache issue - Refresh page (F5 button) to see modified content

I’m using FileCache in my cache component. In my controller, I’m using :
[
'class' => 'yii\filters\HttpCache',
'only' => ['view'],
'etagSeed' => function ($action, $params) {
$model = $this->findModel(Yii::$app->request->get("id"));
return $model->update_time;
},
],
With etagSeed and/or lastModified I got the same behavior issue:
The cache works well with yii\filters\PageCache and yii\filters\HttpCache. But when the cache is invalidated (for example after updating the page), I still see the old cached content.
So when I'm visiting the target page from other page, I'm getting the old content. But when I push F5, the page refreshs with the last content.
I've checked the Etag value on my GET request in the browser, and I've found that I'm getting the old Etag value until I refresh the page using the F5 button.
This can be a standard browser behavior when using HttpCache ? But how can I see the new content without refreshing each time the browser to get the last content ?
Am I missing something ?
You could use PageCache and dependency in your controller. Note, that if your page is public, you should be prepared probably to accept an empty ID.
public function behaviors()
{
return [
'viewCache' => [
'class' => 'backend\components\filters\PageCache',
'only' => ['view'],
'duration' => 86400, // 1 day
'variations' => [
$_GET['id'] ?? null,
],
'dependency' => [
'class' => 'yii\caching\DbDependency',
'sql' => 'SELECT update_time FROM my_table WHERE id = :id',
'params' => [
':id' => $_GET['id'] ?? null,
]
]
'enabled' => [
isset($_GET['id']),
],
],
];
}

Laravel auth user on webhook

I am having trouble with authenticated user in my Laravel/Vue app. Once you log in, you can choose to make a purchase via Stripe which leads you off the page, and returns back upon payment.
Just to make sure, I've made an endpoint:
Route::get('test', function(){
return Auth::user();
});
And before and after Stripe, when I hit it, I do get back the user. So authentication is in order.
What happens though is that Stripe upon payment event makes a webhook callback to my route:
Route::post('api/stripe/checkout-session-completed', 'StripeController#checkoutSessionCompleted');
Inside a hook, event is fired which should propagate number of credits purchased to the user who made the purchase, however I am always getting that Auth::user() is not defined.
use Illuminate\Support\Facades\Auth;
...
public function checkoutSessionCompleted()
{
...
$this->handleCheckout($session); // this is Stripe session object
...
}
private function handleCheckout($session)
{
...
event(new PaymentSuccessful($payment, Auth::user()));
...
}
Was this supposed to happen? How can I get the currently auth user if not like this?
Looks like sessions aren't shared when external source makes a POST request to your route. I made a workaround to include user ID within Stripe session metadata, so I can find user by that same ID when request returns via webhook.
$stripeSession = Session::create([
'success_url' => route('stripe.success') . '/?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('stripe.cancel'),
'payment_method_types' => ['card'],
'mode' => 'payment',
'line_items' => [
[
'price_data' => [
'currency' => 'eur',
'product' => env('STRIPE_PRODUCT_ID'),
'unit_amount' => $price->stripe_price * 100,
],
'description' => "Credits to receive: $price->quantity",
'quantity' => 1,
],
],
'metadata' => [
'quantity' => $price->quantity,
'user_id' => Auth::user()->id,
],
'customer_email' => optional(Auth::user())->email ?? null,
'client_reference_id' => optional(Auth::user())->id ?? null,
]);

Stripe: handle multiple Webhooks

I'm working on a project which has two types of products: subscriptions and event registration.
I'm using Stripe Checkout Session for both. As their process is different, I'm using two webhooks; one for each.
e.g:
http://example.com/payment/subscription_webhooks.php
http://example.com/payment/event_webhooks.php
The problem is that once a checkout session is completed, whatever if it's for subscriptions or event registration, both webhooks are triggered.
I'm looking for a solution to define which webhook should be triggered.
Have 1 end point for both session.complete triggers, but send metadata to the endpoint and throw an IF statement into your listener so you can see which session you're listening for.
Here is some code I wrote up.
Checkout Session A:
$checkout_session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'gbp',
'unit_amount' => '1000',
'product_data' => [
'name' => 'Example Product',
],
],
'quantity' => 1,
]],
'metadata' => ["session" => "session_a"],
'mode' => 'payment',
'success_url' => "https://www.example.co.uk/success",
'cancel_url' => "https://www.example.co.uk/cancel",
]);
Checkout Session B:
$checkout_session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'gbp',
'unit_amount' => '1000',
'product_data' => [
'name' => 'Example Product',
],
],
'quantity' => 1,
]],
'metadata' => ["session" => "session_b"],
'mode' => 'payment',
'success_url' => "https://www.example.co.uk/success",
'cancel_url' => "https://www.example.co.uk/cancel",
]);
Take note that the metadata is different in the 2 above examples.
Now, use SWITCH and CASE to find which one you're listening for.
Webhook
$payload = #file_get_contents('php://input');
$event = null;
try {
$event = \Stripe\Event::constructFrom(json_decode($payload, true));
} catch(\UnexpectedValueException $e) {
http_response_code(400);
exit();
}
switch($event->data->object->metadata['package']) {
case 'session_a':
// Do your Session A stuff, SQL etc
break;
case 'session_b':
// Do your Session B stuff, SQL etc
}
Some useful tips:
You can pass the session ID from your checkout.session start through to your success page or hook, then grab data...
Checkout session page
'success_url' => "https://www.example.co.uk/success?session_id={CHECKOUT_SESSION_ID}",
session_id={CHECKOUT_SESSION_ID} is exactly as I've written it, you don't actually need to enter the checkout session ID, just literally copy it exactly like its written.
Hook page or Success page
if($_GET['session_id']){
$session_id = $_GET['session_id'];
}
$stripe = new \Stripe\StripeClient('API key');
$session_data = $stripe->checkout->sessions->retrieve($session_id,[]);
$payment_status = $session_data->payment_status;
$payment_address0 = $session_data->shipping->name;
$payment_address1 = $session_data->shipping->address->line1;
$payment_address2 = $session_data->shipping->address->line2;
$payment_address3 = $session_data->shipping->address->city;
$payment_address4 = $session_data->shipping->address->country;
$payment_address5 = $session_data->shipping->address->postal_code;
$payment_buyer_email = $session_data->customer_details->email;
$payment_metadata = $session_data->metadata->user_id;
You can grab data, just checkout the Stripe docs for the JSON response.
Anyone learning Stripe, all seems real confusing, but keep trying, testing, var_dump your results etc and you'll soon see what's happening.
You only need 1 webhook endpoint, then you handle switch case (or if else) for each event of these subscription (subscriptions and event registration in your case).
And i as you, i don't know how to distinguish what event for subscriptions, and what for event registration.
You can create a webhook endpoint on Stripe through the dashboard or through the API [1] and when doing so specify which types of events you want to be notified about.
You'll likely want to remove one of those webhook endpoints and use only one for all checkout.session.completed events.
[1] https://stripe.com/docs/api/webhook_endpoints/create

Need to clear Google OAuth cache?

I set up the Google OAuth with Hybridauth and it works from my development folder e.g. http://example.com/dev and callback is located at http://example.com/dev/callback.php
Now I want to move the dev folder to the production folder e.g. http:/example.com/backend and callback is changed to http://example.com/backend/callback.php
I changed the Authorized redirect URIs from Google console, but Google keeps the old callback link when I do log in.
Is it necessary to clear cache from Google or wait for an amount of time to let Google update the changes?
$config = [ 'callback' => 'example.com/backedn/callback.php',
'providers' => [ 'Google' => [ 'enabled' => true,
'keys' => [ 'id' => 'xxxxx.apps.googleusercontent.com',
'secret' => 'xxxxxxx', ],
'scope' => 'email', ], ], ];
Sorry for my careless mistake as I call the old script from the popup
function auth_popup( provider ){
// replace 'path/to/hybridauth' with the real path to this script
var authWindow = window.open('http://example.com//dev/callback.php?provider='+provider, 'authWindow', 'width=600,height=400,scrollbars=yes');
return false;
}

Categories