Payment Provider Migration Plan

Migrating from Stripe v2 to Stripe v3 + adding Klarna as alternative payment method.
INTERACTIVE Select text or right-click any element to leave feedback. Export as JSON and paste into Claude Code.

How this works: Claude Code generates these interactive HTML plans during development. You review them in the browser, highlight text or right-click elements to leave comments, then export the feedback JSON. Paste it back into Claude Code and it knows exactly what to change. The feedback is anchored to specific sections — no ambiguity about what you're referring to.

Current State WORKING

  • Stripe v2 API integration via stripe-legacy package
  • Credit card payments (Visa, Mastercard, Amex)
  • Webhook handler at /webhooks/stripe for payment confirmations
  • Refund processing via admin dashboard
  • 3D Secure v1 (deprecated by Stripe, deadline: Q3 2026)

Monthly volume

MetricValue
Transactions/month~12,000
GMV~$840,000
Avg. ticket size$70
Refund rate3.2%

Problems to Solve URGENT

  • 3DS v1 sunset: Stripe deprecating 3D Secure v1 in Q3 2026 — must migrate to Payment Intents API
  • Cart abandonment: 34% drop-off at payment step. Klarna "Pay Later" could recover ~15% of lost conversions
  • No idempotency: Duplicate charges reported 2-3x/month due to retry logic without idempotency keys
  • Webhook reliability: No signature verification on incoming webhooks — security risk
  • Legacy SDK: stripe-legacy is unmaintained, last update 18 months ago

Target Architecture

flowchart TD
    CHECKOUT["Checkout Page"]

    CHECKOUT --> INTENT["Create PaymentIntent
(server-side)"] INTENT --> CHOOSE{"Payment
Method?"} CHOOSE -->|Credit Card| STRIPE_EL["Stripe Elements
(3DS v2 built-in)"] CHOOSE -->|Klarna| KLARNA_SDK["Klarna Widget
(Pay Later / Installments)"] STRIPE_EL --> CONFIRM["confirmPayment()"] KLARNA_SDK --> REDIRECT["Klarna Redirect
+ Return URL"] CONFIRM --> WH_STRIPE["Webhook:
payment_intent.succeeded"] REDIRECT --> WH_KLARNA["Webhook:
klarna.order.approved"] WH_STRIPE --> VERIFY["Verify Signature
(stripe-signature header)"] WH_KLARNA --> VERIFY VERIFY --> FULFILL["Fulfill Order
+ Send Confirmation"] FULFILL --> LEDGER["Record in
Payment Ledger"] style STRIPE_EL fill:#dbeafe,stroke:#1d4ed8 style KLARNA_SDK fill:#fce7f3,stroke:#db2777 style VERIFY fill:#dcfce7,stroke:#16a34a style FULFILL fill:#dcfce7,stroke:#16a34a

Implementation Phases

Phase Scope Risk Done when
Phase 1 DONE
Stripe SDK Upgrade
Replace stripe-legacy with stripe@14.
Migrate from Charges API to Payment Intents API.
Add idempotency keys to all payment operations.
Verify webhook signatures with stripe.webhooks.constructEvent().
MEDIUM All existing credit card flows work with new SDK. Zero duplicate charges for 7 days.
Phase 2 DONE
3DS v2 Migration
Replace custom 3DS v1 iframe with Stripe Elements.
Payment Sheet handles SCA challenges automatically.
Test with 4000 0025 0000 3155 (requires auth) and 4000 0000 0000 3220 (3DS2 required).
LOW 3DS v2 challenges complete in-page without redirect. Auth success rate >95%.
Phase 3 IN PROGRESS
Klarna Integration
Add Klarna as payment method on PaymentIntent creation.
"Pay in 30 days" + "Pay in 3 installments" options.
Klarna widget in checkout with amount breakdown.
Handle async Klarna webhooks (authorized → captured).
MEDIUM Klarna option visible in checkout. E2E test: create order → Klarna auth → webhook → fulfillment.
Phase 4
Monitoring + Rollout
Payment success rate dashboard (Grafana).
Alert on >5% failure rate or webhook delivery delay >5min.
A/B test: Klarna vs. card-only checkout conversion.
Gradual rollout: 10% → 50% → 100%.
LOW Dashboard live. Conversion lift from Klarna measured. Full rollout complete.

Klarna Payment Flow (Phase 3)

sequenceDiagram
    participant U as Customer
    participant FE as Frontend
    participant BE as Backend API
    participant S as Stripe API
    participant K as Klarna

    U->>FE: Select "Pay with Klarna"
    FE->>BE: POST /api/payments/create-intent
    BE->>S: stripe.paymentIntents.create({
payment_method_types: ['klarna']}) S-->>BE: { client_secret, id } BE-->>FE: { clientSecret } FE->>S: stripe.confirmKlarnaPayment(clientSecret) S->>K: Redirect to Klarna authorization K->>U: Show "Pay in 30 days" offer U->>K: Approve payment K->>S: Authorization confirmed S->>FE: Redirect to return_url Note over S,BE: Async webhook (1-30s later) S->>BE: POST /webhooks/stripe
payment_intent.succeeded BE->>BE: Verify signature + fulfill order BE->>U: Order confirmation email

Architecture Decisions

  • DECIDED Use Stripe's built-in Klarna integration (not direct Klarna API) — simpler, one webhook endpoint, unified refund flow
  • DECIDED Payment Intents API with automatic_payment_methods instead of hard-coding method types — future-proof for Apple Pay, Google Pay
  • DECIDED Webhook signature verification mandatory — reject unsigned events with 400
  • OPEN Should we store full Stripe events in a payment_events table for audit trail, or rely on Stripe Dashboard?
  • OPEN Klarna minimum order amount: 35 EUR or 50 EUR? Need data on avg. cart value for Klarna-eligible products.

Risks & Mitigations

  • Klarna rejection rate: Klarna rejects ~20% of first-time buyers → Mitigation: Show credit card fallback immediately on rejection, don't dead-end the checkout
  • Webhook ordering: Stripe doesn't guarantee event order → Mitigation: Idempotent handlers, check PaymentIntent status before fulfilling
  • Currency mismatch: Klarna only supports EUR in DE/AT → Mitigation: Hide Klarna option for non-EUR orders
  • Refund complexity: Klarna partial refunds have 14-day settlement delay → Mitigation: Show "refund pending" status in admin dashboard

Code References

FilePurpose
src/services/payment/stripe-client.ts Stripe SDK initialization, PaymentIntent creation, idempotency key generation
src/routes/api/payments/create-intent.ts POST endpoint: creates PaymentIntent with card + klarna methods
src/routes/webhooks/stripe.ts Webhook handler with signature verification + event routing
src/components/checkout/PaymentStep.tsx Stripe Elements + Klarna widget in checkout flow
src/services/payment/fulfillment.ts Order fulfillment triggered by successful payment events

Status