Accept credit and debit card payments through Stripe. This add-on integrates Stripe’s Payment Intents API with the Larapen Shop, providing secure, PCI-compliant card processing with 3D Secure support, webhook-driven order updates, and one-click refunds.

Payment Intents API

Uses Stripe’s latest Payment Intents flow for SCA-compliant, secure card payments.

3D Secure

Automatic or always-on 3D Secure verification for strong customer authentication (SCA).

Webhook Processing

Handles payment success, failure, refund, and dispute events via signed webhooks.

Encrypted Credentials

API keys are encrypted at rest using Laravel’s Crypt facade. Never stored in plaintext.

Use Cases

Online Store with Card Payments

You run a shop on Larapen selling physical or digital products. Customers select “Credit Card” at checkout, enter their card details in a Stripe Elements form, and pay instantly.

  • Install the Stripe add-on alongside the Shop add-on.
  • Enter your Stripe API keys in Admin → Stripe → Settings.
  • Stripe appears as a payment option at checkout automatically.
  • Orders are confirmed in real time via webhooks.

Digital Downloads with Instant Delivery

You sell digital products (e-books, software, templates). Stripe confirms payment immediately, triggering order completion and granting download access.

Multi-Currency International Sales

Configure the currency to match your target market (USD, EUR, GBP, etc.). Stripe handles currency conversion and international card networks.

Requirements

  • Larapen CMS v1.0.0 or later
  • PHP 8.3+
  • MySQL 8.0+
  • The Shop add-on (required dependency)
  • A Stripe account with API keys (see Obtaining API Keys)
  • The stripe/stripe-php Composer package
Note: The Stripe add-on depends on the Shop add-on. It registers itself as a payment gateway via the PaymentGatewayInterface contract and is automatically discovered by the shop checkout system.

Installation

Step 1: Upload the Add-on

In the admin panel, go to Admin → Extensions → Add-ons and click the Upload Add-on button. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.

Step 2: Activate the Add-on

Find Stripe Payment Gateway in the list and click Activate. Its migrations, seeders (if any) and permissions are set up automatically.

Step 3: Configure

Navigate to Admin → Stripe → Settings and enter your Stripe Publishable Key, Secret Key, and Webhook Signing Secret. See Configuration.

Step 4: Set Up Webhooks

In the Stripe Dashboard, create a webhook endpoint pointing to https://yoursite.com/stripe/webhook, then copy the signing secret and paste it in the admin settings. See Webhook Setup.

Purchase Code (License Key)

Stripe Payment Gateway is sold as a separate product, so it has its own purchase code (license key), distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate Stripe Payment Gateway in Admin panel → Add-ons.

Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.

Platform / Marketplace How you get the purchase code Where to find it again
bedigit.com Store
In-site purchase (Shop)
Generated automatically when the order is paid, then sent by email, either in its own license email, or inside the order confirmation email. My Account → My Licenses on bedigit.com
Gumroad Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com
Envato Market
CodeCanyon
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. Envato account → Downloads → License certificate & purchase code
Check your spam folder. For both bedigit.com Store and Gumroad purchases, the purchase code is delivered by email. Automated license emails are very often filtered, so if the message is not in your inbox, look in your spam / junk folder before contacting support, and add our sender address to your contacts or allow list.

1. bedigit.com Store (in-site purchase)

  • As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit: buying 3 units gives 3 distinct keys).
  • It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
  • The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
  • The matching invoice is under My Account → My Orders.

2. Gumroad

  • A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
  • The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
  • When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
  • Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.

3. Envato Market (CodeCanyon)

  • Envato purchase codes are issued and delivered by Envato Market, never emailed by us, so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
  • Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
  • An Envato purchase code looks like 12345678-90ab-cdef-1234-567890abcdef (8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one.
  • Official Envato article: Where Is My Purchase Code?
Lost your purchase code? Search your mailbox (spam folder included) for “license” or “purchase code”, then check My Account → My Licenses on bedigit.com for Store and Gumroad purchases, or Downloads → License certificate on Envato. If it is still missing, open a ticket on our Help Center with your order number (Store), Gumroad sale ID or buyer email (Gumroad), or Envato username and item name (Envato).

Configuration

All settings are managed in Admin → Stripe → Settings (stored in the settings table, group stripe).

Setting Description Default
stripe_public_key Stripe Publishable Key (starts with pk_). Encrypted at rest. (empty)
stripe_secret_key Stripe Secret Key (starts with sk_). Encrypted at rest. (empty)
stripe_webhook_secret Webhook signing secret (starts with whsec_). Encrypted at rest. (empty)
stripe_capture_method How payments are captured: automatic (immediate) or manual (authorize then capture later). automatic
stripe_currency Three-letter ISO currency code in lowercase (e.g. usd, eur, gbp). usd
stripe_request_three_d_secure When to request 3D Secure: automatic (only when required by the issuing bank) or any (always request). automatic
Security: API keys are encrypted using Laravel’s Crypt::encryptString() before being stored in the database. They are decrypted at runtime when needed. Never share your secret key.

Environment Variables

Environment variables serve as defaults. Settings saved in the admin panel override them.

Note: Environment variables are used as fallback defaults when no value is saved in the admin panel. Admin panel settings always take priority and are stored encrypted.

Obtaining API Keys

  1. Log in to the Stripe Dashboard.
  2. Navigate to Developers → API Keys.
  3. Copy the Publishable key (starts with pk_test_ or pk_live_).
  4. Reveal and copy the Secret key (starts with sk_test_ or sk_live_).
  5. Paste both keys in Admin → Stripe → Settings.
Test vs. Live: Use test keys (pk_test_ / sk_test_) during development. Switch to live keys for production. Test card numbers are available at docs.stripe.com/testing.

Test Card Numbers

Card Number Scenario
4242 4242 4242 4242 Successful payment
4000 0025 0000 3155 Requires 3D Secure authentication
4000 0000 0000 9995 Declined (insufficient funds)

Use any future expiry date (e.g. 12/34) and any 3-digit CVC.

Admin: Settings

The settings page (Admin → Stripe → Settings) is organized into two sections:

API Keys

  • Publishable Key: Masked password field with show/hide toggle. Starts with pk_.
  • Secret Key: Masked password field with show/hide toggle. Starts with sk_. Encrypted before storage.
  • Webhook Signing Secret: Masked password field. Starts with whsec_. Used to verify incoming webhook signatures.

A warning banner reminds admins that keys are encrypted before storage and that fields should be left empty to retain current values.

An info box provides direct links to:

Payment Options

  • Capture Method: Automatic (charge immediately) or Manual (authorize now, capture later).
  • Currency: Three-letter currency code in lowercase (e.g. usd, eur, gbp).
  • 3D Secure: Automatic (only when required by the issuing bank) or Always request.

Payment Flow

The Stripe add-on uses the Payment Intents API for SCA-compliant card processing. Here is the complete payment flow:

  1. Checkout Selection: The customer selects “Credit Card” as the payment method at checkout. The Stripe payment form (Stripe Elements) appears.
  2. Order Creation: The shop creates an order and calls StripeGateway::createPaymentIntent($order).
  3. Payment Intent: The gateway creates a Stripe PaymentIntent via the API, stores the details locally in stripe_payment_intents, and returns the client_secret.
  4. Client-Side Confirmation: The browser uses stripe.confirmCardPayment() with the client secret and card details collected via Stripe Elements.
  5. 3D Secure: If the card requires authentication, Stripe displays a 3D Secure challenge. The customer completes it in a modal.
  6. Server Confirmation: On success, the browser redirects to /stripe/confirm?payment_intent={id}. The StripeController::confirm() method retrieves the PaymentIntent from Stripe, updates the local record, and marks the order as paid.
  7. Webhook Backup: Stripe also sends a payment_intent.succeeded webhook. This ensures the order is marked paid even if the customer closes the browser before the redirect completes.
Dual Confirmation: The system uses both client-side redirect confirmation and server-side webhooks. The webhook acts as a safety net: whichever arrives first marks the order as paid; the second is a no-op.

Payment Confirmation

After client-side card confirmation succeeds, the browser redirects to the server-side confirmation endpoint:

GET /stripe/confirm?payment_intent={id}
Description

Retrieves the PaymentIntent from Stripe, verifies its status, updates the local record, and marks the associated order as paid.

Possible Outcomes
succeeded Order marked as paid. Redirect to success page.
requires_action Additional authentication needed. Redirect back to checkout with client secret.
Other Payment failed. Redirect to checkout with error message.

Webhook Setup

Webhooks ensure your site receives payment status updates even if the customer closes their browser.

Creating a Webhook Endpoint in Stripe

  1. Go to Stripe Dashboard → Developers → Webhooks.
  2. Click Add endpoint.
  3. Enter your endpoint URL: https://yoursite.com/stripe/webhook
  4. Select the following events:
    • payment_intent.succeeded
    • payment_intent.payment_failed
    • charge.refunded
    • charge.dispute.created
  5. Click Add endpoint to save.
  6. Reveal the Signing secret (starts with whsec_) and paste it in Admin → Stripe → Settings.
Important: The webhook endpoint (/stripe/webhook) is excluded from CSRF verification. Authentication is handled via Stripe’s signature verification instead.

Processing Refunds

Refunds are processed through the StripeGateway::refund() method, which can be called from the shop’s order management interface.

Refund Flow

  1. Admin initiates a refund from the order detail page in the admin panel.
  2. The system calls StripeGateway::refund($transaction, $amount, $reason).
  3. A Stripe\Refund is created via the API using the original PaymentIntent ID.
  4. A new Transaction record is created with type refund.
  5. The refund result is returned (success, pending, or failure).

Partial Refunds

The $amount parameter allows partial refunds. The amount is specified in the order’s currency (not in cents: the gateway handles cent conversion internally).

Refund Statuses

Status Description
succeeded Refund processed immediately. Transaction status: completed.
pending Refund is being processed (can take 5–10 business days for some payment methods). Transaction status: pending.
failed Refund could not be processed. Error details logged.

Updating

There are two ways to update this add-on: via the admin panel (recommended) or manually replacing files.

Method 1: Admin Panel Upload (Recommended)

  1. Download the latest .zip file of this add-on.
  2. Go to Admin panel → Add-ons and click the Upload button.
  3. Select or drag the .zip file into the upload area.
  4. A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
  5. Go to Admin panel → System Update (/admin/update) to apply any pending database migrations.

Method 2: Manual File Replacement

Step 1: Replace Files

Replace the add-on directory with the new version.

Step 2: Update Stripe PHP SDK

Step 3: Run Migrations

php artisan migrate

Pending migrations run once, so the command is safe to repeat.

Step 4: Clear Caches

php artisan config:clear
php artisan route:clear
php artisan view:clear

Step 5: Verify

Visit Admin → Stripe → Settings and confirm your API keys are still configured. Process a test payment to verify the integration.

Backup first: Always back up your database before running migrations on a production system.

Uninstallation

Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find Stripe Payment Gateway and click Deactivate.

  • Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
  • Its database tables and all the data they hold are kept, and its files stay under extensions/addons/stripe/. Nothing is deleted.
  • The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
  • Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.

Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.

Removing

Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:

  1. Deactivate Stripe Payment Gateway (see Uninstallation).
  2. Click Remove on its card and confirm the prompt.

The admin panel then, in one pass:

  • runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
  • revokes the permissions declared in its addon.json;
  • rolls back its migrations (this drops its database tables and every row they hold) and purges its entries from the migrations table, so a later reinstall migrates from scratch;
  • deletes its published assets: public/addons/stripe/, public/vendor/stripe/ and storage/app/public/addons/stripe/;
  • deletes the add-on directory extensions/addons/stripe/;
  • deletes its row in the addons table (the recorded purchase code goes with it) and clears the application cache.
This cannot be undone. Back up your database before removing an add-on whose data you may still need: installing it again later creates empty tables, not your old content.

Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/stripe/. In that last case, give that user write permission on the directory and on its parent, then try again.

Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.

Troubleshooting

Stripe not appearing as a payment method at checkout

  • Ensure the Stripe add-on is activated in Admin → Add-ons.
  • Ensure the Shop add-on is also activated (Stripe depends on it).
  • Verify that both stripe_public_key and stripe_secret_key are configured. The isAvailable() method returns false if either is empty.

Payment fails with “Invalid API Key”

  • Check that the secret key starts with sk_test_ (test mode) or sk_live_ (production).
  • If you recently rotated your keys in the Stripe Dashboard, update them in the admin settings.
  • Ensure the key is properly encrypted. Try clearing the value and re-entering it.

Webhooks returning 400 errors

  • Verify the stripe_webhook_secret is set in admin settings.
  • The secret must match the specific endpoint you created in the Stripe Dashboard (each endpoint has its own unique signing secret).
  • Ensure your server’s clock is synchronized (Stripe rejects signatures with excessive time drift).
  • Check that the webhook URL is https://yoursite.com/stripe/webhook (not http://).

3D Secure challenge not appearing

  • In test mode, use the test card 4000 0025 0000 3155 which always requires 3D Secure.
  • With automatic mode, 3DS only triggers when the issuing bank requires it. Switch to any to force it for testing.
  • Ensure Stripe.js is loaded correctly. Check the browser console for JavaScript errors.

Order not marked as paid after successful payment

  • Check server logs for errors during the /stripe/confirm redirect.
  • Verify the webhook is configured and receiving events. Check the Stripe webhook logs for delivery attempts.
  • Ensure the stripe_payment_intents table has a record for the PaymentIntent ID. If not, the payment was not initiated through the standard flow.
  • Check that the Payable interface is correctly implemented on the Order model with a working markAsPaid() method.

Refund fails with “No such payment intent”

  • The refund uses the gateway_transaction_id from the Transaction record. Ensure this contains the original Stripe PaymentIntent ID (starts with pi_).
  • You cannot refund a PaymentIntent that has not yet succeeded.

Customer created on every payment

  • The getOrCreateCustomer() method checks for an existing stripe_customers record by user_id before creating a new Stripe Customer. If duplicates appear, verify the user_id foreign key is set correctly.
  • Guest checkouts (no authenticated user) do not create Stripe Customer objects.

Currency mismatch errors

  • The stripe_currency setting must match the currency used by the shop. For example, if the shop prices are in EUR, set the Stripe currency to eur.
  • Currency codes must be lowercase (e.g. usd, not USD).

Stripe Payment Gateway v1.0.0: Part of the Larapen CMS platform.

© BeDigit. All rights reserved.

Was this article helpful?

Thank you for your feedback!

Still need help? Create a support ticket

Create a Ticket
Apr 07, 2026