upi_sms_parser 0.1.0 copy "upi_sms_parser: ^0.1.0" to clipboard
upi_sms_parser: ^0.1.0 copied to clipboard

A pure-Dart library for parsing Indian UPI transaction SMS messages. Extracts amount, merchant, UPI reference, account number, balance, transaction type, and timestamp from bank SMS. Includes a multi- [...]

upi_sms_parser #

A pure-Dart library for parsing Indian UPI transaction SMS messages. No Flutter dependency — works in any Dart project (Flutter apps, server-side Dart, CLI tools, tests).

Features #

  • Multi-gate SMS filter to distinguish genuine UPI/bank transaction messages from OTPs, promotional messages, balance/EMI alerts, and mobile recharge confirmations
  • Extracts: amount, merchant/recipient, UPI reference number, masked bank account number, available balance, transaction type (debit/credit), and timestamp
  • Handles 8 different date/time formats used across Indian banks and UPI apps — from "26/04/2026 at 10:38 PM" to SBI's compact "27Mar26"
  • Recognises sender IDs for all major Indian banks — SBI, HDFC, ICICI, Axis, Kotak, PNB, BOB, Canara, Union Bank, IDFC FIRST, RBL, IndusInd, Federal Bank, Yes Bank, and more
  • Recognises UPI apps — Google Pay, PhonePe, Paytm, BHIM, Swiggy, Amazon Pay, and others
  • Zero Flutter dependency — only dart:core is used, so this package drops cleanly into any Dart project

Installation #

Add this to your package's pubspec.yaml:

dependencies:
  upi_sms_parser: ^0.1.0

Then run:

dart pub get

Quick start #

import 'package:upi_sms_parser/upi_sms_parser.dart';

void main() {
  final parser = UpiParser();

  const sms = 'Payment Confirmed! Your Swiggy Order #236190425176316 of '
      'Rs. 356.0 was successfully paid on 26/04/2026 at 10:38 PM. '
      'Txn ID: 260298464000257. Enjoy your order!';

  final result = parser.parse(sms, 'TX-SWIGGY-S');

  if (result != null) {
    print('Amount: ₹${result.amount}');     // ₹356.0
    print('Type: ${result.type.name}');     // debit
    print('Merchant: ${result.merchant}');  // null (Swiggy order SMS doesn't
                                             // use a merchant pattern this
                                             // package recognises)
    print('UPI Ref: ${result.upiRef}');     // 260298464000257
    print('Date: ${result.timestamp}');     // 2026-04-26 22:38:00.000
  }
}

parser.parse returns null when the SMS either (a) doesn't look like a genuine UPI transaction (OTP, promo, recharge confirmation, ...), or (b) looks like one but no amount could be extracted from it. Every other field on ParsedTransaction is independently nullable — null simply means that particular SMS didn't phrase that piece of information in a way the package recognises.

To process a whole inbox at once, use parseAll:

final results = parser.parseAll([
  {'body': 'Rs.356 debited from A/c X6299...', 'address': 'JK-SBIUPI-S'},
  {'body': 'Your OTP is 123456. Do not share.', 'address': 'JK-SBIUPI-S'},
]);
// results.length == 1 — the OTP message is silently skipped

See example/example.dart for a complete, runnable walkthrough.

Supported SMS formats #

A non-exhaustive sample of the message shapes the package recognises:

Source Example SMS snippet What gets extracted
SBI (bank-issued) A/C X6299 debited by 356.00 on date 26Apr26 trf to Swiggy Refno 26029846... amount, type, account, merchant, ref, date
Swiggy (app-issued) Your Swiggy Order #236190425176316 of Rs. 356.0 ... paid on 26/04/2026 at 10:38 PM. Txn ID: 260298464000257 amount, type, ref, date
HDFC / ICICI / Axis Rs.1,372.00 credited to A/c XX1234 on 27-04-2026 17:13. Avl Bal: Rs.12,450 amount, type, account, balance, date
PhonePe / GPay / Paytm ₹58 paid to Mr PRADEEP KUMAR via UPI on 06-Jun-26. UPI Ref No 1234567890 amount, type, merchant, ref, date

Messages that look transactional but aren't — OTPs, promotional offers, mobile recharge confirmations, low-balance/EMI-due alerts — are rejected by UpiSmsFilter before any field extraction is attempted. See How the filter works below.

API Reference #

UpiParser #

The main, high-level entry point — most apps only need this class.

Member Description
ParsedTransaction? parse(String body, String address) Filters then fully parses one SMS. Returns null if it isn't a genuine UPI transaction, or if the amount couldn't be extracted.
List<ParsedTransaction> parseAll(List<Map<String, String>> messages) Parses a batch of {'body': ..., 'address': ...} maps and returns only the ones that parsed successfully — silently skipping the rest.
bool isUpiTransaction(String body, String address) Quick yes/no filter check without running field extraction.

ParsedTransaction #

The immutable result returned by UpiParser.parse.

Field Type Notes
amount double? Rupee amount. The only field parse treats as required — parse returns null rather than a result with amount: null.
type UpiTransactionType debit, credit, or unknown.
merchant String? Recipient/merchant name, e.g. "Swiggy", "Mr PRADEEP KUMAR".
upiRef String? Bank-assigned transaction/reference ID — useful as a dedupe key.
bankAccount String? Masked account number, e.g. "X6299", "XX6299".
availableBalance double? Balance remaining after the transaction, if the SMS states one.
timestamp DateTime Parsed from the SMS body; falls back to DateTime.now() if no date pattern matched.
rawSms String The original, unmodified SMS text.
isDebit / isCredit bool Convenience getters over type.

UpiTransactionType #

enum UpiTransactionType { debit, credit, unknown }

debit = money left the account, credit = money arrived, unknown = neither set of keywords was present in the SMS body.

UpiSmsFilter #

The standalone filtering gate sequence — use this directly if you want to decide "is this a real transaction SMS?" without paying for extraction.

Member Description
static bool isUpiTransactionSms(String body, String address) Runs the full 5-gate check (see below). true only if every gate passes.
static bool isPromotionalSender(String address) Standalone Gate-0 check: does the sender ID carry a DLT promotional prefix (AP-, CP-, DP-, EP-, FP-)?

UpiSmsExtractor #

The standalone field-extraction functions — use these directly if you only need one specific piece of data and don't want to run the filter or build a full ParsedTransaction.

Member Returns
static double? extractAmount(String sms) Transaction amount in rupees.
static String? extractMerchant(String sms) Merchant/recipient name.
static UpiTransactionType extractTransactionType(String sms) debit / credit / unknown.
static String? extractUpiRef(String sms) UPI reference / transaction ID.
static String? extractAccountNumber(String sms) Masked bank account number.
static double? extractAvailableBalance(String sms) Balance remaining after the transaction.
static DateTime extractTimestamp(String sms) Parsed transaction date/time, or DateTime.now() as a fallback.

How the filter works #

UpiSmsFilter.isUpiTransactionSms runs an SMS through five gates, in order, short-circuiting on the first failure. An SMS must clear all five to be treated as a genuine transaction record:

  1. Gate 0 — Promotional sender check. Rejects messages whose sender ID carries a DLT promotional prefix (AP-, CP-, DP-, EP-, FP-). Blocks: "AP-AIRTEL-P: Get 1GB extra data..."

  2. Gate 1 — Money must be mentioned. The body must contain either a currency marker (, Rs., INR, "rupees") OR a plain "debited/credited by/of/with <amount>" phrase (some banks, notably SBI, omit the currency symbol entirely). Blocks: "Your delivery is on the way!" (no money mentioned at all)

  3. Gate 2 — Money must have actually moved. Mentioning a rupee figure isn't enough — the message needs an action word like "debited", "credited", "sent", "received", "paid", "trf to", etc. Blocks: "Your account balance is Rs.500. Maintain minimum balance." (mentions an amount, but nothing moved)

  4. Gate 3 — Must be traceable to a real banking rail. Either the sender ID matches a known bank/UPI/app code (SBI, HDFC, GPay, PhonePe, ...), OR the body itself carries a transaction reference ("UPI Ref", "Txn ID", "NEFT", "IMPS", ...). Blocks: "Hey, I paid you ₹500 for dinner, sent via phonepe!" from a personal contact — looks transactional, but isn't bank-issued and carries no reference number.

  5. Gate 4 — Known non-transaction phrasing blocklist. Even messages that clear gates 1–3 are rejected if they contain tell-tale OTP, recharge, balance-alert, or phishing phrases ("otp", "do not share", "recharge of", "minimum balance", "emi due", "click here", ...). Blocks: "Your OTP for UPI transaction is 123456. Do not share. Amount: Rs.500" (looks like a transaction at first glance — but it's a phishing-style OTP message borrowing transactional language)

If you only need the Gate-0 check on its own (e.g. for a lightweight inbox-wide pre-filter), call UpiSmsFilter.isPromotionalSender directly.

Contributing #

Found a bank or UPI app whose SMS format isn't recognised? Contributions are welcome — the most common additions are:

  • New bank sender codes — add the bank's short code (lower-case) to the bankSenderCodes list inside UpiSmsFilter.isUpiTransactionSms (Gate 3), so its SMS pass the "is this from a real bank?" check.
  • New amount/merchant/balance phrasings — add a new entry to the relevant pattern map in UpiSmsExtractor (extractAmount, extractMerchant, extractAvailableBalance, ...). Patterns are tried in declaration order and the first match wins, so put more specific formats earlier.
  • New date/time formatsUpiSmsExtractor.extractTimestamp tries eight patterns in sequence; append a new one (with a comment showing a real example string) if you encounter a format none of the existing eight handle.
  • New non-transaction phrases — if a promotional/OTP/recharge message is slipping through, add its tell-tale phrase (lower-case) to the blocklist in Gate 4.

Whatever you change, please add a corresponding test to test/upi_sms_parser_test.dart using a real (anonymised) SMS example — that's what keeps regressions from creeping back in as the pattern lists grow.

0
likes
150
points
84
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A pure-Dart library for parsing Indian UPI transaction SMS messages. Extracts amount, merchant, UPI reference, account number, balance, transaction type, and timestamp from bank SMS. Includes a multi-gate filter to distinguish genuine UPI transaction SMS from promotional messages, OTPs, and recharge confirmations.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

More

Packages that depend on upi_sms_parser