Skip to main content
Point of Salefor WooCommerce

A shared device on a busy counter, built to be trusted.

Registers get left unattended, passed between people and used in a rush. So nothing the browser sends can set a price, choose a register or approve a refund.

Principles

What could go wrong, and what stops it

Someone changes a price in the browser
The server prices every line itself. Price changes and discounts need permission or a supervisor's approval, a reason, and are recorded.
An approval is reused for something else
Each approval is single-use, lasts 120 seconds, and is tied to the action, the approver, the cashier, the register and the exact amounts shown. Anything different is refused.
Someone guesses a supervisor's PIN
PINs are hashed, easy ones are refused, and wrong guesses pause that supervisor: five on one register, twenty across them all. Each lockout within a day doubles the pause, up to a day, and if it keeps happening, managers see a warning in wp-admin.
A device pretends to be another register
A supervisor or manager sets each device up as a register, and the server reads which register it is from a secure cookie, never from what the browser sends. Device access expires after 90 days unused and can be revoked.
Someone signs in another way
Register staff can only use the register through the normal WordPress login in the browser. Application passwords and token logins are turned off for them.
A card is charged twice
Every order, payment and refund carries a one-time key and takes a lock, so a retry can't charge again. Taking cash cancels any card attempt still waiting.
A card payment doesn't match the sale
Before taking the money, your site checks the amount, currency and order with Stripe. A mismatch cancels the payment.
A Stripe message is faked, or comes from another site
Stripe's messages to your site must be signed and recent, and your site fetches the payment from Stripe again before acting on it. A payment from another site on the same Stripe account, such as a staging site, is ignored and recorded. A message can only ever complete a payment, never cancel one.
Card details are exposed
Cards are processed by Stripe, a PCI DSS Level 1 provider. Card details go from the reader to Stripe and are never stored in the register or WooCommerce. The Stripe secret key never reaches the browser.
A register is left unattended
The idle lock is kept on the server and locks the whole login, including wp-admin, not just the screen.
A cashier works out the expected cash
During a blind count the server withholds the expected amount, including from the Orders list, until the drawer is closed.
Someone harvests customer details
Searches need at least three characters, and emails and phone numbers are masked in every result and order. Adding a customer never reveals whether an email address already has an account.
Someone fakes a receipt printer
Each register's printer has its own password, shown once and stored only as a hash, and printers connect over HTTPS only. Repeated bad passwords block the address. A print job not collected within 30 seconds is canceled, so a late drawer kick can never open the drawer.
A script on the page misbehaves
The register page loads none of your theme's or other plugins' scripts, and a strict content security policy allows only its own code.
Someone floods the register
Orders, payments, refunds, approvals and searches are rate limited, and receipt emails and new customers are capped per location each day.
Nobody knows who did what
An append-only audit log records approvals, refusals, PIN changes, lockouts, cash movements and failed card attempts.

Your data stays yours

  • Orders, products and customers are ordinary WooCommerce data, in your own database.
  • Supervisor PINs and store credit codes are stored only as scrambled hashes.
  • Deleting the plugin keeps its data unless you turn on Delete all Point of Sale data. It never deletes orders, products or customers.
  • The Stripe secret key comes from your wp-config file, an environment variable or the WooCommerce Stripe Gateway settings. The plugin never stores it or sends it to the browser.

Your part

The register is only as safe as the site it runs on. Three things are worth doing.

Use HTTPS everywhere
The register and receipt printers only work over HTTPS. Once your whole site is on HTTPS, turn on HSTS at your web server or CDN so a register can't be pushed back to an insecure connection.
Include the register in your backups
A normal full database backup already covers it. If your backup tool picks tables, include the plugin's own tables, which are listed in the plugin's documentation.
Keep Stripe secrets in your config
Put the Stripe secret key and webhook secret in wp-config or environment variables, so they stay out of the database.

Found a security problem?

Tell us privately through the contact form and leave out anything that would let someone else use it. We’ll look into it and let you know what we find.

Ring up a sale yourself.

The live demo is the real register, running in your browser with nothing to install. Turn on your screen reader and try a full sale.