Skip to content

Merchant branding

MER-08 local sandbox supports merchant display-name/logo proposals in the merchant portal. Owner/Admin members submit their current branding version, a name and optional PNG. Omitted/null logo means removal on approval; the portal preserves the current logo unless replacement/removal is selected. Read-only members can inspect their merchant’s proposals but cannot change them. Integration API keys cannot submit or approve branding.

An MFA-authenticated operations checker, distinct from the submitter, approves or rejects with a reason. The approved identity stays active while a proposal is pending or rejected. One pending proposal is allowed per merchant. Submission retries with the same key/normalized payload return the current proposal; changed payloads conflict. A terminal decision can only replay the same reviewer/key/decision/reason. Audit records and terminal proposal payloads cannot be rewritten.

Upload a PNG no larger than 48 KiB and 256 pixels per side. The server checks dimensions before decoding, strips metadata by re-encoding and limits the sanitized result to 32 KiB. SVG, external image URLs, malformed files and control characters in names are rejected. These are bounded local storage adapters; production object storage, content scanning, brand-rights and KYB approval remain release requirements.

Each new payment or payment link captures the approved name and immutable branding reference. New branding never changes an existing checkout/receipt or idempotency replay. The logo proxy verifies that payment’s checkout capability; PDF receipts embed the approved image and retain exact payment/refund amounts. No external image request is made. Checkout still works without JavaScript and remains under 150 KB including its logo.

Merchant identity routes: GET/POST /portal/merchants/{merchant}/branding, GET /portal/merchants/{merchant}/branding/{id}. Use fresh X-Futa-Membership-Version; submission requires Idempotency-Key. Staff routes: GET /staff/branding, GET /staff/branding/{id}, POST /staff/branding/{id}/decision with an idempotency key. OpenAPI 0.14.0 defines request/response contracts.