Skip to content

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

MethodHow it worksPlatforms
Manual entryType amount, pick category, who spent, pot/own-pocketAll
Receipt scan (OCR)Snap or pick a bill photo; on-device ML Kit reads the total and pre-fills the amountAll (dev build)
Share from an appShare a UPI payment text from GPay/PhonePe into WeSpend; it opens pre-filledAll (dev build)
SMS auto-detectReads bank/UPI debit SMS and notifies you to log itAndroid 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.

Spend together, settle simply.