NEXUS
Getting Started

Getting started with CloudPay

From a fresh machine to CloudPay running locally in about an hour: the web app on port 3000 and the API on port 5085, signed in through the development Clerk instance, using the development database and Clover's sandbox. It ends with your first invoice. The guide follows development, the branch all work starts from.

Checked against development at 5dbe126 on .

Before you start

Install the tools, and ask for access on day one: the access takes longer than the setup.

Tools
ToolVersionWhy
Node.js and npmNode 20 with npm 11 or laterThe web app. The Docker image builds on node:20-alpine and nothing else pins a version. npm 10, which comes with Node 20, rejects the lockfile: run npm install -g npm@11, as the Dockerfile does. On Node 25 and later the web tests need a flag (see Troubleshooting).
.NET SDK10The API targets net10.0. There is no global.json; this guide was checked with 10.0.400.
Azure CLIAny recent versionaz login lets the API read the email and SMS credentials from the development Key Vault
Access to ask for
AccessFromNeeded for
The Clover-CloudPay project in Azure DevOpsThe teamCloning and pull requests
A Clerk account on the development instance, linked to a merchant that is connected to Clover's sandboxA platform admin: an invitation from the development environment, or Link Existing User for an account you already haveSigning in. Without the link you only see Account Pending Setup.
The development Clerk publishable and secret keys, and read access to the development Key VaultThe teamThe web app's .env.local, and sending emails and texts from your machine
Optional: a Clover dev device on that merchant running Cloud Pay Display, and DMS client credentialsThe teamTaking a sale on a terminal, and the DMS pages

Get the code

One repository holds both apps. Its default branch is main, which is production, so switch to development after cloning.

Check your tools, clone and install (Bash or PowerShell)
node --version; npm --version; dotnet --version   # v20.x, 11.x or later, 10.0.x

git clone https://micampdevscottsdale.visualstudio.com/Clover-CloudPay/_git/Clover-CloudPay
cd Clover-CloudPay
git checkout development

npm ci          # root: installs only concurrently
npm run setup   # runs npm ci inside src/web

Configure the API

The API runs without edits. appsettings.Development.json already points at the shared development resources, and the http launch profile sets ASPNETCORE_ENVIRONMENT=Development and sends invitation links from your API to http://localhost:3000/sign-up.

KeyDevelopment valueLocally
ConnectionStrings:DefaultConnectionThe shared development databaseKeep it, or point it at your own SQL Server (see Choose a database)
Clover:Environmentsandbox, with the sandbox app's ClientId and ClientSecretKeep it. Every Clover call from the API goes to the sandbox.
Clerk:Authority, Clerk:AuthorizedParties, Cors:AllowedOriginsThe development Clerk instance. The parties and the origins include http://localhost:3000.Take the web app's Clerk keys from the same instance, and keep the web app on port 3000
KeyVault:VaultUriThe development Key VaultNeeds az login. When empty, secrets come from the Secrets section instead.
BackgroundJobs:EnabledNot set, so trueSet it to false (below)
In the terminal that will start the API (PowerShell)
$env:BackgroundJobs__Enabled = "false"   # Bash: export BackgroundJobs__Enabled=false
az login                                 # lets the API read the Key Vault

Change anything else the same way: environment variables override the JSON files, and a double underscore stands for the : in a key. Do not edit the committed appsettings files, and never add real secrets to them.

Configure the web app

The web app reads src/web/.env.local, which git ignores. Write it from the block below rather than copying .env.example: that file names the Clover values without their NEXT_PUBLIC_ prefix, so the code never reads them, and lists a DATABASE_URL nothing uses.

src/web/.env.local
# The API on your machine, no trailing slash
NEXT_PUBLIC_BACKEND_URL=http://localhost:5085

# Clerk: the development instance, the same one the API trusts
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=<your-clerk-publishable-key>
CLERK_SECRET_KEY=<your-clerk-secret-key>
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/

# Optional: only for the DMS pages
# DMS_API_URL=<dms-service-url>
# DMS_CLIENT_ID=<dms-client-id>
# DMS_CLIENT_SECRET=<dms-client-secret>
VariableLocallyNotes
NEXT_PUBLIC_BACKEND_URLRequiredEvery call to the API, and the Content-Security-Policy in proxy.ts
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY and the Clerk URLsRequired<ClerkProvider> in the browser and clerkMiddleware in proxy.ts. The URLs keep sign-in and sign-up on the app's own pages.
NEXT_PUBLIC_TARGET_CLOVER_DOMAIN, NEXT_PUBLIC_REMOTE_APP_IDOptionalUnset under next dev, the device SDK and the Clover route handlers use Clover's sandbox, and the SDK connects as a built-in app id (services/clover/sdkConfig.ts). For terminal sales, use the development values from the team.
DMS_API_URL, DMS_CLIENT_ID, DMS_CLIENT_SECRETOnly for the DMS pagesRead on the server only, in lib/dms/dmsServer.ts
CLERK_WEBHOOK_SECRET, NEXT_PUBLIC_CLARITY_PROJECT_IDNot neededClerk's webhooks go to the development environment, not to your machine. Clarity loads only in production builds.

Choose a database

The API needs SQL Server. Every time it starts, it applies any pending EF Core migrations before it listens (ApplyMigrationsAsync in Program.cs).

OptionWhat you getWatch out for
Shared development database (the default)Merchants already connected to Clover's sandbox, with their users, invoices and the webhook copies of their payments. You can sign in and work at once.Your branch's migrations change it for everyone, dev2 included, so keep them additive. Turn the background jobs off.
Your own SQL ServerA private database. On the first start the 14 migrations create every table.It starts empty: no merchant, no Clover connection, no users. Connecting a merchant needs a platform admin account and a Clover OAuth round trip back to http://localhost:3000/callback. Clover tokens cannot be copied in: they are encrypted with the key ring in their own database.
Use your own SQL Server (PowerShell, before starting the API)
$env:ConnectionStrings__DefaultConnection = "Server=<your-sql-server>;Database=<your-database>;Trusted_Connection=True;TrustServerCertificate=True"

Run both apps

  1. 1
    StartIn the terminal where you set BackgroundJobs__Enabled, run npm run dev from the repository root. It starts both apps side by side, with their output prefixed [web] and [api]. The API applies pending migrations first, then listens on http://localhost:5085.
  2. 2
    Check the APIhttp://localhost:5085/health answers Healthy. Swagger UI is at http://localhost:5085/swagger (Development only).
  3. 3
    Open the web apphttp://localhost:3000. Signed out, it sends you to /sign-in; /home is the public landing page.
One app at a time
npm run dev:web    # from the root: the web app only, port 3000
npm run dev:api    # from the root: the API only, port 5085

# From src/api: HTTPS on 7291 as well (needs a trusted ASP.NET Core dev certificate)
dotnet run --project src/CloudPayClover.Api --launch-profile https

Send your first invoice

An invoice uses the whole chain: Clerk sign-in, the API session, the database, email through the Key Vault, and Clover's sandbox checkout. It needs no terminal.

  1. 1
    Sign inOpen http://localhost:3000/sign-in and sign in with your development Clerk account. The onboarding guard calls POST /api/v1/auth/session on your API and opens the Sales Terminal.
  2. 2
    Fill in the invoiceSwitch the Sales Terminal from Clover Payment to Send Invoice, and enter a small amount. The merchant's tax rate is added.
  3. 3
    Send it to yourselfUnder Delivery Method choose Email, type your own address and press Send Invoice. The API saves the invoice as Outstanding, logs Created invoice … for merchant … and sends the email. Dashboard, Invoice Transactions now lists it.
  4. 4
    Open the pay linkThe email's Pay button points at http://localhost:5085/api/v1/hosted-checkout/{invoiceId}, which is your own API, so open it on this machine. It redirects to Clover's sandbox checkout page.
  5. 5
    Pay it (optional)Pay with a test card from Clover's sandbox documentation. Clover sends the payment webhook to the development API, not to yours; on the shared database the invoice turns Paid for you as well.
What Send Invoice posts to your API (createInvoice in InvoiceService.ts)
POST http://localhost:5085/api/v1/invoices
Authorization: Bearer <api-jwt-from-auth-session>
Content-Type: application/json

{
  "amount": 1, "taxAmount": 0, "tipAmount": 0, "totalAmount": 1,
  "sendToEmail": "<your-email>", "sendToSMS": "",
  "orderId": null, "cloverPaymentId": "", "serviceAdvisorId": null,
  "employeeId": null, "dmsSnapshot": null
}

With a Clover dev device on the merchant, take a sale too: on Clover Payment pick the device and press Connect to Device, wait for Ready, enter an amount and press Send to {device}. The customer side then runs on the device.

Run the tests

No pipeline runs on a pull request, and the web pipeline runs no tests at all, so run both suites before you open one. Neither needs the database: the API tests use EF Core's in-memory provider and the web tests use jsdom.

From the repository root
cd src/api
dotnet test CloudPayClover.slnx      # or one class: dotnet test --filter "FullyQualifiedName~InvoiceCommandServiceTests"

cd ../web
npm run typecheck
npm test                             # or one file: npx vitest run services/dms/__tests__/DmsService.test.ts
Results on `development` at `5dbe126` (2026-10-27)
CommandResult
dotnet test CloudPayClover.slnx277 xUnit tests in two projects. 275 passed; 2 failed on the Windows 11 machine used for this guide (see Troubleshooting).
npm run typecheckPasses
npm test227 Vitest tests in 29 files, all passing (run on Node 26 with web storage turned off)
npm run lintFails: it runs next lint, which Next.js 16 removed

Troubleshooting

SymptomCause and fix
Every web test fails with Cannot read properties of undefined (reading 'clear')Node 25 and later define their own localStorage, which hides jsdom's. Use Node 20, or turn Node's web storage off for the run (below).
Two API tests fail on Assert.DoesNotContainSms_TruncatesLongCustomerNames_WithAsciiEllipsis and SendAsync_EmailBody_OmitsCardRowForCashPayments compare strings with the current culture. On the machine used for this guide they failed under .NET's default ICU globalization and passed with NLS (below). If only these two fail, the cause is the machine, not your change.
The API stops at startup with a SQL errorMigrations run before the API listens, so it must reach the database in ConnectionStrings:DefaultConnection. Check the value and your network, and ask the team if the development database refuses your connection.
Account Pending Setup after signing inYour Clerk user has no cloverMerchantId in its public metadata. Ask a platform admin to link you to the sandbox merchant.
POST /api/v1/auth/session answers 401, or the browser reports CORS errorsThe web app's Clerk keys must come from the instance in the API's Clerk:Authority, the web app must run on http://localhost:3000, and NEXT_PUBLIC_BACKEND_URL must point at your API.
Workarounds for the two test problems (PowerShell)
# Web tests on Node 25 or later, from src/web
$env:NODE_OPTIONS = "--no-experimental-webstorage"
npm test
Remove-Item Env:NODE_OPTIONS

# The two culture-sensitive API tests under NLS, from src/api
$env:DOTNET_SYSTEM_GLOBALIZATION_USENLS = "1"
dotnet test tests/CloudPayClover.Infrastructure.Tests --filter "FullyQualifiedName~Sms_TruncatesLongCustomerNames_WithAsciiEllipsis|FullyQualifiedName~SendAsync_EmailBody_OmitsCardRowForCashPayments"
Remove-Item Env:DOTNET_SYSTEM_GLOBALIZATION_USENLS

Next steps