Skip to content

Terminal Payments

Hyvä POS is in closed beta

Hyvä POS is currently in a closed beta (pilot phase) with a small group of merchants. It is not yet generally available: the App Store release follows the pilot, and features and configuration may still change - possibly in backwards-incompatible ways - before the general release. Want to take part? Sign up at hyva.io/pos.

An integrated terminal means the app drives the card machine: the amount appears on the terminal on its own, the customer taps or inserts, and the sale completes when the payment confirms. Nobody types an amount twice, and a typo on the terminal can never charge the wrong amount.

How Provider Integrations Work

Terminal integrations are built into the native app: each payment provider's terminal protocol is implemented and tested by Hyvä, in partnership with that provider. It is not a plug-in system where any PSP can be added by a third party.

  • Mollie - available today, on the free tier and PRO.
  • Adyen, Pay. and Stripe - in development, release date unknown. Like Mollie, they will be available on both tiers.

The Flow at the Register

  1. The cashier picks Card at checkout. The payment panel shows the paired terminal by name; a counter with several terminals configured switches per payment from the same panel.
  2. The cashier taps pay. The app creates a payment with the provider and the amount appears on the terminal - for a split sale, only the card portion. A connected customer display shows "Please pay" with the amount.
  3. The customer taps or inserts. The app polls the payment every two seconds while an overlay shows the waiting state.
  4. On confirmation the overlay closes and the app places the Magento order, recording the provider's payment id and the card metadata with the sale. The payment happens first: an order is never placed for money that was not collected.

There is no tip prompt in the flow today - tipping is not yet available in Hyvä POS.

What the Cashier Sees While Waiting

The overlay tracks the payment from the register side:

  • Waiting: the terminal's name and the amount, with Cancel always available. Cancelling does not just close the overlay - it cancels the payment at the provider too, so the customer cannot complete a payment the cashier already abandoned.
  • Taking too long: after a threshold the overlay suggests the terminal may be unavailable and shows the fallback actions.
  • Declined / cancelled / expired: the reason, with Retry as the primary action. The cart is untouched - retry, switch to another paired terminal from the overlay, or back out and pick a different tender.
  • Test mode: when the provider is configured with a test key, a visible test-mode badge shows on the payment panel so a test key cannot silently take real sales.

Where It's Configured

  • In the app: Settings → Payments → Card Payments. Switch the processing mode from Manual to the provider, enter the provider credentials, and pair a terminal. The provider pages walk through it.
  • In Magento (PRO): the same credentials and method mapping live at Stores → Configuration → Hyvä → POS with per-location overrides, and are pushed to every register. Locked fields arrive managed on the device - Magento wins; unlocked fields remain locally editable. See global settings.
  • On PRO, re-pairing to a different terminal is gated by the hardware-configuration permission (managers and up by default); taking payments through the paired terminal is not.

Engineered Against the Failure Cases

Card payments are where a network dip hurts most, so every provider integration is held to the same design:

  • Idempotent payments: every payment carries a unique key, so a network drop during creation can never double-charge; retrying returns the same payment. A double-tap on the pay button is rejected outright - only one payment can be in flight.
  • Recovery over failure: on provider server errors the app retries with the same key and, if needed, matches the payment among recent transactions rather than showing a false failure.
  • A bounded wait: the app polls the payment for up to 60 seconds. An abandoned payment is cancelled at the terminal too - the cashier tapping Cancel or the wait timing out both cancel server-side, so a stray card tap cannot complete a payment the register already gave up on.
  • Failure is explicit: a declined, cancelled or expired payment shows the reason on the overlay with Retry as the primary action; the cart is untouched, so the cashier retries, switches to another terminal mid-payment, or picks a different tender.
  • If the app dies mid-payment: the payment can still complete at the terminal; the truth lives with the payment provider, and reconciliation against the provider's dashboard covers the edge case.
  • An escape hatch: if the terminal stays unreachable, Process manually hands the payment to the cashier - after an explicit confirmation - to settle as a manual card entry. The cart is never lost to a dead terminal.

What Ends Up on the Receipt and the Order

The provider returns the full card metadata on confirmation, and the app stores it with the sale:

  • On the terminal slip: card brand and masked number, cardholder where the scheme provides it, chip/contactless indicator, authorization code, acquirer reference (ARN), EMV application details, entry and verification mode, and the authorization timestamp. Customer and merchant copies have their own auto-print switches (per device, or pushed from Magento on PRO).
  • Because the metadata is stored with the sale, slips reprint faithfully later - offline included - from the receipts list.
  • The Magento order books under the provider's payment method (see the provider page for the mapping) with the POS payment details in the order comments, and the sale keeps a direct link to the payment in the provider's dashboard for reconciliation.

Payment First, Order Second

The order is placed only after the provider confirms the payment, never the other way around - an order is never created for money that was not collected. The reverse gap is covered too:

  • The sale is recorded on the device the moment payment confirms, before the order call to Magento. A hiccup between payment and order placement never loses the sale - it shows as pending and syncs on retry.
  • On PRO, a network drop after the payment simply queues the finished order; the receipt prints from the local record and the queue drains on reconnect.
  • Every sale carries a unique id, so an order-placement retry returns the existing Magento order instead of creating a duplicate - see how orders are placed.

Note that the payment itself always needs a route to the provider: terminal payments are payments over the internet, so a fully offline counter takes cash or falls back to the manual reference flow while the connection is down.

No Integrated Terminal?

The manual reference flow covers any terminal from any provider, today: type the amount into the terminal as you always have, and record the payment in the app with the terminal's reference number.

Refunds go back to the card on both tiers

A refund of a terminal-paid sale finds its way back to the customer's card on both tiers. The app refunds through the terminal provider directly when the sale carries a terminal payment; otherwise the credit memo it creates in Magento is an online refund, and the provider's Magento module (Mollie's, for example) returns the payment automatically. Only sales recorded with the manual reference flow have no payment to refund against - those are reversed on the standalone terminal by hand.