Overview
Icon for PayPal

PayPal

Integrate with PayPal, built by an official PayPal Partner.

PayPal for Medusa

Accept PayPal and credit card payments in your Medusa v2 store β€” built by an official PayPal Partner.

✨ What you get

Feature Details πŸ”΅ PayPal Buttons One-click PayPal checkout for your customers πŸ’³ Card Payments Secure credit & debit card fields, hosted by PayPal (PCI compliant) πŸ›  Admin Dashboard Connect your PayPal account and manage everything from Medusa Admin 🌍 Test & Live modes Try everything safely in Sandbox before going live πŸ” 3D Secure Extra card security, configurable in one click πŸ”„ Reliable by design Payments are verified with PayPal at every step β€” webhooks, retries, and safety nets are handled for you

βœ… Requirements

  • Medusa v2.12+
  • Node.js 20+
  • PostgreSQL

πŸš€ Installation

In your Medusa backend folder, run:

npm install @easypayment/medusa-payment-paypal

βš™οΈ Setup β€” 5 steps

1. Add the plugin to Copy to clipboardmedusa-config.ts

import { loadEnv, defineConfig } from "@medusajs/framework/utils"
loadEnv(process.env.NODE_ENV || "development", process.cwd())
export default defineConfig({
projectConfig: {
databaseUrl: process.env.DATABASE_URL,
http: {
storeCors: process.env.STORE_CORS!,
adminCors: process.env.ADMIN_CORS!,
authCors: process.env.AUTH_CORS!,
jwtSecret: process.env.JWT_SECRET || "supersecret",
cookieSecret: process.env.COOKIE_SECRET || "supersecret",
},
},
plugins: [
{
resolve: "@easypayment/medusa-payment-paypal",
options: {},

2. Run database migrations

npx medusa db:migrate

3. Connect your PayPal account

  1. Start your Medusa server
  2. Open Medusa Admin β†’ Settings β†’ PayPal
  3. Choose Sandbox (testing) or Live (real payments)
  4. Click Connect to PayPal and follow the steps

That's it β€” your credentials are saved automatically. Webhooks are registered for you too.

πŸ’‘ Prefer manual setup? Click Insert credentials manually and paste your Client ID and Secret from developer.paypal.com.

4. Turn PayPal on in your region

Go to Medusa Admin β†’ Settings β†’ Regions β†’ your region and enable:

Payment provider What it is Copy to clipboardpp_paypal_paypal PayPal Buttons Copy to clipboardpp_paypal_card_paypal_card Card payments

5. Add PayPal to your storefront

The checkout UI comes as a separate package β€” install it in your storefront project:

πŸ“¦ @easypayment/medusa-paypal-ui β€” ready-made PayPal components for Next.js storefronts, with a step-by-step guide.

🎨 Customize (optional)

Everything is managed in Medusa Admin β†’ Settings β†’ PayPal β€” changes apply instantly, no restart needed:

  • PayPal Settings β€” turn PayPal on/off, button color, shape, and label
  • Advanced Card Payments β€” turn card payments on/off, 3D Secure mode
  • Additional Settings β€” capture vs. authorize, brand name shown at PayPal, invoice prefix

πŸ” Going live β€” two things we recommend

  1. Copy to clipboardPAYPAL_ENCRYPTION_KEY β€” set this to any long random text in your server's environment. Your PayPal credentials are then stored encrypted in the database. Keep the key safe: if it's lost, just reconnect PayPal.
  2. Copy to clipboardMEDUSA_BACKEND_URL β€” set this to your backend's public address (e.g. Copy to clipboardhttps://api.mystore.com) so PayPal can reach your store for payment notifications.

Advanced options (for developers β€” everything works without these)
Variable Default What it does Copy to clipboardSTOREFRONT_URL (unset) Storefront address used for PayPal return/cancel pages (can also be set in Admin) Copy to clipboardPAYPAL_ENCRYPTION_STRICT Copy to clipboardfalse Copy to clipboardtrue = refuse to save credentials unencrypted Copy to clipboardPAYPAL_SELLER_NONCE (auto) Set a fixed random string when running multiple server instances Copy to clipboardPAYPAL_ADMIN_ORIGIN first Copy to clipboardADMIN_CORS entry Restricts the onboarding popup to your admin URL Copy to clipboardPAYPAL_WEBHOOK_COMPLETE_CART Copy to clipboardtrue Safety net that finishes an order when the payment succeeded but the buyer's browser closed. Copy to clipboardfalse disables it Copy to clipboardPAYPAL_RATE_LIMIT_MAX / Copy to clipboardPAYPAL_RATE_LIMIT_WINDOW_MS (off) / Copy to clipboard60000 Optional request limit for the public checkout routes Copy to clipboardPAYPAL_WEBHOOK_RATE_LIMIT_MAX / Copy to clipboardPAYPAL_WEBHOOK_RATE_LIMIT_WINDOW_MS (off) / Copy to clipboard60000 Optional request limit for the webhook endpoint (separate from the above) Copy to clipboardPAYPAL_WEBHOOK_REPLAY_WINDOW_MINUTES Copy to clipboard60 Rejects webhook deliveries older than this Copy to clipboardPAYPAL_WEBHOOK_STALE_PROCESSING_MS Copy to clipboard300000 When the retry job re-picks-up interrupted webhook events Copy to clipboardPAYPAL_WEBHOOK_ID_LIVE / Copy to clipboardPAYPAL_WEBHOOK_ID_SANDBOX (auto) Manual webhook-id override (normally automatic) Copy to clipboardPAYPAL_HTTP_TIMEOUT_MS Copy to clipboard30000 Timeout for calls to PayPal Copy to clipboardPAYPAL_CURRENCY Copy to clipboardEUR Fallback currency if none is configured Copy to clipboardPAYPAL_ALERT_WEBHOOK_URLS (unset) Comma-separated URLs that receive operational alerts (including dead-lettered webhook events) Copy to clipboardPAYPAL_SETTINGS_CACHE_MS Copy to clipboard15000 In-process cache for plugin settings reads; Copy to clipboard0 disables Copy to clipboardPAYPAL_SEND_SHIPPING_ADDRESS Copy to clipboardfalse Copy to clipboardtrue sends the cart's shipping address on PayPal orders (Copy to clipboardSET_PROVIDED_ADDRESS) β€” improves PayPal Seller Protection eligibility Copy to clipboardPAYPAL_PARTNER_SERVICE_URL (built-in) Override the partner onboarding service used by the connect flow Copy to clipboardPAYPAL_PARTNER_MERCHANT_ID_SANDBOX / Copy to clipboardPAYPAL_PARTNER_MERCHANT_ID_LIVE (built-in) Override the partner merchant ids used for onboarding

Declined cards: when PayPal declines a capture, Copy to clipboard/store/paypal/capture-order responds Copy to clipboard402 with a buyer-friendly Copy to clipboardmessage plus a structured Copy to clipboarddecline object β€” Copy to clipboard{ code, message, retryable, avs_code?, cvv_code? } β€” so storefronts can show "insufficient funds, try another card" and decide whether to offer a retry.

Monitoring: operational counters live in the Copy to clipboardpaypal_metric table; every webhook is recorded in Copy to clipboardpaypal_webhook_event with automatic retries (events that exhaust retries are dead-lettered and pushed to Copy to clipboardPAYPAL_ALERT_WEBHOOK_URLS); audit events are logged as JSON lines tagged Copy to clipboard"log":"paypal_audit" (secrets redacted).

πŸ’¬ Support

πŸ“„ License

MIT Β© Easy Payment

You may also like

Browse all integrations

Build your own

Develop your own custom integration

Build your own integration with our API to speed up your processes. Make your integration available via npm for it to be shared in our Library with the broader Medusa community.

gift card interface

Ready to build your custom commerce setup?