Integrate Stripe

Edit on GitHub

This document describes how to integrate the spryker-eco/stripe module into your Spryker project. The module communicates directly with the Stripe API from your application and embeds Stripe Elements into the checkout payment step.

Prerequisites

Step 1: Install the package

composer require spryker-eco/stripe

Step 2: Remove old ACP MessageBroker plugins (if upgrading from ACP)

If you are upgrading from the ACP-based Stripe integration, remove the following plugins from src/Pyz/Zed/MessageBroker/MessageBrokerDependencyProvider.php.

Remove these imports and their instantiations from getMessageHandlerPlugins():

// Remove these use statements:
use Spryker\Zed\Payment\Communication\Plugin\MessageBroker\PaymentMethodMessageHandlerPlugin;
use Spryker\Zed\Payment\Communication\Plugin\MessageBroker\PaymentOperationsMessageHandlerPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\MessageBroker\PaymentAppOperationsMessageHandlerPlugin;
use Spryker\Zed\SalesPaymentDetail\Communication\Plugin\MessageBroker\SalesPaymentDetailMessageHandlerPlugin;
use Spryker\Zed\MerchantApp\Communication\Plugin\MessageBroker\MerchantAppOnboardingMessageHandlerPlugin;

For a full migration guide, see Migrate from the ACP Stripe app.

Step 3: Update OMS configuration

In config/Shared/config_default.php, configure OMS to use the Stripe state machines:

use Spryker\Shared\Oms\OmsConstants;
use Spryker\Shared\Sales\SalesConstants;
use Spryker\Zed\Oms\OmsConfig;
use SprykerEco\Shared\Stripe\StripeConfig;

$config[OmsConstants::PROCESS_LOCATION] = [
    OmsConfig::DEFAULT_PROCESS_LOCATION,
    APPLICATION_ROOT_DIR . '/vendor/spryker-eco/stripe/config/Zed/oms',
];
$config[OmsConstants::ACTIVE_PROCESSES] = [
    'StripeManual01', // Use StripeManualMarketplace01 for marketplace
];
$config[SalesConstants::PAYMENT_METHOD_STATEMACHINE_MAPPING] = [
    StripeConfig::PAYMENT_METHOD_NAME => 'StripeManual01', // Use StripeManualMarketplace01 for marketplace
];

Step 4: Register Stripe OMS command and condition plugins

In src/Pyz/Zed/Oms/OmsDependencyProvider.php, register the Stripe command and condition plugins.

Command plugins

use SprykerEco\Zed\Stripe\Communication\Plugin\Oms\Command\StripeCancelCommandPlugin;
use SprykerEco\Zed\Stripe\Communication\Plugin\Oms\Command\StripeCaptureCommandPlugin;
use SprykerEco\Zed\Stripe\Communication\Plugin\Oms\Command\StripeRefundCommandPlugin;

In extendCommandPlugins():

$commandCollection->add(new StripeCaptureCommandPlugin(), 'Stripe/Capture');
$commandCollection->add(new StripeRefundCommandPlugin(), 'Stripe/Refund');
$commandCollection->add(new StripeCancelCommandPlugin(), 'Stripe/Cancel');
Capture behavior

StripeCaptureCommandPlugin always captures the full authorized amount. Stripe allows only one capture per PaymentIntent. Items canceled after capture are handled via refunds.

For marketplace only, also register the merchant payout commands:

$commandCollection->add(new MerchantPayoutCommandByOrderPlugin(), 'SalesPaymentMerchant/Payout');
$commandCollection->add(new MerchantPayoutReverseCommandByOrderPlugin(), 'SalesPaymentMerchant/ReversePayout');

Condition plugins

In extendConditionPlugins():

use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusAuthorizationFailedConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusAuthorizedConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusCanceledConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusCancellationFailedConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusCapturedConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusCaptureFailedConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusCaptureRequestedConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusOverpaidConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusUnderpaidConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusRefundedConditionPlugin;
use Spryker\Zed\PaymentApp\Communication\Plugin\Oms\Condition\IsPaymentAppPaymentStatusRefundFailedConditionPlugin;

$conditionCollection->add(new IsPaymentAppPaymentStatusAuthorizationFailedConditionPlugin(), 'Payment/IsAuthorizationFailed');
$conditionCollection->add(new IsPaymentAppPaymentStatusAuthorizedConditionPlugin(), 'Payment/IsAuthorized');
$conditionCollection->add(new IsPaymentAppPaymentStatusCanceledConditionPlugin(), 'Payment/IsCanceled');
$conditionCollection->add(new IsPaymentAppPaymentStatusCancellationFailedConditionPlugin(), 'Payment/IsCancellationFailed');
$conditionCollection->add(new IsPaymentAppPaymentStatusCapturedConditionPlugin(), 'Payment/IsCaptured');
$conditionCollection->add(new IsPaymentAppPaymentStatusCaptureFailedConditionPlugin(), 'Payment/IsCaptureFailed');
$conditionCollection->add(new IsPaymentAppPaymentStatusCaptureRequestedConditionPlugin(), 'Payment/IsCaptureRequested');
$conditionCollection->add(new IsPaymentAppPaymentStatusOverpaidConditionPlugin(), 'Payment/IsOverpaid');
$conditionCollection->add(new IsPaymentAppPaymentStatusUnderpaidConditionPlugin(), 'Payment/IsUnderpaid');
$conditionCollection->add(new IsPaymentAppPaymentStatusRefundedConditionPlugin(), 'Payment/IsRefunded');
$conditionCollection->add(new IsPaymentAppPaymentStatusRefundFailedConditionPlugin(), 'Payment/IsRefundFailed');

For marketplace only, also register:

$conditionCollection->add(new IsMerchantPaidOutConditionPlugin(), 'SalesPaymentMerchant/IsMerchantPaidOut');
$conditionCollection->add(new IsMerchantPayoutReversedConditionPlugin(), 'SalesPaymentMerchant/IsMerchantPayoutReversed');

Step 5: Register the Stripe payout transmission plugin (marketplace only)

In src/Pyz/Zed/SalesPaymentMerchant/SalesPaymentMerchantDependencyProvider.php:

use SprykerEco\Zed\Stripe\Communication\Plugin\SalesPaymentMerchant\StripePayoutTransmissionPlugin;

In getMerchantPayoutTransmissionPlugins():

return [new StripePayoutTransmissionPlugin()];

Step 6: Register the Stripe checkout post-save plugin

In src/Pyz/Zed/Checkout/CheckoutDependencyProvider.php:

use SprykerEco\Zed\Stripe\Communication\Plugin\Checkout\StripeCheckoutPostSavePlugin;

In getCheckoutPostHooks():

new StripeCheckoutPostSavePlugin(),

Step 7: Register Stripe Yves checkout plugins

In src/Pyz/Yves/CheckoutPage/CheckoutPageDependencyProvider.php:

use SprykerEco\Shared\Stripe\StripeConfig as SharedStripeConfig;
use SprykerEco\Yves\Stripe\Plugin\StepEngine\StripeStepHandlerPlugin;
use SprykerEco\Yves\Stripe\Plugin\StepEngine\StripeSubFormPlugin;

In extendPaymentMethodHandler():

$paymentMethodHandler->add(new StripeStepHandlerPlugin(), SharedStripeConfig::PAYMENT_METHOD_NAME);

In extendSubFormPluginCollection():

$paymentSubFormPluginCollection->add(new StripeSubFormPlugin());

Step 8: Register the payment method filter plugin

To filter out the Stripe payment method when it’s not available (for example, when Stripe is not configured), register the filter plugin in src/Pyz/Zed/Payment/PaymentDependencyProvider.php:

use SprykerEco\Zed\Stripe\Communication\Plugin\Payment\StripePaymentMethodFilterPlugin;

In getPaymentMethodFilterPlugins():

new StripePaymentMethodFilterPlugin(),

Step 9: Register Stripe routes

In src/Pyz/Yves/Router/RouterDependencyProvider.php:

use SprykerEco\Yves\Stripe\Plugin\Router\StripeRouteProviderPlugin;

In getRouteProvider():

new StripeRouteProviderPlugin(),

Step 10: Add the Stripe payment form to the checkout payment template

In src/Pyz/Yves/CheckoutPage/Theme/default/views/payment/payment.twig, add the Stripe form to the customForms definition:


{% define data = {
    customForms: {
        'Stripe/stripe': ['stripe'],
    },
} %}

Step 11: Register the marketplace installer plugin (marketplace only)

In src/Pyz/Zed/Installer/InstallerDependencyProvider.php:

use SprykerEco\Zed\Stripe\Communication\Plugin\Installer\StripeMarketplaceInstallerPlugin;

In getInstallerPlugins():

new StripeMarketplaceInstallerPlugin(),

Step 12: Allow Stripe controllers in Merchant Portal security config (marketplace only)

In src/Pyz/Zed/SecurityMerchantPortalGui/SecurityMerchantPortalGuiConfig.php, extend the route patterns to include Stripe:

<?php

namespace Pyz\Zed\SecurityMerchantPortalGui;

use Spryker\Zed\SecurityMerchantPortalGui\SecurityMerchantPortalGuiConfig as SprykerSecurityMerchantPortalGuiConfig;

class SecurityMerchantPortalGuiConfig extends SprykerSecurityMerchantPortalGuiConfig
{
    protected const MERCHANT_PORTAL_ROUTE_PATTERN = '^/((.+)-merchant-portal-gui|multi-factor-auth-merchant-portal/(merchant-user|user-management)|stripe)/';
    protected const IGNORABLE_PATH_PATTERN = '^/(security-merchant-portal-gui|multi-factor-auth-merchant-portal|stripe)';
}

Step 13: Configure Stripe credentials

Option A: Environment variable-based credentials

In config/Shared/config_local.php:

use SprykerEco\Shared\Stripe\StripeConstants;

$config[StripeConstants::STRIPE_SECRET_KEY] = 'sk_live_***';
$config[StripeConstants::STRIPE_PUBLISHABLE_KEY] = 'pk_live_***';
$config[StripeConstants::STRIPE_WEBHOOK_SECRET] = 'whsec_***';
// Marketplace only:
$config[StripeConstants::STRIPE_WEBHOOK_SECRET_CONNECT] = 'whsec_***';

Option B: Back Office configuration

This option requires the Spryker Configuration feature.

  1. Create src/Pyz/Shared/Stripe/StripeConfig.php:
<?php

namespace Pyz\Shared\Stripe;

use SprykerEco\Shared\Stripe\StripeConfig as SprykerEcoStripeConfig;

class StripeConfig extends SprykerEcoStripeConfig
{
    public function isConfigurationModuleUsed(): bool
    {
        return true;
    }
}
  1. Run the configuration sync:
vendor/bin/console configuration:sync
  1. Configure credentials in the Back Office under Configuration > Integrations > Stripe.

To enable credential validation on save, register StripeCredentialsPreSavePlugin in ConfigurationDependencyProvider::getConfigurationValuePreSavePlugins().

Step 14: Import payment methods

Option 1: Import using the module’s configuration file:

vendor/bin/console data:import --config=vendor/spryker-eco/stripe/data/import/stripe.yml

Option 2: Copy CSV files and import individually:

Copy the CSV files from vendor/spryker-eco/stripe/data/import/ to data/import/, then run:

vendor/bin/console data:import payment-method
vendor/bin/console data:import payment-method-store
vendor/bin/console data:import glossary

Step 15: Run code generation and database migration

vendor/bin/console propel:install
vendor/bin/console transfer:generate

Run the following commands only when you initially set up the database in an environment (for example, right after docker/sdk boot deploy.dev.yml on a fresh local setup). Don’t run them as part of routine deployments to an already-initialized environment:

vendor/bin/console setup:init-db
acl-entity:synchronize is Merchant Portal-only

acl-entity:synchronize is provided by spryker/acl-merchant-portal, which is part of the Merchant Portal ACL feature. Run this command only if your project uses Merchant Portal roles and permissions (typically marketplace projects).

vendor/bin/console acl-entity:synchronize

Step 16: Register the Stripe webhook in the Stripe Dashboard

In your Stripe Dashboard, in the search field input Webhooks and open Webhooks list page. Register a webhook endpoint pointing to:

  1. Create the webhook for changing payment statuses by adding the next information:
https://your-domain.com/stripe/notification

Configure it to listen to the following events:

Event Meaning
payment_intent.amount_capturable_updated Authorized
payment_intent.succeeded Captured or partially captured
payment_intent.payment_failed Capture failed
payment_intent.canceled Canceled
charge.failed Capture failed (post-capture)
charge.refunded Refunded or partially refunded
charge.refund.updated Refund failed
  1. Create the webhook for the merchant accounts onboarding: For marketplace only, register a second Connect endpoint pointing to the same URL and enable Listen to events on connected accounts.
https://your-domain.com/stripe/notification

Configure it to listen to the following events:

Event
account.updated
account.application.authorized
capability.updated
person.created
person.updated
account.external_account.created
Webhook secret fallback

The webhook handler tries the standard secret first, then falls back to the Connect secret. Each processed webhook event stores raw Stripe details as JSON in spy_payment_app_payment_status_history.context.

Step 17: Register your domain in the Stripe Dashboard

Google Pay and Apple Pay require domain registration. In your Stripe Dashboard, go to Settings > Payments > Payment method domains and add your domain.

Step 18: Verify the installation

  1. Place a test order and confirm the payment step renders Stripe Elements.
  2. Complete payment and confirm the order transitions to the authorized state.
  3. Capture the order and verify the Stripe/Capture OMS command triggers (full amount captured).
  4. Send a test webhook and confirm order status updates.
  5. (Marketplace only) Log in to Merchant Portal and verify Payment Settings.
  6. (Marketplace only) Complete merchant onboarding to Stripe Connect.
  7. (Marketplace only) Trigger a payout and verify the Stripe Connect transfer appears.

Network access for Stripe.js and the Stripe API

The storefront loads Stripe.js directly from https://js.stripe.com/v3/ to render Stripe Elements and confirm payments in the browser. If your project enforces a Content Security Policy or an outbound network allowlist for the storefront, allow:

  • https://js.stripe.com (script and frame source for Stripe.js and Elements)
  • https://api.stripe.com (the domain Stripe.js itself calls from the browser to create and confirm payments)

$config[Spryker\Shared\Kernel\KernelConstants::DOMAIN_WHITELIST] is used to validate URLs the application redirects the customer’s or merchant’s browser to via redirectResponseExternal(). For checkout, this doesn’t apply: SprykerEco\Yves\Stripe\Controller\PaymentController only redirects to internal routes, and Stripe Elements/Stripe.js talks to Stripe directly from the browser without a full-page redirect, so no checkout-related entry is needed in the whitelist.

For marketplace projects it’s different, Merchant Portal’s Stripe Connect onboarding (SprykerEco\Zed\Stripe\Communication\Controller\OnboardingController) and Express Dashboard access (SprykerEco\Zed\Stripe\Communication\Controller\DashboardController) do redirect the merchant’s browser to a Stripe-owned domain (connect.stripe.com, returned by the Stripe Account Links and Login Links APIs). As of the current module version, both controllers build that redirect with a plain Symfony\Component\HttpFoundation\RedirectResponse instead of redirectResponseExternal(), so DOMAIN_WHITELIST isn’t actually consulted for them either — but this is an internal implementation detail, not a documented guarantee. If your project uses Merchant Portal Stripe onboarding, keep connect.stripe.com whitelisted rather than relying on that bypass.

Migrating from the ACP Stripe app

If you are migrating from the previous ACP-based Stripe integration, see Migrate from the ACP Stripe app.