Stripe in C#, part 4: Checkout Sessions — the whole payment page, hosted
Part 4from the Stripe in C# series · 6 parts in all
Sometimes the right integration is no integration: Stripe Checkout is a hosted payment page — card fields, wallet buttons, 3DS handling, localization, and PCI scope all outsourced. You create a Session server-side, redirect the browser to Stripe's URL, and receive the customer back at a URL you chose. For MapleCart's store this collapsed two sprints of work into an afternoon.
Creating the session
using Stripe.Checkout;
var sessionService = new SessionService();
var session = sessionService.Create(new SessionCreateOptions
{
// The mode IS the contract: payment = one-off, setup = save card only,
// subscription = recurring (Stripe Billing drives it from here).
Mode = "payment",
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions
{
PriceData = new SessionLineItemPriceDataOptions
{
Currency = "usd",
UnitAmount = 4599, // minor units again
ProductData = new SessionLineItemPriceDataProductDataOptions
{
Name = "Canvas tote bag",
Description = "Heavy 16oz cotton, natural",
},
},
Quantity = 2,
},
},
SuccessUrl = "https://maplecart.example/order/thanks?session_id={CHECKOUT_SESSION_ID}",
CancelUrl = "https://maplecart.example/cart",
CustomerEmail = "[email protected]", // or Customer = cus_... if known
Metadata = new Dictionary<string, string> { { "orderId", "1042" } },
});
return Redirect(session.Url); // browser goes to Stripe, comes back after
What you get for free — and the two traps
Free: Apple Pay/Google Pay, card-element UX, 3DS, receipts, tax optional. Trap one: the
success redirect is not a receipt — the customer can close the
tab before redirecting, so fulfillment must key off checkout.session.completed
and/or the PaymentIntent webhook, not off the redirect (part 6 builds the receiver).
Trap two: sessions expire (24h default; configurable, minimum 30 minutes) — abandoned
sessions are normal and fine.
Verifying the return trip
// On the success page: fetch the session server-side. Never parse the URL
// client-side and trust it - amounts and status are server-side facts.
var s = sessionService.Get(Request.Query["session_id"]);
if (s.PaymentStatus == "paid")
{
var orderId = s.Metadata["orderId"]; // correlation back to your domain
ShowThanks(orderId); // still: fulfill via webhook only
}
Payment Links: checkout without any code
For a fixed-price product, the dashboard can mint a
payment link — a durable URL with the same session semantics, zero code. The
session API remains the answer the moment quantity, metadata, or per-order logic enters.
Next: the aftermath — refunds, disputes, and invoices.