payfrit-beacon-ios/CLAUDE.md
Koda c193f0abd1 feat: full parity with Android beacon provisioner
- Fix BeaconShardPool: replace all 64 UUIDs to match Android + migration.sql
  (iOS had a completely different set of UUIDs — would cause shard resolution failures)
- Add frames 3-6 disable to write sequence (16 → 24 steps, matches Android's
  DXSmartProvisioner.writeBeaconConfig() exactly)
- Add CLAUDE.md with full project docs, API endpoints, protocol reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 17:40:43 +00:00

150 lines
6 KiB
Markdown

# Payfrit Beacon iOS
Native Swift iOS app for provisioning iBeacon hardware at restaurant tables. Repo: `payfrit-beacon-ios` on Forgejo.
## Purpose
Utility app for restaurant setup staff to:
1. Scan for nearby BLE iBeacons
2. Check UUID against known bulk manufacturer defaults (ban list)
3. Assign table names to beacons (smart-incremented)
4. Save beacon + auto-create service point records via API
## Environment
- **Server**: dev.payfrit.com (always dev — this is a setup tool)
- **API Base**: `https://dev.payfrit.com/api`
- **IS_DEV**: Driven by `DEV` compiler flag (orange DEV banner on all screens)
- **OTP**: Dev server uses magic OTP (no Twilio)
## Database (dev schema — migrated Jan 2026)
Connected via API, not direct SQL. **Dev DB uses clean, unprefixed names.** PKs are always `ID`. FKs reference their parent table.
| Table | PK | Key Columns |
|-------|----|-------------|
| Beacons | `ID` | `UUID`, `Name`, `BusinessID`, `IsActive` |
| ServicePoints | `ID` | `BusinessID`, `Name`, `TypeID`, `Code`, `SortOrder`, `IsActive`, `BeaconID` |
| Businesses | `ID` | `Name`, `ParentBusinessID` |
**Note**: `POST /api/beacons/save.php` auto-creates a ServicePoint when saving a beacon.
## API Endpoints Used
| Method | Endpoint | Auth | Purpose |
|--------|----------|------|---------|
| POST | /auth/loginOTP.php | No | Send OTP to phone |
| POST | /auth/verifyLoginOTP.php | No | Verify OTP, get token |
| POST | /businesses/list.php | Yes | List user's businesses |
| POST | /businesses/get.php | Yes | Get single business |
| POST | /servicepoints/list.php | Yes | List service points for business |
| POST | /servicepoints/save.php | Yes | Create/update service point |
| POST | /beacon-sharding/allocate_business_namespace.php | Yes | Allocate UUID+Major shard |
| POST | /beacon-sharding/get_beacon_config.php | Yes | Get complete beacon config |
| POST | /beacon-sharding/register_beacon_hardware.php | Yes | Register provisioned device |
| POST | /beacon-sharding/verify_beacon_broadcast.php | Yes | Verify beacon is broadcasting |
| POST | /beacon-sharding/resolve_business.php | Yes | Resolve business by UUID+Major |
| POST | /beacon-sharding/allocate_servicepoint_minor.php | Yes | Auto-assign minor value |
| GET | /beacons/list_all.php | No | All active beacons (UUID→ID map) |
| POST | /beacons/lookup.php | No | Lookup beacon assignments by UUID |
| POST | /beacons/lookupByMac.php | No | Lookup beacon by MAC address |
| POST | /beacons/list.php | Yes | List beacons for a business |
| POST | /beacons/save.php | Yes | Create/update beacon + auto-create service point |
| POST | /beacons/wipe.php | Yes | Wipe/deactivate a beacon |
## Build & Deploy
Build in Xcode targeting iOS 17+. No local testing — deploy to device for BLE testing.
## Project Structure
```
PayfritBeacon/
├── Api.swift REST client, all API calls, auth token mgmt
├── BeaconBanList.swift Known bad UUID prefixes (factory defaults)
├── BeaconProvisioner.swift DX-Smart CP28 GATT provisioner (24-step write sequence)
├── BeaconScanner.swift iBeacon CLLocationManager scanner
├── BeaconShardPool.swift 64 Payfrit shard UUIDs (matches Android + migration.sql)
├── BLEBeaconScanner.swift CoreBluetooth BLE scanner + BeaconType enum
├── BusinessListView.swift Business selection screen
├── DebugLog.swift Shared logging singleton
├── DevBanner.swift Orange "DEV" banner overlay
├── LoginView.swift OTP login screen
├── PayfritBeaconApp.swift App entry point
├── RootView.swift Root navigation
├── ScanView.swift Main provisioning hub
├── ServicePointListView.swift Service point list + beacon assignment
└── UUIDFormatting.swift UUID string formatting extensions
```
## DX-Smart CP28 Provisioning Protocol
### BLE Service & Characteristics
- **Service**: `0000FFE0-0000-1000-8000-00805F9B34FB`
- **FFE1** (Notify): Response notifications (RX)
- **FFE2** (Write): Command TX
- **FFE3** (Write): Password authentication
### Packet Format
`[4E][4F][CMD][LEN][DATA...][XOR_CHECKSUM]`
Header: `4E 4F` (fixed). Checksum: XOR of CMD ⊕ LEN ⊕ DATA bytes.
### Command Codes
| Code | Purpose |
|------|---------|
| 0x11-0x16 | Select frame 1-6 |
| 0x60 | Save config to flash |
| 0x61 | Set frame as device info |
| 0x62 | Set frame as iBeacon |
| 0x71 | Write device name (max 20 ASCII) |
| 0x74 | Write UUID (16 bytes BE) |
| 0x75 | Write Major (2 bytes BE) |
| 0x76 | Write Minor (2 bytes BE) |
| 0x77 | Write RSSI@1m (1 byte signed) |
| 0x78 | Write adv interval (1 byte, x100ms) |
| 0x79 | Write TX power (1 byte, 0-7 index) |
| 0xA0 | Trigger off |
| 0xFF | Disable frame (no data) |
### 24-Step Write Sequence
1. DeviceName (0x71) → Frame1 select (0x11) → Frame1 type (0x61) → Frame1 RSSI/AdvInt/TxPow
2. Frame2 select (0x12) → Frame2 iBeacon (0x62) → UUID/Major/Minor/RSSI/AdvInt/TxPow
3. TriggerOff (0xA0)
4. Disable frames 3-6 (select 0x13-0x16 + 0xFF for each)
5. SaveConfig (0x60)
### Authentication
Passwords tried in order: `555555`, `dx1234`, `000000`. Written to FFE3 with `.withResponse`.
## Ban List UUIDs
Hardcoded in `BeaconBanList.swift`. Known factory-default prefixes:
- `E2C56DB5` — Apple AirLocate sample
- `B9407F30` — Estimote default
- `FDA50693` — Generic Chinese bulk default
- `F7826DA6` — Kontakt.io default
- `74278BDA` — Generic bulk default
- `00000000` / `FFFFFFFF` — Unconfigured hardware
## Dependencies
- SwiftUI + Combine
- CoreBluetooth (BLE GATT operations)
- CoreLocation (CLBeaconRegion for iBeacon ranging)
- Security (Keychain for auth token storage)
## App Flow
1. **Login** → OTP (Keychain-saved token for re-auth)
2. **Select Business** → Auto-select if one, list if multiple
3. **Select Service Point** → Table assignment target
4. **Scan** → BLE scan → show results with status badges
5. **Provision** → Connect → Auth → Write 24-step config → Save
6. **Register** → API call to register hardware
7. **Repeat** or **Done**