EP Ecommerce PayPal
EP Ecommerce PayPal handles PayPal payments for EP Ecommerce using the PayPal REST API v2 (Orders). Implements the standard server-create, client-approve, server-capture flow, with OAuth2 access token management, webhook verification, and optional PayPal Subscriptions for recurring billing.
Published by ElmsPark Studio.
Overview
Section titled “Overview”- PayPal Orders API v2 with a proper server-side create and capture, not the legacy Express Checkout.
- PayPal JS SDK buttons on the frontend with configurable colour and shape.
- Sandbox and live credential pairs with mode switching.
- OAuth2 access token managed internally with request-level caching.
- Webhook verification via PayPal’s
verify-webhook-signatureAPI. - Admin refunds via the Captures API.
- Coexists with EP Ecommerce Stripe — both render into the same payment slot, so customers can pick their preferred method.
- PayPal Subscriptions support when EP Ecommerce Subscriptions is active.
Requirements
Section titled “Requirements”- PageMotor 0.8.2b or later
- EP Ecommerce (base plugin)
- EP Suite base class
- EP Ecommerce Products for the checkout UI
- A PayPal Developer account with REST API credentials
- EP Ecommerce Subscriptions (optional, for recurring billing via PayPal)
Setting up PayPal
Section titled “Setting up PayPal”Step 1 — Create the PayPal app
Section titled “Step 1 — Create the PayPal app”- Log in to the PayPal Developer Dashboard.
- Under Apps & Credentials, click Create App.
- Name it (e.g. “PageMotor ecommerce”), pick Merchant, click Create App.
- Copy the Client ID and Secret for both the Sandbox and Live environments.
Step 2 — Configure the plugin
Section titled “Step 2 — Configure the plugin”Open Plugin Settings → EP Ecommerce PayPal.
- Enable PayPal payments. Toggle on.
- Mode. Sandbox for testing, Live for production.
- Sandbox credentials. Client ID and secret.
- Live credentials. Client ID and secret for production.
- Hide These Funding Options (0.1.14). PayPal shows several buttons by default. Tick any of Pay Later, Debit or Credit Card, PayPal Credit and Venmo to keep them off your checkout.
Step 3 — Set up the webhook
Section titled “Step 3 — Set up the webhook”- Back in the PayPal Developer Dashboard, open your app.
- Scroll to Webhooks, click Add Webhook.
- Webhook URL: copy from the plugin settings page (displayed in the Webhook section).
- Event types to subscribe to:
PAYMENT.CAPTURE.COMPLETEDPAYMENT.CAPTURE.DENIED- If using subscriptions, also:
BILLING.SUBSCRIPTION.ACTIVATED,BILLING.SUBSCRIPTION.CANCELLED,BILLING.SUBSCRIPTION.EXPIRED,PAYMENT.SALE.COMPLETED.
- Save. Copy the generated Webhook ID.
- Paste the Webhook ID into the plugin’s Webhook ID setting for signature verification.
How the payment flow works
Section titled “How the payment flow works”- Customer clicks the PayPal button on your site.
- Plugin exchanges Client ID + Secret for an OAuth2 access token (cached per request).
- Plugin creates a PayPal Order with the product amount and purchase details.
- PayPal JS SDK renders the button. The email and name fields are checked before the popup opens (0.1.14); an empty or malformed field shows a message on the page and the popup stays closed. The customer then approves in the PayPal popup.
- Frontend receives the approved order ID, sends it to the plugin’s capture endpoint.
- Plugin calls PayPal’s Capture API to finalise the payment.
- PayPal fires
PAYMENT.CAPTURE.COMPLETEDto your webhook. - Plugin verifies the webhook signature, marks the order paid, triggers fulfilment.
- Customer sees success message, receives EP Email confirmation.
Subscription flow (with EP Ecommerce Subscriptions)
Section titled “Subscription flow (with EP Ecommerce Subscriptions)”When subscription products are purchased via PayPal:
- Plugin creates a PayPal Subscription object via the Subscriptions API.
- Customer approves the subscription in PayPal’s popup.
- PayPal fires
BILLING.SUBSCRIPTION.ACTIVATED— plugin grants initial access. - On each renewal, PayPal fires
PAYMENT.SALE.COMPLETED— plugin extends access. - On cancel or expire, PayPal fires
BILLING.SUBSCRIPTION.CANCELLEDorBILLING.SUBSCRIPTION.EXPIRED— plugin revokes access according to your cancellation-policy setting.
Refunds
Section titled “Refunds”From the EP Ecommerce orders list, any paid PayPal order has a Refund button:
- Full refund. Refunds the entire capture.
- Partial refund. Enter an amount.
- Refund is submitted to PayPal’s Captures API. Status updates when PayPal confirms.
- As with Stripe, fulfilment reversal is business-rule dependent and not automatic.
Coexistence with Stripe
Section titled “Coexistence with Stripe”Both payment providers render into the same .ep-ecommerce-payment-slot div in the checkout. If both are active, the customer sees both options and picks their preferred method. Each provider independently tracks its own orders, so there’s no cross-contamination.
Payment description
Section titled “Payment description”What appears on the customer’s PayPal account as the purchase description. Defaults to the product name; supports {product_name} placeholder. PayPal has no strict length limit like Stripe, but keep it readable.
Troubleshooting
Section titled “Troubleshooting”“PayPal buttons don’t appear on the checkout”
Section titled ““PayPal buttons don’t appear on the checkout””Check the browser console. Common causes:
- Client ID is wrong or empty.
- Mode is set to Live but you’re using sandbox credentials, or vice versa.
- PayPal JS SDK is being blocked by an ad-blocker or CSP header. Loosen CSP if needed.
“The PayPal popup opens and closes straight away”
Section titled ““The PayPal popup opens and closes straight away””Before 0.1.14 the form was validated after PayPal had opened its window, so an empty email or name closed the popup with the message left behind it. Update; validation now runs before the popup opens and the message shows on the page.
“Webhook signature verification fails”
Section titled ““Webhook signature verification fails””The Webhook ID saved in the plugin doesn’t match the one in PayPal. Rotate in PayPal, paste the new ID, try again.
“Sandbox works, Live fails with authentication error”
Section titled ““Sandbox works, Live fails with authentication error””Live credentials must come from the live side of your PayPal app (same dashboard, different toggle). Mixing sandbox Client ID with live Secret is a common trip-up.
“Orders stay in Pending forever”
Section titled ““Orders stay in Pending forever””The webhook isn’t reaching your site, or is being rejected. Check PayPal Developer Dashboard → Webhooks → your webhook → event history. Failed deliveries show status codes. Common causes: firewall blocking PayPal IPs, HTTPS certificate issues, wrong URL path.
“I get a refund error ‘Capture not found’”
Section titled ““I get a refund error ‘Capture not found’””The payment was authorised but not captured, or was captured through a different flow (direct Express Checkout instead of Orders API). These cases happen on very old orders. Refund through PayPal directly.
Changelog
Section titled “Changelog”0.1.22
Section titled “0.1.22”- Settings language menu. The language menu in this plugin’s settings now lists only the languages it is actually translated into, plus English, so you can no longer pick a language that changes nothing.
- Danish. Adds a Danish translation.
0.1.21
Section titled “0.1.21”- Background jobs and customer actions can always reach PayPal. EP Ecommerce Subscriptions now finds this plugin directly, however your site happens to load its plugins. Before, if EP Ecommerce loaded after this plugin, the scheduled PayPal payment sync could not reach PayPal at all.
- Update together with EP Ecommerce Subscriptions 0.2.26 (either order is safe).
0.1.20
Section titled “0.1.20”- PayPal subscription payments now reach your books from background jobs too. EP Ecommerce Subscriptions reads each subscription’s payments and refunds from PayPal on a schedule. When that ran as a background job, this plugin could not always see its own PayPal keys, so it never logged in to PayPal and nothing was read until someone next used the site. It now loads its saved keys itself when it needs them.
- On a live site this also stops those background jobs quietly trying PayPal’s test (sandbox) server instead of the live one.
0.1.19
Section titled “0.1.19”- PayPal now tells your site how much of a subscription payment you refunded. When you refund a subscription payment in PayPal, your site receives PayPal’s refund notice and passes it to EP Ecommerce Subscriptions, which records the exact amount and date. EP Finance Sources then takes exactly that amount out of your books.
- One step for you: in your PayPal developer dashboard, add the event PAYMENT.SALE.REFUNDED to the webhook your site uses. The PayPal settings page lists every event to subscribe to.
- This needs EP Ecommerce Subscriptions 0.2.25 or later; with an older version the notice is accepted and ignored.
0.1.18
Section titled “0.1.18”- The Refund button now takes back what the order gave, just as a refund made in PayPal does. Refunding an order from your admin used to mark it refunded but leave the customer’s membership, download links and licence keys working. Now they are withdrawn once PayPal confirms the refund.
- A refund PayPal is still processing no longer marks the order refunded early. You see a message that PayPal is processing it, and the order is marked refunded when PayPal tells your site it has completed.
- A refund PayPal refuses no longer marks the order refunded. You see “PayPal could not complete this refund” and the order stays as it was.
0.1.17
Section titled “0.1.17”- PayPal payment notifications now work. Every notification PayPal sent to your site was being turned away, so if a buyer’s browser lost its connection straight after paying, the order was never completed. Your site now reads its PayPal settings correctly when a notification arrives.
- A PayPal notification now only completes the order it belongs to, for the right amount. Nothing else can complete an order by quoting its number.
- PayPal subscriptions now work. A subscriber used to be billed by PayPal every month and never given access, because the site never heard that the subscription had started. Access is now granted as soon as the buyer returns from PayPal, and if they close the tab first, PayPal’s notification grants it on the next visit to your site. Renewals and cancellations reach EP Ecommerce Subscriptions too.
- A subscription only activates the order it was started for, on the plan that product names.
- Refunding a PayPal payment in full now takes back what it bought: the order is marked refunded, a membership ends, download links stop and licence keys are revoked. Partial refunds leave access in place. Add
PAYMENT.CAPTURE.REFUNDEDto your webhook’s events in the PayPal dashboard; the settings screen now lists it. - PayPal donations now charge the amount the donor chose. A pay-what-you-like donation paid with PayPal used to charge the product’s list price (or fail if that was zero). It now charges what the donor typed, within the minimum you set, and asks for an amount before the PayPal window opens. Needs EP Ecommerce 0.1.43.
- If a buyer has typed a discount code, the PayPal button now explains that codes work with card payments, rather than charging the full price.
- The README now lists the refund event (
PAYMENT.CAPTURE.REFUNDED) this plugin handles.
0.1.16
Section titled “0.1.16”- Fixes a doubled cache-busting tag on its checkout script. On PageMotor 0.11 and later the page asked for the file with two version tags on the end of its address (
?v=…?v=…), because the core had started adding its own tag and the plugin was still adding one too. The file loaded correctly, so nothing was visibly broken; the address was just malformed and every cache saw a longer key than it needed. The plugin now leaves the tag to the core on 0.11 and later, and still adds its own on older cores, where nothing else would. - No change to what that script does.
0.1.15
Section titled “0.1.15”- Fixes stored keys and passwords reading as empty after a PageMotor 0.11.3 or 0.11.4 update. After the core update, every secret this plugin had encrypted at rest came back blank, so anything that needed it failed with an authentication error until the value was typed in again. Nothing was deleted: the encrypted value was still in the settings row, but PageMotor 0.11.3 moved the site secret that opens it, and this plugin was still looking in the old place. It now finds the secret in both places, so an existing value opens again without re-entry, and a value that was re-entered in the meantime keeps working and is moved back under the site secret.
- If you updated PageMotor and then re-entered a key or password, there is nothing to do. If you updated and have not re-entered it, this release restores it on the next page load.
0.1.14
Section titled “0.1.14”7 September 2026. Email and name are validated before the PayPal popup opens instead of after. New Hide These Funding Options setting. One-time orders return the approval URL as subscriptions already did, for custom checkouts. The script loads only on checkout pages and is served with a version stamp so browsers pick up each release.
0.1.13
Section titled “0.1.13”31 August 2026. Stored secrets moved out of reach of API and MCP connections.
Feedback and corrections
Section titled “Feedback and corrections”For a quick question about this plugin, EP Support inside your admin is the fastest option. The chat widget sits on every EP plugin settings page and knows which one you’re on, with starter questions and links preloaded for that exact screen.
For anything bigger — a bug report, a feature request, or a “how do I…” that needs a real reply — open a ticket at help.elmspark.com. A real person, helped by AI, writes the reply. Usually within a few hours. Tickets don’t disappear into the void.