Location Entity — User Flow Review

Assessing Yuvraj's #678 testing scope against user flows and briefing Paras for #679 integration work

Date: 24 April 2026 Prepared for: Qamar (PM) Yuvraj Test Report: 09:05 BST

Test Summary

Yuvraj has completed 17 test cases + 5 screen recordings covering the Location Entity feature (#678: Kiosk Management with Location Assignment).

Test Report Timestamp: 24 Apr 2026, 09:05 BST

✅ Well Covered (9/17 tests)

✓ Test 3 Kiosk Creation with New Location

Create location on-the-fly during kiosk creation. Covers form flow and data persistence.

✓ Test 5 Kiosk Reuses Existing Location

Verify same location can be assigned to multiple kiosks. Critical for GASDS aggregation.

✓ Tests 7–9 Location Immutability

Edit creates new location doc; old kiosks point to original. Essential for HMRC audit trail.

✓ Test 10 Required Field Validation

Create button blocked until name and location selected. Form validation working.

✓ Tests 11–12 Geo Validation Edge Cases

Latitude 0, Longitude 0 allowed. Invalid ranges (lat 91, lng 181) rejected with inline errors.

✓ Tests 13–14 Error Handling – Network Failures

Location API failure shows inline error; kiosk save failure shows toast. Graceful degradation.

✓ Test 15 Stale Data Recovery

Editing kiosk with missing/invalid location mapping triggers warning and blocks save.

✓ Test 16 Role-Based Access Control

Non-admin accounts cannot create/edit kiosks or locations. RBAC enforced.

✓ Test 17 Firestore Indexing

Post-deployment verification: no "missing index" error when loading locations list.

⚠️ Partially Covered or Unclear (2 items)

? Spec Field isCommunityBuilding Toggle

Spec requires boolean toggle for GASDS community building rules. Tests mention "fill required fields" but no explicit mention of testing this toggle. Needs confirmation from Yuvraj: Is this included in test 10 (required field validation)?

? Edge Case Postcode Format Validation

Test 10 checks "required" but no explicit test for UK postcode regex or edge cases (outward/inward code split, spaces). HMRC-critical field for GASDS eligibility.

? Default Country Field Default

Spec says default is "UK". No explicit test for this. Confirm in test 3 (create location) that country defaults to UK.

❌ Out of Scope for #678 (Not Yuvraj's Job)

  • Two-Button Front Door [Donation]/[Payment] — P0 priority but separate workstream. This UI gate determines campaign compliance mode. Not part of Location Entity creation.
  • Campaign Compliance Lock — P0, separate. Sets campaign to Donation Mode or Activity Mode. Needed for GASDS eligibility logic.
  • Donation Flow Integration — This is #679 (Paras's work). At payment time, donation must inherit location_id and location_snapshot from kiosk.
  • Magic Link Page Location Context — Future enhancement. Magic link page could show location context but not required for launch.
  • GASDS Cap Tracking & Admin Dashboard — Growth-tier feature. Requires donation aggregation logic to be working first.

Question for Yuvraj

Before marking #678 as ready for merge, please confirm:

  • 1 Is isCommunityBuilding toggle tested? (Test 10: required field validation)
  • 2 Are postcode format validations (UK regex) included in the error handling tests?
  • 3 Can you confirm country field defaults to "UK" in test 3?
  • 4 Are the 5 screen recordings linked in the PR description for audit trail?

Location Entity in User Flows

The Location Entity (#678) is the foundation. It doesn't appear directly in donor-facing flows but is critical infrastructure for GASDS compliance and reporting.

Flow 1: Kiosk Tap-to-Pay (Donation Entry Point)

Step 0 (TO-BE): Two-Button Front Door
Donor sees [Payment] | [Donation]. Determines campaign compliance mode.
⚠️ P0, NOT BUILT. Location doesn't interact directly here.
Step 1–2: Campaign List & Amount
Donor selects campaign and amount at a physical kiosk.
🔗 Location Touch: The kiosk has location_id stored in the kiosk document.
Step 3: Gift Aid Eligibility Check
System asks: "Is this a UK domicile donation?" (postcode check).
🔗 Location Touch: Kiosk's location.postcode helps pre-populate or validate donation origin.
Step 4–5: Payment & Webhook
Stripe payment succeeds. Webhook creates donation record.
🔗 Location Touch: Donation must inherit location_id and location_snapshot from kiosk. This is #679's job (Paras).
Step 6: Thank You + QR Magic Link
Kiosk displays "Thank you for £X donation to [Campaign]" and shows QR code.
🔗 Future: Magic link page could show "Donated at [Location Name]" but not required for MVP.

Flow 2: Magic Link – Gift Aid Capture

Step 1: Donor Scans QR → /link/[token]
QR code from kiosk screen links to magic link page.
🔗 Location Context: The magic link page could reference the kiosk's location (optional enhancement).
Step 2: Gift Aid Form & Home Address
Donor enters home address postcode and Gift Aid declaration.
⚠️ Important: Donor's HOME postcode ≠ Kiosk location postcode. Different purposes. Not stored as location_snapshot.
Step 3: Declaration Saved
Gift Aid eligibility verified. Declaration saved to donation record.
The kiosk's location_id on the donation enables GASDS aggregation at report time.

Admin Flows

Kiosk Management: Create/Edit Kiosks with Location
Admin → Kiosks → Add/Edit Kiosk. Assign location (new or existing).
✅ This is what Yuvraj is testing in #678.
Location Management: (Future)
Admin screen to view/edit/delete location records across the org. Not in MVP.
Campaign Management: Run Campaigns at Locations
Campaign → select kiosk → kiosk carries campaign's location and compliance mode.
GASDS Dashboard: (Growth Tier)
Queries: "How much GASDS has building X used this tax year?" Uses location_id as grouping key.
⚠️ Depends on donations having location_id and location_snapshot (Paras's #679).

GASDS 3-Entity Architecture

Entity Represents Key Fields Governance
Location Physical building name, postcode, city, lat, lng, isCommunityBuilding First-class Firestore collection (immutable, new doc on edit)
Kiosk Device at location name, location_id (reference) Points to Location. Can be reused across kiosks.
Campaign Fundraising initiative name, complianceMode (Donation|Activity), kiosk assignments Carries compliance mode. Only Donation Mode is GASDS-eligible.
Donation Transaction record amount, location_id, location_snapshot, campaignMode, taxYear Inherits location_id and snapshot from kiosk at payment time. Immutable after creation.

Data Capture Matrix: Location Fields

Where location data is captured or used across flows:

Flow Step Data Captured Source Field Type
Admin: Kiosk Creation kiosk.location_id Admin selects location Reference (FK to locations/)
Flow 1: Payment Webhook donation.location_id Read from kiosk document Reference (FK)
Flow 1: Payment Webhook donation.location_snapshot { name, postcode, city } Fetch locations/{location_id} at payment time Embedded document (denormalized)
Flow 1: Gift Aid Check Kiosk location's postcode (context only) Kiosk document For pre-population, not validation
GASDS Reporting Sum by location_id, tax year, compliance mode Donation queries Aggregation key

Issue #679: Donation Location Handling

Once Yuvraj's #678 is merged, your job (#679) is to make donations actually use the Location entity. This is the "connecting piece" between admin setup and GASDS compliance.

The 8 Critical Points You Must Know

1. Hybrid Approach: location_id + location_snapshot

Each donation must store both:

  • location_id — Reference to the Location document (FK). Used for GASDS aggregation queries. Enables "which building?"
  • location_snapshot — Embedded snapshot { name, postcode, city }. Immutable copy taken at payment time. Resilient to future location edits.

Why both? location_id enables efficient queries; location_snapshot ensures audit trail (if location is later edited, the donation still shows what it was at payment time).

2. Fetch-Before-Write Pattern

At donation creation time, your payment webhook must:

Order of operations:

  1. Read kiosk document → get kiosk.location_id
  2. Fetch locations/{location_id} from Firestore
  3. Extract { name, postcode, city } into location_snapshot object
  4. Create donation with both location_id and location_snapshot

Do NOT skip the fetch. If you try to guess or defer, you risk stale or missing data.

3. Snapshot Immutability

Once a donation is created, its location_snapshot must NEVER be updated.

Example: If an admin later edits the location (which creates a new location document), old donations keep their original snapshot. New donations at that location get the new snapshot.

Code rule: location_snapshot is write-once. No update operations on it.

4. Postcode is HMRC-Critical

location_snapshot.postcode must ALWAYS be present. This field is needed for:

  • GASDS Eligibility: HMRC checks building location postcodes against GASDS list
  • R68 CSV Export: The tax report to HMRC includes building postcode
  • Audit Trail: Postcode is the primary identifier of a building

Validation: When writing a donation, check that location_snapshot.postcode is not null/empty. If missing, reject donation creation.

5. GASDS Aggregation Depends on location_id

To calculate "How much GASDS has this building used this tax year?", the system queries:

db.collection('donations') .where('location_id', '==', 'locations/abc123') .where('taxYear', '==', 2026) .where('campaignMode', '==', 'donation') .get() .then(snap => { const total = snap.docs.reduce((sum, doc) => sum + doc.data().amount, 0); console.log(`Building used £${total} GASDS`); });

The location_id is the grouping key. Without it, GASDS reporting is impossible. Do not allow donations without a location_id.

6. Campaign Mode Matters for GASDS

Only Donation Mode campaigns are GASDS-eligible. Activity Mode (madrasah fees, event registrations) can NEVER be claimed under GASDS, even at the same location.

Your donation record needs both fields:

  • location_id — "At which building?"
  • campaignMode — "Is this a Donation or Activity?"

GASDS calculations filter by campaignMode == 'donation'. This ensures Activity Mode donations don't count toward the £8k cap.

7. Timing: Depends on #678

You can prepare your test plan and architecture now, but you cannot start coding until #678 is merged.

Ready-to-code checklist:

  • ✓ #678 merged to main
  • ✓ Locations collection exists in Firestore
  • ✓ At least one test location created by admin
  • ✓ At least one test kiosk with location_id assigned

Once those boxes are ticked, you have everything you need.

8. Validation Rules for Donation Creation

Before writing a donation, validate:

  • location_id must exist and reference a valid location document
  • location_snapshot.name must be non-empty
  • location_snapshot.postcode must be non-empty and match UK postcode regex
  • location_snapshot.city must be non-empty
  • campaignMode must be 'donation' or 'activity'
  • taxYear must be set (derived from donation date)

If any validation fails, reject the donation and log an error. Do not write partial records.

Firestore Schema You'll Write To

donations/ collection document structure (relevant fields):

{ "id": "donations/abc123", "amount": 25.00, "currency": "GBP", "location_id": "locations/xyz789", "location_snapshot": { "name": "1st Bristol Muslim Scouts", "postcode": "BS1 5NA", "city": "Bristol" }, "campaignMode": "donation", "campaignId": "campaigns/def456", "taxYear": 2026, "giftAidEligible": true, "donorHomePostcode": "M1 1AE", "createdAt": "2026-04-24T09:30:00Z", "webhook_source": "stripe_payment_intent.succeeded" }

Note: location_snapshot is embedded (denormalized). donorHomePostcode is separate (donor's home, not kiosk location).

Test Plan Skeleton (for you to flesh out)

  • Payment webhook receives donation_created event with location_id from kiosk — Happy path
  • Webhook fetches location document and embeds snapshot into donation — Verify fetch succeeds
  • location_snapshot is immutable after creation — Try updating it; confirm it fails or is ignored
  • Donation with missing location_id is rejected — Error handling
  • Donation with missing postcode in location_snapshot is rejected — HMRC validation
  • GASDS aggregation query correctly sums by location_id + tax year + campaign mode — Reporting
  • Activity Mode donations do NOT count in GASDS sum — Compliance gate
  • Webhook handles location fetch timeout gracefully (circuit breaker) — Resilience

Questions to Ask Qamar Before You Start

  • 1 Should the donation webhook retry failed location fetches, or fail the entire donation?
  • 2 Is there a circuit breaker timeout for the location fetch? (e.g., if Firestore is slow)
  • 3 Should location_snapshot be logged separately for audit, or just in the donation record?
  • 4 When does taxYear get set? (Date of donation, or end of calendar year?)

For Qamar: Action Items

Before closing out #678 and starting #679, execute these actions.

❓ Ask Yuvraj (before merge)

  • Is isCommunityBuilding toggle tested in test 10 (required field validation)?
  • Are postcode format validations (UK regex) included in your error handling tests (tests 13–14)?
  • Can you confirm country field defaults to "UK" when creating a new location (test 3)?
  • Are the 5 screen recordings linked in PR #678 description? (Audit trail)
  • Have you verified Firestore indexing is correct post-deployment (test 17)?

📢 Tell Paras (before he starts #679)

  • Share this review document with him. Point him to the "Paras's #679 Brief" tab.
  • Emphasize: #679 depends on #678 being merged first. He can prepare now; code only after merge.
  • Highlight the 8 critical points: hybrid approach (location_id + snapshot), fetch-before-write, immutability, postcode validation, GASDS aggregation, campaign mode gating.
  • Confirm with him that his test plan covers the 8 scenarios in the skeleton (happy path, validation, error handling, reporting, compliance).
  • Make sure he knows: donation webhook must be idempotent (Stripe can retry). Snapshot must not be updated after creation.

🚫 Blocked / Out of Scope (Do NOT Start Yet)

  • Campaign-Level Compliance Mode — P0, but separate workstream. Proposed architecture ready for team review → see Team Discussion tab.
  • Two-Button Front Door [Donation]/[Payment] — Now part of Campaign Compliance Mode proposal. Conditional: only appears for mixed-mode kiosks.
  • GASDS Admin Dashboard — Growth-tier feature. Depends on #679 (donations with location_id). Prioritize after launch.
  • Location Management UI — Future admin screen to view/edit locations. Not in MVP. Kiosk creation is the entry point for now.

Critical Path: #678 → #679

Phase Issue Owner Status Blocker
Phase 1 #678: Location Entity + Kiosk Management Yuvraj Testing (17 tests + 5 videos) None — ready to merge?
Phase 1→2 #678 Merged to main Yuvraj + Qamar Pending Yuvraj confirmations above
Phase 2 #679: Donation Location Handling Paras Ready to prepare #678 merged
Phase 3 GASDS Reporting (future) TBD Blocked #679 complete + Campaign Compliance Lock

Summary: What's Working, What Needs Confirmation, What's Waiting

✅ What's Working
  • • Yuvraj's 17 tests cover core scenarios well
  • • Location immutability logic tested (HMRC audit trail)
  • • Geo validation, error handling, RBAC all covered
  • • Firestore indexing verification included
⚠️ Needs Confirmation
  • • isCommunityBuilding toggle tested?
  • • Postcode format validation (UK regex)?
  • • Country field defaults to UK?
  • • Screen recordings linked in PR?
❌ Out of Scope (Waiting)
  • • Campaign Compliance Mode (Team Discussion)
  • • Donation integration (Paras #679)
  • • GASDS Dashboard (growth tier)

Next Steps

  1. Today (24 Apr): Share this review with Yuvraj and Paras
  2. Before merge: Get Yuvraj's answers to the 4 confirmation questions
  3. On merge: Mark #678 complete; unblock #679 for Paras
  4. Parallel track: Paras prepares test plan and architecture while Yuvraj finalizes #678
  5. After merge: Paras starts coding #679 (donation webhook integration)

🗣️ Proposed Architecture: Campaign-Level Compliance Mode

This is a proposed product architecture decision for team review. Qamar wants input from the dev team before finalising. Date raised: 24 April 2026.

The Proposal

Instead of routing compliance at the kiosk level or through a separate config screen, compliance mode is set per campaign at creation time. Each campaign has a field: campaignMode: 'donation' | 'activity'. This single field drives all downstream compliance behaviour.

Two Modes, Two Compliance Paths

Donation Mode Activity / Payment Mode
Stripe Rate 1.2% + 20p (charity rate) 1.5% + 20p (standard rate)
Gift Aid Eligible — magic link offered Hard-locked OFF — never offered
GASDS Eligible (if ≤£30 and contactless) Excluded
Accounting Ledger Charitable income Trading income
R68 Export Included Excluded
Record Type type: 'donation' type: 'activity'

Conditional Front Door Logic

The donor-facing [Donation] / [Payment] pathway screen is conditional — it only appears when needed.

Kiosk has ONLY Donation campaigns

No front door shown. Donor goes straight to campaign list. All records automatically type: 'donation'.

Kiosk has ONLY Activity campaigns

No front door shown. Donor goes straight to campaign list. All records automatically type: 'activity'.

Kiosk has BOTH types

Two-pathway filter appears: [Donation] / [Payment]. Filters the campaign list by mode. Donor picks pathway, then sees only campaigns of that type.

Key Insight

The record's compliance flag comes from the campaign mode, not from the donor's button press. The front door is just a conditional UI filter — not a data-capture step. Admin controls the experience by simply assigning or unassigning campaign types to the kiosk.

Why This Design?

Firestore Impact

This requires adding a mode field to the campaign document.

Discussion Questions for the Team

  1. Does campaign-level mode cover all use cases?
    Can anyone think of a scenario where the same campaign needs to accept both donations and payments? (We don't think so — if a charity has both, they'd create two campaigns.)
  2. Should mode be editable after creation?
    Changing mode mid-campaign would mix compliance paths on existing records. Proposed: immutable after first transaction. Editable if zero transactions.
  3. Front-door button labels — "Donation" / "Payment" or something friendlier?
    For a mosque: "Sadaqah / Zakat" vs "Madrasah Fees" is clearer than generic labels. Should mode have a displayLabel override?
  4. Admin UX: How visible should mode be at campaign creation?
    Dropdown? Radio buttons? Default to 'donation' with a toggle? We need this to be obvious and not easy to get wrong.
  5. When should we build this?
    This is P0 for compliance but not blocking pilot (pilot charities are donation-only). Target: pre-full-launch (end of June)?

Three Ways to Achieve Compliance Separation

Campaign-level mode is the compliance engine — it must exist. But there are multiple ways to route donors to the right campaign without them thinking about compliance. These approaches work together, not as alternatives.

1. Campaign-Level Mode (Foundation)

Each campaign has mode: 'donation' | 'activity'. This drives Stripe rate, Gift Aid, GASDS, R68, ledger classification. This is the compliance engine — non-negotiable.

When a kiosk has both types: the conditional front door [Donation] / [Payment] appears and filters the campaign list by mode.

✅ Covers every scenario. Single source of truth.

⚠️ Mixed-mode kiosks require a front door step — one extra tap.

2. Kiosk-Level Mode Filter (Speed Layer)

Optional modeFilter on the kiosk document. A kiosk marked "Donation" only shows donation-mode campaigns. A kiosk marked "Payment" only shows activity-mode campaigns. No front door needed — the device is single-purpose.

Use case: A mosque puts one tablet at the entrance (donations) and one at the madrasah desk (fees). Volunteers don't need training. Donors tap and go.

✅ Fastest donor experience. Zero confusion. Same dashboard, full compliance on both devices.

Architecture: just an optional modeFilter: 'donation' | 'activity' | null on the kiosk document. Campaign mode is still the compliance engine.

3. QR Code Per Campaign (Already Planned)

Each campaign already gets its own QR code as a default deliverable. The QR links directly to that specific campaign — the donor never sees a campaign list or a front door. Compliance is inherited automatically from the campaign's mode.

Use case: Print QR codes on collection boxes, posters, or leaflets. "Scan here for Sadaqah" next to "Scan here for Madrasah Fees." Physical separation without multiple devices. No kiosk hardware needed.

✅ Zero hardware cost. Works anywhere. Already in the build plan.

This means every printed QR code is inherently compliant — it points to a campaign that already knows whether it's donation or activity mode.

The Combined Pitch

"SwiftCause gives you three ways to collect — kiosk with smart routing, dedicated devices, or printed QR codes — and every single one is HMRC-compliant out of the box."

No competitor offers this. Dona and GoodBox are donation-only. CollecTin charges for two separate hardware units. HibaBox doesn't distinguish at all. SwiftCause does it in software, at the campaign level, across any collection method.

Discussion: Kiosk Mode Filter — Build Priority?

Approach 1 (campaign mode) is P0 and must be built pre-launch. Approach 3 (QR per campaign) is already planned. Approach 2 (kiosk mode filter) is a simple addition — one optional field on the kiosk document + a filter in the campaign list query. Should we include it in the campaign compliance mode sprint, or park it as a fast-follow?

Locked UX Principle (Still Applies)

No third button. No picker lists. No phase-2 complexity on the front door.

If future requirements introduce donor IDs, member categories, or restricted fund codes, those route via back-office rules — never the front door. The front door is either invisible (single mode) or exactly two choices (mixed mode). This is a permanent constraint.

🔍 Competitor Analysis: How Others Handle Donation vs Payment Compliance

Research into how UK charity donation platforms handle the split between charitable income (donations) and trading income (payments/fees). None of them solve this problem properly. SwiftCause's campaign-level compliance mode would be a genuine differentiator.

The Market Landscape

Platform Approach Donation/Payment Split? Gift Aid Handling GASDS Mixed-Use Support
Dona Donation-only terminals ❌ No — everything is a donation Card-linked (register once, auto-applies) ✅ Supported ❌ Can't collect fees
GoodBox Donation-only devices ❌ No — donation only Via Swiftaid partnership ✅ Supported ❌ Can't collect fees
CollecTin Two separate hardware SKUs ⚠️ Yes — but at device level (buy 2 units) Via Give A Little integration ✅ Supported ⚠️ Need 2 devices
Give A Little Software platform, Gift Aid optional per campaign ⚠️ Partial — GA toggleable per campaign Optional per campaign ✅ Supported ⚠️ Closest to our approach
HibaBox Mosque-specific kiosks ❌ No visible compliance split HMRC export from dashboard Unclear ⚠️ Handles both but may not distinguish
SwiftCause (proposed) Campaign-level compliance mode ✅ Yes — full software routing Auto-routed by campaign mode ✅ Mode-gated ✅ One device, full separation

The Four Approaches in the Market

Approach A: "Donation-Only"

Used by: Dona, GoodBox, CollecTin (charity SKU)

Sidestep the problem. Only accept donations. If a charity needs to collect fees, tell them to use a separate system.

✅ Clean and compliant

❌ Loses mixed-use charities (mosques, churches with nurseries, event charities)

Approach B: "Two Devices"

Used by: CollecTin (separate SKUs)

Sell separate hardware for each purpose. Compliance guaranteed because the device itself is single-purpose.

✅ Compliance guaranteed

❌ Terrible UX — two devices, two dashboards, double the cost

Approach C: "Ignore the Problem"

Used by: HibaBox (arguably)

Accept all payments and label them donations. The platform doesn't distinguish. This works until HMRC audits.

✅ Simple to build

❌ HMRC risk — trading income classified as charitable income with Gift Aid claimed on it

Approach D: Campaign-Level Compliance (SwiftCause)

Used by: No one yet — this is novel

Campaign mode drives Stripe rate, Gift Aid, GASDS, R68, and ledger classification. One device, one dashboard, full separation.

✅ Full compliance + one device + mixed-use support

✅ Enables the market nobody else serves

Deep Dive: Each Competitor

Dona — Pure Donation Terminal

Founded 2019 by Regium Consulting (London). Self-service terminals with contactless, Apple/Google Pay, Chip & PIN. Processing fee: 1.58% via Stripe.

Gift Aid: Card-linked — donor registers once, Gift Aid auto-applies to all future donations from that card. Clever UX but only works for donations.

The gap: If a mosque wants to collect Madrasah fees on the same device, Dona can't do it. The charity needs a completely separate payment system for trading income. Two systems, two reconciliation workflows.

Source: donadonations.com

GoodBox — FCA-Regulated Donation Devices

FCA regulated, PCI & GDPR compliant. Offers GoodPlate (church collection plate form factor) and GBx Flex. Partners with Swiftaid for automated Gift Aid claims.

GASDS: Explicitly supported — their reporting provides when/where evidence for HMRC claims. All contactless donations under £30 qualify.

The gap: Same as Dona — donation-only. No concept of trading income. A church running a café or nursery can't collect those payments through GoodBox without creating a compliance risk.

Source: goodbox.com

CollecTin — Hardware-Level Compliance Split

UK-designed and assembled. Interesting approach: they sell two separate product SKUs:

  • "CollecTin Solo for UK Charities" — zero-rated for VAT, designed for fundraising donations
  • "CollecTin Solo for UK VAT Applicable Uses" — standard VAT, for trading/commercial payments

The insight: CollecTin recognised the problem exists but solved it at the hardware level. If you need both types, you buy two devices. Compliant but expensive and operationally clunky.

Fees: 1.49% for contactless (via SumUp), 2.5% for online/QR.

Source: collectin.com

Give A Little — Closest to Our Approach

World's only dedicated "point of donation" software platform. ISO 27001 + 9001 certified. Integrates with SumUp and Stripe hardware.

Key similarity: Gift Aid is "optional on campaigns" — this is conceptually similar to setting compliance mode at the campaign level.

Where we go further: Give A Little's campaign-level Gift Aid toggle is just an on/off switch. SwiftCause's campaign mode drives six downstream systems: Stripe rate, Gift Aid eligibility, GASDS eligibility, R68 export inclusion, ledger classification, and conditional front door. That's a much deeper compliance integration.

Source: givealittle.co

HibaBox — Mosque-Specific, Compliance Gap

Cloud-based donation management built for mosques and Islamic centres. Handles donations (zakat, sadaqah) AND program administration (Quran classes, fee collection).

The problem: They handle both donation and trading income but don't appear to distinguish between them at the compliance level. Gift Aid summaries are exportable from the dashboard, but there's no visible mechanism to prevent Gift Aid claims on trading income (madrasah fees).

The risk: If a mosque claims Gift Aid on madrasah fees collected through HibaBox, that's an HMRC compliance violation. The platform doesn't protect them from this.

Source: hibabox.com

HMRC Rules: Why This Matters

The core HMRC principle: If the charity does not provide goods or services in return for payment, the payment has the character of a charitable donation. If goods or services are provided, it's trading income.

Gift Aid and GASDS can ONLY be claimed on charitable donations — never on trading income. Claiming Gift Aid on trading income is a compliance violation that can result in penalties and clawback.

The 2025 Code of Fundraising Practice (effective 1 November 2025) adds new requirements for contactless and "convenience giving" — charities must ensure donors can identify the charity, understand how donations are used, and see any processing fees. Unstaffed collection devices have additional transparency rules.

Records must be kept for at least 6 years from the end of the tax year they relate to.

Recommendation: Three Sharpening Points

1. Default to Donation Mode

Most charities are donation-only. Don't make them actively choose "donation" — default to it. Only surface the activity option when the admin creates a campaign involving goods/services. Consider a helper prompt: "Will donors receive goods or services in exchange for this payment?" — Yes → activity mode, No → donation mode.

2. Safety Net for Wrong Mode Selection

The "immutable after first transaction" rule is correct. But what if an admin sets the wrong mode and processes 50 transactions before realising? For pilot: make the mode selection very prominent with a clear warning — "This cannot be changed after the first transaction is processed." Post-launch: consider a dispute/correction workflow.

3. Admin-Configurable Front Door Labels

"Donation" / "Payment" is generic. For a mosque: "Sadaqah" / "Madrasah Fees" is far clearer. For a church: "Offering" / "Hall Hire". A displayLabel field on campaign mode would make the UX feel purpose-built for the religious sector — your pilot market. Quick win, big impact.

Competitive Positioning Summary

The market has a gap. Competitors either dodge the donation-vs-payment problem (Dona, GoodBox), solve it with hardware (CollecTin), or ignore it (HibaBox). Nobody does software-level compliance routing at the campaign level.

SwiftCause's campaign-level compliance mode directly enables the mixed-use charity market — mosques, churches with trading arms, charities running events — that none of the competitors serve well. This is a defensible differentiator, not a catch-up feature.