Accept payments with PayPal on your Larapen Shop. This add-on integrates the PayPal REST API to create orders, capture payments, handle webhooks, and process refunds: all through the standard Larapen payment gateway interface.

PayPal Checkout

Redirect customers to PayPal’s hosted checkout. No credit card form needed on your site.

Sandbox & Live

Switch between sandbox (testing) and live (production) modes from the admin panel with a single toggle.

Webhook Support

Receive real-time payment notifications via PayPal webhooks for capture, denial, and refund events.

Refund Processing

Issue full or partial refunds directly through the gateway. Refund transactions are tracked automatically.

Encrypted Credentials

API keys and secrets are encrypted before storage using Laravel’s Crypt facade.

Polymorphic Payables

Works with any model implementing the Payable contract: not limited to shop orders.

Use Cases

Online Store with PayPal Checkout

You run an e-commerce store using the Larapen Shop add-on and want to offer PayPal as a payment option alongside other gateways (e.g. Stripe).

  • Install and activate the PayPal add-on.
  • Enter your PayPal REST API credentials in the admin panel.
  • Customers see PayPal as a payment method during checkout.
  • On selection, they are redirected to PayPal to complete payment, then returned to your site.

Digital Product Sales

You sell digital downloads (e-books, software licenses, templates) and want secure, instant payment confirmation.

  • PayPal webhooks confirm payment in real time, even if the customer closes the browser before returning.
  • The shop marks the order as paid and unlocks digital download links automatically.

Multi-Currency Store

You sell to international customers in multiple currencies.

  • Configure the default PayPal currency in admin settings (USD, EUR, GBP, etc.).
  • Each order sends the correct currency code to PayPal based on the shop configuration.

Requirements

  • Larapen CMS v1.0.0 or later
  • PHP 8.3+
  • MySQL 8.0+
  • The Shop add-on (required dependency)
  • A PayPal Business account with REST API credentials
  • The srmklive/paypal Composer package (PayPal SDK)
Note: The Shop add-on must be installed and active before this add-on can function. The PayPal add-on registers itself as a payment gateway that the shop discovers automatically.

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 PayPal 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 → PayPal → Settings and enter your PayPal API credentials. See also Getting PayPal Credentials. See Configuration.

Purchase Code (License Key)

PayPal 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 PayPal 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 → PayPal → Settings (stored in the settings table, group paypal).

Setting Description Default
paypal_mode API mode: sandbox for testing or live for production payments. sandbox
paypal_client_id PayPal REST API Client ID. Stored encrypted in the database. (empty)
paypal_client_secret PayPal REST API Client Secret. Stored encrypted in the database. (empty)
paypal_webhook_id PayPal Webhook ID for verifying incoming webhook event signatures. Stored encrypted. (empty)
paypal_currency ISO 4217 currency code used for PayPal transactions (e.g. USD, EUR, GBP). USD
paypal_brand_name Brand name displayed on the PayPal checkout page (max 127 characters). (app name)

Database Settings → Config Mapping

Settings stored in the database override the config file defaults at boot time via the service provider:

Database Key Config Key Encrypted?
paypal_mode paypal.mode No
paypal_client_id paypal.{mode}.client_id Yes
paypal_client_secret paypal.{mode}.client_secret Yes
paypal_webhook_id paypal.webhook_id Yes
paypal_currency paypal.currency No
paypal_brand_name paypal.brand_name No
Note: The paypal_client_id and paypal_client_secret are stored against the currently active mode. If mode is sandbox, they map to paypal.sandbox.client_id and paypal.sandbox.client_secret.

Environment Variables

Note: Environment variables are used as defaults. Settings saved in the admin panel (stored encrypted in the database) override them.

Getting PayPal Credentials

  1. Go to developer.paypal.com/dashboard and log in with your PayPal Business account.
  2. Navigate to Apps & Credentials.
  3. Click Create App (or select an existing app).
  4. Copy the Client ID and Client Secret from the app details page.
  5. For sandbox testing, toggle to the Sandbox tab to get sandbox credentials.
  6. For webhooks, go to Webhooks in the dashboard, create a webhook pointing to https://yoursite.com/paypal/webhook, and copy the Webhook ID.

Required Webhook Events

When creating your PayPal webhook, subscribe to these events:

  • PAYMENT.CAPTURE.COMPLETED: payment was successfully captured
  • PAYMENT.CAPTURE.DENIED: payment capture was denied
  • PAYMENT.CAPTURE.REFUNDED: a refund was processed
Important: The webhook URL must be publicly accessible over HTTPS. Sandbox webhooks require a live URL (not localhost). Use a tunnel service like ngrok for local testing.

Admin: Settings

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

API Credentials

  • Client ID: masked password field with show/hide toggle. Your PayPal REST API Client ID.
  • Client Secret: masked password field with show/hide toggle. Stored encrypted in the database.
  • Webhook ID: masked password field with show/hide toggle. Used for verifying webhook signatures. Optional but recommended.
Security: All three credential fields are encrypted with Laravel’s Crypt::encryptString() before being saved to the settings table. They are decrypted only when displayed in the form or when configuring the PayPal API client. Leave fields empty to keep current values.

Payment Options

  • Mode: dropdown to select Sandbox (Testing) or Live (Production). Controls which set of API credentials is used.
  • Currency: three-character ISO 4217 currency code (e.g. USD, EUR, GBP). Used as the default currency for PayPal orders.
  • Brand Name: the name shown on the PayPal checkout page (max 127 characters). Falls back to the application name if empty.

A help card at the top of the settings page provides direct links to:

Checkout Flow

The PayPal payment flow follows the standard redirect-based checkout pattern:

  1. Customer selects PayPal: during shop checkout, the customer chooses PayPal as their payment method.
  2. Order creation: the shop calls PaypalGateway::createPaymentIntent($order), which creates a PayPal order via the REST API with CAPTURE intent.
  3. Local record: a record is saved to the paypal_orders table with the PayPal Order ID, amount, currency, approval URL, and the polymorphic payable reference.
  4. Redirect to PayPal: the customer is redirected to PayPal’s hosted checkout page (the approval_url from the API response).
  5. Customer approves: the customer logs into PayPal, reviews the order, and clicks “Pay”.
  6. Return to site: PayPal redirects the customer back to /paypal/return?token={paypal_order_id}.
  7. Payment capture: the PaypalController::return() method calls PaypalGateway::confirmPayment() to capture the authorized payment.
  8. Order completion: if capture succeeds, the order is marked as paid, a transaction record is created, and the customer is redirected to the success page.

Cancellation

If the customer clicks “Cancel” on the PayPal checkout page, they are redirected to /paypal/cancel. The controller redirects them back to the shop checkout page with a “Payment cancelled” warning message.

Dual confirmation: Payments are confirmed both by the return redirect (immediate) and by webhooks (asynchronous). This ensures orders are marked as paid even if the customer closes their browser before the return redirect completes.

Payment Confirmation

When a payment is captured successfully, the gateway performs these actions:

  1. Updates the paypal_orders record with: status = COMPLETED, capture_id, payer_id, payer_email, and confirmed_at.
  2. Calls $payable->markAsPaid('paypal', $captureId) on the order model.
  3. Creates a Transaction record in the shop_transactions table with:
    • gateway = 'paypal'
    • gateway_transaction_id = {capture_id}
    • status = 'completed'
    • type = 'payment'
    • Metadata including paypal_order_id, payer_id, and payer_email

Refunds

The gateway supports full and partial refunds via PaypalGateway::refund().

Refund Process

  1. The admin initiates a refund from the shop order management.
  2. The gateway calls PayPal’s refundCapturedPayment() API using the original capture ID.
  3. If successful, a new Transaction record is created with type = 'refund'.
  4. The order status is updated if the refund covers the full amount.

Refund Statuses

Status Description
COMPLETED Refund processed immediately.
PENDING Refund is pending (e.g. eCheck payments). A PAYMENT.CAPTURE.REFUNDED webhook will confirm it later.
Note: Partial refunds are supported. You can refund any amount up to the original payment amount. An optional reason/note can be included, which PayPal displays to the buyer.

Webhook Setup

PayPal webhooks provide asynchronous payment event notifications. They serve as a safety net to confirm payments even when the customer’s return redirect fails.

Webhook URL

Configure PayPal to send webhook events to:

This endpoint is CSRF-exempt and does not require authentication.

Configuration

  1. Go to PayPal Developer Dashboard → Webhooks.
  2. Click Add Webhook.
  3. Enter your webhook URL.
  4. Select the three required events (see below).
  5. Copy the generated Webhook ID and paste it in Admin → PayPal → Settings.

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: Run Migrations

php artisan migrate

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

Step 3: Clear Caches

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

Step 4: Verify

Visit PayPal → Settings and confirm your API credentials are still configured. Try a sandbox test payment to ensure everything works correctly.

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 PayPal 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/paypal/. 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 PayPal 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/paypal/, public/vendor/paypal/ and storage/app/public/addons/paypal/;
  • deletes the add-on directory extensions/addons/paypal/;
  • 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/paypal/. 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

“PayPal API credentials are not configured”

  • Ensure you have entered both the Client ID and Client Secret in Admin → PayPal → Settings.
  • Verify the correct Mode is selected: sandbox credentials do not work in live mode and vice versa.
  • Check that the credentials are for the correct mode (sandbox vs. live).

“PayPal authentication failed”

  • Double-check that the Client ID and Client Secret are correct (no extra spaces or line breaks).
  • Ensure your PayPal app is not suspended or deleted.
  • Verify your server can reach api-m.sandbox.paypal.com (sandbox) or api-m.paypal.com (live) over HTTPS.
  • Check server logs for detailed error messages from the PayPal API.

Customer redirected to checkout but payment not captured

  • The return URL may not have been reached (customer closed browser). Check if the webhook received a PAYMENT.CAPTURE.COMPLETED event.
  • Ensure the webhook URL is correctly configured in the PayPal Developer Dashboard.
  • Verify the paypal_orders record was created (check the status column).

Webhooks not being received

  • Verify the webhook URL is publicly accessible over HTTPS.
  • Check the PayPal Developer Dashboard → Webhooks → Events for delivery status.
  • Ensure the webhook is not behind IP-based firewall rules that block PayPal’s servers.
  • For local development, use a tunnel service (e.g. ngrok) to expose your local server.

Webhook signature verification failing

  • Ensure the Webhook ID in admin settings matches the one in the PayPal Developer Dashboard.
  • If you recently recreated the webhook, update the Webhook ID in your settings.
  • Leave the Webhook ID empty to disable signature verification (not recommended for production).

Refund fails: “Refund failed”

  • Ensure the original payment was captured (status COMPLETED).
  • Check that the refund amount does not exceed the original payment amount.
  • PayPal may reject refunds for payments older than 180 days.
  • Check server logs for the specific PayPal API error message.

Orders stuck in CREATED or APPROVED status

  • The customer may have approved the payment but the capture failed. Check server logs for errors during confirmPayment().
  • Try processing the capture manually via the PayPal Merchant Dashboard.
  • Ensure the srmklive/paypal package is up to date.

“Array to string conversion” errors

  • This typically occurs when the srmklive/paypal library receives unexpected config keys. The gateway filters config to only pass supported keys. Ensure you are using a compatible version of the package.
  • Clear the config cache: php artisan config:clear

PayPal 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