Skip to main content

Merchant Page Editing

Open a merchant from Merchants. The H1 is Merchant Details. Header: Edit Merchant and Delete Merchant. Tablist Merchant sections: Overview, Enquiries, Analytics, Admin. All editing is inline. Do not start from /admin/merchants/ or local-server.js.

Stay on Overview for the App card (Environment, Sandbox preview, Header snippet, Features). Click ⓘ for a short answer and Read full guide →. Admin help & docs.

Live URL: https://admin.finmatch.io/merchants/?id=M000101. Path IDs like /merchants/M000101 404. See Admin URLs.

Accessing the Merchant Page​

From Merchants Table​

  1. Click Company Name: Click on any merchant's company name in the merchants table
  2. Actions Menu: Click the "⋯" (three dots) button → "✏️ Edit"

From Other Pages​

  • Stripe Page: Click on linked merchant name to navigate to merchant page
  • Direct URL: https://admin.finmatch.io/merchants/?id=M000101 (path IDs like /merchants/M000101 404 on production)

Merchant Page Structure​

The merchant page H1 is Merchant Details. Header actions are Edit Merchant and Delete Merchant. The tablist aria-label is Merchant sections. The tabs are Overview, Enquiries, Analytics, and Admin.

James-only Settings hub (signed in as james@finmatch.io; not general operator chrome). Page title Settings. Menu aria-label Merchant settings. Profile row avatar F plus Merchant name (checker reads F Merchant name), aria-label FinMatch Account. Menu row Performance. Subpages: FinMatch Account (back Back to Settings), Websites (Back to FinMatch Account). FinMatch Account → Stripe is a linker, not a read-only copy. First row is Link / Unlink (same words as the Stripe Billing card). Link opens Link Stripe Customer. Do not treat this menu as the live path for other operators.

The merchant page has four tabs. Cards below are on Overview.

TabWhat it is
OverviewMerchant, Finance Offer Summary, Stripe Billing, Merchant Status, Contact, Lender, and App cards.
EnquiriesFinance Assistant enquiry rows. Columns and expanded tiles: Enquiries. Credit Product is not “what Propensio received”.
AnalyticsStorefront analytics capture for this merchant, plus a Looker embed when configured. Global capture toggles live on Analytics.
AdminToolbar Admin actions: JSON, History, and Quote. These live on this tab, not in the page header. JSON opens View JSON with Merchant profile, Merchant configuration, and Partner API view. Legend JSON field shading key: Profile only, Settings only, Duplicate (in both), Conflict (different shape), Partner API metadata. About this view and Field reference. Provenance counters: Duplicates, Conflicts. Badges: admin + system, admin (credentials), system meta, external api. Section headings: CONFLICTS — SAME KEY, DIFFERENT DATA, DUPLICATES — SAME KEY, SAME MEANING. Field reference tables Merchant profile fields, Merchant configuration fields, Partner API view fields with columns Parameter, Type / Format, Description, Example, Source of truth, Maps to. Copy Copy profile JSON, Copy settings JSON, Copy Partner API JSON. Idle JSON panels show Loading.... The tab shell shows Loading history…. The history card then shows
Loading history... while it fetches, No history events yet. when
the feed is empty, or **History service endpoint is not deployed yet for
this environment.** Toolbar Expand all, Collapse all, **Copy all
for AI**, and per-event Copy for AI. Event cards open **Technical
details** and Raw event JSON. Quote is the same three-source table as
Quote comparison.

View JSON → Field reference Parameters include fcaStatus on both Merchant profile fields and Merchant configuration fields (profile is canonical; runtime is the stamped copy so the storefront can skip rate cards without reading merchants.json). Duplicate note: Canonical on the profile; stamped onto runtime so the SDK can skip rate-card downloads for Not authorised merchants. Merchant configuration also lists merchantMinimumDeposit (Merchant-wide minimum customer deposit (floor over per-product rules). Empty/absent = no global floor.) and merchantDepositRules (legacy). There is no Credit Products control for merchantMinimumDeposit yet — author per-product Min Deposit % as today. See Merchant FCA status and Calculator deposit dropdown.

There is no Lender setup card (removed Sep 2026). Overview cards:

  1. Merchant — Merchant ID, Finance Assistant API Key (Copy; idle Loading…, empty Not provisioned — click Copy to create), Company Name, Company Number, Domain, Testing domain, Ecommerce platform, FCA status, CreditSafe (opens CreditSafe from Company Number, or N/A)
  2. Finance Offer Summary — Template and Preview. Mode buttons: Price template, No-price template, Non-regulated template, Badge template (Quick-Start). Editors: Finance Offer Summary Editor (Tokenised) - Price, Finance Offer Summary Editor (Tokenised) - No Price, Finance Offer Summary Editor (Tokenised) - Non-regulated, Finance Offer Summary Editor (Tokenised) - Badge (Quick-Start). Preview title Current draft. Reset current template restores the active editor. Storefront Template mode (Auto / always) is a different control: Finance offer summary template mode. Partner API quotes need these profile template strings even when Finance offer summary enabled is off. Partner API quotes.
  3. Stripe Billing — Customer ID, Name, Email, Phone, Address, Billing Status, Next Billing, Created (Link/Unlink)
  4. Merchant Status — FinMatch / lender approval / live / termination (separate from the SDK kill-switch). See Merchant Status.
  5. Contact — two cards, both titled Contact. Tell them apart by Type: Primary Contact and Technical. Fields: First Name, Last Name, Email, Phone. Empty values show N/A. Save/commit: Contact cards.
  6. Lender — Lender Name and Credit Products (profile mirror). Add lender, Remove lender, and Save credit products. Empty select: Select lender to add.... When none are assigned: No lenders assigned yet. Add a lender to configure products. Idle status: Edit checkboxes, apply URLs, and min deposit % rules. Empty products table: No products for this lender. Loading: Loading credit products.... Load failure: Unable to load credit products. Missing product id shows Unknown ID. Per-lender table columns Product ID, Credit Product, Min Deposit %, plus Direct apply URL. Placeholder: https://hummuk.app.link/… or https://example.com/apply. ⓘ About direct apply URLs. Attaches a link to the Apply for finance button (for example a Humm app link). If Finance Assistant is off for this lender, the button opens this link. If Finance Assistant is on, this link is only a backup if the Finance Assistant start fails. Not a setup wizard. Assignments: Calculator deposit dropdown. Per-product Min Deposit % is still the admin authoring path. Partner API also applies a merchant-wide floor when runtime merchantMinimumDeposit is set (View JSON Field reference); that object has no Credit Products control yet.
  7. App — Environment and Environment integrity, Sandbox preview, Header snippet (and Detected snippet when the scan disagrees), Snippet status and Snippet details, Finance Assistant Proxy URL, Features, Pages with embed snippets

Overview ⓘ titles: About Finance Assistant API keys, About the ecommerce platform, About the merchant FCA status, How merchant status is computed, How App environment works, How environment integrity is checked, How Sandbox preview works, About the Header snippet, When Detected snippet appears, How snippet status works, Possible snippet detail messages, About the Finance Assistant Proxy URL, How Features and Save features work, About pages with embed snippets.

Overview toolbar titles: Validate merchant environment against router and snippet detection, Share header snippet by email, Re-check this merchant now and update the shared snapshot, Show snippet details.

Finance Assistant Settings is not a fifth tab. Open it from the Finance Assistant row in the Features matrix (cog). Snap / Zopa / Humm / Propensio credentials live there. See Bon Voyage and Zopa Start and Lender Apply start.

Enquiries​

Open the Enquiries tab on the merchant page. The table columns are Timestamp, Customer, Lender, Credit Product, Purchase, Loan Amount, and Status. Load row: Loading enquiries.... Empty table: No enquiries found.

Click a row to expand tiles:

TileFields
Customer DetailsName, Email, Mobile, Address. Headless Propensio rows show Customer -.
Finance Details (at application start)Purchase Amount, Deposit, Loan Amount, Upfront Payment, Monthly Payment, Cost for Credit, Total Payable. Snapshot at Apply, not the later agreed figure.
Credit Product (at application start)Lender, Product, Product ID, Term, APR. This is our calculator snapshot. It is not what Propensio received (the start request has no FinMatch product; the webhook has no product). Live today a hidden dropdown can still fill this name. Agreed, not shipped: - when the dropdown was hidden. See Admin Enquiries: Credit Product column.
Propensio application statusCurrent status, Agreed loan (from webhook loan_amount after decision), Originate application ID, Application number, Initial decision recorded, Status history.
Zopa application statusCurrent status, Zopa application ID, Zopa reference, Lender reference, Referred reason, Status updated, E-sign, Satisfaction note, Application.
Reference InfoReference #, Source Page, Email Status, Email Sent, Email To, Actions (Resend email). Snap rows dim the M…-P… prefix (Not sent to Snap) and highlight the R segment (Sent to Snap as invoiceNumber).

Older enquiry rows may still name the retired DivideBuy brand; those join the Zopa session store. DivideBuy is not a live lender.

Analytics​

Open the Analytics tab on the merchant page. The banner title is Storefront analytics capture. The lead is Whether this merchant posts events to the Analytics API (BigQuery).

The pill is On or Off (or Unknown if the capture policy cannot load). Change status here opens Admin → Analytics → Event capture by merchant. Capture is not toggled on this tab.

Below the banner, a Looker embed mounts when configured. The iframe title is Merchant analytics. If no embed URL is set, the placeholder subtitle is Connect a Looker Studio report to show charts for this merchant. Do not copy invoice or SKU pound figures from cost tools into this public docs site. Do not copy Looker setup steps that name internal BigQuery tables onto this menu page.

Recent activity​

Below the Overview cards, Recent activity is collapsed by default. It shows the last 20 history events from GET /admin/merchant-history/:id — the same feed as Admin → History. Expand it for a compact audit. While it loads it shows Loading recent activity…. If the history endpoint is not on this environment it shows Recent activity unavailable: history endpoint not deployed in this environment. If the renderer cannot load it shows History renderer unavailable. Request failures use the same Recent activity unavailable prefix with the status or error. None of those states block editing.

Presence banner​

If another admin edited this merchant in the last five minutes, an amber banner names them and says Refresh recommended before editing. Refresh reloads the merchant and shows Refreshing… while it loads. The banner is not a lock: you can still save. It stays hidden when you are the only recent editor, or if history cannot load.

Save conflict​

If you click Save Changes after someone else wrote the same merchant, Changed while you were editing opens. It is not the presence banner (that one is a warning before you edit).

The table columns are Field, On server (theirs), and Your draft (yours). If there is no field-level diff it says No field-level diff available; the underlying record changed since you opened the editor.

Choose:

  • Keep mine and overwrite — your draft wins
  • Discard mine, use server — reload their version
  • Cancel — stay in the editor

When the other admin is known, the description names them (Another admin (… ) saved changes while you were editing. Compare and decide:).

Merchant Status​

The Status column and the Merchant Status card are the same computed badge: merchantStatus. There is no writable profiles[merchantId].status field.

Precedence (first match wins):

OrderInputBadge
1termination.status === 'terminated'Terminated
2finmatchApproval.status === 'suspended'Suspended
3billing.status is not activeSuspended
4FinMatch approval is not approvedPending
5No lender with lenderApprovals[*].status === 'approved'Pending
6liveToggle.enabled === trueLive
7otherwiseApproved

A new merchant created from Add Merchant with no Stripe customer has inactive billing, so the table shows Suspended. After Link, merchant-api copies billing from the live Stripe subscription (active / trialing → billing active) so status is not stuck Suspended waiting for a webhook. The badge then becomes Pending until FinMatch and at least one lender are approved. Unlink still marks billing inactive. Approved is eligible to go live; Live is the live toggle.

Merchant status is separate from the SDK Features kill-switch. The card subtitle is Controls commit immediately. Merchant status is separate from the SDK Features kill-switch — changing status here does not change sdkEnabled or take the merchant's integration offline. ENFORCE_STATUS_SDK_GATING is off, so changing status does not set sdkEnabled or take the integration offline. Use the App card Features toggle for that.

The card’s Status inputs checklist is Billing active, FinMatch approved, Approved lenders, and Live toggle. The computed badge row is computed from inputs above. Status-input flags: SUSPENDED — overrides all other inputs when FinMatch is suspended, and Account terminated — overrides everything when the account is terminated. Controls are FinMatch approval, Lender approvals (or No lenders assigned to this merchant.; lender-row placeholder Suspend reason (optional)), Live (Merchant is eligible to go live. when it is), and Danger zone (Terminate account; placeholder Reason for termination (recommended); Terminating blocks this company from re-registering with the same details and overrides every other status.). A terminated merchant hides those controls and shows Termination plus Reinstate account. Terminated muted copy: This account is terminated, which overrides every other status. Re-signup with the same company details is blocked, and the FinMatch / lender / live controls are hidden until the account is reinstated.

Click-to-commit​

These controls commit immediately. They do not use Edit Merchant / Save Changes. They do not flip sdkEnabled. While a write runs, the card shows Saving… and disables the controls. Success: Saved. plus a toast. Failure: Update failed. plus Failed to update merchant status: and the reason; the card reloads from the server.

ControlConfirm first?What the write doesSuccess toast
FinMatch ApproveNoPUT …/finmatch-approval with action approveFinMatch approval granted.
FinMatch SuspendAre you sure you want to suspend FinMatch approval for this merchant?action suspendFinMatch approval suspended — merchant is now suspended.
FinMatch ClearAre you sure you want to clear FinMatch approval for this merchant?action clearFinMatch approval cleared.
Lender ApproveNoPUT …/lender-approvalLender display name, then approved for this merchant.
Lender SuspendNo. Optional Suspend reason (optional) is sent only on suspend.action suspendLender display name, then suspended for this merchant.
Lender ClearClear plus the lender display name, then approval for this merchant?action clearLender display name, then approval cleared.
Live onSet this merchant LIVE? This makes the merchant status "live". Cancel unticks the box.PUT …/live enabled trueMerchant is now live.
Live offNoenabled falseLive mode disabled — merchant returned to approved.
Terminate accountPrompt: type the company name exactly (or the merchant id if the card has no name). Mismatch toast: Company name did not match — termination cancelled. This is not Delete Merchant’s Type delete to confirm.PUT …/termination action terminate plus optional reasonMerchant account terminated. The same company details are now blocked from re-registering.
Reinstate accountReinstate this terminated merchant? Re-signup with the same company details will be allowed again.action reinstateMerchant account reinstated.

Live stays disabled when the merchant is not eligible. Hover: Not eligible: plus blockers (Billing inactive, FinMatch not approved, FinMatch suspended, No approved lender, Account terminated). Enabling then failing the server check toasts Merchant is not eligible to go live yet. Resolve the blockers listed below and try again.

Terminate hides FinMatch / lender / live controls until reinstate. Delete Merchant (header) removes the profile, router row, and CORS hosts. Terminate keeps the merchant and blocks re-signup with the same company details. See Deleting a Merchant.

Environment and integrity​

Top of the App card. The Environment badge is Production (p), Staging (s), or Test (t) from the merchant profile / router. Changing it in Edit Merchant regenerates the Header snippet for that path. Do not switch a live merchant to Test to try a config change — use Sandbox preview.

Check env reloads the router, runs Check snippet, then refreshes Environment integrity. Check env title: Validate merchant environment against router and snippet detection.

BadgeMeaning
PASSProfile env, router env, and detected snippet env agree.
WARNNotes only (for example snippet env unavailable, or a router-managed /s SDK whose env comes from the router not the snippet path).
FAILMissing router row, or profile / router / detected snippet env disagree.

Sandbox preview​

On the App card, under Environment. Forks this merchant’s config onto Test for preview links while live shoppers stay on Production. App environment must stay p. Turn on sandbox, Open preview, View changes, Promote to production, Discard changes, and Turn off sandbox live on this row. Idle Loading…. Other row states: Off, On, On (paused), Status unavailable. The grid is Your admin edits save to, Preview link loads (Production build / Test build), and Sandbox contents (Matches production when the draft has not drifted). The bottom dock has Merchant configuration, FinMatch app, and Open preview. Click confirms, paused Open preview, and promote vs discard: Sandbox preview.

Do not switch Environment to Test to try a config change. Full runbook: Sandbox preview.

Header snippet​

The copy-paste <script> tag for this merchant’s current App environment. Paste it in the site <head>. The envelope button on the row opens Send Header Snippet Email (Template, To, Subject, Message). Envelope title: Share header snippet by email. The code row copy button is Copy code. Changing Environment in Edit Merchant regenerates it.

Format, env paths, and the email modal: Merchant Snippet Integration.

Detected snippet​

Detected snippet appears on the App card only when Snippet status is Snippet found and the scanned script path is for a different environment than the App badge. It shows the script tag the scan found. If it matches Header snippet, the row stays hidden.

Use this with Environment integrity when a merchant still has a Test or Staging snippet on a Production-routed shop.

Finance Assistant Proxy URL​

The App card shows the Cloudflare Worker URL the storefront uses to reach Finance Assistant. Grey text is the platform default. Orange text is a custom override.

  • Override (or Edit Override) reveals an input. Type the URL, then click Save features on the same card. That PATCH also writes financeAssistantProxyUrl.
  • Reset immediately restores the default (does not wait for Save features).

This is not Finance Assistant Settings (the Features cog). Credentials stay on that settings view.

Features​

On the App card, Features is the per-merchant storefront control. Save features commits this block. It does not use Edit Merchant / Save Changes.

SDK kill-switch​

The toggle next to the Features label is sdkEnabled. It defaults on unless the profile stored false.

When off:

  • The five feature tiles hide.
  • The storefront SDK is disabled for this merchant.
  • Disabled, Learn more. expands optional reasons (team notes, not a lock). Lead copy: Record why features are off (optional — for your team's reference). Reasons: No active Stripe billing, No snippet active on merchant site, Merchant appears to have switched to a non-supported lender.
  • A Suggested tag appears on a reason when billing is not active or snippet status is not Snippet found. Tick the box if you want it stored.

Turning the toggle back on shows the tiles again. Click Save features either way. Merchant Status does not flip this switch.

Feature tiles​

When the kill-switch is on, five tiles appear. If the Lender card has no lenders, the matrix says No lenders configured. Advisory: Assign lenders and add credit products from the Lender card before enabling features.

Each tile has a master toggle. When a tile is on: a lender matrix, + Add lender, a remove ×, and a Settings cog (hover title Settings). The cog is visible only while that tile is on. When every assigned merchant lender is already on that tile, the picker shows All lenders assigned.

TileLender columnsSettings cogTile description
Pay monthly messageOnPay monthly settings (styling tiers)Add a finance quote with "Learn more" link next to your prices.
Finance offer summaryEmbed, BadgeFinance offer summary settings (template mode, Quick-Start badge)Embed a finance summary with "Learn more" link, or float a Quick-Start badge on every page.
E-Commerce modalPopup, EmbedEcommerce modal settingsPop up or embeddable finance information designed to drive customers to a checkout.
CalculatorPopup, EmbedCalculator editorPop up or embeddable finance calculator.
Finance AssistantOnFinance Assistant settings (credentials). Envelope icon opens the Enquiries tab.Enables customers to initiate a credit application.

Inline advisories on the tiles:

  • Finance Assistant: Requires Calculator in popup or embed mode with at least one lender assigned.
  • Calculator popup: Popup mode is launched from Pay monthly message "Learn more" links.
  • E-Commerce modal popup: Popup mode is launched from Pay monthly message or Finance offer summary "Learn more" links.
  • E-Commerce modal embed: Embed mode requires a snippet on the merchant site wherever the modal should appear.
  • Calculator embed: Embed mode requires a snippet on the merchant site wherever the calculator should appear.
  • Finance offer summary embed: Requires a <div id="finance_offer_summary"> mount point on the merchant site.
  • E-Commerce modal mount: Requires mount divs on the merchant site — one per embedded lender with that lender's …-modal-container id and embed-…-modal class (copy from E-commerce Modal settings).
  • Calculator mount: Requires mount divs on the merchant site — one per embedded lender with that lender's …-calc-container id and embed-…-calculator class (copy from Calculator settings).

Embed columns also show copyable mount snippets for the merchant site.

Price mark (Pay monthly tile). Lead copy: Add a price mark so FinMatch knows which prices should show finance marketing. Ask your website manager to add it in the theme — ideally on product prices and cart totals. Mark the price the customer pays (the sale price when discounted), not a “was” / compare-at price.

Copy actions: Copy static price mark demo, Copy Shopify price mark example. Labels: Static price (demo), Example in a theme template. Shopify Dawn example — open the price snippet and add the teal line to the sellable price element. Steps (Where to edit in Shopify): Online Store, Themes → Edit code, Snippets, price.liquid. Legend: FinMatch addition. Other platforms: same idea — put data-finmatch-price on the sellable / sale price in your product and cart templates.

Embed mount table: Embed code, Copy embed code, Copy finance offer summary embed code, Copy all embed codes. FOS helper: Add one mount div on the merchant site for the finance offer summary, with class="finance-message".

Saving Features​

Save features stays disabled until something changes. It writes runtime financeFeaturesVisibility (PUT /admin/merchant-config/:id) and the profile sdkEnabled / sdkDisabledReasons / financeAssistantProxyUrl (PATCH /api/merchants/:id). Status text: Saved to runtime config + merchant profile.

Pages with embed snippets​

Read-only table on the App card. It reports pages the snippet snapshot found with calculator or modal embed codes (snippetStatus.pagesWithEmbedSnippets). It is not a Features control.

Empty copy: None detected. Run "Check snippet" to scan for embed codes. After a scan, columns are Page (URL) then one column per detected lender (hover title Calculator / Modal) with Calc ✓ and/or Modal ✓.

Refresh with Check snippet. Check snippet title: Re-check this merchant now and update the shared snapshot. Toast Checking snippet..., then Snippet check updated. or Failed to check snippet. The write updates today’s shared snapshot plus this merchant’s badge, details, and Pages with embed snippets. The App card shows Snippet checked at next to that control. The plus toggle title is Show snippet details. Expanded title: Hide snippet details. Badge meanings: Snippet status. How the snapshot is built: Snippet snapshot.

Contact cards​

Overview has two cards titled Contact. Type is read-only: Primary Contact on the first card, Technical on the second. Neither Type row becomes an input.

Empty First Name, Last Name, Email, and Phone show N/A. Admin → Contacts is a stub; these cards are the live edit path. See Contacts.

Save / commit​

  1. Click Edit Merchant in the page header. Toast: Edit mode enabled. Click "Save Changes" when done. The header button becomes Save Changes and Cancel appears.
  2. Both Contact cards swap First Name, Last Name, Email, and Phone into inputs. Display N/A becomes an empty input. Email uses an email input; Phone uses a tel input. There are no placeholders on these fields.
  3. Click Save Changes. The header button shows Saving... and is disabled until the request finishes. A second click while Saving... is ignored.
  4. Save writes the Contacts API first (create, update, or delete per card), then PUTs the merchant profile with contactIds only. The profile contacts object is cleared on that write. PII is not stored in merchants.json.
  5. Success toast: Merchant updated successfully! Edit mode exits. The cards reload from the Contacts API. Still-empty fields show N/A again.

Cancel discards the draft and restores the last loaded values (including N/A).

What Save does per card​

Trimmed values. If any of First Name, Last Name, Email, or Phone is non-empty, that card’s record is created (no contactIds yet) or updated (primary / technical). If all four are empty and a contact id existed, that record is deleted and the matching contactIds slot is cleared.

No admin toast requires email or phone. Contacts-api create only requires the merchant id. The save-conflict popup does not list contact fields.

Contact write failures surface as the API message, or Failed to update merchant. Please try again. Cross-merchant guards still apply: Save blocked: you started editing … but are now viewing … and Save blocked: edit started on … but detail view now shows …. Missing draft: Required form fields not found. Please refresh and try again.

Inline Editing​

Enabling Edit Mode​

  1. Navigate to the merchant page
  2. Click "Edit Merchant" in the page header (next to Delete Merchant)
  3. Fields on the Merchant card and both Contact cards become editable
  4. Button changes to "Save Changes"
  5. A "Cancel" button appears
  6. Toast: Edit mode enabled. Click "Save Changes" when done.

Editing Fields​

When in edit mode, the following fields become editable:

  • Company Name - Text input
  • Domain - URL input (automatically adds https:// if missing)
  • Company Number - Text input (optional)
  • Contact First Name, Last Name, Email, Phone on both Contact cards (see Contact cards)

Note: Merchant ID can be edited. Changes are validated and processed via the merchant ID change flow.

Saving Changes​

  1. Make your edits
  2. Click "Save Changes" (the button shows Saving... while the request runs)
  3. Contact cards sync through the Contacts API, then the merchant profile is saved
  4. Page refreshes with updated data
  5. Edit mode exits automatically
  6. Success toast: Merchant updated successfully!

Canceling Edits​

  1. Click "Cancel" button
  2. All changes are discarded
  3. Page reverts to read-only view
  4. Original data is restored

Stripe Billing Management​

The Stripe Billing card has a Link/Unlink button in the header:

  • "Link" - Shows when no Stripe customer is linked
  • "Unlink" - Shows when a Stripe customer is already linked (green background)

Linking a Stripe Customer​

  1. Click "Link" on the Stripe Billing card header (or, on the James-only Settings preview, FinMatch Account → Stripe first row Link)
  2. Link Stripe Customer opens showing the live Stripe customer list
  3. Search by Customer ID, Name, or Email (also Subscription ID; the placeholder is still Search by Customer ID, Name, or Email...). Ltd and LIMITED are the same legal name, so ACCESS TRAINING (WALES) LIMITED prefills and matches Access Training (Wales) Ltd. A unique match sorts to the top. Confirm in the picker. Do not auto-link.
  4. View customer details:
    • Customer ID
    • Name
    • Email
    • Subscription Plan
    • Current FinMatch Merchant (if linked)
  5. Click "Link" button next to desired customer
  6. Connection is saved. Billing hydrates from the live Stripe subscription (active / trialing → billing active)
  7. Page refreshes showing linked customer

Unlinking a Stripe Customer​

  1. Click "Unlink" on the Stripe Billing card header (or James-only FinMatch Account → Stripe first row Unlink)
  2. Confirm the action
  3. Connection is removed. Billing is marked inactive.
  4. Page refreshes showing "Not linked" status

Universal Card Editing Layer​

The merchant page uses a universal editing layer that can be extended to other cards:

Current Support​

  • ✅ Merchant card - Full inline editing
  • ✅ Stripe Billing card - Link/Unlink functionality
  • ✅ Contact cards - Inline editing (saved through Contacts API sync)
  • ✅ Lender card - Names and credit products (derived profile mirror; not a setup wizard)
  • ✅ App card - Environment integrity (Check env), snippet checks, sandbox preview, Finance Assistant Proxy URL, Features (Save features, not Edit Merchant), Pages with embed snippets (read-only)
  • There is no Lender setup card. Do not document one.

Other Overview cards use the same inline editor when they have editable fields.

Left nav on merchant detail is the same shell as the rest of admin (admin/js/nav-builder.js). Live paths sit at the root of admin.finmatch.io (for example /merchants/?id=…, not /admin/merchants/). See Admin URLs and Live admin navigation.

The breadcrumb shows:

  • Merchants → {Merchant Name}

Clicking "Merchants" returns to the merchants table.

Calculator preview layout bands​

On merchant calculator / modal preview, the width notice summary titles are Too narrow, Compact layout, Standard layout, and Maximum width. Hints: Below the Compact floor (feature under 304px)., Includes a breakpoint at 349px feature (381px merchant column and below)., Normal spacing and typography., The end of the standard range. Stops include Too narrow and Standard. Details table: Preview width details. Compact is one layout with two columns, not two named breakpoints. Below floor copy: Feature is Npx — Compact starts at 304px.

BreakpointMerchant columnFinance feature widthFeature content paddingDefault field font size
Comfortable382–451px350–419px15px14px
Tight336–381px304–349px10px12px

The width notice table highlights the active column. Changing App → Environment is not how you preview a layout — use Sandbox preview for config forks.

API Integration​

Update Merchant​

Endpoint: PUT /api/merchants/{merchantId}

Request Body:

{
"merchantName": "Updated Company Name",
"companyName": "Updated Company Name",
"domain": "https://updated-domain.com",
"companyNo": "12345678"
}

Response: Updated merchant object

Endpoint: PUT /api/merchants/{merchantId}/stripe-connection

Request Body:

{
"stripeCustomerId": "cus_xxxxx"
}

Response: Updated merchant object with Stripe connection

Endpoint: DELETE /api/merchants/{merchantId}/stripe-connection

Response: Updated merchant object without Stripe connection

Best Practices​

  1. Always Save: Click "Save Changes" after editing, or "Cancel" to discard
  2. Validate Domain: Domain is automatically normalized (adds https:// if missing)
  3. Check Stripe Status: Verify Stripe customer is correctly linked before saving
  4. Use Search: Use search in Stripe linking modal to find customers quickly
  5. Review Changes: Page refreshes after save - verify changes are correct

Troubleshooting​

Critical: mixed merchant on screen (stale client state)

If the URL shows one FinMatch ID (e.g. ?id=M524059) and the title matches that merchant, but contacts, lenders, header snippet, features matrix, or Admin tab actions (History, JSON, Quote) appear to belong to another merchant, treat this as a client-side state bug, not corrupt merchants.json.

Why it happened (historical): showMerchantDetails performs async work (contacts API, lender config). Overlapping calls could leave window.currentMerchantDetails, window.currentMerchantSourceConfig, or parts of the DOM out of sync with the visible URL. Saves could then hit the wrong merchant ID (e.g. spurious “Merchant with ID … already exists”). Navigation without re-showing the previous merchant and sequence guards on detail render reduce this.

Admin tab / sub-pages: Actions that open History, JSON, Quote, or Finance Assistant settings now resolve the merchant from ?id= first, then fall back to window.currentMerchantDetails, so they stay aligned with the address bar even if the global lags briefly.

If it still looks wrong after a hard refresh: Verify data (e.g. GET /api/merchants/{id} and contact IDs) — wrong contactIds can legitimately point another merchant’s contact row.

Operational workaround: Hard refresh the page (Cmd+Shift+R / Ctrl+Shift+R) or navigate away and open the merchant again from the table.

Edit Mode Not Working​

Check:

  1. Merchant page is loaded (window.currentMerchantDetails exists)
  2. JavaScript console for errors
  3. Network tab for API call failures

Check:

  1. Stripe customers are loaded (stripeCustomers array exists)
  2. API endpoint is accessible
  3. Merchant ID is correct

Check:

  1. URL is the query form (/merchants/?id=M000101 on production)
  2. nav-builder.js is loaded
  3. Navigation uses getAdminBasePath() / getMerchantsAppBase(), not a hardcoded /admin/ prefix