Skip to content

feature(PayPal): collect and send device_info to determine app switch eligibility#1600

Open
santugowda wants to merge 16 commits into
braintree:mainfrom
santugowda:paypal-app-switch-device-info
Open

feature(PayPal): collect and send device_info to determine app switch eligibility#1600
santugowda wants to merge 16 commits into
braintree:mainfrom
santugowda:paypal-app-switch-device-info

Conversation

@santugowda

@santugowda santugowda commented May 14, 2026

Copy link
Copy Markdown

Summary of changes

Collects and sends device information as app_switch_context.device_info in PayPal Hermes requests when enablePayPalAppSwitch is enabled. This data allows the PayPal server to determine app switch eligibility, primarily for low-RAM devices.

Background

The PayPal App Switch flow launches the PayPal native app directly, bypassing browser checkout. On low-RAM devices, running two apps simultaneously (the merchant app + PayPal app) can cause the OS to kill the merchant app in the background due to memory pressure — leading to a broken checkout experience when the user tries to return.

The PayPal server uses memory_available_mb and memory_total_mb to gate app switch eligibility — if the device doesn't have enough RAM, the server skips app switch and falls back to browser checkout (Chrome Custom Tab), which is lighter on memory.

This PR adds collection and injection of:

  • model — device model (e.g. SM-A166U) for any device-specific overrides the server may have
  • memory_available_mb — free RAM at request time
  • memory_total_mb — total device RAM

These are injected into the request body under app_switch_context.device_info, giving the server everything it needs to decide whether to serve an app switch URL or a browser URL in the response.

JSON structure sent to BTGW

Note: The flat top-level os_type, os_version, and merchant_app_return_url keys are intentionally retained. BTGW reads these flat fields to determine app switch eligibility — sending only the nested structure broke the billing/vault app-switch flow (see commit 434accf). The nested app_switch_context now carries only device_info; the previously-nested native_app block (os_type/os_version/app_url) was removed because it simply duplicated the flat fields and is not consumed by BTGW. Retiring the flat fields depends on a future BTGW migration, tracked separately.

{
  "launch_paypal_app": true,
  "os_type": "Android",
  "os_version": "36",
  "merchant_app_return_url": "https://...",
  "app_switch_context": {
    "device_info": {
      "model": "SM-A166U",
      "memory_available_mb": 350,
      "memory_total_mb": 4096
    }
  }
}

Changes:

  • PayPalRequest: removed the now-unused NATIVE_APP_KEY, APP_URL_KEY, and OS_TYPE_VALUE constants
  • PayPalCheckoutRequest / PayPalVaultRequest: removed the nested app_switch_context.native_app block (redundant with the retained flat os_type/os_version/merchant_app_return_url keys)
  • PayPalInternalClient: injectDeviceInfo() adds device_info (model, memory_available_mb, memory_total_mb) into app_switch_context, creating the object when absent
  • Updated unit tests to verify the new request shape

Steps to Test

  1. Set enablePayPalAppSwitch = true on PayPalCheckoutRequest or PayPalVaultRequest
  2. Ensure the merchant app is configured with an App Link return URL
  3. Trigger a PayPal checkout or vault flow
  4. Intercept the request to paypal_hermes/create_payment_resource or paypal_hermes/setup_billing_agreement
  5. Confirm app_switch_context.device_info is present in the request body with model, memory_available_mb, and memory_total_mb
  6. On a device with sufficient RAM, confirm the server returns an app switch URL (PayPal app launches)
  7. On a low-RAM device, confirm the server falls back to browser checkout (Chrome Custom Tab launches)

AI Usage

Which AI Agent Was Used?

  • Claude

How was AI used?
Code generation, cross-codebase analysis (Android SDK, BTGW, backend), refactoring the request structure, on-device request/response verification, and writing tests.

Estimated AI Code Contribution

  • less than 30%
  • 30 - 60%
  • 60 - 100%

Checklist

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

Authors


Inner Source Process

Internal to PayPal contributors should fill out this section. All others can delete.

PR should follow these steps before codeowners review will begin:

  1. Comment /inner source on this PR — this will automatically add the inner source and tech lead review required labels. Open the PR in a draft state.
  2. PR should be reviewed by and approved by your team's technical lead, we do not allow LGTM reviews, there should be comments and feedback provided on all PR reviews
  3. Once the above steps are completed, comment /ready on this PR — this will automatically remove the tech lead review required label. Move the PR to ready to review.
  4. PR comments must be addressed within 24 hours, if you are unable to address within this timeframe, move the PR back to a draft state so our team knows not to review

Demo

Request body sent (key fields):

{
  "launch_paypal_app": true,
  "os_type": "Android",
  "os_version": "36",
  "merchant_app_return_url": "https://mobile-sdk-demo-site-838cead5d3ab.herokuapp.com/braintree-payments",
  "app_switch_context": {
    "device_info": {
      "model": "SM-S936U",
      "memory_available_mb": 350,
      "memory_total_mb": 11113
    }
  }
}

token=232345522J779231L

EC flow

paypal_appswitch_test.mp4

Billing Successful app-switch

BillingWithSupportedDevice.mp4

Billing on Low Ram device not app-switching

BillingWithLowRamDevice.mp4

Inner Source Checklist

  • Added all labels to the PR
  • Provide steps to test the flows changed, if applicable in the summary
  • Demo video of the functionality, if applicable
  • All upstream dependencies are merged in and this PR can be released at any time; PRs should not be opened until this is true
  • Unit tests and builds have been run locally and pass/compile as expected

@saralvasquez saralvasquez added inner source This PR is internal to PP but external to the mobile SDK team tech lead review required labels May 18, 2026
Santhosh Bhutegowda and others added 2 commits June 9, 2026 12:27
… eligibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@santugowda
santugowda force-pushed the paypal-app-switch-device-info branch from 0025eb8 to f30f4ca Compare June 9, 2026 17:29
@santugowda
santugowda marked this pull request as ready for review June 9, 2026 17:29
@santugowda
santugowda requested a review from a team June 9, 2026 17:29
@santugowda
santugowda requested a review from a team as a code owner June 9, 2026 17:29
@jaxdesmarais

Copy link
Copy Markdown
Contributor

@santugowda please follow the Inner Source Process in the PR description.

@jaxdesmarais
jaxdesmarais marked this pull request as draft June 10, 2026 14:25
- Wrap ActivityManager.getMemoryInfo() in withContext(Dispatchers.IO) to
  avoid Binder IPC on Dispatchers.Main
- Switch getJSONObject → optJSONObject in injectDeviceInfo for defense-in-depth
  (outer guard already ensures the key exists, but this makes the function safe
  in isolation)
- Extract hardcoded "ANDROID" string to OS_TYPE_VALUE constant in PayPalRequest
- Rename MODEL_KEY → DEVICE_MODEL_KEY for consistency with sibling constants
- Promote activityManager to class field in unit tests with non-zero default
  memory values in beforeEach, removing per-test mock duplication
- Add instrumentation test for PayPalVaultRequest app switch nested structure
- Add unit test for vault path device_info injection
- Add unit test for no-op when app switch enabled but no app link present

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@santugowda
santugowda marked this pull request as ready for review June 13, 2026 02:20
@santugowda
santugowda marked this pull request as draft June 13, 2026 02:22
@santugowda
santugowda marked this pull request as ready for review June 13, 2026 18:38
@santugowda

Copy link
Copy Markdown
Author

/inner source

@santugowda
santugowda marked this pull request as draft June 26, 2026 16:49
@santugowda

santugowda commented Jun 26, 2026

Copy link
Copy Markdown
Author

@anbojorquez @ayer-ribeiro could you please review this PR? Thanks!

Comment thread PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalCheckoutRequest.kt Outdated
Comment thread PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt Outdated
Comment thread PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt Outdated
…eInfo

ActivityManager.getMemoryInfo(), Build.MODEL, and JSON operations are
not I/O-bound; no dispatcher switch is needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@ayer-ribeiro ayer-ribeiro left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

@santugowda
santugowda marked this pull request as ready for review June 26, 2026 21:10
@santugowda
santugowda requested a review from ayer-ribeiro June 26, 2026 21:11
@santugowda

Copy link
Copy Markdown
Author

/ready

santugowda and others added 2 commits July 7, 2026 10:23
Signed-off-by: Sara Vasquez <98496950+saralvasquez@users.noreply.github.com>
@saralvasquez

Copy link
Copy Markdown
Contributor

In testing the PayPal checkout flows I noticed that single payment with app switch works as expected with the new fields. However, when I enable PP app switch and try to checkout with vault I get an error PayPal is currently unable to handle the request. Were you able to get the SDK demo app vault flow to work as expected?

Copilot AI left a comment

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.

Pull request overview

This PR enhances PayPal Hermes request payloads when PayPal app switch is enabled by adding device memory/model data under app_switch_context.device_info, enabling the server to determine app-switch eligibility on low-RAM devices. It also introduces a nested app_switch_context.native_app structure and updates tests/changelog accordingly.

Changes:

  • Inject device_info (model, available/total memory MB) into PayPal Hermes request bodies when app switch is enabled and an app link is available.
  • Add nested app_switch_context.native_app request structure and update request constants.
  • Update unit/instrumentation tests and add a changelog entry.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt Injects device memory/model into request JSON under app_switch_context.device_info.
PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalCheckoutRequest.kt Adds app_switch_context.native_app structure to checkout request body when app switch is enabled.
PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalVaultRequest.kt Adds app_switch_context.native_app structure to vault request body when app switch is enabled.
PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalRequest.kt Adds constants for new nested keys and device info fields.
PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalInternalClientUnitTest.kt Adds coverage asserting device_info injection and absence when app switch/context isn’t applicable.
PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalCheckoutRequestUnitTest.kt Updates request-body assertions to validate nested app_switch_context.native_app structure.
PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalVaultRequestUnitTest.kt Updates request-body assertions to validate nested app_switch_context.native_app structure.
PayPal/src/androidTest/java/com/braintreepayments/api/paypal/PayPalCheckoutRequestTest.kt Updates instrumentation tests to validate nested app-switch JSON structure.
PayPal/src/androidTest/java/com/braintreepayments/api/paypal/PayPalVaultRequestTest.kt Adds instrumentation coverage for nested native_app structure when app switch is enabled.
CHANGELOG.md Adds an entry describing the new device info fields sent for PayPal app switch eligibility.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt Outdated
Use a safe cast for the ACTIVITY_SERVICE lookup and return the original
request body if the service is unavailable, so device_info injection
never crashes a merchant's checkout on the request path.

Add a unit test covering the null-service fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@noguier

noguier commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

As a follow up, i tested the flows and inspected request that are sent to paypal_hermes/create_payment_resource or paypal_hermes/setup_billing_agreement. Confirmed that the changes are working as expected.

Comment thread CHANGELOG.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalCheckoutRequest.kt Outdated

@saralvasquez saralvasquez left a comment

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.

A couple open questions but the logic makes sense to me

Comment thread PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalRequest.kt Outdated
@noguier

noguier commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Hey there again, I tested the flows one more time, and the Checkout flow does not app switch anymore. The vault completes with no problems though. Please note we will want both flows working exactly as expected before merging. Attaching the video:

Screen.Recording.2026-07-09.at.2.43.41.PM.mov

@saralvasquez

Copy link
Copy Markdown
Contributor

I noticed the same behavior when re-testing just now

@santugowda

santugowda commented Jul 9, 2026

Copy link
Copy Markdown
Author

ok, should be a error on BE. It's the same experience on main branch as well.
Let me debug this flow at my end.

@santugowda

santugowda commented Jul 10, 2026

Copy link
Copy Markdown
Author

Single Payment is working now.

SinglePaymentInProd.mp4

Slack thread has the info about yesterday's error, yet to close out on the root cause.

Remove the nested native_app block (os_type/os_version/app_url) from the
PayPal app-switch request. Those values duplicate the flat top-level
fields (os_type/os_version/merchant_app_return_url) that BTGW reads, so the
nested copy was redundant. app_switch_context now carries only device_info.

injectDeviceInfo now creates app_switch_context when absent, since the
request no longer builds it. Updated unit tests to expect the new shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
.build()
}

private fun injectDeviceInfo(context: Context, requestBody: String): 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.

nit: will defer to android folks but append may be a better name vs inject

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.

Yeah I agree here. Append is preferable

@jaxdesmarais

Copy link
Copy Markdown
Contributor

@santugowda instrumentation tests are failing here but passing on other branches. Is this something you want to take a look at?

@saralvasquez saralvasquez left a comment

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've tested both checkout and vault and both work as expected. I just have a couple nits about comments and I'd like to see the change Jax mentioned about inject vs append. I'll approve once we have a clean build besides the known security check failure

santugowda and others added 3 commits July 14, 2026 16:20
…edundant test comments

Address review feedback on PR braintree#1600:
- Rename injectDeviceInfo -> appendDeviceInfo per reviewer preference
- Remove unnecessary explanatory comments in PayPal request unit tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…text.native_app

createRequestBody no longer emits the nested app_switch_context.native_app
structure, so the instrumentation tests now assert the flat app switch fields
(os_type, os_version, merchant_app_return_url) and that app_switch_context is
absent. Fixes the failing PayPal instrumentation tests on this branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@santugowda

Copy link
Copy Markdown
Author

Pushed the changes addressing all the above nit comments, append refactor and instrumentation changes.

@saralvasquez saralvasquez left a comment

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.

Once we get a clean build I'd say this is ready to go in

@santugowda

Copy link
Copy Markdown
Author

CI is 🟢 except for the known Dependency Review failure.

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

Labels

inner source This PR is internal to PP but external to the mobile SDK team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants