Skip to content

Compose smart button#1531

Merged
saperi22 merged 30 commits intoui-components-compose-supportfrom
compose-smart-button
Mar 11, 2026
Merged

Compose smart button#1531
saperi22 merged 30 commits intoui-components-compose-supportfrom
compose-smart-button

Conversation

@saperi22
Copy link
Copy Markdown
Collaborator

@saperi22 saperi22 commented Feb 12, 2026

Summary of changes

  • Compose Smart button
  • Added analytics for presentment and selection.

Testing:

  • Tested AuthTab flow - works
  • Tested CCT flow - works
  • Tested app switch flow - works

Dependent on braintree/browser-switch-android#127 being merged in and released.

Checklist

  • Added a changelog entry
  • Relevant test coverage
  • Tested and confirmed payment flows affected by this change are functioning as expected

Authors

List GitHub usernames for everyone who contributed to this pull request.

Comment thread UIComponents/build.gradle
Use anticipated dependency version.
@saperi22 saperi22 marked this pull request as ready for review March 3, 2026 19:15
@saperi22 saperi22 requested a review from a team as a code owner March 3, 2026 19:15
@noguier
Copy link
Copy Markdown
Contributor

noguier commented Mar 4, 2026

I tested this on my device and the flow works correctly, the only two things i noticed:

  1. When I choose Payment Button Compose from the demo app menu , and the app switched to the new screen i see lingering "An error occurred: User did not complete payment flow"
  2. The browser payment flow works great, but I was not able to app switch to PP app

PS:
Interestingly, I couldn't app switch from Payment Buttons or PayPal modules on this branch. But i am app switching correctly on main

Update this has been resolved.

saperi22 added 2 commits March 4, 2026 12:14
Restrict view model and repository to library group.
Make sure that handle return is called only after flow is launched.
@noguier
Copy link
Copy Markdown
Contributor

noguier commented Mar 4, 2026

i also wondering why the Instrumentation Tests are failing, I havent see this error before Caused by: org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find com.braintreepayments.api:browser-switch:3.5.0.

@saperi22
Copy link
Copy Markdown
Collaborator Author

saperi22 commented Mar 4, 2026

i also wondering why the Instrumentation Tests are failing, I havent see this error before Caused by: org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find com.braintreepayments.api:browser-switch:3.5.0.

Browser Switch 3.5.0 hasn't been published yet. I was using a 3.4.1-SNAPSHOT and didn't want to forget to change it. So, I made the change to 3.5.0. It is expected to fail until browser switch is published.

@noguier
Copy link
Copy Markdown
Contributor

noguier commented Mar 4, 2026

ahhh totally makes sense, thank you!

}
}

suspend fun getPendingRequest(): String? {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we return a non null string here or throw an exception if the dataStore does not contain a pendingnRequest?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

https://github.com/braintree/braintree_android/pull/1531/changes#diff-23d7463af226bd84a2ce92dfa837cbda03e749c683c45da489b4439978fb988aR161

I handle an empty pendingRequest here: https://github.com/braintree/braintree_android/pull/1531/changes#diff-23d7463af226bd84a2ce92dfa837cbda03e749c683c45da489b4439978fb988aR161

Non-null would be good, but I think I'd just have to return an empty string at the data store level and rest of the code would remain relatively the same. A few return types can be made non-null.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not sure if we can throw an exception. We could log the exception and return a failure to the merchant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the preferred error handling pattern is to throw exceptions and catch them at some level. I think at the ViewModel we could have a try/catch, then the view state could emit the error to the view.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I skipped the view model and am using the repository directly in the composable.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I am of the opinion that the repository should be able to send out an empty string.
I'm handling the exception at the view level where we know the state of the flow and we expect this value to be not empty.
Would you agree with my reasoning?

Copy link
Copy Markdown
Contributor

@saralvasquez saralvasquez left a comment

Choose a reason for hiding this comment

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

Looks great! Tested on physical device and emulator and both worked as expected. Just a quick question. Did we want to set up a similar kind of layout for the different button color options as we did for the XML based buttons page?

@OptIn(ExperimentalCoroutinesApi::class)
class PayPalPendingRequestRepositoryTest {

@get:Rule
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cool! That makes everything so much cleaner

@saperi22
Copy link
Copy Markdown
Collaborator Author

saperi22 commented Mar 9, 2026

Looks great! Tested on physical device and emulator and both worked as expected. Just a quick question. Did we want to set up a similar kind of layout for the different button color options as we did for the XML based buttons page?

I have a different ticket to setup the demo app. This one is to make sure the flow works.
I don't want to bloat the PR and sit on it forever.

@saperi22 saperi22 merged commit daf8428 into ui-components-compose-support Mar 11, 2026
13 of 14 checks passed
@saperi22 saperi22 deleted the compose-smart-button branch March 11, 2026 18:21
saperi22 added a commit that referenced this pull request Mar 31, 2026
* Paypal buttons compose (#1528)

* WIP: add dependencies and PayPalButton

* paypal button

* Add logo offset.

* cleanup

* cleanup

* Working circular progress indicator

* Add corner shape

* WIP: interaction states

* focus indicator

* make linter gods happy

* linter

* linter

* use colorResource

* update focus padding to use a resource

* remove spacers from preview

* Compose smart button (#1531)

* Compiling demo of PayPal smart button on Compose. Doesn't run successfully. The flow is broken.

* Reorder buttons in demo app

* refactor file name

* fix button size

* fix button size in demo

* Skip modular and store pendingRequest state within composable.
Works end to end and shows a successful response.

* hoist style to parent composable

* update demo app

* remove unused classes

* linter'

* remember PayPalLauncher and PayPalClient beyond recomposition

* Address PR comments

* surface failure scenarios to host app

* persist pending request to data store

* linter

* Cleanup.
Use anticipated dependency version.

* Use scope properly

* linter fixes

* Address PR comments.
Restrict view model and repository to library group.
Make sure that handle return is called only after flow is launched.

* make function private

* add tests

* Add analytics events for paypal compose button flow

* fix CI

* fix test.

* remove viewmodel to simplify flow

* linter

* add kdoc on composable

* Address PR comments

* add jvmoverloads

* make repository return a non null value for pending request

* Venmo compose button (#1541)

* Compiling demo of PayPal smart button on Compose. Doesn't run successfully. The flow is broken.

* Reorder buttons in demo app

* refactor file name

* fix button size

* fix button size in demo

* Skip modular and store pendingRequest state within composable.
Works end to end and shows a successful response.

* hoist style to parent composable

* update demo app

* remove unused classes

* linter'

* remember PayPalLauncher and PayPalClient beyond recomposition

* Address PR comments

* surface failure scenarios to host app

* persist pending request to data store

* linter

* Cleanup.
Use anticipated dependency version.

* Use scope properly

* linter fixes

* Address PR comments.
Restrict view model and repository to library group.
Make sure that handle return is called only after flow is launched.

* make function private

* add tests

* Add analytics events for paypal compose button flow

* fix CI

* fix test.

* remove viewmodel to simplify flow

* linter

* add kdoc on composable

* Address PR comments

* Venmo buttons compose UI

* Update analytics

* Venmo compose button

* update demo app

* rename options

* update checkout values

* consolidate extension functions

* refactoring

* refactor

* add kdoc

* Update repository to take in a moduleName parameter and update tests

* rename file

* Add exception message

* update venmo button content description

* update to 3.5.1 of browser switch

* rename buttons

* update changelog

* Making ActivityResultRegistry a required parameter for Venmo/PayPal Launcher constructors. This preserves backwards compatibility.

* Renaming VenmoSmartButton to VenmoButton

* Add Changelog entry

* linter fix

* Update UIComponents/src/main/java/com/braintreepayments/api/uicomponents/compose/PayPalButton.kt

Co-authored-by: Sara Vasquez <98496950+saralvasquez@users.noreply.github.com>

* Update UIComponents/src/main/java/com/braintreepayments/api/uicomponents/compose/VenmoButton.kt

Co-authored-by: Sara Vasquez <98496950+saralvasquez@users.noreply.github.com>

* update changelog.md

* update demo app

* change requireNotNull to if condition

* Sends merchants a Failure result when compose is not started from a ComponentActivity

* linter

* address PR comments

* enable button after a failure

* enable button after a failure

* fix critical issue where flow freezes when activity is null

* remove setting intent to null, need documentation for merchant to that on their end

---------

Co-authored-by: Sara Vasquez <98496950+saralvasquez@users.noreply.github.com>
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.

5 participants