Skill: ivangdavila/stripe-api-integration (version 1.0.2)

--- name: Stripe API Integration slug: stripe-api-integration version: 1.0.2 homepage: https://clawic.com/skills/stripe-api-integration description: Complete Stripe API integration for payments, subscriptions, checkout, invoices, webhooks, Connect, Issuing, Terminal, and Treasury. changelog: Use ~/stripe-api-integration/ as config path (matching slug). metadata: {"clawdbot":{"emoji":"💳","requires":{"env":["STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"],"config":["~/stripe-api-integration/"]},"primaryEnv":"STRIPE_SECRET_KEY","os":["linux","darwin","win32"]}} --- # Stripe API Integration Complete Stripe API reference. See auxiliary files for detailed operations. ## Quick Start ```bash curl https://api.stripe.com/v1/balance -u "$STRIPE_SECRET_KEY:" ``` ## Setup On first use, read `setup.md`. Preferences stored in `~/stripe-api-integration/memory.md`. ## When to Use Any Stripe operation: payments, subscriptions, invoices, checkout, webhooks, Connect, Issuing, Terminal, Treasury. ## Architecture ``` ~/stripe-api-integration/ ├── memory.md # Account context └── webhooks.md # Webhook configs ``` ## Quick Reference | Topic | File | |-------|------| | Setup & memory | `setup.md`, `memory-template.md` | | Payments, refunds, disputes | `payments.md` | | Customers, products, prices | `customers.md` | | Subscriptions, usage billing | `subscriptions.md` | | Checkout Sessions | `checkout.md` | | Connect (marketplaces) | `connect.md` | | Webhooks & events | `webhooks.md` | | Invoices, quotes, tax | `invoices.md` | | Issuing, Terminal, Treasury, Identity, Radar | `advanced.md` | ## Core Rules 1. **Test mode first** — Use `sk_test_*` keys. Test card: `4242424242424242` 2. **Amounts in cents** — $10.00 = 1000 3. **Idempotency keys** — Prevent duplicate charges 4. **Webhooks required** — Never trust API response alone 5. **Expand objects** — Use `?expand[]=customer` for related data 6. **Pagination** — Use `starting_after` for large lists 7. **Error handling** — See `payments.md` for error codes ## Authentication **Required environment variables:** - `STRIPE_SECRET_KEY` — API key for all Stripe operations (starts with `sk_test_` or `sk_live_`) - `STRIPE_WEBHOOK_SECRET` — Signing secret for webhook verification (starts with `whsec_`) ```bash curl https://api.stripe.com/v1/customers -u "$STRIPE_SECRET_KEY:" ``` ## Common Traps - Amount in dollars not cents → 100x wrong charge - No idempotency key → duplicate charges - Skip webhook verification → accept fake events - Ignore `requires_action` → 3DS stuck ## External Endpoints | Endpoint | Purpose | |----------|---------| | `https://api.stripe.com/v1/*` | API | ## Security & Privacy **Environment variables used:** - `STRIPE_SECRET_KEY` — for API authentication - `STRIPE_WEBHOOK_SECRET` — for webhook signature verification **Sent to Stripe:** Customer info, payment data via api.stripe.com **Stays local:** API keys (never logged), ~/stripe-api-integration/ preferences **Never:** Log card numbers, skip webhook verification, expose keys ## Trust This skill sends data to Stripe (stripe.com). ## Related Skills Install with `clawhub install <slug>` if user confirms: - `api` — REST API patterns - `saas` — SaaS metrics - `webhook` — Webhook patterns ## Feedback - If useful: `clawhub star stripe-api-integration` - Stay updated: `clawhub sync`

path: advanced.md
content: # Advanced — Stripe API Integration

Issuing (virtual/physical cards), Terminal (POS), Treasury (banking), Identity (verification), Radar (fraud).

---

## Issuing (Create Cards)

### Create Cardholder
```bash
curl https://api.stripe.com/v1/issuing/cardholders \
  -u "$STRIPE_SECRET_KEY:" \
  -d "name=Jenny Rosen" \
  -d "email=[REDACTED_SECRET]" \
  -d "type=individual" \
  -d "billing[address][line1]=123 Main St" \
  -d "billing[address][city]=San Francisco" \
  -d "billing[address][state]=CA" \
  -d "billing[address][postal_code]=94111" \
  -d "billing[address][country]=US"
```

### Create Virtual Card
```bash
curl https://api.stripe.com/v1/issuing/cards \
  -u "$STRIPE_SECRET_KEY:" \
  -d "cardholder=ich_XXX" \
  -d "currency=usd" \
  -d "type=virtual" \
  -d "spending_controls[spending_limits][0][amount]=50000" \
  -d "spending_controls[spending_limits][0][interval]=monthly"
```

### Create Physical Card
```bash
curl https://api.stripe.com/v1/issuing/cards \
  -u "$STRIPE_SECRET_KEY:" \
  -d "cardholder=ich_XXX" \
  -d "currency=usd" \
  -d "type=physical" \
  -d "shipping[name]=Jenny Rosen" \
  -d "shipping[address][line1]=123 Main St" \
  -d "shipping[address][city]=San Francisco" \
  -d "shipping[address][state]=CA" \
  -d "shipping[address][postal_code]=94111" \
  -d "shipping[address][country]=US"
```

### List Transactions
```bash
curl "https://api.stripe.com/v1/issuing/transactions?card=ic_XXX&limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

### Approve/Decline Authorization (Real-time)
```python
# Webhook handler for issuing_authorization.request
def handle_authorization(event):
    auth = event['data']['object']
    
    # Your logic here
    if auth['amount'] > 100000:  # Over $1000
        return {"approved": False}
    
    return {"approved": True}
```

---

## Terminal (Point of Sale)

### Create Connection Token
```bash
curl https://api.stripe.com/v1/terminal/connection_tokens \
  -u "$STRIPE_SECRET_KEY:"
```

### Register Reader
```bash
curl https://api.stripe.com/v1/terminal/readers \
  -u "$STRIPE_SECRET_KEY:" \
  -d "registration_code=puppies-plug-could" \
  -d "label=Front Counter" \
  -d "location=tml_XXX"
```

### Create Location
```bash
curl https://api.stripe.com/v1/terminal/locations \
  -u "$STRIPE_SECRET_KEY:" \
  -d "display_name=HQ" \
  -d "address[line1]=123 Main St" \
  -d "address[city]=San Francisco" \
  -d "address[state]=CA" \
  -d "address[postal_code]=94111" \
  -d "address[country]=US"
```

### Process Payment
```bash
# Create PaymentIntent
curl https://api.stripe.com/v1/payment_intents \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=1000" \
  -d "currency=usd" \
  -d "payment_method_types[]=card_present" \
  -d "capture_method=manual"

# Process on reader
curl https://api.stripe.com/v1/terminal/readers/tmr_XXX/process_payment_intent \
  -u "$STRIPE_SECRET_KEY:" \
  -d "payment_intent=pi_XXX"
```

### Simulate Reader (Test Mode)
```bash
curl https://api.stripe.com/v1/test_helpers/terminal/readers/tmr_XXX/present_payment_method \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Treasury (Banking-as-a-Service)

### Create Financial Account
```bash
curl https://api.stripe.com/v1/treasury/financial_accounts \
  -u "$STRIPE_SECRET_KEY:" \
  -d "supported_currencies[]=usd" \
  -d "features[card_issuing][requested]=true" \
  -d "features[deposit_insurance][requested]=true" \
  -d "features[financial_addresses][aba][requested]=true"
```

### Get Balance
```bash
curl https://api.stripe.com/v1/treasury/financial_accounts/fa_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

### Create Outbound Transfer (ACH)
```bash
curl https://api.stripe.com/v1/treasury/outbound_transfers \
  -u "$STRIPE_SECRET_KEY:" \
  -d "financial_account=fa_XXX" \
  -d "amount=10000" \
  -d "currency=usd" \
  -d "destination_payment_method=pm_XXX" \
  -d "description=Vendor payment"
```

### Create Outbound Payment
```bash
curl https://api.stripe.com/v1/treasury/outbound_payments \
  -u "$STRIPE_SECRET_KEY:" \
  -d "financial_account=fa_XXX" \
  -d "amount=5000" \
  -d "currency=usd" \
  -d "customer=cus_XXX" \
  -d "description=Refund"
```

### List Transactions
```bash
curl "https://api.stripe.com/v1/treasury/transactions?financial_account=fa_XXX" \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Identity (Verification)

### Create Verification Session
```bash
curl https://api.stripe.com/v1/identity/verification_sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "type=document" \
  -d "options[document][require_matching_selfie]=true"
```

### Get Verification Result
```bash
curl https://api.stripe.com/v1/identity/verification_sessions/vs_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

### Verification Types
- `document` — ID document verification
- `id_number` — ID number check (SSN, etc.)
- `address` — Address verification

---

## Radar (Fraud Prevention)

### Create Radar Rule
```bash
# Via Dashboard: stripe.com/dashboard/radar/rules
# Rules use Stripe's rule language
```

### Review Payments
```bash
curl "https://api.stripe.com/v1/reviews?limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

### Approve Review
```bash
curl https://api.stripe.com/v1/reviews/prv_XXX/approve \
  -u "$STRIPE_SECRET_KEY:"
```

### Value Lists (Block/Allow)
```bash
# Create blocklist
curl https://api.stripe.com/v1/radar/value_lists \
  -u "$STRIPE_SECRET_KEY:" \
  -d "alias=blocked_emails" \
  -d "name=Blocked Emails" \
  -d "item_type=email"

# Add item
curl https://api.stripe.com/v1/radar/value_list_items \
  -u "$STRIPE_SECRET_KEY:" \
  -d "value_list=rsl_XXX" \
  -d "value=[REDACTED_SECRET]"
```

### Early Fraud Warnings
```bash
curl "https://api.stripe.com/v1/radar/early_fraud_warnings?limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Financial Connections (Plaid Alternative)

### Create Session
```bash
curl https://api.stripe.com/v1/financial_connections/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "account_holder[type]=customer" \
  -d "account_holder[customer]=cus_XXX" \
  -d "permissions[]=balances" \
  -d "permissions[]=transactions"
```

### List Connected Accounts
```bash
curl "https://api.stripe.com/v1/financial_connections/accounts?account_holder[customer]=cus_XXX" \
  -u "$STRIPE_SECRET_KEY:"
```

### Refresh Balance
```bash
curl https://api.stripe.com/v1/financial_connections/accounts/fca_XXX/refresh \
  -u "$STRIPE_SECRET_KEY:" \
  -d "features[]=balance"
```

---

## Climate (Carbon Removal)

### Create Order
```bash
curl https://api.stripe.com/v1/climate/orders \
  -u "$STRIPE_SECRET_KEY:" \
  -d "product=climsku_frontier_offtake_portfolio_2027" \
  -d "metric_tons=1"
```

### List Products
```bash
curl https://api.stripe.com/v1/climate/products \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Sigma (SQL Queries)

### Create Scheduled Query
```bash
curl https://api.stripe.com/v1/sigma/scheduled_query_runs \
  -u "$STRIPE_SECRET_KEY:" \
  -d "query=SELECT * FROM charges WHERE created > date_sub(now(), interval 7 day)"
```

### Get Query Results
```bash
curl https://api.stripe.com/v1/sigma/scheduled_query_runs/sqr_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

---

## ID Prefixes

| Prefix | Resource |
|--------|----------|
| `ich_` | Issuing Cardholder |
| `ic_` | Issuing Card |
| `iauth_` | Issuing Authorization |
| `tml_` | Terminal Location |
| `tmr_` | Terminal Reader |
| `fa_` | Financial Account |
| `vs_` | Verification Session |
| `rsl_` | Radar Value List |
| `fca_` | Financial Connections Account |


path: checkout.md
content: # Checkout Sessions — Stripe API

## Checkout Modes

| Mode | Use Case | Creates |
|------|----------|---------|
| `payment` | One-time purchase | PaymentIntent |
| `subscription` | Recurring billing | Subscription |
| `setup` | Save card for later | SetupIntent |

## Complete Checkout Flow

### 1. Create Checkout Session
```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=subscription" \
  -d "customer=cus_XXX" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1" \
  -d "success_url=https://example.com/success?session_id={CHECKOUT_SESSION_ID}" \
  -d "cancel_url=https://example.com/cancel" \
  -d "allow_promotion_codes=true" \
  -d "billing_address_collection=required" \
  -d "customer_update[address]=auto" \
  -d "customer_update[name]=auto"
```

### 2. Redirect Customer
```javascript
// Frontend - redirect to Stripe Checkout
window.location.href = session.url;
```

### 3. Handle Success
```bash
# Get session details after success
curl https://api.stripe.com/v1/checkout/sessions/cs_XXX?expand[]=subscription&expand[]=customer \
  -u "$STRIPE_SECRET_KEY:"
```

## Custom Fields

### Add Custom Fields
```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=payment" \
  -d "custom_fields[0][key]=company" \
  -d "custom_fields[0][label][type]=custom" \
  -d "custom_fields[0][label][custom]=Company Name" \
  -d "custom_fields[0][type]=text" \
  -d "custom_fields[1][key]=size" \
  -d "custom_fields[1][label][type]=custom" \
  -d "custom_fields[1][label][custom]=T-Shirt Size" \
  -d "custom_fields[1][type]=dropdown" \
  -d "custom_fields[1][dropdown][options][0][label]=Small" \
  -d "custom_fields[1][dropdown][options][0][value]=S" \
  -d "custom_fields[1][dropdown][options][1][label]=Medium" \
  -d "custom_fields[1][dropdown][options][1][value]=M" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1" \
  -d "success_url=https://example.com/success" \
  -d "cancel_url=https://example.com/cancel"
```

## Trial Periods

### Add Trial to Subscription
```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=subscription" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1" \
  -d "subscription_data[trial_period_days]=14" \
  -d "success_url=https://example.com/success" \
  -d "cancel_url=https://example.com/cancel"
```

### Trial Without Payment Method
```bash
# Requires subscription_data[trial_settings][end_behavior][missing_payment_method]
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=subscription" \
  -d "payment_method_collection=if_required" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1" \
  -d "subscription_data[trial_period_days]=14" \
  -d "subscription_data[trial_settings][end_behavior][missing_payment_method]=cancel" \
  -d "success_url=https://example.com/success" \
  -d "cancel_url=https://example.com/cancel"
```

## Quantity Adjustable

```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=subscription" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=5" \
  -d "line_items[0][adjustable_quantity][enabled]=true" \
  -d "line_items[0][adjustable_quantity][minimum]=1" \
  -d "line_items[0][adjustable_quantity][maximum]=100" \
  -d "success_url=https://example.com/success" \
  -d "cancel_url=https://example.com/cancel"
```

## Metadata

```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=payment" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1" \
  -d "metadata[order_id]=12345" \
  -d "metadata[campaign]=summer_sale" \
  -d "payment_intent_data[metadata][order_id]=12345" \
  -d "success_url=https://example.com/success" \
  -d "cancel_url=https://example.com/cancel"
```

## Embedded Checkout

```javascript
// Initialize Stripe.js
const stripe = Stripe('pk_xxx');

// Create checkout session on server, return client_secret
const { clientSecret } = await fetch('/create-checkout-session').then(r => r.json());

// Mount embedded checkout
const checkout = await stripe.initEmbeddedCheckout({
  clientSecret,
});
checkout.mount('#checkout');
```

## Session Expiration

- Default: 24 hours
- Custom: Set `expires_at` (minimum 30 minutes, maximum 24 hours)

```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=payment" \
  -d "expires_at=$(($(date +%s) + 3600))" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1" \
  -d "success_url=https://example.com/success" \
  -d "cancel_url=https://example.com/cancel"
```

## Recovery

### Recover Abandoned Checkout
```bash
# List expired sessions
curl "https://api.stripe.com/v1/checkout/sessions?status=expired&limit=100" \
  -u "$STRIPE_SECRET_KEY:"

# Sessions include customer email if provided
# Use for recovery emails
```

## Common Patterns

### Upsell at Checkout
```bash
# Add upsell items with adjustable quantity starting at 0
-d "line_items[1][price]=price_ADDON"
-d "line_items[1][quantity]=0"
-d "line_items[1][adjustable_quantity][enabled]=true"
-d "line_items[1][adjustable_quantity][minimum]=0"
-d "line_items[1][adjustable_quantity][maximum]=10"
```

### Collect Tax ID
```bash
-d "tax_id_collection[enabled]=true"
```

### Collect Shipping Address
```bash
-d "shipping_address_collection[allowed_countries][0]=US"
-d "shipping_address_collection[allowed_countries][1]=CA"
```


path: connect.md
content: # Stripe Connect — Marketplace Payments

## Connect Account Types

| Type | Control | Onboarding | Payouts | Use Case |
|------|---------|------------|---------|----------|
| Standard | Low | Stripe-hosted | Direct to account | Independent businesses |
| Express | Medium | Stripe-hosted | Platform controls | Gig workers, sellers |
| Custom | Full | Build your own | Platform controls | White-label platforms |

## Create Connected Account

### Express Account
```bash
curl https://api.stripe.com/v1/accounts \
  -u "$STRIPE_SECRET_KEY:" \
  -d "type=express" \
  -d "country=US" \
  -d "email=[REDACTED_SECRET]" \
  -d "capabilities[card_payments][requested]=true" \
  -d "capabilities[transfers][requested]=true"
```

### Standard Account
```bash
curl https://api.stripe.com/v1/accounts \
  -u "$STRIPE_SECRET_KEY:" \
  -d "type=standard" \
  -d "country=US" \
  -d "email=[REDACTED_SECRET]"
```

### Custom Account
```bash
curl https://api.stripe.com/v1/accounts \
  -u "$STRIPE_SECRET_KEY:" \
  -d "type=custom" \
  -d "country=US" \
  -d "email=[REDACTED_SECRET]" \
  -d "capabilities[card_payments][requested]=true" \
  -d "capabilities[transfers][requested]=true" \
  -d "business_type=individual"
```

## Account Onboarding

### Create Account Link (Express/Custom)
```bash
curl https://api.stripe.com/v1/account_links \
  -u "$STRIPE_SECRET_KEY:" \
  -d "account=acct_XXX" \
  -d "refresh_url=https://example.com/reauth" \
  -d "return_url=https://example.com/return" \
  -d "type=account_onboarding"
```

### Check Onboarding Status
```bash
curl https://api.stripe.com/v1/accounts/acct_XXX \
  -u "$STRIPE_SECRET_KEY:"

# Check: charges_enabled, payouts_enabled, requirements
```

## Payment Flows

### Direct Charge (Funds go to connected account)
```bash
curl https://api.stripe.com/v1/payment_intents \
  -u "$STRIPE_SECRET_KEY:" \
  -H "Stripe-Account: acct_XXX" \
  -d "amount=10000" \
  -d "currency=usd" \
  -d "application_fee_amount=1000"
```

### Destination Charge (Funds go to platform, then transferred)
```bash
curl https://api.stripe.com/v1/payment_intents \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=10000" \
  -d "currency=usd" \
  -d "transfer_data[destination]=acct_XXX" \
  -d "transfer_data[amount]=9000"
```

### Separate Charges and Transfers
```bash
# 1. Charge customer
curl https://api.stripe.com/v1/payment_intents \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=10000" \
  -d "currency=usd"

# 2. Transfer to connected account
curl https://api.stripe.com/v1/transfers \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=9000" \
  -d "currency=usd" \
  -d "destination=acct_XXX" \
  -d "source_transaction=ch_XXX"
```

## Application Fees

### On Direct Charge
```bash
-d "application_fee_amount=1000"
```

### On Destination Charge
```bash
-d "transfer_data[amount]=9000"  # Platform keeps 10000-9000 = 1000
```

## Payouts to Connected Accounts

### Create Payout (for Express/Custom)
```bash
curl https://api.stripe.com/v1/payouts \
  -u "$STRIPE_SECRET_KEY:" \
  -H "Stripe-Account: acct_XXX" \
  -d "amount=10000" \
  -d "currency=usd"
```

### Check Account Balance
```bash
curl https://api.stripe.com/v1/balance \
  -u "$STRIPE_SECRET_KEY:" \
  -H "Stripe-Account: acct_XXX"
```

## Refunds with Connect

### Refund Direct Charge (Refund from connected account)
```bash
curl https://api.stripe.com/v1/refunds \
  -u "$STRIPE_SECRET_KEY:" \
  -H "Stripe-Account: acct_XXX" \
  -d "charge=ch_XXX"
```

### Refund Destination Charge (Reverse transfer too)
```bash
curl https://api.stripe.com/v1/refunds \
  -u "$STRIPE_SECRET_KEY:" \
  -d "charge=ch_XXX" \
  -d "reverse_transfer=true"
```

### Refund Application Fee
```bash
curl https://api.stripe.com/v1/refunds \
  -u "$STRIPE_SECRET_KEY:" \
  -d "charge=ch_XXX" \
  -d "refund_application_fee=true"
```

## Express Dashboard

### Create Login Link
```bash
curl https://api.stripe.com/v1/accounts/acct_XXX/login_links \
  -u "$STRIPE_SECRET_KEY:"
```

Redirects to Express dashboard where sellers can:
- View balance and payouts
- Update banking info
- See transaction history

## Account Updates

### Update Connected Account
```bash
curl https://api.stripe.com/v1/accounts/acct_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "business_profile[url]=https://seller.example.com" \
  -d "business_profile[mcc]=5734"
```

### Delete Connected Account
```bash
curl -X DELETE https://api.stripe.com/v1/accounts/acct_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

## Connect Webhooks

| Event | When | Action |
|-------|------|--------|
| `account.updated` | Account status changes | Check charges_enabled |
| `account.application.authorized` | OAuth authorized | Store account ID |
| `account.application.deauthorized` | OAuth revoked | Disable features |
| `payout.paid` | Payout sent | Notify seller |
| `payout.failed` | Payout failed | Alert and retry |

## Common Patterns

### Split Payment (Multiple Sellers)
```bash
# Pay to platform, then transfer to multiple accounts
curl https://api.stripe.com/v1/payment_intents \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=10000" \
  -d "currency=usd"

# Transfer to Seller A
curl https://api.stripe.com/v1/transfers \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=4000" \
  -d "destination=acct_SELLER_A" \
  -d "source_transaction=ch_XXX"

# Transfer to Seller B
curl https://api.stripe.com/v1/transfers \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=5000" \
  -d "destination=acct_SELLER_B" \
  -d "source_transaction=ch_XXX"

# Platform keeps 1000
```

### Hold and Release
```bash
# Create transfer with manual payout timing
curl https://api.stripe.com/v1/accounts/acct_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "settings[payouts][schedule][delay_days]=manual"

# Later, trigger payout
curl https://api.stripe.com/v1/payouts \
  -u "$STRIPE_SECRET_KEY:" \
  -H "Stripe-Account: acct_XXX" \
  -d "amount=5000" \
  -d "currency=usd"
```


path: customers.md
content: # Customers & Products — Stripe API

## Customers

### List Customers
```bash
curl "https://api.stripe.com/v1/customers?limit=10&email=[REDACTED_SECRET]" \
  -u "$STRIPE_SECRET_KEY:"
```

Query parameters: `limit`, `starting_after`, `ending_before`, `email`, `created`

### Get Customer
```bash
curl https://api.stripe.com/v1/customers/cus_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

### Create Customer
```bash
curl https://api.stripe.com/v1/customers \
  -u "$STRIPE_SECRET_KEY:" \
  -d "email=[REDACTED_SECRET]" \
  -d "name=John Doe" \
  -d "metadata[user_id]=123"
```

### Update Customer
```bash
curl https://api.stripe.com/v1/customers/cus_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "name=Jane Doe"
```

### Delete Customer
```bash
curl -X DELETE https://api.stripe.com/v1/customers/cus_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Products

### List Products
```bash
curl "https://api.stripe.com/v1/products?limit=10&active=true" \
  -u "$STRIPE_SECRET_KEY:"
```

### Create Product
```bash
curl https://api.stripe.com/v1/products \
  -u "$STRIPE_SECRET_KEY:" \
  -d "name=Pro Plan" \
  -d "description=Full access to all features"
```

### Update Product
```bash
curl https://api.stripe.com/v1/products/prod_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "name=Updated Plan" \
  -d "active=true"
```

### Delete Product
```bash
curl -X DELETE https://api.stripe.com/v1/products/prod_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Prices

### List Prices
```bash
curl "https://api.stripe.com/v1/prices?product=prod_XXX&active=true" \
  -u "$STRIPE_SECRET_KEY:"
```

### Create Recurring Price
```bash
curl https://api.stripe.com/v1/prices \
  -u "$STRIPE_SECRET_KEY:" \
  -d "product=prod_XXX" \
  -d "unit_amount=1999" \
  -d "currency=usd" \
  -d "recurring[interval]=month"
```

### Create One-time Price
```bash
curl https://api.stripe.com/v1/prices \
  -u "$STRIPE_SECRET_KEY:" \
  -d "product=prod_XXX" \
  -d "unit_amount=4999" \
  -d "currency=usd"
```

### Update Price
```bash
curl https://api.stripe.com/v1/prices/price_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "active=false"
```

---

## Coupons

### Create Percentage Coupon
```bash
curl https://api.stripe.com/v1/coupons \
  -u "$STRIPE_SECRET_KEY:" \
  -d "percent_off=25" \
  -d "duration=repeating" \
  -d "duration_in_months=3"
```

### Create Fixed Amount Coupon
```bash
curl https://api.stripe.com/v1/coupons \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount_off=1000" \
  -d "currency=usd" \
  -d "duration=once"
```

### Create Promotion Code
```bash
curl https://api.stripe.com/v1/promotion_codes \
  -u "$STRIPE_SECRET_KEY:" \
  -d "coupon=COUPON_ID" \
  -d "code=SUMMER25" \
  -d "max_redemptions=100"
```

---

## ID Prefixes Reference

| Prefix | Resource |
|--------|----------|
| `cus_` | Customer |
| `prod_` | Product |
| `price_` | Price |
| `pm_` | Payment Method |


path: invoices.md
content: # Invoices & Billing — Stripe API

## Invoices

### Create Invoice
```bash
curl https://api.stripe.com/v1/invoices \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "auto_advance=false"
```

### Add Invoice Item
```bash
curl https://api.stripe.com/v1/invoiceitems \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "price=price_XXX" \
  -d "invoice=in_XXX"
```

### Finalize Invoice
```bash
curl https://api.stripe.com/v1/invoices/in_XXX/finalize \
  -u "$STRIPE_SECRET_KEY:"
```

### Send Invoice
```bash
curl https://api.stripe.com/v1/invoices/in_XXX/send \
  -u "$STRIPE_SECRET_KEY:"
```

### Pay Invoice
```bash
curl https://api.stripe.com/v1/invoices/in_XXX/pay \
  -u "$STRIPE_SECRET_KEY:"
```

### Void Invoice
```bash
curl https://api.stripe.com/v1/invoices/in_XXX/void \
  -u "$STRIPE_SECRET_KEY:"
```

### Mark Uncollectible
```bash
curl https://api.stripe.com/v1/invoices/in_XXX/mark_uncollectible \
  -u "$STRIPE_SECRET_KEY:"
```

### List Invoices
```bash
curl "https://api.stripe.com/v1/invoices?customer=cus_XXX&status=paid" \
  -u "$STRIPE_SECRET_KEY:"
```

Status: `draft`, `open`, `paid`, `void`, `uncollectible`

---

## Billing Portal

### Create Portal Session
```bash
curl https://api.stripe.com/v1/billing_portal/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "return_url=https://example.com/account"
```

Portal allows customers to:
- Update payment methods
- View invoice history
- Cancel/pause subscriptions
- Download invoices

Configure in Dashboard > Settings > Billing > Customer Portal

---

## Quotes

### Create Quote
```bash
curl https://api.stripe.com/v1/quotes \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1"
```

### Finalize Quote
```bash
curl https://api.stripe.com/v1/quotes/qt_XXX/finalize \
  -u "$STRIPE_SECRET_KEY:"
```

### Accept Quote
```bash
curl https://api.stripe.com/v1/quotes/qt_XXX/accept \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Stripe Tax

### Create Tax Calculation
```bash
curl https://api.stripe.com/v1/tax/calculations \
  -u "$STRIPE_SECRET_KEY:" \
  -d "currency=usd" \
  -d "line_items[0][amount]=1000" \
  -d "line_items[0][reference]=L1" \
  -d "customer_details[address][country]=US" \
  -d "customer_details[address][state]=CA"
```

### Enable Tax on Checkout
```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "mode=payment" \
  -d "automatic_tax[enabled]=true" \
  -d "line_items[0][price]=price_XXX" \
  -d "line_items[0][quantity]=1" \
  -d "success_url=https://example.com/success" \
  -d "cancel_url=https://example.com/cancel"
```

---

## Usage-Based Billing (Meters)

### Create Meter
```bash
curl https://api.stripe.com/v1/billing/meters \
  -u "$STRIPE_SECRET_KEY:" \
  -d "display_name=API Calls" \
  -d "event_name=api_call" \
  -d "default_aggregation[formula]=sum"
```

### Report Usage Event
```bash
curl https://api.stripe.com/v1/billing/meter_events \
  -u "$STRIPE_SECRET_KEY:" \
  -d "event_name=api_call" \
  -d "payload[stripe_customer_id]=cus_XXX" \
  -d "payload[value]=100"
```

### Create Metered Price
```bash
curl https://api.stripe.com/v1/prices \
  -u "$STRIPE_SECRET_KEY:" \
  -d "product=prod_XXX" \
  -d "currency=usd" \
  -d "recurring[interval]=month" \
  -d "recurring[usage_type]=metered" \
  -d "recurring[meter]=mtr_XXX" \
  -d "unit_amount=1"
```

---

## Subscription Schedules

### Create Schedule (Future Start)
```bash
curl https://api.stripe.com/v1/subscription_schedules \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "start_date=$(date -d '+7 days' +%s)" \
  -d "end_behavior=release" \
  -d "phases[0][items][0][price]=price_XXX" \
  -d "phases[0][iterations]=1"
```

### Schedule Upgrade
```bash
curl https://api.stripe.com/v1/subscription_schedules \
  -u "$STRIPE_SECRET_KEY:" \
  -d "from_subscription=sub_XXX" \
  -d "phases[0][items][0][price]=price_BASIC" \
  -d "phases[0][iterations]=3" \
  -d "phases[1][items][0][price]=price_PRO" \
  -d "phases[1][iterations]=12"
```

---

## ID Prefixes Reference

| Prefix | Resource |
|--------|----------|
| `in_` | Invoice |
| `ii_` | Invoice Item |
| `qt_` | Quote |
| `sub_sched_` | Subscription Schedule |
| `mtr_` | Meter |


path: memory-template.md
content: # Memory Template — Stripe API

Create `~/stripe-api-integration/memory.md` with this structure:

```markdown
# Stripe Memory

## Status
status: ongoing
version: 1.0.0
last: YYYY-MM-DD
integration: pending | done | declined

## Context
<!-- What you know about their Stripe setup -->
<!-- Payment type: subscriptions, one-time, both -->
<!-- Customer type: B2B, B2C, marketplace -->
<!-- Tech stack if mentioned -->

## Products
<!-- Products and prices they've created or discussed -->
<!-- Format: prod_XXX: Product Name (price_XXX: $X/mo) -->

## Preferences
<!-- Test mode vs live mode -->
<!-- Webhook patterns they use -->
<!-- Any custom flows established -->

## Notes
<!-- Internal observations -->
<!-- Common operations they request -->
<!-- Patterns to remember -->

---
*Updated: YYYY-MM-DD*
```

## Status Values

| Value | Meaning | Behavior |
|-------|---------|----------|
| `ongoing` | Still learning | Ask questions when relevant |
| `complete` | Has enough context | Work normally |
| `paused` | User said "not now" | Don't ask, work with what you have |
| `never_ask` | User said stop | Never ask for more context |

## Key Principles

- **No config keys visible** — use natural language, not "subscription_mode: true"
- **Learn from behavior** — if they always create customers first, remember that
- **Track products loosely** — note what they create but don't maintain full inventory
- Update `last` on each use


path: payments.md
content: # Payment Intents & Methods — Stripe API

## Payment Intents

### Create Payment Intent
```bash
curl https://api.stripe.com/v1/payment_intents \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=2000" \
  -d "currency=usd" \
  -d "customer=cus_XXX" \
  -d "payment_method_types[]=card" \
  -d "metadata[order_id]=6735"
```

### Confirm Payment Intent
```bash
curl https://api.stripe.com/v1/payment_intents/pi_XXX/confirm \
  -u "$STRIPE_SECRET_KEY:" \
  -d "payment_method=pm_XXX"
```

### Capture Payment Intent (manual capture)
```bash
curl https://api.stripe.com/v1/payment_intents/pi_XXX/capture \
  -u "$STRIPE_SECRET_KEY:"
```

### Cancel Payment Intent
```bash
curl https://api.stripe.com/v1/payment_intents/pi_XXX/cancel \
  -u "$STRIPE_SECRET_KEY:"
```

### List Payment Intents
```bash
curl "https://api.stripe.com/v1/payment_intents?customer=cus_XXX&limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Payment Methods

### List Payment Methods
```bash
curl "https://api.stripe.com/v1/payment_methods?customer=cus_XXX&type=card" \
  -u "$STRIPE_SECRET_KEY:"
```

### Get Payment Method
```bash
curl https://api.stripe.com/v1/payment_methods/pm_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

### Attach to Customer
```bash
curl https://api.stripe.com/v1/payment_methods/pm_XXX/attach \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX"
```

### Detach from Customer
```bash
curl https://api.stripe.com/v1/payment_methods/pm_XXX/detach \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Setup Intents (Save Cards)

### Create Setup Intent
```bash
curl https://api.stripe.com/v1/setup_intents \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "payment_method_types[]=card"
```

### Confirm Setup Intent
```bash
curl https://api.stripe.com/v1/setup_intents/seti_XXX/confirm \
  -u "$STRIPE_SECRET_KEY:" \
  -d "payment_method=pm_XXX"
```

---

## Refunds

### Full Refund
```bash
curl https://api.stripe.com/v1/refunds \
  -u "$STRIPE_SECRET_KEY:" \
  -d "payment_intent=pi_XXX"
```

### Partial Refund
```bash
curl https://api.stripe.com/v1/refunds \
  -u "$STRIPE_SECRET_KEY:" \
  -d "payment_intent=pi_XXX" \
  -d "amount=500"
```

### Refund with Reason
```bash
curl https://api.stripe.com/v1/refunds \
  -u "$STRIPE_SECRET_KEY:" \
  -d "payment_intent=pi_XXX" \
  -d "reason=requested_by_customer"
```

Reasons: `duplicate`, `fraudulent`, `requested_by_customer`

---

## Disputes (Chargebacks)

### List Disputes
```bash
curl "https://api.stripe.com/v1/disputes?limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

### Submit Evidence
```bash
curl https://api.stripe.com/v1/disputes/dp_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "evidence[customer_name]=John Doe" \
  -d "evidence[customer_email]=[REDACTED_SECRET]" \
  -d "evidence[product_description]=Premium subscription"
```

### Accept Dispute (Close)
```bash
curl https://api.stripe.com/v1/disputes/dp_XXX/close \
  -u "$STRIPE_SECRET_KEY:"
```

---

## Balance

### Get Balance
```bash
curl https://api.stripe.com/v1/balance \
  -u "$STRIPE_SECRET_KEY:"
```

### List Balance Transactions
```bash
curl "https://api.stripe.com/v1/balance_transactions?limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

### Create Payout
```bash
curl https://api.stripe.com/v1/payouts \
  -u "$STRIPE_SECRET_KEY:" \
  -d "amount=10000" \
  -d "currency=usd"
```

---

## ID Prefixes Reference

| Prefix | Resource |
|--------|----------|
| `pi_` | Payment Intent |
| `pm_` | Payment Method |
| `seti_` | Setup Intent |
| `ch_` | Charge |
| `re_` | Refund |
| `dp_` | Dispute |
| `po_` | Payout |


path: setup.md
content: # Setup — Stripe API Integration

This file guides the initial configuration when the user first uses this skill.

## Integration

Ask how they want to use Stripe:
- "Are you building subscriptions, one-time payments, or both?"
- "Do you have a Stripe account already, or starting fresh?"
- "Want me to help whenever payments come up, or only when you ask?"

## Understand Their Use Case

- What are they selling? (SaaS, products, services)
- Who are their customers? (B2B, B2C, marketplace)
- What payment flows do they need? (checkout, invoices, subscriptions)

## Technical Context

- What's their stack? (Node, Python, Ruby, etc.)
- Are they using Stripe already or migrating?
- Do they need Connect for marketplace payments?

## Memory Storage

Store context in `~/stripe-api-integration/memory.md`:
- Their primary use case (subscriptions, one-time, both)
- Their customer type (B2B/B2C/marketplace)
- Their tech stack if mentioned
- Test vs live mode preference
- Any specific patterns they've established

## When Ready

Once you know:
1. What they're building (subscriptions, payments, etc.)
2. Basic technical context

...you're ready to help. Details emerge naturally through use.


path: subscriptions.md
content: # Subscriptions — Stripe API

## Subscription Lifecycle

```
created → active → past_due → canceled
                 ↘ unpaid ↗
                 ↘ paused → active
```

## Status Reference

| Status | Meaning | Action |
|--------|---------|--------|
| `incomplete` | Initial payment failed | Retry or cancel |
| `incomplete_expired` | 23h passed without payment | Start over |
| `trialing` | In trial period | No action needed |
| `active` | Payments current | Normal operation |
| `past_due` | Payment failed, in retry | Contact customer |
| `unpaid` | Retries exhausted | Cancel or intervention |
| `canceled` | Subscription ended | Resubscribe if wanted |
| `paused` | Collection paused | Resume when ready |

## Create Subscription Patterns

### Simple Subscription
```bash
curl https://api.stripe.com/v1/subscriptions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "items[0][price]=price_XXX"
```

### With Trial
```bash
curl https://api.stripe.com/v1/subscriptions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "items[0][price]=price_XXX" \
  -d "trial_period_days=14"
```

### With Specific Trial End
```bash
curl https://api.stripe.com/v1/subscriptions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "items[0][price]=price_XXX" \
  -d "trial_end=$(($(date +%s) + 604800))"  # 7 days from now
```

### With Default Payment Method
```bash
curl https://api.stripe.com/v1/subscriptions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "items[0][price]=price_XXX" \
  -d "default_payment_method=pm_XXX"
```

### With Coupon
```bash
curl https://api.stripe.com/v1/subscriptions \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "items[0][price]=price_XXX" \
  -d "coupon=WELCOME20"
```

## Plan Changes (Upgrades/Downgrades)

### Immediate Upgrade with Proration
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "items[0][id]=si_XXX" \
  -d "items[0][price]=price_HIGHER" \
  -d "proration_behavior=create_prorations"
```

### Downgrade at Period End
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "items[0][id]=si_XXX" \
  -d "items[0][price]=price_LOWER" \
  -d "proration_behavior=none" \
  -d "billing_cycle_anchor=unchanged"
```

### Preview Proration
```bash
curl https://api.stripe.com/v1/invoices/upcoming \
  -u "$STRIPE_SECRET_KEY:" \
  -d "customer=cus_XXX" \
  -d "subscription=sub_XXX" \
  -d "subscription_items[0][id]=si_XXX" \
  -d "subscription_items[0][price]=price_NEW" \
  -d "subscription_proration_behavior=create_prorations"
```

## Cancellation Patterns

### Cancel Immediately
```bash
curl -X DELETE https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

### Cancel at Period End
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "cancel_at_period_end=true"
```

### Cancel at Specific Date
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "cancel_at=$(($(date +%s) + 2592000))"  # 30 days from now
```

### Undo Pending Cancellation
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "cancel_at_period_end=false"
```

## Pause and Resume

### Pause Collection
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "pause_collection[behavior]=mark_uncollectible"
```

Behaviors:
- `mark_uncollectible`: Invoices created but marked uncollectible
- `keep_as_draft`: Invoices created as drafts
- `void`: Invoices voided

### Pause Until Date
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "pause_collection[behavior]=mark_uncollectible" \
  -d "pause_collection[resumes_at]=$(($(date +%s) + 2592000))"
```

### Resume
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "pause_collection="
```

## Multiple Items (Add-ons)

### Add Item to Subscription
```bash
curl https://api.stripe.com/v1/subscription_items \
  -u "$STRIPE_SECRET_KEY:" \
  -d "subscription=sub_XXX" \
  -d "price=price_ADDON" \
  -d "quantity=1"
```

### Remove Item
```bash
curl -X DELETE https://api.stripe.com/v1/subscription_items/si_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "proration_behavior=create_prorations"
```

### Update Item Quantity
```bash
curl https://api.stripe.com/v1/subscription_items/si_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "quantity=5"
```

## Billing Cycle

### Change Billing Anchor (Next Renewal Date)
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "billing_cycle_anchor=now" \
  -d "proration_behavior=create_prorations"
```

### Bill Immediately (Mid-cycle)
```bash
curl https://api.stripe.com/v1/subscriptions/sub_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "billing_cycle_anchor=now"
```

## Metered Billing

### Report Usage
```bash
curl https://api.stripe.com/v1/subscription_items/si_XXX/usage_records \
  -u "$STRIPE_SECRET_KEY:" \
  -d "quantity=100" \
  -d "timestamp=$(date +%s)" \
  -d "action=increment"
```

Actions:
- `increment`: Add to existing usage
- `set`: Replace usage for timestamp

### Get Usage Summary
```bash
curl "https://api.stripe.com/v1/subscription_items/si_XXX/usage_record_summaries?limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

## Failed Payment Handling

### Retry Invoice Payment
```bash
curl https://api.stripe.com/v1/invoices/in_XXX/pay \
  -u "$STRIPE_SECRET_KEY:"
```

### Update Payment Method and Retry
```bash
# Update customer's default payment method
curl https://api.stripe.com/v1/customers/cus_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "invoice_settings[default_payment_method]=pm_NEW"

# Retry the failed invoice
curl https://api.stripe.com/v1/invoices/in_XXX/pay \
  -u "$STRIPE_SECRET_KEY:"
```

## Webhook Events for Subscriptions

| Event | When | Action |
|-------|------|--------|
| `customer.subscription.created` | New subscription | Provision access |
| `customer.subscription.updated` | Plan change, renewal | Update access |
| `customer.subscription.deleted` | Canceled | Revoke access |
| `customer.subscription.trial_will_end` | 3 days before trial ends | Send reminder |
| `customer.subscription.paused` | Subscription paused | Limit access |
| `customer.subscription.resumed` | Subscription resumed | Restore access |
| `invoice.payment_failed` | Payment failed | Send dunning email |
| `invoice.paid` | Subscription renewed | Confirm renewal |


path: webhooks.md
content: # Webhooks — Stripe API

## Webhook Fundamentals

Webhooks are essential for:
- Async payment confirmation (3D Secure, bank transfers)
- Subscription lifecycle events
- Dispute notifications
- Payout confirmations

**Never rely solely on API responses.** Always confirm with webhooks.

## Create Webhook Endpoint

### Via API
```bash
curl https://api.stripe.com/v1/webhook_endpoints \
  -u "$STRIPE_SECRET_KEY:" \
  -d "url=https://example.com/webhooks/stripe" \
  -d "enabled_events[]=payment_intent.succeeded" \
  -d "enabled_events[]=payment_intent.payment_failed" \
  -d "enabled_events[]=customer.subscription.created" \
  -d "enabled_events[]=customer.subscription.updated" \
  -d "enabled_events[]=customer.subscription.deleted" \
  -d "enabled_events[]=invoice.paid" \
  -d "enabled_events[]=invoice.payment_failed"
```

### Via Dashboard
1. Go to Developers > Webhooks
2. Add endpoint
3. Select events
4. Copy signing secret

## Essential Events

### Payments
| Event | When | Critical |
|-------|------|----------|
| `payment_intent.succeeded` | Payment completed | Yes |
| `payment_intent.payment_failed` | Payment failed | Yes |
| `payment_intent.requires_action` | 3DS needed | Yes |
| `charge.refunded` | Refund processed | Yes |
| `charge.dispute.created` | Chargeback filed | Yes |

### Subscriptions
| Event | When | Critical |
|-------|------|----------|
| `customer.subscription.created` | New subscription | Yes |
| `customer.subscription.updated` | Plan changed | Yes |
| `customer.subscription.deleted` | Canceled | Yes |
| `customer.subscription.trial_will_end` | 3 days before trial ends | Important |
| `customer.subscription.paused` | Paused | Important |

### Invoices
| Event | When | Critical |
|-------|------|----------|
| `invoice.paid` | Payment succeeded | Yes |
| `invoice.payment_failed` | Payment failed | Yes |
| `invoice.upcoming` | Invoice will be created | Informational |
| `invoice.finalized` | Invoice ready to pay | Informational |

### Checkout
| Event | When | Critical |
|-------|------|----------|
| `checkout.session.completed` | Payment successful | Yes |
| `checkout.session.expired` | Session expired | Important |
| `checkout.session.async_payment_succeeded` | Async payment done | Yes |
| `checkout.session.async_payment_failed` | Async payment failed | Yes |

## Signature Verification

### Python
```python
import stripe
import os

stripe.[REDACTED_SECRET]'STRIPE_SECRET_KEY']
endpoint_secret = os.environ['STRIPE_WEBHOOK_SECRET']

def handle_webhook(request):
    payload = request.body
    sig_header = request.headers.get('Stripe-Signature')
    
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        return 400, 'Invalid payload'
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return 400, 'Invalid signature'
    
    # Handle event
    return 200, 'OK'
```

### Node.js
```javascript
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

app.post('/webhooks/stripe', express.raw({type: 'application/json'}), (req, res) => {
  const sig = req.headers['stripe-signature'];
  
  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  
  // Handle event
  res.json({received: true});
});
```

### Go
```go
import (
    "github.com/stripe/stripe-go/v76/webhook"
)

func handleWebhook(w http.ResponseWriter, req *http.Request) {
    payload, _ := io.ReadAll(req.Body)
    sigHeader := req.Header.Get("Stripe-Signature")
    
    event, err := webhook.ConstructEvent(payload, sigHeader, endpointSecret)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    
    // Handle event
    w.WriteHeader(http.StatusOK)
}
```

## Event Handling Pattern

```python
def handle_event(event):
    event_type = event['type']
    data = event['data']['object']
    
    handlers = {
        'payment_intent.succeeded': handle_payment_success,
        'payment_intent.payment_failed': handle_payment_failure,
        'customer.subscription.created': handle_subscription_created,
        'customer.subscription.updated': handle_subscription_updated,
        'customer.subscription.deleted': handle_subscription_deleted,
        'invoice.paid': handle_invoice_paid,
        'invoice.payment_failed': handle_invoice_failed,
        'charge.dispute.created': handle_dispute,
    }
    
    handler = handlers.get(event_type)
    if handler:
        handler(data)
    else:
        print(f'Unhandled event type: {event_type}')
```

## Idempotency

Webhooks may be sent multiple times. Always handle idempotently:

```python
def handle_payment_success(payment_intent):
    payment_id = payment_intent['id']
    
    # Check if already processed
    if Order.query.filter_by(stripe_payment_id=payment_id).first():
        return  # Already handled
    
    # Process payment
    order = Order(
        stripe_payment_id=payment_id,
        amount=payment_intent['amount'],
        customer_id=payment_intent['customer']
    )
    db.session.add(order)
    db.session.commit()
```

## Webhook Best Practices

### 1. Return 200 Quickly
```python
def webhook_endpoint(request):
    event = verify_signature(request)
    
    # Queue for async processing
    queue.enqueue(process_event, event)
    
    # Return immediately
    return 200
```

### 2. Handle Retries
Stripe retries for up to 3 days with exponential backoff:
- 1 hour, 2 hours, 4 hours, 8 hours...

### 3. Log Everything
```python
def process_event(event):
    logger.info(f"Processing {event['type']}: {event['id']}")
    try:
        handle_event(event)
        logger.info(f"Success: {event['id']}")
    except Exception as e:
        logger.error(f"Failed: {event['id']}: {e}")
        raise
```

### 4. Test with CLI (Optional)

The Stripe CLI is optional and only for local development testing.

```bash
# If Stripe CLI is installed, use for local testing:
stripe listen --forward-to localhost:3000/webhooks/stripe

# Trigger test events
stripe trigger payment_intent.succeeded
stripe trigger customer.subscription.created
```

Note: Stripe CLI installation is not required. Use the Dashboard for webhook testing in production.

## List and Manage Endpoints

### List Endpoints
```bash
curl https://api.stripe.com/v1/webhook_endpoints \
  -u "$STRIPE_SECRET_KEY:"
```

### Update Endpoint
```bash
curl https://api.stripe.com/v1/webhook_endpoints/we_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "enabled_events[]=charge.dispute.created" \
  -d "enabled_events[]=charge.dispute.closed"
```

### Disable Endpoint
```bash
curl https://api.stripe.com/v1/webhook_endpoints/we_XXX \
  -u "$STRIPE_SECRET_KEY:" \
  -d "disabled=true"
```

### Delete Endpoint
```bash
curl -X DELETE https://api.stripe.com/v1/webhook_endpoints/we_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

## Debugging

### View Recent Events
```bash
curl "https://api.stripe.com/v1/events?limit=10" \
  -u "$STRIPE_SECRET_KEY:"
```

### Get Specific Event
```bash
curl https://api.stripe.com/v1/events/evt_XXX \
  -u "$STRIPE_SECRET_KEY:"
```

### Resend Event (via Dashboard)
Dashboard > Developers > Events > Select event > Resend


Security scan verdicts — ClawScan: clean | static analysis:  | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)