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.

Source: modules/stripe · npm: @86d-app/stripe The Stripe module implements the PaymentProvider interface from @86d-app/payments using raw fetch() calls to the Stripe REST API. There is no Stripe SDK dependency. It includes a webhook endpoint with HMAC-SHA256 signature verification and timestamp replay protection.

Installation

npm install @86d-app/stripe @86d-app/payments
Register both modules and pass the provider instance to the payments module:
import stripe, { StripePaymentProvider } from "@86d-app/stripe";
import payments from "@86d-app/payments";
import { createModuleClient } from "@86d-app/core";

const stripeProvider = new StripePaymentProvider("sk_live_...");

const client = createModuleClient([
  stripe({
    apiKey: "sk_live_...",
    webhookSecret: "whsec_...",
  }),
  payments({ provider: stripeProvider }),
]);
You can also configure both modules via config.json:
{
  "modules": ["@86d-app/payments", "@86d-app/stripe"],
  "moduleOptions": {
    "@86d-app/stripe": {
      "apiKey": "sk_live_...",
      "webhookSecret": "whsec_..."
    }
  }
}

Configuration

apiKey
string
required
Your Stripe secret key. Use sk_test_... for development and sk_live_... for production. The admin dashboard masks all but the first 7 characters.
webhookSecret
string
Webhook signing secret from the Stripe dashboard (whsec_...). When provided, every incoming webhook request is verified using HMAC-SHA256 with a 5-minute timestamp tolerance. Without this value, all webhook requests are accepted, which is useful for local development but not suitable for production.

PaymentProvider API

StripePaymentProvider implements the PaymentProvider interface. Use it directly when you need to create or manage payment intents in server-side code.

createIntent

Creates a Stripe PaymentIntent and returns a clientSecret in providerMetadata. Pass amount in the smallest currency unit (cents for USD).
const provider = new StripePaymentProvider("sk_live_...");

const intent = await provider.createIntent({
  amount: 2500,      // $25.00
  currency: "usd",
});
// {
//   providerIntentId: "pi_...",
//   status: "pending",
//   providerMetadata: { clientSecret: "pi_...secret_..." }
// }

confirmIntent

Confirms a PaymentIntent after the customer completes payment on the client side.
await provider.confirmIntent("pi_...");

cancelIntent

Cancels an uncaptured PaymentIntent.
await provider.cancelIntent("pi_...");

createRefund

Issues a full or partial refund. Omit amount for a full refund.
await provider.createRefund({
  providerIntentId: "pi_...",
  amount: 1000,                       // $10.00 partial refund
  reason: "requested_by_customer",
});

Status mapping

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

Store endpoints

MethodPathDescription
POST/stripe/webhookReceive and verify Stripe webhook events

Webhook setup

1

Set the environment variable

Add your webhook signing secret to your environment:
STRIPE_WEBHOOK_SECRET=whsec_...
2

Register your endpoint in Stripe

In the Stripe dashboard, add a new endpoint:
https://yourdomain.com/api/stripe/webhook
3

Select events to listen to

Enable at minimum:
  • payment_intent.succeeded
  • payment_intent.payment_failed
  • charge.refunded

How signature verification works

The webhook endpoint reads the raw request body before JSON parsing (required for HMAC integrity), then verifies the Stripe-Signature header:
signed_payload = <timestamp> + "." + <raw_body>
expected_sig   = HMAC-SHA256(webhookSecret, signed_payload)
The v1 signature from the header is compared using constant-time comparison to prevent timing attacks. Requests with invalid or expired signatures (older than 5 minutes) return 401. Verification uses the Web Crypto API. No external dependencies.

Types

interface StripeOptions {
  apiKey: string;
  webhookSecret?: string;
}

interface ProviderIntentResult {
  providerIntentId: string;
  status: "pending" | "processing" | "succeeded" | "cancelled" | "failed";
  providerMetadata?: Record<string, unknown>;
}

interface ProviderRefundResult {
  providerRefundId: string;
  status: "pending" | "succeeded" | "failed";
  providerMetadata?: Record<string, unknown>;
}
The full PaymentProvider interface from @86d-app/payments:
interface PaymentProvider {
  createIntent(params: {
    amount: number;
    currency: string;
    metadata?: Record<string, unknown>;
  }): Promise<ProviderIntentResult>;

  confirmIntent(providerIntentId: string): Promise<ProviderIntentResult>;

  cancelIntent(providerIntentId: string): Promise<ProviderIntentResult>;

  createRefund(params: {
    providerIntentId: string;
    amount?: number;
    reason?: string;
  }): Promise<ProviderRefundResult>;
}