How to Add Stripe Checkout to a Custom PHP Website (No Plugins)
💳 PHP · Updated June 2026 · 12 min read · by Jacob Campbell
Stripe is the cleanest payments API available, and wiring it straight into PHP means no monthly SaaS fees, no WooCommerce bloat, and none of Shopify’s per-transaction cut. What follows is the exact implementation running in real BuiltToWinWeb client stores: plain PHP, the official Stripe SDK, secure webhooks, and automated email confirmations. You’ll see how the pieces fit together — checkout session, redirect, webhook verification, fulfilment — so you can take live payments without handing a slice to a platform. Everything here is production code, not a throwaway demo.
Key facts
- 0% — Platform fee / txn
- 98 — Checkout Lighthouse
- 0.6s — Checkout LCP
- $0/mo — Platform cost
Why native PHP + Stripe beats any page builder
Shopify charges 0.5–2% per transaction on top of Stripe’s own fee. WooCommerce adds 14+ plugin dependencies that drag your checkout LCP to 3.8s. A custom PHP integration delivers a checkout that loads in under 0.6s and keeps every cent of margin.
- Zero platform transaction fees — just Stripe’s 2.9% + 30¢.
- Checkout Lighthouse score: 98 vs Shopify’s typical 61.
- Full webhook control — custom fulfilment, inventory, email.
- No recurring ecommerce-platform subscription.
- You own the code, host anywhere, migrate freely.
Step 1 — install the Stripe PHP SDK
Composer is the cleanest approach. If your server doesn’t support Composer, download the SDK ZIP and drop it in your project root.
composer require stripe/stripe-php
Then load it at the top of your checkout script:
require_once __DIR__. '/vendor/autoload.php';
\Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET_KEY'));
Step 2 — create a Checkout session (server-side)
Never put your secret key in the browser. Create a server-side endpoint the frontend POSTs to:
<?php
require_once __DIR__. '/vendor/autoload.php';
\Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET_KEY'));
$session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'usd',
'unit_amount' => 175000,
'product_data' => ['name' => 'Business Pro Website'],
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => 'https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://yoursite.com/pricing',
]);
header('Content-Type: application/json');
echo json_encode(['url' => $session->url]);
Step 3 — redirect the user from the frontend
A simple fetch call on your pricing-page button:
document.getElementById('buy-btn').addEventListener('click', async () => {
const res = await fetch('/create-checkout-session.php', { method: 'POST' });
const data = await res.json();
window.location.href = data.url;
});
Step 4 — handle webhooks (order fulfilment)
Stripe POSTs to your webhook endpoint when payment completes. This is where you trigger email, update inventory and record the order — NOT on the success page (the user may never reach it).
<?php
$payload = file_get_contents('php://input');
$sig = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$secret = getenv('STRIPE_WEBHOOK_SECRET');
try {
$event = \Stripe\Webhook::constructEvent($payload, $sig, $secret);
} catch (\Exception $e) {
http_response_code(400); exit;
}
if ($event->type === 'checkout.session.completed') {
$session = $event->data->object;
fulfill_order($session);
}
http_response_code(200);
Register the webhook URL in the Stripe dashboard under Developers → Webhooks. Test locally with stripe listen --forward-to localhost/webhook.php.
Step 5 — send a confirmation email with PHPMailer
Inside your fulfill_order() function, get the customer email from the session and run PHPMailer:
function fulfill_order($session) {
$email = $session->customer_details->email;
$name = $session->customer_details->name;
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.yourhost.com';
$mail->Username = getenv('SMTP_USER');
$mail->Password = getenv('SMTP_PASS');
$mail->setFrom('hello@yoursite.com', 'Your Business');
$mail->addAddress($email, $name);
$mail->Subject = 'Payment received — thank you!';
$mail->Body = "Hi $name, your order is confirmed.";
$mail->send();
}
Security checklist
- Store keys in environment variables — never hard-code them in PHP files.
- Always verify Stripe webhook signatures with
constructEvent(). - Use HTTPS everywhere — Stripe requires it.
- Idempotency: check whether the order already exists before processing.
- Rate-limit
/create-checkout-session.php(e.g. 10 requests/hour per IP).
Custom PHP + Stripe vs Shopify vs WooCommerce
| Metric | Custom PHP + Stripe | Shopify | WooCommerce |
|---|---|---|---|
| Checkout LCP | 0.6s | 1.9s | 2.8s |
| Lighthouse score | 98 | 61 | 55 |
| Platform fee / txn | 0% | 0.5–2% | 0%* |
| Monthly platform cost | $0 | $39–399 | $0* |
| Plugin dependencies | 0 | n/a | 14+ |
| Code ownership | 100% | 0% | ~60% |
Real business impact
On a store doing $50,000/month in sales, Stripe’s 2.9% + 30¢ is unavoidable. But Shopify’s extra 0.5–2% platform fee costs $250–1,000 more per month. Custom PHP removes that.
- Shopify Advanced ($399/mo) + 0.5% on $50,000 = $649/mo in platform costs.
- Custom PHP + Stripe = $0/mo in platform costs (just Stripe’s standard fees).
- Break-even on a $5,600 store: under 9 months.
- Checkout pages load 3× faster = measurably lower cart-abandonment.
The math is simple: if you process more than $15,000/month, a custom PHP store pays for itself in under a year.
Sources & further reading
Related services
- Lighthouse 100 — Why the custom checkout scores 98+.
- What makes a website load fast — The speed that lifts checkout conversion.
- Migrate off WooCommerce — Move to a custom store without losing SEO.
Frequently asked questions
Do I need Composer to use Stripe with PHP?
Composer is recommended but not required. You can download the Stripe PHP SDK as a ZIP and include it manually with require_once; Composer just makes updates easier.
Is it safe to process payments without a platform like Shopify?
Yes. Stripe is PCI-DSS Level 1 certified. Your server never handles raw card numbers — Stripe’s JS tokenises them client-side, and your PHP only receives a session ID.
How do I test Stripe without charging real cards?
Use Stripe test mode with an sk_test_ key. Use test card 4242 4242 4242 4242 with any future expiry and any 3-digit CVC.
Can I add subscriptions with this approach?
Yes. Change the Checkout session mode from 'payment' to 'subscription' and pass a recurring price ID instead of price_data; Stripe handles the billing cycles.
How much does a custom Stripe store cost?
BuiltToWinWeb ecommerce stores are a flat $5,600 one-time fee — no monthly platform costs and you own the code forever.
Why is a custom checkout faster than Shopify?
No theme runtime, no app scripts and no plugin bloat — just the code the checkout needs, so LCP lands near 0.6s versus ~1.9s on Shopify.
Do you build custom Stripe stores?
Yes — I build custom PHP ecommerce with native Stripe, webhooks, email confirmation and 98+ Lighthouse, typically delivered in 3–4 weeks.
Want a custom Stripe store built for you?
BuiltToWinWeb builds custom PHP ecommerce stores with native Stripe integration, 98+ Lighthouse scores and zero monthly platform fees. One flat fee — you own the code forever, usually delivered in 3–4 weeks.
Top comments (0)