anjlab/android-inapp-billing-v3: a Play Billing wrapper whose maintainers have stepped back
A lightweight implementation of Android In-app Billing Version 3
At a glance
- What is it?
- A small Java library that hides the Play Billing client behind a BillingProcessor and a four-method handler, updated to target Play Billing 9.1.0. The convenience is real, the deprecated path quietly flattens every subscription to a single offer, and the project is openly looking for maintainers while sitting on a custom licence.
- Who is it for?
- Adopt this library if you want Play Billing behind a four-method callback interface and you can afford to be the person who does the next upgrade, because the Anjlab team states plainly that no further bug fixes or new features will be implemented by them and only outside pull requests are reviewed. Read LICENSE before you do, since GitHub cannot classify it and a payment-adjacent dependency deserves a deliberate decision rather than a default.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 51 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The library is fine, the maintainership is not
The first thing the README says after the title is a request for maintainers, and the repository carries a topic tag saying the same. The wording is unambiguous: only pull requests from external contributors are being reviewed, accepted and welcomed, and no more bug fixes or new features will be implemented by the Anjlab team. That sentence is the most important line in the whole project for anyone evaluating it, and it deserves to be read as a fact about the dependency rather than as a formality. This library sits between your app and Google's billing client. When Play Billing moves, and it moves regularly, somebody has to update the wrapper, and the people who built it have said publicly that it will not be them. External pull requests are still welcome, which is the mitigating half of the sentence, but a pull request you do not write is a pull request you do not control. The repository is not archived and the last push was on 2026-08-10, so nothing here is abandoned. It is being handed over, and the difference between those two states matters when the dependency handles money.
One coordinate on Maven Central, and a compatibility floor Google sets
Installation is one Gradle dependency:
repositories {
mavenCentral()
}
dependencies {
implementation 'com.anjlab.android.iab.v3:library:3.0.0'
}The README carries a Maven Central badge, so the artifact is published rather than built from source, and the version to pin is 3.0.0. The interesting part is what the upgrade did to your build. Version 3.0.0 raises minSdkVersion to 23, which is Android 6.0, and the README explains why in one sentence: Play Billing 8.1 dropped support for API 21 and 22, so this library has nowhere to sit below that line. It also requires compileSdk 35 or newer, because Play Billing 9.x depends on androidx.core 1.15.0, which enforces that floor. So the compatibility window of this library is not chosen by its authors, it is inherited from Google, and the causal chain is legible in the README: Google drops an API level, the wrapper's floor moves, your build breaks. The escape hatch is stated too. If you still ship to API 21 or 22, pin to 2.2.0 or raise your own minSdkVersion. That advice is dated by definition, because it names a version that will eventually stop receiving the Google updates it was pinned to get.
The deprecated path gives you one offer, and the price you show is the price charged
Version 3.0.0 targets com.android.billingclient:billing:9.1.0. Play Billing 9 replaced the old listing types with ProductDetails, and this library kept the old names alive: the SkuDetails type and the getPurchaseListingDetailsAsync and getSubscriptionListingDetailsAsync methods are preserved for source compatibility and marked @Deprecated. Underneath, they translate from ProductDetails, and that translation is where the real behaviour change lives. Play Billing 9 flattens multi-offer subscriptions down to a single offer, so the wrapper has to choose one, and it chooses by eligibility: a free trial first, then a discounted introductory offer, then the base plan. It then uses that same offer for the purchase itself, which is the property that makes it safe. The price your UI displays is the price the customer is charged, rather than a display value and a charge value resolved independently and occasionally disagreeing. What you lose is equally concrete. If your subscription uses multiple promotional offers or pricing phases, this path cannot represent them, and the README tells you to migrate to the ProductDetails-based API instead. Keeping the old methods working was the right call for compatibility, and it also means an upgrade can leave you on a deprecated path that quietly cannot do what your product design requires.
Four callbacks, two error namespaces, and a cancellation reported as an error
The whole integration surface is the BillingProcessor class plus the IBillingHandler interface it takes as its third constructor argument. You instantiate it in your Activity, passing a Context, a licence key, and your handler, then call initialize:
bp = new BillingProcessor(this, "YOUR LICENSE KEY FROM GOOGLE PLAY CONSOLE HERE", this);
bp.initialize();The handler has four required methods and one optional. onBillingInitialized tells you the client is ready. onProductPurchased receives a product ID and a PurchaseInfo, and is the only place a purchase becomes real. onPurchaseHistoryRestored fires once Play has returned everything you already own, which is the callback that stops a reinstalled app from charging again for something the customer bought. onBillingError carries an int and a Throwable, and the interface to read here is the error code space, which is split in two. Codes that come from Play are BillingClient.BillingResponseCode constants, and codes this library raises itself are Constants.BILLING_ERROR_* with values of 100 and above, so you can tell them apart by magnitude. A detail worth knowing before you write the handler: the cancellation of the buy dialog arrives through onBillingError as USER_CANCELED, so a cancel path is an error path, not a neutral event. The fifth method, onPurchasePending, is a default method, which is the only hint that it is optional.
The pending callback exists so your app does not grant the item early
onPurchasePending is the most instructive method in the interface, and the README is unusually direct about its contract. It fires when Play reports a purchase in the PENDING state, which is the deferred payment case: cash at a convenience store, carrier billing, or a slow card authorisation. The rule it carries is short and absolute. The purchase is not entitled yet, so do not grant anything there, show a payment pending UI instead, and wait for the transition to PURCHASED, which arrives through onProductPurchased. That instruction is a security and accounting boundary, and it is the kind of thing a convenience wrapper can quietly violate if it maps pending to success for you. It does not. The same caution applies to how you read purchase results generally: this library hands you a product ID and a PurchaseInfo and leaves entitlement decisions to your code. Nothing here verifies that a customer is entitled beyond what the Play client reports, and the one verification hook available is the licence key in the constructor, which is the subject of the next point. Because the method is a default method on the interface, a project that never overrides it simply never learns that a purchase was pending, which is fine for consumables and a data loss bug for anything else.
The licence key parameter, and the correlation option that sends nothing to Google
The constructor's middle argument is your licence key from the Google Developer console, described as being used to verify purchase signatures, and the README tells you exactly where to find it: Google Play Console, your app name, then Services and APIs. It also tells you that you can pass NULL to skip the check. That is stated as a convenience and it is a real decision: a NULL here means you are not verifying signatures locally, so your server is relying entirely on what the Play client reports. For an app shipping digital goods, that verification is the main defence against a forged purchase, and turning it off is a decision that belongs in a threat model rather than in a code review comment. Alongside it the library accepts Play's optional obfuscated identifiers on purchase, subscribe and updateSubscription. You pass an obfuscatedAccountId and an obfuscatedProfileId, and the stated purpose is to correlate a purchase with your own account records without sending Google any personal data. That is a different kind of privacy decision from the licence key: the identifiers are deliberately non-identifying on Google's side while remaining useful to you, which is a pattern worth copying even in projects that do not use this library.
What the repository shows about running the project, and where it stops being the right choice
The top-level listing describes a project with real process around it. There is a connected-check workflow with a build badge, a checkstyle.xml, a gradlew wrapper alongside build.gradle and settings.gradle, a sample/ directory with its own manifest, resources and sources, and three documents that matter more than the README: CHANGELOG.md, UPGRADING.md and ISSUE_TEMPLATE.md. The existence of a version-by-version upgrade guide is the tell that this library has survived more than one breaking change and expects to survive the next. The history is preserved too, with the original Google v2 billing implementation archived on a v2_billing_1_1_0 branch, so the project has been through a rename of ownership and a rename of API at least once. One item needs attention before you adopt. The licence is recorded as a custom one that GitHub cannot classify, so there is no standard identifier to check against and the LICENSE file is the only source. For a library that sits on the payment path, read it yourself before you depend on it. The clearest case for going around this library is subscriptions with multiple promotional offers or pricing phases, because the deprecated compatibility path flattens those, and the documented answer there is the ProductDetails-based API rather than another version of this wrapper.
Editorial conclusion
Adopt this library if you want Play Billing behind a four-method callback interface and you can afford to be the person who does the next upgrade, because the Anjlab team states plainly that no further bug fixes or new features will be implemented by them and only outside pull requests are reviewed. Read LICENSE before you do, since GitHub cannot classify it and a payment-adjacent dependency deserves a deliberate decision rather than a default. Two things to verify in your own project first: that your minSdk is at least 23 and compileSdk at least 35, or the dependency will not resolve cleanly, and whether your subscriptions use pricing phases or more than one promotional offer, because the deprecated SkuDetails path in 3.0.0 collapses those to a single eligible offer. If either answer goes the wrong way, the documented escape is the ProductDetails-based API described in UPGRADING.md.
Frequently asked questions
How do I add anjlab/android-inapp-billing-v3 to an Android project?
Add the Maven Central coordinate com.anjlab.android.iab.v3:library:3.0.0 as an implementation dependency. The README also requires your project to build against compileSdk 35 or newer and minSdk 23 or newer, which version 3.0.0 raised as a breaking change.
Is anjlab/android-inapp-billing-v3 still being maintained?
The repository is not archived and the last push was on 2026-08-10, but the README states the project is looking for maintainers and that no further bug fixes or new features will be implemented by the Anjlab team. Only pull requests from external contributors are reviewed and accepted.
What happens to SkuDetails and getPurchaseListingDetailsAsync in version 3.0.0?
They are preserved for source compatibility but marked @Deprecated, and they translate from Play Billing 9 ProductDetails, which flattens multi-offer subscriptions to one offer. The library picks the offer you are eligible for, a free trial first, then a discounted introductory offer, then the base plan. Consumers needing multiple promotional offers or pricing phases should move to the ProductDetails-based API.
What must I do when onPurchasePending fires in android-inapp-billing-v3?
Do not grant the item. The README states the purchase is not entitled yet, so show a payment pending UI and wait for the transition to PURCHASED, which arrives through onProductPurchased. Pending covers deferred payment such as cash at a convenience store, carrier billing and slow card authorisation.
Can I skip licence key verification in android-inapp-billing-v3?
The BillingProcessor constructor accepts a licence key used to verify purchase signatures, and the README says you can pass NULL to skip the check. The key is found in Google Play Console under your app name, then Services and APIs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/anjlab-android-inapp-billing-v3)