Skip to content

Accept PayPal in a Laravel shop that runs on a WordPress database ​

The shop moved from WooCommerce to Laravel and kept its orders, its products and its customers. Stripe takes cards. Then the first customer writes: "Do you take PayPal?" Half the shoppers in North America and Europe would rather approve a payment in an account they already have than type a card into a site they met today, and a shop without the button loses some of them at the last step.

On a Laravel site PayPal is a small project of its own: the REST API's approve-and-return dance, a capture that must happen exactly once even when the payer closes the tab, a webhook PayPal must be asked to verify, refunds that have to find their way back to the transaction, and twenty-four currencies of which three are paid in whole units. kitloom/payments has PayPal as a gateway: the keys and the webhook go on a settings page, and the shop takes PayPal like any other way to pay.

Ask the agent ​

With kitloom/ai connected, an agent whose user manages the payment settings reads the ways to pay (payments_methods): each gateway and offline method, whether it is on, the currencies it takes, which credentials are still empty, and whether its webhook still needs setting up in the gateway's dashboard. So it answers:

  • "Why does the checkout not offer PayPal?" — the Secret is empty, or the method is off.
  • "Is the PayPal webhook set up, or are we only capturing on return?"
  • "Which of our methods refund from the order page?"

It never sees a key's value and changes nothing here: who is paid is decided on a page a person opens. For everything else in the shop — orders, products, coupons — it has the shop's own tools.

Set up in the admin ​

Payments → Settings shows every way to pay on one page, the online gateways and the offline methods. The PayPal section:

  1. Enabled puts PayPal on the checkout; Title is what the payer sees there, PayPal unless you say otherwise.
  2. Client ID and Secret from a REST app on developer.paypal.com, under Apps & Credentials. The secret is kept encrypted with the application key and never shown back; an empty field later keeps the one saved.
  3. sandbox on means the Sandbox app: test payments with the sandbox personal accounts PayPal made for you, no money moves. When the shop goes live, paste the Live app's keys and turn it off. Keys of one with the switch of the other fail with PayPal's Client Authentication failed, which is the first thing to check.
  4. The webhook. Copy the Webhook address the page shows, add it under the app's Webhooks on developer.paypal.com with the checkout and payment capture events ticked, and paste the Webhook ID PayPal shows back. Every notification is then verified with PayPal against that id; without it every notification is refused and PayPal keeps trying for a few days. A sandbox app needs a webhook of its own.

Save, and PayPal is a way to pay at the checkout.

What happens at the checkout ​

The payer picks PayPal, is sent to PayPal to approve, and comes back. The order is captured there and then: the page they land on already says paid, the shop's "order received" email goes out, and the invoice, if the Invoices module is on, is made.

A payer who closes the tab at PayPal does not leave an order half paid. The webhook captures it when PayPal reports the approval, and an hour on the reconciliation of pending payments asks PayPal about any it has not heard of. Whichever gets there first captures, and the order is announced paid once.

A refund from the order page goes back to the PayPal transaction, in full or in part, as with every other gateway. A refund made in PayPal itself, or a dispute PayPal decided for the buyer, comes in by the webhook and is recorded on the payment. The payment's reference is PayPal's transaction id, as PayPal's activity lists it, so the two sides reconcile.

Currencies: AUD, BRL, CAD, CHF, CNY, CZK, DKK, EUR, GBP, HKD, HUF, ILS, JPY, MXN, MYR, NOK, NZD, PHP, PLN, SEK, SGD, THB, TWD and USD. HUF, JPY and TWD are paid in whole units: an amount with a fraction in them is refused before the payer is sent anywhere, not at PayPal's door. Which of them your account takes depends on its country.

For a Laravel developer ​

PayPal is a gateway of kitloom/payments, on PayPal's REST API (Orders v2) through Laravel's HTTP client, no SDK. Payments are for any model, not only the shop's orders: an invoice or a booking implements Payable, takes the HasPayments trait, and sends the payer on its way.

php
return $order->pay('paypal')->redirect();   // to PayPal; on the return the payment is captured and $order->paid() is called

The engine underneath is fomvasss/laravel-billing with its payments table and its other gateways — Stripe, Paddle, Monobank, LiqPay, WayForPay, Hutko. The keys saved on the settings page win over config/billing.php and the environment, so a site is moved from Sandbox to Live without a deploy. It comes with both admins, Filament and Nova.

Watch it ​

A short lesson from the admin, narrated:

Take PayPal payments: the keys, Sandbox or Live, the webhook

Try it ​

Open a sandbox in one click, with no sign-up: Filament or Nova. Every detail of the gateway — the webhook's events, refunds, the agent's tool — is in the Payments guide; how the shop itself came over from WooCommerce is in WooCommerce to Laravel.

Last updated:

kitloom — modules and themes for Laravel.
Contact · Pricing · Services · License · Refunds · Privacy · Community · YouTube · [email protected]