UPDATED I built a pricing page that uses Stripe Checkout to use both a One-Time payment button for product 1 and a Subscription payment button for product 2.

My goal is to redirect the one time payment button to Stripe Checkout with a one time payment, and separately redirect the subscription payment to a checkout with a recurring payment.
According to STRIPE this can be done using Subscription as the Mode in the CheckoutSession in create-checkout-session.php (sample project) :
The mode of the Checkout Session. Required when using prices or setup mode. Pass subscription if the Checkout Session includes at least one recurring item.
Contrary to the Stripe Docs the following line of code: 'mode' => 'subscription', activates subscription payments ONLY, but it doesnt redirect one time payments. For one-time payments to work I must change it to: 'mode' => 'payment', but then subscription payments don't work.
Here's the php code in question:
<?php
require_once 'shared.php';
$domain_url = $config['domain'];
// 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'],
'mode' => 'subscription',
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]]
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
And here's the javascript code:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId
})
}).then(function(result) {
return result.json();
});
};
// Handle any errors returned from Checkout
var handleResult = function(result) {
if (result.error) {
var displayError = document.getElementById("error-message");
displayError.textContent = result.error.message;
}
};
/* Get your Stripe publishable key to initialize Stripe.js */
fetch("./config.php")
.then(function(result) {
return result.json();
})
.then(function(json) {
var publishableKey = json.publishableKey;
var subscriptionPriceId = json.subscriptionPrice;
var onetimePriceId = json.onetimePrice;
var stripe = Stripe(publishableKey);
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("subscription-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(subscriptionPriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("onetime-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(onetimePriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
});
Is it even possible to have both one time payments and recurring payments on the same page with Stripe Checkout? How can I accomplish this?
Update according to Bemn:
$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'],
'mode' => $body->mode
'line_items' => [[
'price' => $body->price_xxx,
// For metered billing, do not pass quantity
'quantity' => 1,
]],
'line_items' => [[
'price' => $body->price_zzz,
// For metered billing, do not pass quantity
'quantity' => 1,
]]
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
And the JS:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId, mode) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
mode: mode // <-- passing the mode, e.g. 'payment' or 'subscription'
})
}).then(function(result) {
return result.json();
});
};
And the HTML:
<div data-stripe-priceid="pricexxx" data-stripe-mode="payment" id="onetime-btn" class="bold mt-2 d-inline-block w-100-after-md max-width-xxs py-2 btn btn-secondary">Ore Time</div>
<div data-stripe-priceid="pricexxx" data-stripe-mode="subscription" id="subscription-btn" class="bold mt-2 d-inline-block w-100-after-md max-width-xxs py-2 btn btn-secondary">Ore Time</div>
Yes. The key is you should pass the correct options to generate the corresponding Stripe Checkout session ID.
Backend: Have a function to accept Stripe's price ID and payment mode as input and return a Stripe Checkout session ID as the output.
Frontend: Pass payment mode information to /create-checkout-session.php. (see the Note if you are unable to do so)
The following solution assuming that:
.createCheckoutSession() in js frontend.I think you are close. What you need to do is passing the mode information to your API endpoint as well:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId, mode) { // <-- add a mode parameter
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
mode: mode // <-- passing the mode, e.g. 'payment' or 'subscription'
})
}).then(function(result) {
return result.json();
});
};
If so, each checkout button in the page should have corresponding info of the priceId and payment mode. You can do so by storing them using data attribute:
<div data-stripe-priceid="price_yyy" data-stripe-mode="subscription">Recurrent</div>
<div data-stripe-priceid="price_zzz" data-stripe-mode="payment">1-time</div>
If so, you can get the data attributes by e.g. a click event.
Note: If you cannot add an extra param to indicate mode, you neeed to identify if the given price ID is a 1-time or recurrent product in the backend. See https://stripe.com/docs/api/prices/object?lang=php#price_object-type for more details.
Here are 2 sample code snippets from Stripe's documentation. Direct copying of them does not work.
Reference for PAY: https://stripe.com/docs/checkout/integration-builder
$checkout_session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'usd',
'unit_amount' => 2000,
'product_data' => [
'name' => 'Stubborn Attachments',
'images' => ["https://i.imgur.com/EHyR2nP.png"],
],
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => $YOUR_DOMAIN . '/success.html',
'cancel_url' => $YOUR_DOMAIN . '/cancel.html',
]);
In your case, you may not need to define 'price_data'. Instead, you should use 'price', like the next example.
Reference for SUB: https://stripe.com/docs/billing/subscriptions/checkout#create-session
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => 'https://example.com/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://example.com/canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'line_items' => [[
'price' => $body->priceId,
// For metered billing, do not pass quantity
'quantity' => 1,
]],
]);
line_items, you can just simply using 'price' and pass the price ID (e.g. price_xxx), which means your 'line_items' will looks like this:'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]],
For 'mode', use the value from your API request. It should be something like:
'mode' => $body->mode
Which means in your backend you better define a function (e.g. generate_checkout_session) to:
priceId and mode from the parsed datapriceId and mode in \Stripe\Checkout\Session::create andcheckout_session IDHope this (and the reference urls) can help you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With