Paper Receipt Android SDK 2.5.0
2.5.0 builds on 2.4.1 and pairs with Activation SDK 1.2.0, the same version as 2.4.x. Its headline changes:
- UPC product lookup is public API again. Look up a product by UPC from Kotlin or Java, with no extra module.
- Trip Resolution. When a US scan ends with no merchant identified, the SDK asks the Trip Resolution API as a last resort, so fewer receipts come back without a store.
- Merchant-detection signals.
ScanResults.merchantDetection()shows which detectors identified the store. - Account linking tells "sign in again" apart from a generic failure. A new
REAUTHENTICATION_REQUIREDerror code.
This release contains a breaking change: com.microblink.ProductLookup is no longer public. See Breaking changes. It also ships with a known receipt-recognition regression — read Known issues before you upgrade.
Installation
Bump the BOM to 2.5.0. If you also use the Activation SDK, keep its BOM at 1.2.0 (see the Android SDK Version Matrix).
dependencies {
implementation(platform("com.microblink.blinkreceipt:blinkreceipt-bom:2.5.0"))
implementation("com.microblink.blinkreceipt:blinkreceipt-recognizer")
implementation("com.microblink.blinkreceipt:blinkreceipt-camera-ui")
// Only if you use Activation
implementation(platform("com.actualplatform:activation-bom:1.2.0"))
implementation("com.actualplatform:activation")
}
The build requirements are unchanged from 2.4.1: compileSdk 37 (or higher) and minSdk 24. Your app's targetSdk does not need to change. As with every release, recompile your app against the new version.
The SDK ships its own consumer ProGuard/R8 rules and is safe under R8 full mode.
Breaking changes
com.microblink.ProductLookup is no longer public
com.microblink.ProductLookup was an internal product-intelligence lookup interface, unrelated to UPC lookup, and was never intended for client use. It is no longer public, and the name now belongs to the new com.microblink.core.ProductLookup model returned by UPC product lookup. Remove any references to the old interface.
What's new
- UPC product lookup.
RecognizerClient().lookup(upc)returns aTask<ProductLookup>, from Kotlin or Java; pass an optionalCancellationTokento cancel the request. See UPC product lookup. - Trip Resolution as a last-resort merchant lookup. See Trip Resolution.
ScanResults.merchantDetection(). The merchant-detection signals behind a scan's merchant — phone, logo, text, tax ID, CSV, product-search, long-tail, and Trip Resolution matches — so a misidentified store can be traced to the detector responsible. It isnullwhen no detector produced a signal. These signals are not yet forwarded to the Activation SDK. SeeMerchantDetection.- Account linking:
REAUTHENTICATION_REQUIREDerror code (blinkreceipt-account-linking). When a retailer requires the user to sign in again during a background or order-fetch session, the error is now reported asREAUTHENTICATION_REQUIRED(code1010, matching iOS) instead ofINTERNAL_ERROR. LikeINTERNAL_ERROR, it is non-recoverable and opens no WebView — prompt the user to relink the account.
UPC product lookup
Look up a product by its UPC. The returned task never fails: a network or server error, including a timeout, is reported in ProductLookup.message, and an unknown UPC returns an empty ProductLookup. It is cancelled only if you cancel the CancellationToken you passed.
- Kotlin
- Java
import com.google.android.gms.tasks.CancellationTokenSource
import com.microblink.RecognizerClient
import kotlinx.coroutines.tasks.await
// Suspends; call from a coroutine. Cancelling the coroutine cancels the request.
val cancellation = CancellationTokenSource()
val product = RecognizerClient().lookup("012345678905", cancellation.token).await(cancellation)
if (product.message == null) {
showProduct(product.name, product.brand, product.rewarded)
}
import com.google.android.gms.tasks.CancellationTokenSource;
import com.microblink.RecognizerClient;
RecognizerClient client = new RecognizerClient();
CancellationTokenSource cancellation = new CancellationTokenSource();
client.lookup("012345678905", cancellation.getToken()).addOnSuccessListener(product -> {
if (product.getMessage() == null) {
showProduct(product.getName(), product.getBrand(), product.isRewarded());
}
});
// Later, for example when the screen closes:
cancellation.cancel();
ProductLookup is Parcelable and carries the nullable upc, name, url, brand, and message, plus rewarded and brandParticipates (isRewarded() and isBrandParticipates() from Java), which default to false. lookup returns a Google Play services Task<ProductLookup>; the token is optional (lookup(upc) also works). blinkreceipt-recognizer exposes play-services-tasks, so Task and CancellationToken resolve without extra dependencies. To await(cancellation) from Kotlin, add org.jetbrains.kotlinx:kotlinx-coroutines-play-services to your app. This replaces com.microblink.barcode.RecognizerClient, which was removed with the barcode module. See ProductLookup.
Trip Resolution
When a scan ends with no merchant — no detector settled on a winner and the receipt has no merchant name — the SDK calls the Trip Resolution API. It runs only after the other merchant lookups have come up empty, so it is used only when every other detector failed.
- What you get. A merchant name found in time fills
ScanResults.merchantName(), withmerchantSource()set to"TRIP_RESOLUTION". Its store fillsretailerId()only when no store was already detected and the SDK recognizes it; otherwiseretailerId()staysUNKNOWN. Duplicate detection and promotion validation wait for the call, so they use the resolved merchant. - How long it takes. The request is abandoned 4 seconds after it is issued. With the bundled camera UI or a direct
RecognizerView, it runs when you callfinishedScanning(), adding at most 4 seconds there. A failed, empty, or late response leaves the merchantnull, with no error reported. - Where it applies. US scans only, and subject to your license's networking restrictions, like the SDK's other ancillary lookups.
- Network allow-lists. The call goes to
https://api.actualplatform.com/trip-resolution/v1/api/trip_resolution/resolve, not to the licensing host. If your app restricts outbound traffic with an allow-list, allowapi.actualplatform.com.
To run it on a preliminary snapshot instead — for the snapshot you will treat as final — call the new RecognizerView.preliminaryResults(true). The snapshot reports PreliminaryResult.tripResolutionRequested() == true; the no-argument preliminaryResults() is unchanged. Request it once: each request can make a network call and wait up to 4 seconds for it. If the pass can't run, or Trip Resolution doesn't apply (a non-US scan, or networking disabled by your license), you receive an empty snapshot still flagged tripResolutionRequested() — keep the snapshot you already have.
Activation scans in the camera UI
The Activation SDK submits the settled preliminary snapshot rather than the final pass, so for a scan that still has no merchant, the camera UI requests one extra preliminary pass with Trip Resolution before handing the snapshot over. In testing this added about 3 seconds to the hand-off of a merchant-less scan; it is bounded at 8 seconds, after which the snapshot is handed over without Trip Resolution. It is skipped when the snapshot already has a merchant or the scan fails the receipt check, and non-US scans are handed over with no added wait.
Known issues
The receipt data extraction update in this release undoes, fully or partly, several receipt-recognition fixes from earlier releases. It was shipped knowingly, and a fix is pending.
- Native crash. A receipt whose subtotal plus taxes rounds to a non-zero amount under $0.10 crashes the scan while its total is checked.
- Extraction accuracy may regress for tax and subtotal, return-receipt detection (
ScanResults.isReturn()), longer store-and-register transaction IDs, auth codes, Kroger and Barnes & Noble receipts, Walmart weighted items with left-indented prices, German discount formats, and a Canadian date format.
If these receipts matter to your app, stay on 2.4.1 until the fix ships.
Changed
More accurate receipt data extraction
Merchant, date, total, and product details are recognized more reliably. No API changes. See also Known issues.
- Dates. Three-letter months may start lowercase (
15-jun-2026). Full-line date-times may omit separators or the year, have a one-digit hour, seconds, or a leading "at", and end inA,P, orpM— for exampleJul6202611:46AM,Jul 23 1:50pM, andJul 17 2026 at 7:26:12 AM. - Transaction IDs.
CHKvalues (for example on Starbucks receipts),Ticket:, andOrder Id:are now recognized as transaction IDs, and validation is tighter to cut false positives. - Duplicate detection. The auth code used for duplicate detection is now the one attached to the last payment method, not the last value on the receipt that looks like an auth code.
- Line items. Once a receipt's subtotal equals the sum of its products and discounts, later frames that add products are ignored, so an already-balanced receipt no longer gains duplicate line items.
- Performance. Merging frames into a scan's results is faster on long receipts.
Fixed
- Native crash while exporting scan results with certain coupon layouts. A receipt where a coupon's linked description line held no product or coupon description no longer crashes.
- The camera opens on Android 7.0–9 when only
CAMERAis granted.CameraViewalso requiredWRITE_EXTERNAL_STORAGEon API 28 and below, so apps that requested onlyCAMERAgotonPermissionDenied()and never saw a preview.CAMERAis now the only permission checked on every supported API level; if you added the storage permission as a workaround, you can drop it. - Merchant-detection data sent with the receipt. The receipt's own phone number is no longer overwritten by the phone-match source (
"Yelp"/"DB"), which now has its ownphone_match_sourcefield, and long-tail merchant matches are now included. - Account linking: script errors
400and1005are reported asPARSING_FAILUREagain (blinkreceipt-account-linking). Since 2022 they had been reported asINTERNAL_ERROR. If your app special-casesINTERNAL_ERROR, these sessions now arrive asPARSING_FAILURE; both are non-recoverable and open no WebView.
Upgrade checklist
- Read Known issues and decide whether to upgrade now or stay on
2.4.1. - Bump the Paper Receipt BOM to
2.5.0. If you use Activation, keep the Activation BOM at1.2.0. - Remove any references to
com.microblink.ProductLookup. For UPC lookups, useRecognizerClient().lookup(upc, token)(from Kotlin,await(cancellation)it viakotlinx-coroutines-play-services). - If your app allow-lists outbound hosts, allow
api.actualplatform.com. - If you use account linking, handle
REAUTHENTICATION_REQUIREDby prompting the user to relink, and review any handling ofINTERNAL_ERRORversusPARSING_FAILURE. - If you added
WRITE_EXTERNAL_STORAGEonly for the camera, remove it.