Author: Abbas Can Ovacık
A Stripe PaymentIntents integration example using PHP 8.2, Apache, and Docker. Covers the full payment flow — card input, billing details, 3D Secure authentication, webhook handling, and CSRF protection. Intended as a learning reference.
- Stripe Elements for secure card input
- Billing address and contact information collection
- Payment processing via Stripe PaymentIntents API
- 3D Secure (3DS) redirect and completion handling
- CSRF protection on payment endpoint
- Stripe webhook support (
payment_intent.succeeded,payment_intent.payment_failed) - File-based request logging (
logs/app-YYYY-MM-DD.log) - SweetAlert2 for user-friendly success/error notifications
- Phone number input masking with Inputmask
- ISO 3166-1 country code dropdown (required by Stripe)
- Dockerized PHP 8.2 + Apache environment
stripe-api-sample/
├── public/ # Apache document root
│ ├── card-payment.html # Payment form UI
│ ├── payment.js # Stripe Elements handler, form submission
│ ├── payment.css # Payment form styles
│ ├── create.php # POST — creates and confirms a PaymentIntent
│ ├── complete.html # Post-payment result page
│ ├── complete.js # Fetches payment status, renders result
│ ├── complete.css # Result page styles
│ ├── complete.php # GET — checks status via ?payment_intent=
│ ├── status.php # POST — checks status from request body
│ ├── public_key.php # GET — returns Stripe publishable key
│ ├── csrf.php # GET — issues a session CSRF token
│ └── webhook.php # POST — handles Stripe webhook events
├── src/
│ └── Logger.php # File logger (writes to logs/)
├── logs/ # Runtime logs — git-ignored, .gitkeep present
├── secrets.php # .env loader + Stripe key provider
├── .env # Environment variables — NOT tracked in git
├── .env.example # Environment variables template
├── composer.json # PHP dependencies (Stripe PHP SDK)
├── Dockerfile # PHP 8.2 + Apache
└── docker-compose.yml # Development environment
cp .env.example .envFill in your Stripe test keys from the Stripe Dashboard:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_... # see Webhook Setup below
APP_URL=http://localhost:8080docker compose run --rm composer installdocker compose up -dOpen http://localhost:8080/card-payment.html.
Install the Stripe CLI, then:
stripe login
stripe listen --forward-to localhost:8080/webhook.phpCopy the whsec_... secret shown in the output into your .env, then restart:
docker compose restartTest with:
stripe trigger payment_intent.succeededSuccessful events appear in logs/app-YYYY-MM-DD.log.
Use these Stripe test cards (any future expiry, any CVC):
| Scenario | Card Number |
|---|---|
| Successful payment | 4242 4242 4242 4242 |
| Card declined | 4000 0000 0000 0002 |
| Insufficient funds | 4000 0000 0000 9995 |
| 3D Secure required | 4000 0025 0000 3155 |
Monitor logs in real time:
tail -f logs/app-$(date +%Y-%m-%d).log| Exception | Client message | Logged |
|---|---|---|
CardException (card declined, insufficient funds, etc.) |
Stripe's user-facing message | Yes (INFO) |
InvalidRequestException |
"Invalid payment request." | Yes (ERROR) |
AuthenticationException |
"Payment service error." | Yes (ERROR) |
Any other \Throwable |
"An unexpected error occurred." | Yes (ERROR) |
Internal error details are never exposed to the client.
- CSRF tokens are issued via
csrf.phpand validated increate.phpon every payment request. payment_intent_idinputs are validated against/^pi_[a-zA-Z0-9_]+$/before being sent to Stripe.- Webhook requests are verified using
\Stripe\Webhook::constructEvent(). logs/is git-ignored and sits outside the Apache document root (public/).- This project is for educational and testing purposes — not production-ready without additional hardening (authentication, database, rate limiting).
- Stripe PHP SDK — Server-side payment processing
- Stripe.js & Elements — Secure client-side card input
- Stripe PaymentIntents API — Payment handling
- SweetAlert2 — Alert notifications
- Inputmask — Phone number formatting
- Docker — Containerisation
MIT License — see LICENSE for details.
Copyright (c) 2026–present Abbas Can Ovacık