PayPal Payment Gateway
Accept payments with PayPal on your Larapen e-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/paypalComposer package (PayPal SDK)
Installation
Step 1: Place the Add-on
Copy or symlink the paypal folder into your Larapen "extensions/addons" directory:
Step 2: Install Dependencies
Ensure the PayPal SDK package is installed:
Step 3: Activate the Add-on
Go to Admin → Add-ons → Installed Add-ons and activate PayPal Payment Gateway.
Step 4: Run Migrations
This creates the paypal_orders table for tracking PayPal order records, captures, and payment metadata.
Step 5: Set Permissions
The add-on registers 2 permissions (see Permissions). Assign them to admin roles via Admin → Users → Roles & Permissions.
Step 6: Configure
Navigate to Admin → PayPal → Settings and enter your PayPal API credentials. See Configuration and Getting PayPal Credentials.
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 |
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
Getting PayPal Credentials
- Go to developer.paypal.com/dashboard and log in with your PayPal Business account.
- Navigate to Apps & Credentials.
- Click Create App (or select an existing app).
- Copy the Client ID and Client Secret from the app details page.
- For sandbox testing, toggle to the Sandbox tab to get sandbox credentials.
- 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 capturedPAYMENT.CAPTURE.DENIED: payment capture was deniedPAYMENT.CAPTURE.REFUNDED: a refund was processed
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.
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)orLive (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:
- Customer selects PayPal: during shop checkout, the customer chooses PayPal as their payment method.
- Order creation: the shop calls
PaypalGateway::createPaymentIntent($order), which creates a PayPal order via the REST API withCAPTUREintent. - Local record: a record is saved to the
paypal_orderstable with the PayPal Order ID, amount, currency, approval URL, and the polymorphic payable reference. - Redirect to PayPal: the customer is redirected to PayPal’s hosted checkout page
(the
approval_urlfrom the API response). - Customer approves: the customer logs into PayPal, reviews the order, and clicks “Pay”.
- Return to site: PayPal redirects the customer back to
/paypal/return?token={paypal_order_id}. - Payment capture: the
PaypalController::return()method callsPaypalGateway::confirmPayment()to capture the authorized payment. - 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.
Payment Confirmation
When a payment is captured successfully, the gateway performs these actions:
- Updates the
paypal_ordersrecord with:status = COMPLETED,capture_id,payer_id,payer_email, andconfirmed_at. - Calls
$payable->markAsPaid('paypal', $captureId)on the order model. - Creates a
Transactionrecord in theshop_transactionstable with:gateway = 'paypal'gateway_transaction_id = {capture_id}status = 'completed'type = 'payment'- Metadata including
paypal_order_id,payer_id, andpayer_email
Refunds
The gateway supports full and partial refunds via PaypalGateway::refund().
Refund Process
- The admin initiates a refund from the shop order management.
- The gateway calls PayPal’s
refundCapturedPayment()API using the original capture ID. - If successful, a new
Transactionrecord is created withtype = 'refund'. - 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. |
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
- Go to PayPal Developer Dashboard → Webhooks.
- Click Add Webhook.
- Enter your webhook URL.
- Select the three required events (see below).
- Copy the generated Webhook ID and paste it in Admin → PayPal → Settings.
Updating
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
Step 3: Clear Caches
Step 4: Verify
Visit PayPal → Settings and confirm your API credentials are still configured. Try a sandbox test payment to ensure everything works correctly.
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) orapi-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.COMPLETEDevent. - Ensure the webhook URL is correctly configured in the PayPal Developer Dashboard.
- Verify the
paypal_ordersrecord was created (check thestatuscolumn).
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/paypalpackage is up to date.
“Array to string conversion” errors
- This typically occurs when the
srmklive/paypallibrary 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