Stripe Payment System Integration
Stripe is de-facto standard for international payments. Documentation exhaustive, SDK covers all major languages, Stripe CLI simplifies local development. Works with Visa, Mastercard, Amex, Apple Pay, Google Pay, SEPA, iDEAL and dozens other methods depending on region.
Important: since March 2022 Stripe suspended work with Russian legal entities. Integration actual for Belarusian, Kazakhstani, European and international businesses.
Installation and Configuration
composer require stripe/stripe-php
// config/services.php
'stripe' => [
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
],
Keys: pk_test_... / sk_test_... for testing, pk_live_... / sk_live_... for production.
Payment Intent — Modern Approach
Stripe recommends using Payment Intents API instead of deprecated Charges API. Payment Intent describes payment intention and manages 3D Secure automatically.
Server — creating Payment Intent:
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => 1500, // in smallest units (cents, kopecks)
'currency' => 'eur',
'metadata' => ['order_id' => $order->id],
'automatic_payment_methods' => ['enabled' => true],
]);
return response()->json([
'clientSecret' => $paymentIntent->client_secret,
]);
Client — Stripe Elements:
import { loadStripe } from '@stripe/stripe-js';
import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';
const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_KEY);
function CheckoutForm({ clientSecret }: { clientSecret: string }) {
const stripe = useStripe();
const elements = useElements();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!stripe || !elements) return;
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: `${window.location.origin}/payment/complete`,
},
});
if (error) {
console.error(error.message);
}
};
return (
<form onSubmit={handleSubmit}>
<PaymentElement />
<button type="submit">Pay</button>
</form>
);
}
export default function Payment({ clientSecret }: { clientSecret: string }) {
return (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<CheckoutForm clientSecret={clientSecret} />
</Elements>
);
}
PaymentElement automatically shows suitable payment methods depending on customer location and Dashboard settings.
Webhook
For local development: stripe listen --forward-to localhost:8000/webhook/stripe
public function handleWebhook(Request $request): Response
{
$payload = $request->getContent();
$sigHeader = $request->header('Stripe-Signature');
try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$sigHeader,
config('services.stripe.webhook_secret')
);
} catch (\Stripe\Exception\SignatureVerificationException $e) {
return response('Invalid signature', 400);
}
match ($event->type) {
'payment_intent.succeeded' => $this->handleSuccess($event->data->object),
'payment_intent.payment_failed' => $this->handleFailed($event->data->object),
default => null,
};
return response('OK', 200);
}
private function handleSuccess(\Stripe\PaymentIntent $intent): void
{
$orderId = $intent->metadata->order_id;
Order::where('id', $orderId)->update([
'status' => 'paid',
'transaction_id' => $intent->id,
]);
}
Refunds
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
$refund = \Stripe\Refund::create([
'payment_intent' => $paymentIntentId,
'amount' => 750, // partial; remove for full
]);
Stripe CLI for Development
# Installation
brew install stripe/stripe-cli/stripe
# Forward webhooks to local server
stripe listen --forward-to http://localhost:8000/webhook/stripe
# Send test event
stripe trigger payment_intent.succeeded
Test cards: 4242424242424242 — success, 4000000000009995 — insufficient funds, 4000002760003184 — requires 3D Secure.
Checkout Session — Alternative for Simple Cases
If custom UI not needed, Stripe Checkout Session provides ready payment page:
$session = \Stripe\Checkout\Session::create([
'line_items' => [[
'price_data' => [
'currency' => 'eur',
'unit_amount' => 1500,
'product_data' => ['name' => 'Product 1'],
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://example.com/cancel',
'metadata' => ['order_id' => $order->id],
]);
return redirect($session->url);
Stripe account activation takes 1–2 days for most jurisdictions. For Belarusian individuals and entities — registration possible via European representative.







