Skip to content

Deploying SaleSprint Pay

SaleSprint Pay is the page a customer lands on when they open an invoice, statement, receipt or quote link, and the hosted page where they pay. On the managed service it is https://pay.salesprint.org. The source is the pay/ directory of the repository.

One deployment serves every tenant. The tenant is in the path of each link, and everything is fetched from the SaleSprint API per request. It has no database, no sessions and no cookies.

Path What
/d/{tenant}/{token} A customer document: invoice, statement, credit note, receipt, quote, order, delivery, refund. Print / save as PDF.
/pay/{tenant}/{slug} The hosted payment page: Mobile Money (MTN, Telecel, AirtelTigo) or card through the tenant’s gateway. Polls until the payment is decided.
/api/health Health probe.
/ A minimal landing page. It lists nothing.
/robots.txt Disallows everything.

It is deliberately separate from the storefront (ecommerce/): billing documents belong to every tenant, including service businesses with no shop. The storefront’s older /document-links/{token} and /pay/{tenant}/{slug} addresses redirect to it, so links already sent keep working.

All settings are server-side environment variables.

Variable Required Notes
SALESPRINT_API_URL yes Base URL of the SaleSprint API, as the pay app’s server can reach it.
PAY_PUBLIC_URL no This site’s public origin, used to build the address a payment gateway sends the customer back to. Defaults to https://pay.salesprint.org in production — set it if you host under another name.
STOREFRONT_PROXY_KEY no The same shared secret the storefront uses. With it, the API records the visitor’s IP on link views. The pages work without it.
SALESPRINT_API_TIMEOUT_MS no How long to wait for the API. Default 10000.
PAY_MEDIA_HOSTS no Comma-separated image hosts allowed by the page’s security policy.
PAY_CHECKOUT_HOSTS no Extra https:// checkout origins the page may send a card payment to. Paystack and theTeller are already allowed.

The directory ships a Dockerfile. The container listens on port 3000.

Terminal window
docker build -t salesprint-pay ./pay
docker run -p 3000:3000 \
-e SALESPRINT_API_URL=https://api.example.com \
-e PAY_PUBLIC_URL=https://pay.example.com \
salesprint-pay

Put it behind TLS on its public hostname and point your health check at /api/health.

For local development: pnpm install, create .env.local with at least SALESPRINT_API_URL, then pnpm dev (port 3001).

The pay app only displays links. The API creates them, and needs to know where the pay app lives.

Set on the API and the scheduler (which sends reminders and statements), with the same values on both:

Variable What it does
PAY_BASE_URL The pay app’s public origin, e.g. https://pay.example.com. Invoice, statement and credit-note links, every pay button and every hosted pay link are built on it. It is also the last fallback for other customer links when a tenant has no shop address. The older name PUBLIC_PORTAL_BASE_URL is still read when this is unset.
DOCUMENT_LINK_SECRET The key every customer link is derived from. Rotating it invalidates every link already sent — that is the intended kill switch, so treat it as long-lived.

With PAY_BASE_URL unset, links fall back to each tenant’s storefront address or their Customer Portal Base Url setting, and a tenant with neither gets no link at all.

PAY_BASE_URL on the API and PAY_PUBLIC_URL on the pay app must be the same origin.

  1. Open /api/health on the pay app.
  2. In a test workspace, post a customer invoice for a customer with an email address.
  3. Open the link in the email. The invoice should render under the business’s name.
  4. If the workspace has a payment gateway, press Pay now and complete a small payment. The page should turn to Payment received on its own.