How Shopvia protects the merchant and the buyer

Written 15 October 2026 for merchants and for the Shopify App Store reviewer. Every claim names the place in our code that makes it true.

What we do

The browser never sets a price

The checkout asks for a quote by sending variant ids and quantities only. The server prices the cart through Shopify, adds the shop's own rules, and signs the result with a secret key. Placing an order re-checks the signature, the session and the quote id before anything is created. An online payment is charged the amount in that signed quote, never an amount sent by the page, and a captured payment that does not match the order is refunded in full. Why it matters: a buyer cannot lower a total by editing a request, and a merchant cannot be charged for an order priced somewhere else.

Where to check: api.v1.sessions.$id.quote.ts, checkout/quote.server.ts, checkout/place.server.ts, payments/attempts.server.ts, payments/confirm.server.ts.

Store configuration stays on the server

Each store's settings are looked up from its own record by a public key. The storefront receives only a short public subset, copied field by field. Fraud lists, billing, messaging settings and secrets are not in it.

Where to check: modules/shop-config/config.ts, api.v1.config.ts, modules/shop-config/config-types.ts.

Buyer contact is handled under a consent line

The phone field sits under a consent line. Contact and address are encrypted when stored. A reminder is queued only when that consent was recorded, and a reply starting with STOP stops it. Checkouts that never became orders are deleted after 30 days.

Where to check: widget/src/drawer/PhoneField.tsx, crypto/cipher.server.ts, jobs/handlers/recovery.ts, messaging/optout.server.ts, jobs/handlers/retention.ts.

Billing is Shopify Billing

Plans are charged by Shopify after the merchant approves them on Shopify's own page. There is no wallet to top up and no per-order credit balance.

Where to check: modules/plan/plan.server.ts, routes/app.plan.tsx.

The store survives the app

The theme's own buy buttons are restored if our checkout has not opened within 4 seconds. A shop that an operator has paused answers new checkouts with 503, and the storefront falls back to the native checkout.

Where to check: widget/src/loader-lib/boot.ts, extensions/ock-theme/blocks/ock-embed.liquid, routes/api.v1.sessions.ts.

Writes are schema-checked on a versioned API

Every buyer-facing checkout and order route lives under /api/v1/ (webhooks are separate and signature-checked). Bodies are parsed with a schema before use and are size-capped. Two small event routes read no fields from the body at all. Order changes by a buyer need a short-lived action token or a one-time code.

Where to check: api.v1.sessions.$id.place.ts, orderpage/actions.server.ts, http/api.ts, orderpage/stepup.server.ts.

How this differs from the common alternative

Some checkout designs compute totals in the browser and send them to the server. Some look a store's configuration up by shop domain and return all of it to any caller. Some re-send the buyer's details on a timer to keep a server copy current. Some bill through a prepaid balance the merchant must keep topped up. We do none of the four.

What we do NOT claim

See also the privacy page for what data we process, why, and for how long.