Stripe
Payment plugin for Stripe, based on Payment Intents and Setup Intents (SCA-compliant, supports saved payment methods).
Installation
Included in the all preset — registerAllPlugins() registers the plugin together with its webhook route.
To register it individually:
import { pluginRegistry } from '@unchainedshop/core';
import { StripePlugin } from '@unchainedshop/plugins/payment/stripe';
pluginRegistry.register(StripePlugin);
Register before startPlatform(). At startup, the plugin enables the webhook route POST /payment/stripe/webhook (path configurable via STRIPE_WEBHOOK_PATH); the Express/Fastify connector mounts it on the Unchained HTTP server. If STRIPE_SECRET is missing, initialization logs a warning and skips this plugin's adapter and route. A missing STRIPE_ENDPOINT_SECRET produces a warning; webhook verification requires it.
The stripe npm package is an optional peer dependency:
npm install stripe
Environment Variables
| Variable | Default | Description |
|---|---|---|
STRIPE_SECRET | - | Stripe secret key (required; the adapter and route are skipped without it) |
STRIPE_ENDPOINT_SECRET | - | Webhook signing secret for signature verification (required for webhooks) |
STRIPE_WEBHOOK_PATH | /payment/stripe/webhook | Webhook endpoint path |
STRIPE_WEBHOOK_ENVIRONMENT | - | Environment tag stored in intent metadata; webhooks for other environments are skipped (multi-environment setups) |
EMAIL_WEBSITE_NAME | Unchained | Fallback description on payment/setup intents |
Create Provider
mutation CreateStripeProvider {
createPaymentProvider(
paymentProvider: {
type: GENERIC
adapterKey: "shop.unchained.payment.stripe"
}
) {
_id
}
}
Provider configuration (via updatePaymentProvider):
| Key | Description |
|---|---|
descriptorPrefix | Custom prefix for statement descriptors (optional) |
Configure Stripe Dashboard
- Go to Developers > Webhooks
- Add endpoint:
https://your-domain.com/payment/stripe/webhook - Select events
payment_intent.succeededandsetup_intent.succeeded - Copy the signing secret to
STRIPE_ENDPOINT_SECRET
Payment Flow
Follows the standard checkout flow. Stripe specifics:
signPaymentProviderForCheckout(orderPaymentId: "...")creates a payment intent and returns its client secret.- Confirm the payment client-side with Stripe.js (
stripe.confirmPayment({ clientSecret, ... })). - On
payment_intent.succeeded, the webhook checks out the cart server-side. - Fallback — checkout with the payment intent id:
mutation {
checkoutCart(paymentContext: { paymentIntentId: "pi_..." }) {
_id
status
}
}
The plugin validates that amount, currency, and orderPaymentId metadata of the payment intent match the order payment.
Saved Payment Methods
- Create a setup intent:
mutation {
signPaymentProviderForCredentialRegistration(
paymentProviderId: "stripe-provider-id"
)
}
-
Confirm it client-side with
stripe.confirmSetup({ clientSecret, ... }). -
On
setup_intent.succeeded, the webhook registers the credentials — or register manually:
mutation {
registerPaymentCredentials(
paymentProviderId: "stripe-provider-id"
transactionContext: { setupIntentId: "seti_..." }
) {
_id
}
}
- Checkout with the saved payment method:
mutation {
checkoutCart(
paymentContext: {
paymentCredentials: {
token: "pm_stripe_payment_method_id"
meta: {
customer: "cus_stripe_customer_id"
payment_method_types: ["card"]
}
}
}
) {
_id
status
}
}
The plugin creates and reuses Stripe customers automatically, deduplicated by metadata["userId"].
Testing
Forward webhooks to your local server with the Stripe CLI:
stripe listen --forward-to http://localhost:4010/payment/stripe/webhook
Test card numbers: see Stripe testing docs.
Adapter Details
| Property | Value |
|---|---|
| Key | shop.unchained.payment.stripe |
| Type | GENERIC |
| Version | 2.0.0 |
| Source | payment/stripe/ |