Skip to content

Conversation

@bboothe-branch
Copy link

Reference

EMT 2431 -- <TITLE>.

Description

Added in the main structure of the potential update for Android Billing Library using Reflection

Testing Instructions

Download and run test app. Currently errors.

Risk Assessment [HIGH || MEDIUM || LOW]

High Risk as this may break android billing entirely if not done correctly.

  • I, the PR creator, have tested — integration, unit, or otherwise — this code.

Reviewer Checklist (To be checked off by the reviewer only)

  • JIRA Ticket is referenced in PR title.
  • Correctness & Style
    • Conforms to AOSP Style Guides
    • Mission critical pieces are documented in code and out of code as needed.
  • Unit Tests reviewed and test issue sufficiently.
  • Functionality was reviewed in QA independently by another engineer on the team.

cc @BranchMetrics/saas-sdk-devs for visibility.

@matter-code-review
Copy link
Contributor

matter-code-review bot commented Sep 25, 2025

Code Quality bug detected new feature maintainability

Reference

SDK-2431 -- Emt 2431 - Draft PR.

Description

Summary By MatterAI MatterAI logo

🔄 What Changed

This pull request upgrades the Google Play Billing Library dependency from version 6.0.1 to 8.0.0. It introduces a new BillingGooglePlayV8.kt implementation to handle in-app purchases and subscriptions using the updated Billing Library APIs, replacing a previous stub. The BillingGooglePlayV6V7.kt file has also been modified, removing override keywords from its startBillingClient and logEventWithPurchase methods and duplicating helper functions for creating BranchUniversalObjects and logging purchase events.

🔍 Impact of the Change

This upgrade modernizes the Branch SDK's integration with Google Play Billing, enabling compatibility with the latest features and improvements in Billing Library v8. However, the current implementation contains critical functional bugs, such as an empty PurchasesUpdatedListener and an incorrect instance creation in BillingGooglePlayV8.getInstance(), which will prevent proper purchase tracking. Additionally, significant code duplication between BillingGooglePlayV6V7.kt and BillingGooglePlayV8.kt introduces maintainability challenges and potential inconsistencies. The changes to BillingGooglePlayV6V7.kt also raise questions about its intended role and interface adherence.

📁 Total Files Changed

  • Branch-SDK/build.gradle.kts: Updated Google Play Billing library dependency from 6.0.1 to 8.0.0.
  • Branch-SDK/src/BillingGooglePlayModules/BillingV6V7/BillingGooglePlayV6V7.kt: Modified to remove override keywords from key methods and added duplicated helper functions for purchase event logging.
  • Branch-SDK/src/BillingGooglePlayModules/BillingV7V8/BillingGooglePlayV8.kt: Removed a previous stub implementation for Billing V8.
  • Branch-SDK/src/BillingGooglePlayModules/BillingV8/BillingGooglePlayV8.kt: Added a new, comprehensive implementation for Google Play Billing Library v8 integration.

🧪 Test Added

N/A

🔒Security Vulnerabilities

N/A

Testing Instructions

N/A

Risk Assessment [MEDIUM]

This PR involves a major version upgrade of a critical dependency and significant refactoring of billing logic. The identified functional bugs (empty listener, incorrect instance creation) and code duplication introduce a medium risk of incorrect purchase tracking and system instability. Thorough testing is required.

  • I, the PR creator, have tested — integration, unit, or otherwise — this code.

Reviewer Checklist (To be checked off by the reviewer only)

  • JIRA Ticket is referenced in PR title.
  • Correctness & Style
    • Conforms to AOSP Style Guides
    • Mission critical pieces are documented in code and out of code as needed.
  • Unit Tests reviewed and test issue sufficiently.
  • Functionality was reviewed in QA independently by another engineer on the team.

cc @BranchMetrics/saas-sdk-devs for visibility.

Tip

Quality Recommendations

  1. Implement the PurchasesUpdatedListener in both BillingGooglePlayV6V7.kt and BillingGooglePlayV8.kt to properly handle purchase state changes and ensure accurate event logging.

  2. Correct the getInstance() method in BillingGooglePlayV8.kt to return an instance of BillingGooglePlayV8 instead of BillingGooglePlay to prevent runtime errors.

  3. Refactor duplicated helper methods (createBUOWithSubsProductDetails, createBUOWithInAppProductDetails, createAndLogEventForPurchase) into a common base class or utility object to improve maintainability and reduce code redundancy.

  4. Clarify the intended role of BillingGooglePlayV6V7.kt and ensure its methods (startBillingClient, logEventWithPurchase) correctly adhere to the BillingGooglePlayInterface contract if it is still meant to implement it.

  5. Enhance error handling in queryProductDetailsAsync callbacks to not just log errors but also propagate them or implement a more robust strategy for handling failed product detail queries.

  6. Add explicit null checks or provide default values for product.subscriptionOfferDetails and product.oneTimePurchaseOfferDetails in createBUOWithSubsProductDetails and createBUOWithInAppProductDetails to avoid returning empty BranchUniversalObjects silently.

Tanka Poem ♫

Billing client awakes,
Version eight now takes the lead,
New flows start to bloom.
Yet, a listener waits, empty,
Code duplication's echo. 🚀

Sequence Diagram

sequenceDiagram
    participant Client
    participant BillingV8 as BillingGooglePlayV8
    participant GooglePlay as Google Play Billing Service
    participant BranchSDK as Branch SDK

    Client->>BillingV8: startBillingClient(callback: (Boolean) -> Unit)
    BillingV8->>GooglePlay: startConnection(BillingClientStateListener)
    GooglePlay-->>BillingV8: onBillingSetupFinished(billingResult)
    BillingV8-->>Client: callback(true/false)

    Client->>BillingV8: logEventWithPurchase(context: Context, purchase: Purchase)

    Note over BillingV8: Querying Subscription Product Details
    BillingV8->>GooglePlay: queryProductDetailsAsync(querySubsProductDetailsParams)
    GooglePlay-->>BillingV8: onProductDetailsResponse(billingResult, subsProductDetailsList)

    alt BillingResult.OK
        BillingV8->>BillingV8: createBUOWithSubsProductDetails(product: ProductDetails)
        BillingV8->>BillingV8: createAndLogEventForPurchase(context, purchase, contentItems, currency, revenue, productType)
        BillingV8->>BranchSDK: logEvent(BRANCH_STANDARD_EVENT.PURCHASE)
    else Error
        BillingV8->>BranchSDK: BranchLogger.e("Failed to query subscriptions...")
    end

    Note over BillingV8: Querying In-App Product Details
    BillingV8->>GooglePlay: queryProductDetailsAsync(queryProductDetailsParams)
    GooglePlay-->>BillingV8: onProductDetailsResponse(billingResult, productDetailsList)

    alt BillingResult.OK
        BillingV8->>BillingV8: createBUOWithInAppProductDetails(product: ProductDetails, quantity: Int)
        BillingV8->>BillingV8: createAndLogEventForPurchase(context, purchase, contentItems, currency, revenue, productType)
        BillingV8->>BranchSDK: logEvent(BRANCH_STANDARD_EVENT.PURCHASE)
    else Error
        BillingV8->>BranchSDK: BranchLogger.e("Failed to query in-app products...")
    end
Loading

@matter-code-review
Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use MatterAI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with MatterAI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

@@ -0,0 +1,40 @@
plugins {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these gradle files necessary to get the library version?

Copy link
Contributor

@gdeluna-branch gdeluna-branch Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to do this with
String billingClientVersion = com.android.billingclient.BuildConfig.VERSION_NAME;
in Java (or Kotlin) instead

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean having that line in the main gradle file instead? So I'll remove the product flavors.

Doe that replace this? :compileOnly("com.android.billingclient:billing:6.0.1")

isRequired = isReleaseBuild()
}

flavorDimensions.add("billing")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep things simple and just check the buildconfig version string and conditionally execute the right library implementation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Removing flavor dimensions

@@ -1,5 +1,7 @@
include(":Branch-SDK")
include(":Branch-SDK-TestBed")
include (":BillingGooglePlayModules:BillingV6V7", ":BillingGooglePlayModules:BillingV8")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, revert this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this as well

public class BillingGooglePlayReflection {
public static BillingGooglePlayInterface getBillingLibraryVersion() {
try {
// Check for a class added in version 8.0 or higher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be hopefully be simplified with my suggestion. Just parse the major version

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a specific method to grab the version so the checks look for what's available in the version that is being used.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realizing that was for the reflection section. Replacing it!

@matter-code-review
Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use MatterAI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with MatterAI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3


public class BillingGooglePlayReflection {
public static BillingGooglePlayInterface getBillingLibraryVersion() {
String billingClient = com.android.billingclient.BuildConfig.VERSION_NAME;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the variable name a bit more relevant like billingClientVersion

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to billingClientVersionString


try {
int majorIndex = billingClient.indexOf(".");
String majorVersion = billingClient.substring(0, majorIndex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth making this version extraction bit into a utility function since this could be used in other places as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created function called dependencyMajorVersionFinder and added it to DependencyUtils.kt

Function takes in a string with the depenency name and pulls out the major version number.

Kept the Version Name extract itself in the BillingGooglePlayReflection class so that the function can take in any method of calling the version name depending on what the class offers.

}

} catch (Exception e) {
System.err.println("Error parsing billing client version: " + e.getMessage());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use BranchLogger.e instead of System.err.println

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to BranchLogger.e

toolVersion = "0.8.10"
}

//configurations.all {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted

@matter-code-review
Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use MatterAI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with MatterAI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

@matter-code-review
Copy link
Contributor

Important

PR Review Skipped

PR review skipped as per the configuration setting. Run a manually review by commenting /matter review

💡Tips to use MatterAI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with MatterAI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

@bboothe-branch
Copy link
Author

Hey @gdeluna-branch I updated all of your comments and also added the V8 logic. Is there a better way to reference them in the BillingGooglePlayReflection.java file? Also, I'm hesitant to delete the BillingGooglePlay.kt file until everything is done and working.

Also still need to build the gradle to reference each version properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants