PruvaDocs

Billing & renewal

The 1-month trial after a 10% deposit, the 3-year contract with a year-1 exit, annual billing, renewal, Cancel trial, Cancel renewal, no refunds for the year already started, and what each order stores.

Every package starts with a 1-month trial after a 10% deposit, then a 3-year contract, billed once a year in advance, per vessel. You can leave in year 1, before the first renewal. Each invoice is one billed year (12 months). The first charge also carries the one-off setup per vessel, Safety Vision setup and installation when the cameras are bought, and the optional gateway kit. Prices come from @pruva/pricing; see Pricing.

Billing notice

A 10% deposit opens a 1-month trial, then billed annually per vessel. 3-year contract. You can leave in year 1. The deposit is not refunded. No refunds after the trial for the year already started; cancel renewal any time.

The free trial

  • 1 month, one per customer. A workspace that has already had a trial pays the full first charge on the day it orders.
  • 10% deposit today. At checkout the buyer chooses a payment method and ticks the consent box. The deposit is taken that day. It is not refunded.
  • The rest of the first charge when the month ends: the order's trial.firstChargeOn. The trial runs from the purchase day through trial.end, the day before.
  • The first billed year (12 months) starts after the trial, on the first-charge day. That year is non-refundable from then on. The contract is 3 years. You can leave in year 1, before the first renewal.
  • Cancelling during the trial stops the rest of the charge. The deposit is not refunded. See Cancel trial.

Every pricing card on the site says "1-month trial · 10% deposit".

Payment schedule

scheduleFrom(purchase, firstChargeUsd, withTrial) in @pruva/pricing computes the schedule, and scheduleLabel() turns it into the line the checkout shows before the order button. For Operations with three Pro modules on 10 vessels, ordered on 16 October 2026:

OrderFree trialTodayFirst chargeTermRenews onCheckout shows
With trial2026-10-16 to 2026-11-15$8,945$89,450 on 2026-11-162026-11-16 to 2027-11-152027-11-16Today: $8,945 deposit · Remainder on Nov 16, 2026
Trial already usednone (already used)$89,450$89,450 on 2026-10-162026-10-16 to 2027-10-152027-10-16Today: $89,450 · Renews on Oct 16, 2027

Before the order button the checkout shows, for an eligible workspace, "Today: $… deposit · Remainder on …", the trial dates, the billed-year start, end and renewal dates, and the first-charge amount. The button says Start 1-month trial.

Trial eligibility

The checkout asks the app GET /api/trial-eligibility?workspace=… (short timeout) whether the workspace can still have a trial.

  • Trial already used. The checkout says "Your free trial was already used", shows the charge-today schedule, builds the order without a trial, and the button says Place demo order.
  • App unreachable. The checkout assumes the trial is unused and says so in small text. The app checks again when the order arrives.

If the app answers 409 trial_used (for example, a second tab used the trial first), nothing is ordered or charged. The checkout shows the message, switches to the no-trial schedule, and unticks the consent box. The buyer must tick it again and place the order without a trial. The site never retries without a trial on its own.

Term dates

A term has three dates. The checkout shows all three before you buy, and the order carries them.

DateMeaning
StartThe first-charge day, after the trial. Without a trial: the day you order.
EndThe last day of the billed year: 12 months after the start, minus one day.
Renews onThe day after the end. The next year is charged on this day.

Example: a term that starts on 23 October 2026 ends on 22 October 2027 and renews on 23 October 2027. The dates come from termFrom() in @pruva/pricing and use UTC days.

Cancel trial

During the trial, an owner opens Settings → Modules & billing in the app, where the module shows the trial days left and a Cancel trial button.

  • The rest of the first charge is not taken. The deposit is not refunded.
  • The trial's modules lock straight away.
  • The trial still counts as used (one per customer).

Only the owner can cancel, and only while the trial runs. Each cancel is written to the audit log as billing.cancel_trial with "(no charge)".

The remainder charge

A job takes the rest of the first charge when the trial month ends: runTrialCharges() in @pruva/core (demo mode runs it lazily) and its SQL twin public.pv_run_trial_charges() (database mode, service role only). For each order whose firstChargeOn has arrived, it records that charge once and moves the order's modules from trial to active.

  • Idempotent. It sets charged_at once. A second run on the same day, or any later day, charges nothing again.
  • Never charges a cancelled trial. Orders with trial_cancelled_at set are skipped.
  • In this build there is no payment provider, so "charging" only records the charge. A real build takes the charge from the payment-method reference on the order (a Stripe invoice, or capturing a 3-D Secure pre-authorisation).

Renewal

A plan renews for another billed year (12 months) unless renewal is cancelled before the renewal date. Leaving in year 1 is cancelling before the first renewal. Prices can change only at renewal, and owners are told before the renewal date.

Cancel renewal

After the trial, an owner opens Settings → Modules & billing in the app and taps Cancel renewal.

  • It stops the next renewal and nothing else.
  • Access continues to the end of the term you paid for.
  • Nothing is refunded.

No refunds

After the trial, there are no refunds for the year already started, including unused months, unused modules or a downgrade. After the billed year ends, data is kept for 90 days so you can export it, then deleted.

Payment method: a reference, never card data

The checkout asks for a payment method; the step is required. This build has no payment provider, so it never shows card-number, expiry or CVC fields and never accepts card data. The only option is "Demo card · no real card (Stripe test mode in production — verify before launch)". The server refuses any request that carries card fields.

A real build collects the card with Stripe Elements or a 3-D Secure pre-authorisation, so the card never touches our servers. Either way, the order carries only a reference:

FieldDemo value
payment.providerdemo (or stripe in a real build)
payment.referencepm_demo_visa_4242; must match pm_…
payment.labelDemo card · no real card; the schema refuses a label that contains a card number

At checkout the buyer must tick a box. It starts unticked, and the order button stays disabled until both the payment method is chosen and the box is ticked. The box says exactly:

I agree to a 10% deposit today, which opens a 1-month trial, followed by a 3-year contract, billed annually in advance per vessel at the price of my billing company's country tier, plus the one-off setup fee, any support plan, any Safety Vision camera line and any onboard hardware shown. Safety Vision includes its camera kit, warranty and replacement, which stay Pruva's property. Hardware is a separate line; Safety Vision and hardware are one USD price in every country and are not discounted by the country tier. I can leave in year 1, before the first renewal. The rest of that first charge is taken when the trial month ends unless I cancel before then. The deposit is not refunded. After the trial there are no refunds for the year already started, and I can cancel renewal at any time before the renewal date.

The server refuses any order without consent or without a payment method, whatever the browser sends. The order the site signs and sends to the app (StoreOrderSchema in @pruva/core) carries:

FieldWhat it holds
consent.acceptedAlways true; an order without consent is never sent.
consent.versionThe version of the consent text, now 2026-10-20.2.
consent.atWhen the ticked form reached the server (server clock, ISO time).
trialstart, end, firstChargeOn; or null when the trial was already used. The trial is 1 month. The billed year starts on firstChargeOn.
chargeTodayUsdThe 10% deposit with a trial; the first charge (annualUsd + oneOffUsd) without one.
paymentThe payment-method reference above.
term.start, term.end, term.renewsOnThe billed-year dates above. The app checks they span 12 months. The contract is 3 years, with an exit in year 1.
sku, modules, vesselsThe package or pack, every module it switches on (the core, the Pro modules, and cameras with Safety Vision) and the number of vessels. support is the support plan.
annualUsdThe year's subscription for all vessels. The app checks it against the price list.
oneOffUsdSetup per vessel, Safety Vision setup and installation, and the optional gateway kit.
firstChargeUsdannualUsd + oneOffUsd. The deposit is taken today; the rest is taken when the trial month ends (or the whole amount today without a trial).

The success page shows the order id, the items, the trial month, the deposit taken today and the remainder date (or the amount charged today when there is no trial), the billed-year dates, "Payment method: Demo card · no real card", "Consent recorded: version … at …", and a link to Settings → Modules & billing. Cancelling the trial there stops the rest of the charge. The deposit is not refunded.

Module accents

Each module has its own accent colour, from @pruva/brand/tokens. The app, the site and these docs use it for borders, chips and icons only, never for body text. Accent-coloured text switches to a darker shade in the light theme so it meets WCAG AA. The brand itself stays monochrome, with brass for primary buttons.

On this page