Stripe: handle multiple Webhooks - php

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

Related

Trying to charge before or after Subscription Schedule is created Stripe API

I am developing a wordpress website for a client.
He needs different types of packages. For most of these packages I developed a simple Stripe checkout webpage, using its documentation.
The problem is that I need this workflow:
first month x dollars
second month x dollars
after one year subscription y dollars
I've already done this using Subscription Schedule. But it needs a customer ofc. How can I charge before and after charging create this Subscription Schedule? I don't know how to deal with this, how to charge using Stripe checkout simple already built page or do I need to create one by myself, where user needs to add his card, pay, and get the customer_id?
function checkout3() {
// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here: https://dashboard.stripe.com/apikeys
\Stripe\Stripe::setApiKey('sk_test_51e7DRPLRnISGb5vSFxnvvuDx1GzhlBIFeazcmpEevsUFf29jHXJ1YgE2xaJ1lGfzjtKzE8uoN0eR9Klaq00CnMFWvfB');
// The price ID passed from the front end.
// $priceId = $_POST['priceId'];
$priceId = 'price_1LPahmIne7DRPLRnFXV6Uz34';
$futureDate= strtotime(date('Y-m-d', strtotime('+1 year')));
$customer = \Stripe\Customer::create(
[
'description' => 'My First Test Customer (created for API docs at https://www.stripe.com/docs/api)',
]
);
$session = \Stripe\SubscriptionSchedule::create([
'customer' => $customer["id"],
'start_date' => 'now',
'end_behavior' => 'release',
'phases' => [
[
'items' => [
[
'price' => 'price_1LRF5CIne7DRPLRnwuLVE2pu',
'quantity' => 1,
],
],
//'end_date' => $futureDate,
'iterations' => 1,
],
[
'items' => [
[
'price' => 'price_1LRF5cIne7DRPLRngevvIZiw',
'quantity' => 1,
],
],
'iterations' => 1,
],
[
'items' => [
[
'price' => 'price_1LPujQIne7DRPLRnj3EOweJN',
'quantity' => 1,
],
],
],
],
]);
// Redirect to the URL returned on the Checkout Session.
// With vanilla PHP, you can redirect with:
//header("HTTP/1.1 303 See Other");
//header("Location: " . '$session->url');
}
So right now, the subscription schedule is added to the Stripe dashboard, the page it's keep loading infinitely, but without charging... How to deal with this?
public static function firebase_checkout3_func() {
$html = "";
$html .= "<form id='firebase-checkout' action='/wp-json/api/checkout2' method='POST'>
<button type='submit' id='checkout-button'>Începe acum</button>
</form>";
return $html;
}
Use Stripe hosted Checkout Page with setup mode to collect
a Customer's SetupIntent
Retrieve the Payment Method inside the SetupIntent
Set it as the customer's invoice_settings.default_payment_method
Create a Subscription Schedule with that Customer Id as normal

Stripe accessing the metadata within a PHP script

I'm trying to retrieve a line of metadata (order_no) within Stripe's checkout.session.completed event but I don't know how to go about it. I'm using PHP. I don't know how to query the event to retrieve the checkout.session object from within a PHP script, so I can read the metadata. Could someone please show by example how to achieve that without installing addons such as Slim, etc. I have looked at multiple examples on this site but have not been able to understand them. Thanks in advance for any help you can give.
header('Content-Type: application/json');
$order = $mysqli->escape_string(md5(rand(0,20)));
$amt = '299';
$checkout_session = \Stripe\Checkout\Session::create(
[
'payment_intent_data'=>['description' =>'my purchases'],
'metadata' => [
'order_id' => $order
],
'line_items' => [
[
'name' => 'My Products',
'description' => 'My online products',
'currency' => 'gbp',
'amount' => $amt,
'quantity' => 1
]],
'success_url' => $MySite.'success.php',
'cancel_url' => $MySite.'cancel.html'
]);
header("HTTP/1.1 303 See Other");
header("Location: " . $checkout_session->url);
}

PHP Stripe: You can not pass `payment_intent_data` in `subscription` mode

I'm trying to create a subscription for the merchant's users but facing "You can not pass payment_intent_data in subscription mode" error. With regular payments, it works well, but subscriptions aren't working.
Here is an example of what I want to do: John has an e-commerce shop based on recurring billing. Matthew is John's customer and wants to purchase a subscription from John. How can I easily take fees and transfer money to John's connect account while using "Stripe Checkout"?
$session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price' => $priceIntent->id,
'quantity' => 1,
]],
'customer' => Auth::User() -> stripe_code,
'mode' => 'subscription',
'payment_intent_data' => [
'application_fee_amount' => $total_fees,
'transfer_data' => [
'destination' => $merchantId,
],
],
'success_url' => env('APP_URL') . '/order/success/{CHECKOUT_SESSION_ID}',
'cancel_url' => env('APP_URL') . '/order/cancel/',
]);
Thanks!
Basically, you can't use payment_intent_data on a Checkout Session in subscription mode, since the subscription creates invoices instead of PaymentIntents.
To do this, you need to use the subscription_data hash: https://stripe.com/docs/api/checkout/sessions/create?lang=php#create_checkout_session-subscription_data-application_fee_percent and specify the merchant account.
Example of the API call with merchant details:
$session = \Stripe\Checkout\Session::create([
'subscription_data' => [
'application_fee_percent' => $fees_percent,
],
],array("stripe_account" => "acct_xxxxxxxxx"));
Also, don't forget to pass all the other required variables in the call.
Cheers :)

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 checkout one-time payments php?

I am trying to set Stripe checkout for my website.
I created and verified my Stripe account, and now I need to set up the checkout page. I downloaded from Github the PHP stripe library (I have PHP 5.6.4, so compatibility is OK). Then I went in the Stripe documentation and found this page.
I created a new page for the checkout, included the init.php from the Stripe library and pasted in the page the code found in the documentation page. When I open it, it gives back an empty page but I don't know why.
I don't know if this is the right procedure, but I searched online for like 2 hours finding nothing, and the documentation doesn't seem clear to me. Can someone help me?
I checked and the page doesn't generate errors. The code of the page is this:
<?php
require_once('assets/stripe/init.php');
\Stripe\Stripe::setApiKey('sk_test_OW6K5e96gNXbAhEvPo15IB3C');
$session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'name' => 'T-shirt',
'description' => 'Comfortable cotton t-shirt',
'images' => ['https://example.com/t-shirt.png'],
'amount' => 500,
'currency' => 'eur',
'quantity' => 1,
]],
'success_url' => 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://example.com/cancel',
]);
?>
This is what I copied in the page, still don't know if there's something to change or some other things to add.
<?php
require_once 'shared.php';
$domain_url = $config['domain'];
$base_price = $config['base_price'];
$currency = $config['currency'];
$quantity = $body->quantity;
// Create new Checkout Session for the order
// Other optional params include:
// [billing_address_collection] - to display billing address details on the page
// [customer] - if you have an existing Stripe Customer ID
// [payment_intent_data] - lets capture the payment later
// [customer_email] - lets you prefill the email input in the form
// For full details see https://stripe.com/docs/api/checkout/sessions/create
// ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'line_items' => [[
'name' => 'Pasha photo',
'images' => ["https://picsum.photos/300/300?random=4"],
'quantity' => $quantity,
'amount' => $base_price,
'currency' => $currency
]]
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
for more details and demo code :
https://github.com/stripe-samples/checkout-one-time-payments/tree/master/client-and-server/server/php/public

Categories