- Stripe Integration
- Master Stripe payment processing integration for robust, PCI-compliant payment flows including checkout, subscriptions, webhooks, and refunds.
- When to Use This Skill
- Implementing payment processing in web/mobile applications
- Setting up subscription billing systems
- Handling one-time payments and recurring charges
- Processing refunds and disputes
- Managing customer payment methods
- Implementing SCA (Strong Customer Authentication) for European payments
- Building marketplace payment flows with Stripe Connect
- Core Concepts
- 1. Payment Flows
- Checkout Sessions
- Recommended for most integrations
- Supports all UI paths:
- Stripe-hosted checkout page
- Embedded checkout form
- Custom UI with Elements (Payment Element, Express Checkout Element) using
- ui_mode='custom'
- Provides built-in checkout capabilities (line items, discounts, tax, shipping, address collection, saved payment methods, and checkout lifecycle events)
- Lower integration and maintenance burden than Payment Intents
- Payment Intents (Bespoke control)
- You calculate the final amount with taxes, discounts, subscriptions, and currency conversion yourself.
- More complex implementation and long-term maintenance burden
- Requires Stripe.js for PCI compliance
- Setup Intents (Save Payment Methods)
- Collect payment method without charging
- Used for subscriptions and future payments
- Requires customer confirmation
- 2. Webhooks
- Critical Events:
- payment_intent.succeeded
-
- Payment completed
- payment_intent.payment_failed
-
- Payment failed
- customer.subscription.updated
-
- Subscription changed
- customer.subscription.deleted
-
- Subscription canceled
- charge.refunded
-
- Refund processed
- invoice.payment_succeeded
-
- Subscription payment successful
- 3. Subscriptions
- Components:
- Product
-
- What you're selling
- Price
-
- How much and how often
- Subscription
-
- Customer's recurring payment
- Invoice
- Generated for each billing cycle 4. Customer Management Create and manage customer records Store multiple payment methods Track customer metadata Manage billing details Quick Start import stripe stripe . api_key = "sk_test_..."
Create a checkout session
session
stripe . checkout . Session . create ( line_items = [ { 'price_data' : { 'currency' : 'usd' , 'product_data' : { 'name' : 'Premium Subscription' , } , 'unit_amount' : 2000 ,
$20.00
'recurring' : { 'interval' : 'month' , } , } , 'quantity' : 1 , } ] , mode = 'subscription' , success_url = 'https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}' , cancel_url = 'https://yourdomain.com/cancel' )
Redirect user to session.url
print ( session . url ) Payment Implementation Patterns Pattern 1: One-Time Payment (Hosted Checkout) def create_checkout_session ( amount , currency = 'usd' ) : """Create a one-time payment checkout session.""" try : session = stripe . checkout . Session . create ( line_items = [ { 'price_data' : { 'currency' : currency , 'product_data' : { 'name' : 'Blue T-shirt' , 'images' : [ 'https://example.com/product.jpg' ] , } , 'unit_amount' : amount ,
Amount in cents
} , 'quantity' : 1 , } ] , mode = 'payment' , success_url = 'https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}' , cancel_url = 'https://yourdomain.com/cancel' , metadata = { 'order_id' : 'order_123' , 'user_id' : 'user_456' } ) return session except stripe . error . StripeError as e :
Handle error
print ( f"Stripe error: { e . user_message } " ) raise Pattern 2: Elements with Checkout Sessions def create_checkout_session_for_elements ( amount , currency = 'usd' ) : """Create a checkout session configured for Payment Element.""" session = stripe . checkout . Session . create ( mode = 'payment' , ui_mode = 'custom' , line_items = [ { 'price_data' : { 'currency' : currency , 'product_data' : { 'name' : 'Blue T-shirt' } , 'unit_amount' : amount , } , 'quantity' : 1 , } ] , return_url = 'https://yourdomain.com/complete?session_id={CHECKOUT_SESSION_ID}' ) return session . client_secret
Send to frontend
const
stripe
=
Stripe
(
"pk_test_..."
)
;
const
appearance
=
{
theme
:
"stripe"
}
;
const
checkout
=
stripe
.
initCheckout
(
{
clientSecret
,
elementsOptions
:
{
appearance
}
,
}
)
;
const
loadActionsResult
=
await
checkout
.
loadActions
(
)
;
if
(
loadActionsResult
.
type
===
"success"
)
{
const
{
actions
}
=
loadActionsResult
;
const
session
=
actions
.
getSession
(
)
;
const
button
=
document
.
getElementById
(
"pay-button"
)
;
const
checkoutContainer
=
document
.
getElementById
(
"checkout-container"
)
;
const
emailInput
=
document
.
getElementById
(
"email"
)
;
const
emailErrors
=
document
.
getElementById
(
"email-errors"
)
;
const
errors
=
document
.
getElementById
(
"confirm-errors"
)
;
// Display a formatted string representing the total amount
checkoutContainer
.
append
(
Total:
${
session
.
total
.
total
.
amount
}
)
;
// Mount Payment Element
const
paymentElement
=
checkout
.
createPaymentElement
(
)
;
paymentElement
.
mount
(
"#payment-element"
)
;
// Store email for submission
emailInput
.
addEventListener
(
"blur"
,
(
)
=>
{
actions
.
updateEmail
(
emailInput
.
value
)
.
then
(
(
result
)
=>
{
if
(
result
.
error
)
emailErrors
.
textContent
=
result
.
error
.
message
;
}
)
;
}
)
;
// Handle form submission
button
.
addEventListener
(
"click"
,
(
)
=>
{
actions
.
confirm
(
)
.
then
(
(
result
)
=>
{
if
(
result
.
type
===
"error"
)
errors
.
textContent
=
result
.
error
.
message
;
}
)
;
}
)
;
}
Pattern 3: Elements with Payment Intents
Pattern 2 (Elements with Checkout Sessions) is Stripe's recommended approach, but you can also use Payment Intents as an alternative.
def
create_payment_intent
(
amount
,
currency
=
'usd'
,
customer_id
=
None
)
:
"""Create a payment intent for bespoke checkout UI with Payment Element."""
intent
=
stripe
.
PaymentIntent
.
create
(
amount
=
amount
,
currency
=
currency
,
customer
=
customer_id
,
automatic_payment_methods
=
{
'enabled'
:
True
,
}
,
metadata
=
{
'integration_check'
:
'accept_a_payment'
}
)
return
intent
.
client_secret
Send to frontend
// Mount Payment Element and confirm via Payment Intents const stripe = Stripe ( "pk_test_..." ) ; const appearance = { theme : "stripe" } ; const elements = stripe . elements ( { appearance , clientSecret } ) ; const paymentElement = elements . create ( "payment" ) ; paymentElement . mount ( "#payment-element" ) ; document . getElementById ( "pay-button" ) . addEventListener ( "click" , async ( ) => { const { error } = await stripe . confirmPayment ( { elements , confirmParams : { return_url : "https://yourdomain.com/complete" , } , } ) ; if ( error ) { document . getElementById ( "errors" ) . textContent = error . message ; } } ) ; Pattern 4: Subscription Creation def create_subscription ( customer_id , price_id ) : """Create a subscription for a customer.""" try : subscription = stripe . Subscription . create ( customer = customer_id , items = [ { 'price' : price_id } ] , payment_behavior = 'default_incomplete' , payment_settings = { 'save_default_payment_method' : 'on_subscription' } , expand = [ 'latest_invoice.payment_intent' ] , ) return { 'subscription_id' : subscription . id , 'client_secret' : subscription . latest_invoice . payment_intent . client_secret } except stripe . error . StripeError as e : print ( f"Subscription creation failed: { e } " ) raise Pattern 5: Customer Portal def create_customer_portal_session ( customer_id ) : """Create a portal session for customers to manage subscriptions.""" session = stripe . billing_portal . Session . create ( customer = customer_id , return_url = 'https://yourdomain.com/account' , ) return session . url
Redirect customer here
Webhook Handling Secure Webhook Endpoint from flask import Flask , request import stripe app = Flask ( name ) endpoint_secret = 'whsec_...' @app . route ( '/webhook' , methods = [ 'POST' ] ) def webhook ( ) : payload = request . data sig_header = request . headers . get ( 'Stripe-Signature' ) try : event = stripe . Webhook . construct_event ( payload , sig_header , endpoint_secret ) except ValueError :
Invalid payload
return 'Invalid payload' , 400 except stripe . error . SignatureVerificationError :
Invalid signature
return 'Invalid signature' , 400
Handle the event
if event [ 'type' ] == 'payment_intent.succeeded' : payment_intent = event [ 'data' ] [ 'object' ] handle_successful_payment ( payment_intent ) elif event [ 'type' ] == 'payment_intent.payment_failed' : payment_intent = event [ 'data' ] [ 'object' ] handle_failed_payment ( payment_intent ) elif event [ 'type' ] == 'customer.subscription.deleted' : subscription = event [ 'data' ] [ 'object' ] handle_subscription_canceled ( subscription ) return 'Success' , 200 def handle_successful_payment ( payment_intent ) : """Process successful payment.""" customer_id = payment_intent . get ( 'customer' ) amount = payment_intent [ 'amount' ] metadata = payment_intent . get ( 'metadata' , { } )
Update your database
Send confirmation email
Fulfill order
print ( f"Payment succeeded: { payment_intent [ 'id' ] } " ) def handle_failed_payment ( payment_intent ) : """Handle failed payment.""" error = payment_intent . get ( 'last_payment_error' , { } ) print ( f"Payment failed: { error . get ( 'message' ) } " )
Notify customer
Update order status
def handle_subscription_canceled ( subscription ) : """Handle subscription cancellation.""" customer_id = subscription [ 'customer' ]
Update user access
Send cancellation email
print ( f"Subscription canceled: { subscription [ 'id' ] } " ) Webhook Best Practices import hashlib import hmac def verify_webhook_signature ( payload , signature , secret ) : """Manually verify webhook signature.""" expected_sig = hmac . new ( secret . encode ( 'utf-8' ) , payload , hashlib . sha256 ) . hexdigest ( ) return hmac . compare_digest ( signature , expected_sig ) def handle_webhook_idempotently ( event_id , handler ) : """Ensure webhook is processed exactly once."""
Check if event already processed
if is_event_processed ( event_id ) : return
Process event
try : handler ( ) mark_event_processed ( event_id ) except Exception as e : log_error ( e )
Stripe will retry failed webhooks
raise Customer Management def create_customer ( email , name , payment_method_id = None ) : """Create a Stripe customer.""" customer = stripe . Customer . create ( email = email , name = name , payment_method = payment_method_id , invoice_settings = { 'default_payment_method' : payment_method_id } if payment_method_id else None , metadata = { 'user_id' : '12345' } ) return customer def attach_payment_method ( customer_id , payment_method_id ) : """Attach a payment method to a customer.""" stripe . PaymentMethod . attach ( payment_method_id , customer = customer_id )
Set as default
stripe . Customer . modify ( customer_id , invoice_settings = { 'default_payment_method' : payment_method_id } ) def list_customer_payment_methods ( customer_id ) : """List all payment methods for a customer.""" payment_methods = stripe . PaymentMethod . list ( customer = customer_id , type = 'card' ) return payment_methods . data Refund Handling def create_refund ( payment_intent_id , amount = None , reason = None ) : """Create a refund.""" refund_params = { 'payment_intent' : payment_intent_id } if amount : refund_params [ 'amount' ] = amount
Partial refund
if reason : refund_params [ 'reason' ] = reason
'duplicate', 'fraudulent', 'requested_by_customer'
refund
stripe . Refund . create ( ** refund_params ) return refund def handle_dispute ( charge_id , evidence ) : """Update dispute with evidence.""" stripe . Dispute . modify ( charge_id , evidence = { 'customer_name' : evidence . get ( 'customer_name' ) , 'customer_email_address' : evidence . get ( 'customer_email' ) , 'shipping_documentation' : evidence . get ( 'shipping_proof' ) , 'customer_communication' : evidence . get ( 'communication' ) , } ) Testing
Use test mode keys
stripe . api_key = "sk_test_..."
Test card numbers
TEST_CARDS
{ 'success' : '4242424242424242' , 'declined' : '4000000000000002' , '3d_secure' : '4000002500003155' , 'insufficient_funds' : '4000000000009995' } def test_payment_flow ( ) : """Test complete payment flow."""
Create test customer
customer
stripe . Customer . create ( email = "test@example.com" )
Create payment intent
intent
stripe . PaymentIntent . create ( amount = 1000 , automatic_payment_methods = { 'enabled' : True } , currency = 'usd' , customer = customer . id )
Confirm with test card
confirmed
stripe . PaymentIntent . confirm ( intent . id , payment_method = 'pm_card_visa'
Test payment method
) assert confirmed . status == 'succeeded'