All checks were successful
Practical field-by-field intake doc for the first merchants — maps each question to its DB column so the call → admin UI is 1:1. Includes a section on marketing-material reuse aimed at franchisees, with written-authorization clauses for the future marketing module. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
324 lines
15 KiB
Markdown
324 lines
15 KiB
Markdown
# Merchant Intake Checklist — First Store Onboarding
|
||
|
||
A practical list of every datum to collect from a new merchant before
|
||
flipping their first store live on the loyalty platform. Each field is
|
||
mapped to the actual database column behind it, so what you collect in
|
||
the intake meeting maps 1:1 to the admin UI you'll fill in afterwards.
|
||
|
||
Use this as a sharable form, or as a script for the first onboarding
|
||
call.
|
||
|
||
## How to use this doc
|
||
|
||
1. Send the merchant the **"What you need to send us"** sections (1–7)
|
||
ahead of the kickoff call, so they have logos / VAT numbers / staff
|
||
info ready.
|
||
2. Use the **business decisions** sections (3, 5) to drive the actual
|
||
conversation — these are the choices only they can make.
|
||
3. After the call, populate the admin UI in the order below; the model
|
||
enforces this dependency chain (merchant → store → loyalty program →
|
||
staff).
|
||
|
||
## TL;DR — what you absolutely cannot launch without
|
||
|
||
- Legal business name + VAT number + business address
|
||
- Owner email (login + invitation target)
|
||
- Store name + store code (URL slug)
|
||
- Loyalty program type (stamps / points / hybrid) + reward structure
|
||
- Privacy policy URL (GDPR)
|
||
- Brand logo (square PNG, transparent bg)
|
||
- Brand primary color (hex)
|
||
- At least one staff PIN
|
||
|
||
Everything else can be filled in iteratively post-launch.
|
||
|
||
---
|
||
|
||
## 1. Business identity (the **merchant** entity)
|
||
|
||
One per merchant — feeds the `merchants` table.
|
||
|
||
| Field | Required? | DB column | Notes |
|
||
|---|---|---|---|
|
||
| Legal business name | ✅ | `name` | Appears on invoices + email footers |
|
||
| Trading name (if different) | optional | (use `name`) | The name customers see |
|
||
| Business description | recommended | `description` | One-line tagline |
|
||
| VAT / tax number | ✅ EU | `tax_number` | LU format: `LU12345678` |
|
||
| Business registration number | ✅ | `tax_number` (or separate field if available) | LU: `RCS Lxxxxxx` |
|
||
| Business address | ✅ | `business_address` | Street + city + ZIP + country |
|
||
| Primary contact email | ✅ | `contact_email` | Where invoices + platform alerts go |
|
||
| Primary contact phone | recommended | `contact_phone` | For support callbacks |
|
||
| Website URL | optional | `website` | Linked from storefront footer |
|
||
|
||
## 2. Owner / admin account
|
||
|
||
The human who logs in as merchant owner.
|
||
|
||
| Field | Required? | Notes |
|
||
|---|---|---|
|
||
| Full name | ✅ | Shown on audit logs |
|
||
| Email (login) | ✅ | Invitation goes here — must be deliverable |
|
||
| Phone | recommended | |
|
||
| Preferred UI language (en / fr / de / lb) | ✅ | Drives the admin dashboard's language |
|
||
|
||
## 3. Each store / point of sale
|
||
|
||
Repeat per location. Feeds the `stores` table. The platform supports
|
||
multi-location from day one — even single-store merchants live in this
|
||
shape.
|
||
|
||
| Field | Required? | DB column | Notes |
|
||
|---|---|---|---|
|
||
| Store name | ✅ | `name` | E.g. "Fashion Hub City Concorde" |
|
||
| Internal store code | ✅ | `store_code` | UPPERCASE slug, no spaces — used in URLs (`/store/FASHIONHUB`). Pick once, hard to change |
|
||
| Subdomain | optional | `subdomain` | E.g. `fashionhub.rewardflow.lu` for the storefront |
|
||
| Physical address | ✅ | `business_address` | Per-location, overrides merchant address if different |
|
||
| Local contact email | optional | `contact_email` | Overrides merchant-level if store has its own |
|
||
| Local phone | optional | `contact_phone` | |
|
||
| Default tax rate % | optional | `letzshop_default_tax_rate` | Defaults to 17% (LU standard VAT) |
|
||
| Default UI language | ✅ | `default_language` / `dashboard_language` | What the staff terminal opens in |
|
||
| Customer-facing languages | ✅ | `storefront_languages` | Subset of `{en, fr, de, lb}` for LU |
|
||
| Opening hours | optional | not in schema yet | Useful context for support |
|
||
|
||
## 4. Staff (per store, for each cashier)
|
||
|
||
Feeds `users` + `store_users` (invitation flow).
|
||
|
||
| Field | Required? | Notes |
|
||
|---|---|---|
|
||
| Full name | ✅ | Shown on the tablet PIN screen |
|
||
| Email | ✅ if web access needed | Owner-only PIN-on-tablet operators don't need an account |
|
||
| 4-digit PIN | ✅ | They pick it; you set it on `/admin/loyalty/pins`; staff types it on the tablet for every transaction |
|
||
| Employee ID / staff number | optional | Shown alongside name in audit trail |
|
||
| Role | ✅ | One of: `merchant_owner`, `store_admin`, `store_staff` |
|
||
|
||
## 5. Loyalty program — the **business decisions**
|
||
|
||
One program per merchant. Feeds the `loyalty_programs` table. This is
|
||
where most of the kickoff conversation happens — help them choose.
|
||
|
||
### 5.1 Program type
|
||
|
||
| Type | When to recommend |
|
||
|---|---|
|
||
| **Stamps** | "Buy 10, get 1 free" model. Cafés, hairdressers, lunch spots, anywhere with a clear unit of sale |
|
||
| **Points** | "Earn X points per €, redeem on rewards menu". Retail with varied basket sizes |
|
||
| **Hybrid** | Both at once. Rare for v1 — defer |
|
||
|
||
### 5.2 If stamps
|
||
|
||
| Field | Typical value | Notes |
|
||
|---|---|---|
|
||
| `stamps_target` | 10 | Stamps to fill a card |
|
||
| `stamps_reward_description` | "Free coffee" | Customer-facing reward name |
|
||
| `stamps_reward_value_cents` | 500 (= €5) | Internal value for analytics |
|
||
|
||
### 5.3 If points
|
||
|
||
| Field | Typical value | Notes |
|
||
|---|---|---|
|
||
| `points_per_euro` | 1, 5, or 10 | The earn rate |
|
||
| `welcome_bonus_points` | 50–100 | Awarded on enrollment, makes new customers feel rewarded |
|
||
| `minimum_purchase_cents` | 500 (= €5) | Minimum sale to earn anything — kills chewing-gum farming |
|
||
| `minimum_redemption_points` | 100 | Minimum points to redeem anything |
|
||
| `points_expiration_days` | 365 | After this, unredeemed points lapse |
|
||
| `points_rewards` (JSON list) | see below | The reward menu |
|
||
|
||
Each entry in `points_rewards` needs: `name`, `points_required`,
|
||
`description`, `value`. Example:
|
||
|
||
```json
|
||
[
|
||
{"name": "€5 voucher", "points_required": 100, "description": "Apply at checkout", "value": 500},
|
||
{"name": "€15 voucher", "points_required": 250, "description": "Apply at checkout", "value": 1500},
|
||
{"name": "Free product XYZ", "points_required": 400, "description": "Pickup in store", "value": null}
|
||
]
|
||
```
|
||
|
||
### 5.4 Anti-fraud knobs
|
||
|
||
| Field | Default | When to change |
|
||
|---|---|---|
|
||
| `cooldown_minutes` | 0 | Set 30–60 for high-volume cafés to stop staff abuse |
|
||
| `max_daily_stamps` | 10 | Lower if low-volume; this caps a single card's daily stamps |
|
||
| `require_staff_pin` | true | Recommended `true` for >1 staff member; tablet handles offline |
|
||
|
||
### 5.5 Cross-location behaviour
|
||
|
||
This is on the merchant settings (`merchant_loyalty_settings`), not the
|
||
program:
|
||
|
||
| Setting | Default | Notes |
|
||
|---|---|---|
|
||
| `allow_cross_location_redemption` | ✅ on | Earn at Store A, redeem at Store B? Yes for chains, no for indies sharing infrastructure |
|
||
| `allow_void_transactions` | ✅ on | Some merchants disable for fraud control |
|
||
| `require_order_reference` | off | Useful if integrating with their POS/till — links each loyalty transaction to a sale receipt |
|
||
|
||
## 6. Branding assets
|
||
|
||
Collect as **files**, not links — we host them.
|
||
|
||
| Asset | Format | Size | DB field |
|
||
|---|---|---|---|
|
||
| Primary logo | PNG, transparent bg | 600×600 min, square | `logo_url` |
|
||
| Hero banner | JPG / PNG | 1200×400 | `hero_image_url` |
|
||
| Brand primary color | Hex | e.g. `#7C3AED` | `card_color` |
|
||
| Brand secondary color | Hex | e.g. `#10B981` | `card_secondary_color` |
|
||
| Card display name | Short string | e.g. "Fashion Hub Rewards" | `card_name` |
|
||
|
||
The logo doubles as the **Google Wallet pass logo**, so 600×600 minimum
|
||
with transparent background is the practical floor — Google enforces
|
||
clarity.
|
||
|
||
## 7. Compliance & GDPR
|
||
|
||
Required for go-live, especially in Luxembourg.
|
||
|
||
| Field | Required? | DB field | Notes |
|
||
|---|---|---|---|
|
||
| Privacy policy URL | ✅ EU | `privacy_url` | Linked from enrollment form. Their site or your CMS |
|
||
| Loyalty T&C | ✅ | `terms_text` or `terms_cms_page_slug` | Inline text or CMS page slug. Cover: how points work, expiry, when rewards can be revoked, contact for issues |
|
||
| DPO contact (if applicable) | depends on size | not in schema | LU rule: >5k customer records → designated DPO |
|
||
| Marketing consent | implicit | (enrollment form has the checkbox) | Required if you'll send birthday / re-engagement emails |
|
||
|
||
## 8. Tablets (if you provide the POS hardware)
|
||
|
||
| Question | Why |
|
||
|---|---|
|
||
| How many tablets per store? | Determines how many device pairings to set up |
|
||
| Existing Android tablets? Model + OS? | Need Android 8.0+ (API 26), Play Services. Fire OS won't work |
|
||
| Source tablets for them? | Lenovo Tab M11 or Samsung Galaxy Tab A9 (€100–200 each) — known good |
|
||
| Mounting? Counter stand or wall? | Affects power/charging plan |
|
||
| Wifi at each location | Offline queue handles brief outages, not all-day. Confirm wifi at the counter |
|
||
|
||
## 9. Initial data migration (nice-to-have)
|
||
|
||
| Question | Why |
|
||
|---|---|
|
||
| Existing loyalty customer list? | If they're switching from another system or paper cards, bulk-import via CSV (email, name, balance, enrollment date) |
|
||
| Approximate initial customer count? | Sizes capacity discussion |
|
||
| Current monthly transaction volume | Same |
|
||
|
||
## 10. Marketing material reuse (for the future marketing module)
|
||
|
||
Many of our early merchants are franchisees (e.g. fashion, food, beauty
|
||
chains) and receive a steady stream of campaign material from their
|
||
franchisor — banners, email templates, social posts, seasonal
|
||
promotions. The future marketing module aims to **republish** this
|
||
material at the local-store level: a franchisee shouldn't have to design
|
||
their own birthday email when corporate already produces five a year.
|
||
|
||
At intake, ask permission-style questions so we have a green light
|
||
*before* we build the ingestion pipeline. We are not asking them to do
|
||
anything today — we're inventorying sources and getting written
|
||
authorization.
|
||
|
||
### 10.1 Existing marketing material — what to inventory
|
||
|
||
| Question | Why we ask |
|
||
|---|---|
|
||
| Is the merchant a franchisee? Of which brand? | Determines which corporate sources may exist |
|
||
| Do they receive a regular **email newsletter** from the franchisor? Forward us 3–5 recent ones | Lets us assess content type, frequency, language mix, formatting |
|
||
| Is there a **franchisor extranet / portal** where campaigns are published? URL + their login (read-only) | The ideal source: structured, dated, multilingual |
|
||
| **Social handles** the franchisor posts under (Instagram, Facebook, LinkedIn) | Public, scrapeable, but low signal-to-noise |
|
||
| **WhatsApp / Telegram broadcast groups** they're part of | Sometimes the only channel — manual forwarding may be the only path |
|
||
| **PDF / image packs** received by email or download link | Common for seasonal campaigns; ask them to share the latest |
|
||
| Frequency: weekly / monthly / per-campaign | Sizes ingestion volume |
|
||
| Languages the franchisor publishes in | Influences whether we re-use or re-translate |
|
||
|
||
### 10.2 Reuse authorization — get this in writing
|
||
|
||
Before any scraping or republishing happens, the merchant must
|
||
authorize us. Capture in the intake (a checkbox on the onboarding form
|
||
or a clause in the merchant agreement):
|
||
|
||
- ✅ **"I authorize {platform} to ingest marketing material I forward,
|
||
share, or grant access to, and to republish it on my behalf via the
|
||
loyalty/marketing channels offered by the platform."**
|
||
- ✅ **"I confirm I have the right (as franchisee) to use this material
|
||
in local marketing, and that the franchisor's brand guidelines or
|
||
licensing terms allow republication via third-party platforms."**
|
||
- ✅ Forward us the **franchisor's brand guidelines / co-op marketing
|
||
policy** if one exists. This determines what we can and can't do
|
||
safely.
|
||
|
||
### 10.3 Practical capture paths (least-effort first)
|
||
|
||
For the future marketing module, in order of how easy they are to
|
||
implement:
|
||
|
||
1. **Forward-to-inbox.** Give the merchant a per-store email alias like
|
||
`marketing+FASHIONHUB@rewardflow.lu`. They forward any
|
||
franchisor email there. We parse + categorize.
|
||
2. **Drag-and-drop upload.** A page where the merchant pastes a URL or
|
||
uploads a PDF/image; we OCR/parse + queue for review.
|
||
3. **Authenticated extranet pull.** They give us read-only credentials
|
||
to the franchisor portal; we poll. Highest signal, highest legal
|
||
bar — needs explicit franchisor clearance.
|
||
4. **Public social scrape.** Last resort, only for material the
|
||
franchisor publishes openly. Respect robots.txt and platform ToS.
|
||
|
||
### 10.4 Republish channels we can plug into
|
||
|
||
What we *do* with this material once captured:
|
||
|
||
- **Storefront banner** rotation (already exists in CMS)
|
||
- **Loyalty welcome / re-engagement / birthday emails** (loyalty module
|
||
v1 — fixed templates; marketing module v2 — campaign-driven)
|
||
- **SMS / WhatsApp** (marketing module v2, deferred)
|
||
- **Push notifications** via the Wallet pass (Google Wallet supports
|
||
this — class-level updates push to every customer's phone)
|
||
|
||
### 10.5 Red flags to surface back to the merchant
|
||
|
||
If during intake any of these come up, flag for legal review **before**
|
||
we touch the material:
|
||
|
||
- Material contains the franchisor's trademarks in a way the franchisee
|
||
doesn't have explicit license to use
|
||
- Material references third-party brands (co-promotions with other
|
||
brands) — those brands didn't consent to our platform
|
||
- Material is in a language the merchant's customer base doesn't speak
|
||
— we don't auto-translate without permission
|
||
- Personal data of named individuals (testimonials, photos) — GDPR
|
||
consent chain may not extend to us
|
||
|
||
## 11. Test customer for verification
|
||
|
||
Before sending the merchant to real customers, run the 8 user-journey
|
||
tests with two test accounts:
|
||
|
||
- The merchant owner's personal email
|
||
- A team member or your own email
|
||
|
||
The end-to-end test list lives in
|
||
[`app/modules/loyalty/docs/user-journeys.md`](../modules/loyalty/user-journeys.md).
|
||
|
||
---
|
||
|
||
## Suggested fill-in order in the admin UI
|
||
|
||
The model has hard FK dependencies, so do it in this order:
|
||
|
||
1. **Create the merchant** (`/admin/merchants/new`) — needs § 1.
|
||
2. **Invite the owner** — needs § 2.
|
||
3. **Create the store(s)** (`/admin/stores/new`) — needs § 3.
|
||
4. **Create the loyalty program** (`/admin/loyalty/programs/new`) —
|
||
needs § 5 + § 6 + § 7.
|
||
5. **Create staff PINs** (`/admin/loyalty/pins/new` per store) — needs
|
||
§ 4.
|
||
6. **Configure cross-location settings**
|
||
(`/admin/loyalty/settings/{merchant}`) — needs § 5.5.
|
||
7. **(Optional) bulk-import customers** via CSV — needs § 9.
|
||
8. **Pair tablet(s)** at each store (`/admin/loyalty/terminals/new`,
|
||
QR or manual) — needs § 8.
|
||
|
||
---
|
||
|
||
## Reference
|
||
|
||
- Database models: `models/database/{merchants,stores,loyalty}.py`
|
||
- Loyalty production launch plan: [`app/modules/loyalty/docs/production-launch-plan.md`](../modules/loyalty/production-launch-plan.md)
|
||
- Go-live readiness snapshot: [`loyalty-go-live-readiness.md`](loyalty-go-live-readiness.md)
|
||
- User-journey E2E tests: [`app/modules/loyalty/docs/user-journeys.md`](../modules/loyalty/user-journeys.md)
|