Skip to main content

Documentation

No results found.
Features

Payment Links

Payment Links is the lightweight "get paid" card: create a shareable link (and matching QR code) for a fixed or open ("payer chooses") amount — without setting up the full E-Commerce store. Visitors open /pay/{token}, se...

Payment Links is the lightweight "get paid" card: create a shareable link (and matching QR code) for a fixed or open ("payer chooses") amount — without setting up the full E-Commerce store. Visitors open /pay/{token}, see the title, description, and amount, and pay through the same embedded Stripe checkout the rest of the CMS uses. Every payment lands in a log with optional payer receipts and an owner notification.


The problem

Plenty of payments don't need a product catalog: a deposit for a job, a consultation fee, a class registration, a "pay what you owe" balance, a tip jar. Setting up the full shop — products, categories, cart, checkout settings — for a one-off charge is overkill, and sending clients to a third-party payment page means another tool, another fee, and a URL that isn't yours.

The fix

A self-contained feature module under app/Features/PaymentLinks/: a dashboard page to create and manage links, a tokenized public pay page served from the install's own domain, embedded Stripe checkout (shared site-wide stripe.key / stripe.secret credentials), and a payments log — Stripe's fees the only cut taken.

Like every addon, it's structured as a feature module gated by feature:payment_links middleware. The service provider boots unconditionally; routes 404 and the sidebar entry hides when the feature is off. Toggling it on runs the module's migrations. Disabling preserves all data.

What the dashboard gets

One page under Dashboard → Payment Links (Manager and up) plus an admin-only Settings page:

  • Payment Links — the link list: title, amount (or Open amount with its minimum), Active/Archived status badge, total collected, payment count, and created date. Row actions: Copy URL, QR code (an inline SVG in a modal, ready to screenshot or print), Open page, Edit, Archive/Restore, and Delete. Links with recorded payments can't be deleted — archive them instead to keep the payment history. Below the list, a Recent payments section shows the last 20 payments across every link: link title, payer name/email (when Stripe provides them), amount, and date.
  • Create / edit modal — title, description (shown to the payer above the checkout), amount mode (Fixed amount or Payer chooses the amount with an optional minimum), currency, and a custom success message shown after payment (and included in the receipt email).
  • Settings (admin only) — the Stripe webhook signing secret (with the endpoint URL to paste into Stripe), a payer-receipts toggle, and the owner-notification address (falls back to the Business settings email).

The dashboard also gets a Payment Links card: total collected in the last 30 days plus the payment count.

What the payer sees

/pay/{token} (a 40-character random token, no login needed) shows the link's title, description, and amount:

  • Fixed amount — the embedded Stripe checkout renders immediately.
  • Open amount — a simple amount form first (with the minimum shown, floor $1, sanity cap 1,000,000.00); submitting reloads the page with the chosen amount and renders the checkout. Amounts are validated server-side against the link's floor, so the form can't be bypassed.
  • Archived links — a friendly "This payment link is no longer active." notice (not a 404, so old printed QR codes degrade gracefully).
  • Stripe not configured — a "payments unavailable" notice instead of a broken checkout.

After paying, the return page confirms the amount and shows the link's success message (or a default thank-you).

Payments, receipts, and the webhook

Payment recording mirrors the invoicing engine: the Stripe webhook (/pay/stripe/webhook, verified against the stripe.webhook_secret.payment_links signing secret, subscribed to checkout.session.completed) and the return page both run the same recorder, deduped by an atomic claim on the checkout session id — exactly one of them lands the payment and sends the emails. Sessions from other features' checkouts are acknowledged and ignored; only sessions stamped with payment_link_id metadata are processed.

When a payment lands:

  • The payer's name and email are captured from Stripe's checkout details.
  • A short receipt goes to the payer (when Stripe gave us their email and receipts are enabled) including the link's success message.
  • An owner notification goes to the configured address (Settings → Notify on payment, falling back to the Business settings email).

Both emails send through the shared Marketing transport; a send failure is reported and swallowed — it never breaks payment recording.

QR codes

Every link's QR code (the same BaconQrCode SVG stack the restaurant table and ticketing check-in QRs use) encodes its public URL — print it on an invoice, a flyer, or a counter card and get paid on scan.

Settings reference

Setting Default Meaning
payment_links.receipts_enabled true Email a receipt to the payer when Stripe provides their address
payment_links.notification_email (Business settings email) Address notified when a link is paid
payment_links.currency (shop currency, else usd) Default currency for new links
stripe.webhook_secret.payment_links — Signing secret for the /pay/stripe/webhook endpoint

Stripe API keys are the shared site-wide pair under Settings → API Keys (stripe.key / stripe.secret).