Client Demo

GolfFund

Point-of-sale for charity golf events.
Accept in-person payments via Tap to Pay on iPhone.

7Default Categories
3Role Levels
3%Processing Fee
<100msReal-time Sync

Tech Stack

Built with production-grade tools for reliability and performance

React Native

0.81.5Expo SDK 54

TypeScript

5.9.2Strict mode

Convex

1.31.7Real-time backend

Clerk

2.19.22Auth + SSO

Stripe Terminal

beta.28Tap to Pay SDK

React Navigation

7.xStack + Tabs

iOS only — Deployment target iOS 16.7+ — New Architecture enabled — Hermes JS engine — Zod 4 validation — Reanimated 4 spring animations

Feature Showcase

Everything needed to run payment collection at a charity golf event

Tap to Pay

Accept contactless NFC payments using just an iPhone. No card reader, no dongle, no extra hardware. Powered by Stripe Terminal SDK with Apple's Tap to Pay framework.

iOS 16.7+ required. Simulated mode in development.

Role-Based Access Control

Three-tier permission system enforced on both frontend and backend

Process payments
View transaction history
View running totals
Create custom categories
Refund transactions
Export CSV
Manage members
Edit event details
View bank account status
Connect bank account
Transfer ownership
Archive event

Global admins bypass all event-level permission checks. Owners are auto-assigned when creating an event.

Payment Flow

From category selection to confirmed payment in under 30 seconds

Fee Calculation Example

Base amount$100.00
Processing fee 3%$3.00
Total charged to customer$103.00
$100.00 goes to charity — $3.00 retained as platform fee

Security Model

Defense in depth: auth, validation, and integrity at every layer

Clerk Auth + JWT

Every API call verified via JWKS signature verification against Clerk. HTTP endpoints use jose library for full JWT validation.

Server-side Fees

Processing fee calculated authoritatively on the server. Client displays estimated fee, server enforces actual charge. No client-side manipulation.

Pending-first Transactions

Transactions always start as "pending". Only Stripe webhooks can transition to "succeeded" or "failed". Client cannot set final status.

Role-based Access

Permissions enforced on both frontend (UI gates) and backend (mutation guards). Global admins bypass event-level checks.

Webhook Verification

Stripe webhook signatures verified via stripe.webhooks.constructEvent(). Prevents forged webhook attacks.

No Secrets in Client

All Stripe secret keys are server-side Convex env vars. Client only holds publishable keys. Tokens stored in encrypted SecureStore.

Stripe Connect Flow

Stripe-hosted onboarding keeps PCI burden off Golfund

STEP 1

Tap "Link Bank Account"

Event owner opens Settings and taps the bank account connection button.

STEP 2

Express Account Created

Stripe Connect creates an Express account with business_type: "non_profit" via our API.

STEP 3

Stripe-Hosted Onboarding

Owner completes identity verification, bank details, and EIN on Stripe's secure hosted flow.

STEP 4

Funds Route Automatically

Account verified. All payments for this event flow directly to the charity's bank account.

No PCI Burden

Stripe hosts the entire onboarding flow. Golfund never touches bank credentials.

Powered by Stripe

Trust badge displayed during onboarding. Industry-standard payment infrastructure.

Per-Event Accounts

Each event connects its own charity bank account. Funds route automatically.

Event & Team Management

Create events, invite volunteers, and manage roles with ease

Create Events

Name, date, and optional location. Creator becomes the Owner automatically.

Invite by Email

Add volunteers and admins by email. They get access instantly.

Role Hierarchy

Owner, Admin, Member. Each level with clearly defined permissions.

Active Event Switching

Each user picks their active event. Own Event A, volunteer at Event B.

Ownership Transfer

Owners can transfer event ownership to another member when needed.

Per-Event Bank Account

Each event connects its own Stripe account. Funds route to the right charity.

Multi-Event Scenario

Owner
Charity Golf Classic
Volunteer
Community Open

A single user can own one event and volunteer at another, switching active events instantly.

Data & Reporting

Real-time insights during the event, detailed reporting after

Real-time Totals Dashboard

Category-by-category breakdown with live running total. Updates across all devices instantly.

Search & Filter History

Full transaction log with text search and category filter. Find any transaction in seconds.

One-Tap CSV Export

Export all transactions to CSV for post-event reporting. Cells sanitized against injection attacks.

Transaction Details

Full receipt view with golfer info, fees, timestamps. Share receipts via native share sheet.

Refund Capability

Admins and owners can process refunds. Status tracked through Stripe webhooks.

Receipt Sharing

Share formatted transaction receipts via Messages, Email, or any installed app.

Sample Event Totals

Golfer Fee50
$12,500.00
Mulligan Package56
$2,800.00
Raffle Tickets78
$1,950.00
Auction Item12
$4,200.00
50/50 Tickets44
$1,100.00
Event Total$22,550.00

App Screens

Full navigation structure and screen inventory

Auth

Login

Email/password + Google SSO via Clerk

Home

Dashboard

Category grid, event header, running total

PaymentEntry

Amount input with presets or custom

PaymentProcessing

NFC tap interface, no back gesture

PaymentConfirmation

Success/failure with haptics

History

TransactionList

Search + filter by category

TransactionDetail

Full receipt + share

Totals

EventTotals

Category breakdown + CSV export

Settings

Settings

User info, Tap to Pay status, sign out

ConnectOnboarding

Link bank account via Stripe

ManageEvents

Create, switch, manage events

ManageMembers

Invite, remove, change roles

CreateCategory

Custom category builder

PaymentProcessing and PaymentConfirmation disable back gesture and hardware back to prevent accidental navigation during payment.

Architecture Overview

Provider stack, data flow, and real-time architecture

Provider Stack

SafeAreaProviderInset management
ClerkProviderAuthentication
ConvexProviderWithClerkReal-time backend
StripeTokenSyncToken bridge
StripeTerminalProviderTap to Pay SDK
ErrorBoundaryCrash recovery
RootNavigatorAuth gate + navigation

No Global State

Convex reactive queries are the single source of truth. No Redux, no Zustand, no Context-based stores.

Reactive Subscriptions

useQuery hooks subscribe to Convex. Data pushes to all connected devices in under 100ms.

Webhook-driven Status

Transaction status flows: pending (creation) then succeeded / failed / refunded (Stripe webhooks only).

What's New

Recent changes and improvements

Donation category removed from defaultsBreaking
Volunteer access locked down -- members can't see banking infoSecurity
Custom category golfer info bridge -- DB flag now respectedFix
Slim bank account warning banner for ownersUI
Golfund logo on login screen + empty stateUI
Stripe trust badge on Connect onboarding screenUI
Icon picker removed -- auto-assign random icon for custom categoriesUX
Auction Item: side-by-side golfer name + item name fieldsFeature
Race condition fix: graceful handling of new user signup flowFix