Capturing expenses
WeSpend offers four ways to log a spend. All of them feed the same pure parser (src/domain/capture/parse.ts), which extracts the amount and merchant from text, so the behaviour is consistent and unit-tested.
Methods
| Method | How it works | Platforms |
|---|---|---|
| Manual entry | Type amount, pick category, who spent, pot/own-pocket | All |
| Receipt scan (OCR) | Snap or pick a bill photo; on-device ML Kit reads the total and pre-fills the amount | All (dev build) |
| Share from an app | Share a UPI payment text from GPay/PhonePe into WeSpend; it opens pre-filled | All (dev build) |
| SMS auto-detect | Reads bank/UPI debit SMS and notifies you to log it | Android only (dev build) |
Receipt scan
On the add-expense screen, tap Camera or Gallery under "Scan a receipt". The image is read with on-device ML Kit text recognition (free, offline), the grand total is extracted, and the amount is pre-filled for you to confirm. The capture is recorded with source: 'ocr'.
Share from an app
WeSpend registers as a text share target. In GPay/PhonePe (or any app), share the payment confirmation and pick WeSpend - the add-expense screen opens with the amount and merchant pre-filled (source: 'share').
SMS auto-detect (Android)
Opt in under Settings -> Reminders & auto-detect -> Read bank/UPI SMS (this requests the SMS permission). When a debit message arrives, WeSpend parses it and raises a notification ("Rs 320 at DMART - tap to add"); tapping opens the pre-filled screen (source: 'sms').
Sideload only
READ_SMS is not approved for Play Store apps, so SMS auto-detect works on a sideloaded APK (exactly how WeSpend is installed on your phones). It is Android-only and off by default.
Native share receiver
Shares are received by a dedicated invisible activity (modules/share-receiver), not the React Native activity. The system sharesheet launches share targets as a separate document task (NEW_DOCUMENT | MULTIPLE_TASK, bypassing singleTask), and routing that into the RN activity caused an entire class of failures: native crashes on strict content providers, relaunch loops, and share events emitted to dying surfaces. The receiver runs inside the sender's task, copies the shared text and files to the app cache while the URI grant is valid, stores the payload in process memory, brings the app's main task forward with its plain launcher intent, and finishes. The JS layer polls the stored payload shortly after mount and on every foreground (use-share-capture), so delivery is pull-based and race-free by construction. This replaced expo-share-intent on Android entirely.
Why these need a dev build
Receipt OCR, share-intent, and SMS rely on native modules that are not bundled in Expo Go. The app therefore runs on a custom dev build rather than Expo Go - see Running the app.