skills/zef-computers/drivers/stripe-payments

stripe-payments

SKILL.md

Stripe Payments

Implement payment acceptance using Stripe's Payment Intents API, Checkout, Elements, and Payment Links.

Priority Table

Category Impact Rules Key Concern
Setup HIGH 3 Integration path, Stripe.js loading, automatic payment methods
Payment Intents HIGH 4 Idempotency, amounts, status handling, intent reuse
Checkout Sessions HIGH 4 Webhook fulfillment, async payments, line items, session ID
Payment Element MEDIUM 4 Return URL, submit state, wallet dedup, billing details
Security HIGH 4 Client secret, SCA optimization, off-session recovery, metadata
Error Handling HIGH 3 User messages, retry strategy, decline classification
Webhooks HIGH 3 Signature verification, idempotent handlers, fast responses

Integration Decision Tree

How much UI control do you need?
|
+-- Minimal code / hosted page?
|   -> Checkout Sessions (references/checkout.md)
|
+-- No code at all?
|   -> Payment Links (references/payment-links.md)
|
+-- Embedded in your app with Stripe UI?
|   -> Payment Element (references/payment-element.md)
|
+-- Full custom UI?
    -> Payment Intents + confirm on client (references/payment-intents.md)

Quick Reference

Setup Rules

  • setup-automatic-payment-methods - Use automatic_payment_methods: { enabled: true } instead of hardcoding payment_method_types
  • setup-load-stripe-js - Always load Stripe.js from js.stripe.com, never self-host or bundle
  • setup-choose-integration - Use Checkout Sessions for most apps; Payment Element for custom UX; Payment Links for no-code

Payment Intent Rules

  • intent-use-idempotency-keys - Always include idempotency keys on create, capture, and refund; derive from business IDs
  • intent-reuse-payment-intents - Update existing PaymentIntents on cart changes instead of creating new ones
  • intent-handle-all-statuses - Handle succeeded, processing, requires_action, requires_payment_method, requires_capture
  • intent-amounts-in-cents - Pass amounts in smallest currency unit (cents): $20.00 = amount: 2000

Checkout Session Rules

  • checkout-webhook-fulfillment - Always use checkout.session.completed webhook for fulfillment, never the success URL
  • checkout-handle-async-payments - Check payment_status in completed event; listen for async_payment_succeeded for bank transfers
  • checkout-retrieve-line-items - Call listLineItems() separately; webhook payload does not include them
  • checkout-include-session-id - Include {CHECKOUT_SESSION_ID} in success URL for status verification

Payment Element Rules

  • element-provide-return-url - Always provide return_url in confirmPayment(); redirect-based methods fail without it
  • element-disable-submit-button - Disable submit button during processing to prevent double submissions
  • element-deduplicate-wallets - Set wallets: { applePay: 'never', googlePay: 'never' } when using Express Checkout Element
  • element-collect-billing-separately - If hiding billing fields, supply them in confirmParams.payment_method_data.billing_details

Security Rules

  • security-protect-client-secret - Never log, URL-embed, or persist client_secret; deliver via HTTPS JSON only
  • security-sca-setup-future-usage - Set setup_future_usage: 'off_session' when saving cards for off-session charging
  • security-off-session-recovery - Implement recovery flow for authentication_required errors on off-session payments
  • security-no-sensitive-metadata - Never store PII, card numbers, passwords, or API keys in metadata

Error Handling Rules

  • error-never-expose-raw-errors - Show user-friendly messages; hide sensitive decline codes (fraudulent, lost_card, stolen_card)
  • error-retry-with-backoff - Retry only connection/API/rate-limit errors with exponential backoff + jitter + idempotency keys
  • error-classify-decline-codes - Route retryable declines (incorrect CVC) differently from terminal ones (insufficient funds)

Webhook Rules

  • webhook-verify-signatures - Use express.raw() for webhook route; JSON-parsed bodies fail signature verification
  • webhook-idempotent-handlers - Check if event was already processed before fulfilling; Stripe retries on failure
  • webhook-return-200-quickly - Acknowledge webhooks immediately; process heavy tasks asynchronously via job queue

Quick Start: Checkout Session (Recommended for MVP)

Server

const session = await stripe.checkout.sessions.create({
  line_items: [{ price: 'price_xxx', quantity: 1 }],
  mode: 'payment', // or 'subscription' or 'setup'
  success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
  cancel_url: 'https://example.com/cancel',
});
// Redirect customer to session.url

Webhook

// Listen for checkout.session.completed
case 'checkout.session.completed':
  const session = event.data.object;
  await fulfillOrder(session);
  break;

Quick Start: Payment Element (Recommended for Custom UX)

Server - Create PaymentIntent

const paymentIntent = await stripe.paymentIntents.create({
  amount: 2000, // $20.00 in cents
  currency: 'usd',
  automatic_payment_methods: { enabled: true },
});
return { clientSecret: paymentIntent.client_secret };

Client - Mount Payment Element

import { loadStripe } from '@stripe/stripe-js';
import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';

const stripePromise = loadStripe('pk_test_xxx');

function CheckoutForm() {
  const stripe = useStripe();
  const elements = useElements();

  const handleSubmit = async (e) => {
    e.preventDefault();
    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: { return_url: 'https://example.com/complete' },
    });
    if (error) setMessage(error.message);
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button>Pay</button>
    </form>
  );
}

function App({ clientSecret }) {
  return (
    <Elements stripe={stripePromise} options={{ clientSecret }}>
      <CheckoutForm />
    </Elements>
  );
}

Payment Intent Lifecycle

requires_payment_method -> requires_confirmation -> requires_action -> processing -> succeeded
                                                                                  -> requires_payment_method (retry)
                                                                    -> canceled

Key states:

  • requires_action - 3DS/SCA authentication needed (handled by stripe.confirmPayment())
  • processing - async payment methods (bank debits) still settling
  • succeeded - funds captured, safe to fulfill

Critical Webhooks

Event When Action
payment_intent.succeeded Payment confirmed Fulfill order
payment_intent.payment_failed Payment declined Notify customer
payment_intent.requires_action Needs 3DS Client handles automatically
charge.dispute.created Chargeback filed See money-management reference
checkout.session.completed Checkout finished Fulfill + redirect

How to Use This Skill

  1. Start here - Use the decision tree above to pick your integration path
  2. Follow rules - Check the relevant rules in rules/ for correct implementation patterns
  3. Deep dive - Consult references/ for comprehensive documentation on specific topics
  4. Test - Use Stripe test cards (4242 4242 4242 4242 for success, 4000 0025 0000 3155 for 3DS)

References

  • references/payment-intents.md - PaymentIntent lifecycle, confirmation, captures, cancellation
  • references/checkout.md - Checkout Sessions, line items, modes, customization
  • references/payment-element.md - Elements setup, appearance API, payment method config
  • references/payment-links.md - No-code payment pages, configuration
  • references/payment-methods.md - Cards, wallets (Apple/Google Pay), bank debits, BNPL
  • references/3d-secure.md - SCA compliance, 3DS flows, exemptions
  • references/error-handling.md - Error types, decline codes, retry strategies
Weekly Installs
3
First Seen
Feb 24, 2026
Installed on
gemini-cli3
codex3
cursor3
opencode3
qoder2
codebuddy2