PayPal Configuration
Complete guide to setting up PayPal payments and subscriptions for EasyCommerce. The plugin uses the PayPal Commerce Platform (Orders v2 + Subscriptions v1) and supports PayPal, PayPal Credit, Venmo, and card processing through PayPal.
Prerequisites
- A PayPal Business account.
- REST API credentials from the PayPal Developer Dashboard.
Getting API Credentials
Step 1: Access the Developer Dashboard
- Go to developer.paypal.com.
- Log in with your PayPal account.
- Open Apps & Credentials.
Step 2: Create an App
- Click Create App.
- Enter an app name (for example, "My Store").
- Select Merchant as the app type.
- Click Create App.
Step 3: Copy Credentials
Both Sandbox and Live environments expose a separate Client ID and Secret. Switch the tab in the dashboard to copy the matching pair.
Enabling and Opening the Configuration
- Open EasyCommerce → Settings → Payments.
- Locate the PayPal card.
- Toggle Enabled on. The Configure button is disabled until you do.
- Click Configure to open the PayPal settings form.
Configuration Sections
1. API Credentials
| Setting | Field name | Description |
|---|---|---|
| Environment Mode | mode | sandbox (testing) or live (production). Required. |
| Sandbox Client ID | sandbox_client_id | Shown when mode = sandbox. |
| Sandbox Client Secret | sandbox_client_secret | Shown when mode = sandbox. Stored encrypted. |
| Live Client ID | live_client_id | Shown when mode = live. |
| Live Client Secret | live_client_secret | Shown when mode = live. Stored encrypted. |
| Merchant ID | merchant_id | Optional PayPal payer merchant ID. |
Switch to live only after thorough testing in sandbox. The plugin validates Client IDs match ^A[A-Z0-9-_]+$ and Secrets match ^E[A-Z0-9-_]+$; mismatched values are rejected on save.
2. Payment Settings
| Setting | Field name | Description | Default |
|---|---|---|---|
| Brand Name | brand_name | Business name shown on PayPal pages. | (empty) |
| Payment Action | payment_action | CAPTURE (immediate) or AUTHORIZE (hold funds, capture later). | CAPTURE |
| Enable PayPal Credit | enable_paypal_credit | Offer PayPal Credit financing. | Yes |
| Enable Venmo | enable_venmo | Show Venmo option (US only). | Yes |
CAPTURE charges immediately. AUTHORIZE holds funds and lets an operator capture the payment later from the order screen; the admin Capture action funnels through the same captureApprovedOrder path the webhook uses, so the two paths cannot diverge.
3. Subscription & Recurring Billing
| Setting | Field name | Description | Default |
|---|---|---|---|
| Enable Subscriptions | enable_subscriptions | Allow recurring billing. | Yes |
| Default Trial Period | trial_period_days | Free trial days (0 to 365). | 0 |
PayPal does not support cancel_at_period_end natively. When EasyCommerce schedules a cancellation at period end, the plugin cancels on PayPal immediately and keeps the local subscription in pending-cancel until the period elapses.
4. Webhooks
| Setting | Field name | Description |
|---|---|---|
| Webhook ID | webhook_id | Required for signature verification. |
Webhook endpoint URL (copy this into the PayPal Dashboard):
https://yoursite.com/index.php?option=com_easycommerce&task=webhook.paypal
The plugin refuses to process any webhook payload unless webhook_id is set and PayPal's verify-webhook-signature endpoint confirms the request. There is no fallback path.
Setting Up Webhooks in PayPal
-
Open the PayPal Developer Dashboard.
-
Go to Apps & Credentials → your app → Webhooks.
-
Click Add Webhook.
-
Enter the Webhook URL above.
-
Select these events (full list — see the legend below for which are critical):
One-off orders and captures:
CHECKOUT.ORDER.APPROVEDCHECKOUT.ORDER.COMPLETEDPAYMENT.CAPTURE.COMPLETEDPAYMENT.CAPTURE.DENIEDPAYMENT.CAPTURE.DECLINEDPAYMENT.CAPTURE.REFUNDEDPAYMENT.CAPTURE.REVERSEDPAYMENT.CAPTURE.PENDING
Subscriptions (CRITICAL for recurring billing):
BILLING.SUBSCRIPTION.PAYMENT.COMPLETED★PAYMENT.SALE.COMPLETED★ (legacy v1 alias — keep both)BILLING.SUBSCRIPTION.PAYMENT.FAILED★BILLING.SUBSCRIPTION.CREATEDBILLING.SUBSCRIPTION.ACTIVATEDBILLING.SUBSCRIPTION.CANCELLEDBILLING.SUBSCRIPTION.EXPIREDBILLING.SUBSCRIPTION.SUSPENDEDBILLING.SUBSCRIPTION.RE-ACTIVATED
Disputes (optional but recommended):
CUSTOMER.DISPUTE.CREATEDCUSTOMER.DISPUTE.UPDATEDCUSTOMER.DISPUTE.RESOLVED
-
Copy the generated Webhook ID back into the plugin settings.
The ★ events are the only signal PayPal sends that tells the plugin a recurring charge succeeded or failed. Without them subscribed:
BILLING.SUBSCRIPTION.PAYMENT.COMPLETEDmissing → every renewal is collected on PayPal's side but no local renewal order materialises, the subscription doesn't advance, the customer's access doesn't extend.PAYMENT.SALE.COMPLETEDmissing → the legacy v1 webhook shape for the same event isn't caught. PayPal merchants on older Billing API contracts only receive this shape; modern merchants receive both. Subscribe to BOTH so the plugin works regardless of which shape PayPal sends.BILLING.SUBSCRIPTION.PAYMENT.FAILEDmissing → dunning never starts for failed renewal attempts; failed subscriptions stay in "active" status silently.
If you ever see this log line for an active subscription, the dashboard config is wrong:
PayPal subscription payment received but no event handler matched.
PayPal has two parallel subscription billing shapes — the legacy v1 (Payouts/Billing Agreements, sends PAYMENT.SALE.COMPLETED) and v2 (Subscriptions API, sends BILLING.SUBSCRIPTION.PAYMENT.COMPLETED). The plugin handles both with the same handler; you should subscribe to both events so older billing-agreement subscriptions on the same merchant account continue to renew correctly.
5. Advanced
| Setting | Field name | Description | Default |
|---|---|---|---|
| Disable Funding Sources | disable_funding | Hide specific funding sources (card, credit, venmo, sepa, bancontact, eps, giropay, ideal, mybank, p24, sofort). Multi-select. | None |
| Debug Mode | debug_mode | Log full API request/response payloads. | No |
| Log Level | log_level | debug, info, warning, error. | error |
Debug logs may contain sensitive request data. Enable only while troubleshooting and disable when done.
Testing in Sandbox
Test Accounts
Create personal (buyer) and business (merchant) accounts at developer.paypal.com/developer/accounts.
Test Card Numbers
| Card | Number |
|---|---|
| Visa | 4012000077777777 |
| Mastercard | 5426064000424979 |
| Amex | 371449635392376 |
Going Live
- Switch Environment Mode to
live. - Enter the Live Client ID and Live Client Secret.
- Update the Webhook ID to the live-app webhook.
- Place a small real purchase end-to-end.
- Watch the logs for any signature or capture failures.
Troubleshooting
Payment Failed
| Error | Likely cause |
|---|---|
| Invalid credentials | Client ID or Secret does not match current mode. |
| Currency not supported | Your PayPal account does not support the order currency. |
| 3D Secure failed | Customer's bank rejected the authentication. |
Webhooks Not Working
- Confirm the Webhook URL is reachable from the public internet.
- Confirm
webhook_idis set; without it the plugin refuses all webhook requests. - Confirm the event subscription list above matches the dashboard.
- Check the EasyCommerce logs for
signature verification failedentries.
Subscription Renewals Not Updating the Local Subscription
Symptom: PayPal charges the customer's billing agreement on the renewal cycle, the money lands in your PayPal balance, but the EasyCommerce subscription doesn't advance, no renewal order appears in admin, the customer's downloads expire.
This happens when the webhook endpoint is not subscribed to BILLING.SUBSCRIPTION.PAYMENT.COMPLETED (v2) and/or PAYMENT.SALE.COMPLETED (v1). PayPal sent the renewal payment notification but the plugin never received the event that would have created the local renewal order.
To fix:
- PayPal Developer Dashboard → Apps & Credentials → your app → Webhooks → Edit and add both
BILLING.SUBSCRIPTION.PAYMENT.COMPLETEDandPAYMENT.SALE.COMPLETED(subscribe to both regardless of which Billing API version your account uses — PayPal sometimes switches between them silently). - Save.
- Recover any already-affected customers using the manual flow: EasyCommerce → Subscriptions → open the affected subscription → "Create Renewal Order", then on that new pending order use 3-dot menu → "Mark as Paid" and paste the PayPal transaction id from the original payment as the transaction reference. This produces the same end-state as a successful webhook would have.
Migrated Subscription Renewals Are Dropped
If a subscription was migrated from an external platform (WooCommerce, manually re-keyed on PayPal, etc.) and never had its PayPal billing-agreement ID pasted in locally, the renewal webhook arrives but the gateway-id lookup fails. The log shows:
Recurring payment webhook received but no local subscription matches its gateway ID.
For migrated subscriptions, paste the PayPal billing-agreement ID into the
subscription's gateway_subscription_id field via the admin UI to enable auto-renewal.
To fix: open the local subscription in admin, find the PayPal billing-agreement ID (I-xxx for v1 or sub_xxx for v2) on the PayPal dashboard for that customer, and paste it into the subscription's gateway_subscription_id field. Future renewals will then auto-match and advance correctly.
Manual Capture from Admin
For orders processed in AUTHORIZE mode, open the order in the admin and click Capture payment. The plugin reuses the same PayPalWebhookService::captureApprovedOrder path the webhook uses, so a successful manual capture and a webhook-driven capture produce identical state.