Skip to main content

Documentation Index

Fetch the complete documentation index at: https://86d.app/docs/llms.txt

Use this file to discover all available pages before exploring further.

86d separates payment logic into two layers: the @86d-app/payments module manages the intent lifecycle (create, confirm, cancel, refund), and a provider module (Stripe, PayPal, Square, or Braintree) handles the actual API communication. This split lets you swap payment providers without touching your checkout flow. All provider modules implement HMAC webhook verification using the Web Crypto API directly, so no extra crypto dependencies are required. Webhooks return 401 on invalid or expired signatures.

Install

npm install @86d-app/stripe @86d-app/payments

Add to config.json

{
  "modules": [
    "@86d-app/payments",
    "@86d-app/stripe"
  ],
  "moduleOptions": {
    "@86d-app/stripe": {
      "apiKey": "sk_live_...",
      "webhookSecret": "whsec_..."
    }
  }
}
Then run:
86d generate

Set environment variables

Set these in your deployment environment (.env.local for local development):
VariableDescription
STRIPE_SECRET_KEYYour Stripe secret key. sk_live_... for production, sk_test_... for testing.
STRIPE_WEBHOOK_SECRETSigning secret from your Stripe webhook endpoint (whsec_...).

Configure a Stripe webhook

In the Stripe dashboard, create a webhook endpoint pointing to:
POST https://your-store.com/api/stripe/webhook
Select these events at minimum:
  • payment_intent.succeeded
  • payment_intent.payment_failed
  • payment_intent.canceled
  • charge.refunded
Copy the signing secret (whsec_...) and set it as STRIPE_WEBHOOK_SECRET.

How the payment flow works

  1. Create intent. At checkout, the server calls provider.createIntent({ amount, currency }) against the Stripe API. This returns a clientSecret in providerMetadata.
  2. Confirm on the client. Your checkout UI uses Stripe.js to call confirmCardPayment(clientSecret) with the customer’s card details. No raw card data ever touches your server.
  3. Webhook confirms. Stripe sends a payment_intent.succeeded event to /api/stripe/webhook. The module verifies the HMAC-SHA256 signature using a timing-safe comparison, then emits a payment.completed domain event that the payments module uses to finalize the order.

Webhook security

The Stripe module verifies each incoming webhook using the Stripe signing scheme:
signed_payload = <timestamp> + "." + <raw_body>
expected_sig   = HMAC-SHA256(webhook_secret, signed_payload)
Signatures are compared with constant-time comparison to prevent timing attacks. Webhooks with timestamps older than 5 minutes are rejected with 401 to protect against replay attacks. Without webhookSecret set, all requests are accepted, which is useful for local development with the Stripe CLI.

Status mapping

Stripe status86d status
succeededsucceeded
canceledcancelled
processing, requires_captureprocessing
requires_payment_method, requires_confirmation, requires_actionpending

Webhook security across all providers

All four payment modules implement the same security model: webhook secrets are captured in a closure at module init time; each incoming request reads the raw body before any JSON parsing (required for HMAC integrity); signatures are verified with timing-safe comparison; and replay attacks are blocked by timestamp tolerance. No external crypto library is used. Verification runs on the Web Crypto API built into the runtime, keeping every payment module publishable as a standalone npm package.