Skip to content

Add protocol-agnostic checkout events for Android - #767

Open
markmur wants to merge 22 commits into
prototype/protocol-agnostic-eventsfrom
protocol-agnostic-events-android
Open

markmur wants to merge 22 commits into
prototype/protocol-agnostic-eventsfrom
protocol-agnostic-events-android

Conversation

@markmur

@markmur markmur commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Stack

Top to bottom:

  1. Web: Add protocol-agnostic checkout events for web #766
  2. Android (this PR): Add protocol-agnostic checkout events for Android #767
  3. Swift events: Protocol-agnostic checkout events for Swift #748
  4. Swift protocol namespace: Namespace Swift protocol payloads #777

Android integrations now receive Kit-owned checkout snapshots through typed start, update, complete, and failure callbacks in the Kotlin presentation builder and Java listener API. The snapshot preserves domain fields and extensions while excluding ucp; line items remain unchanged. The four supported protocol change notifications share one update callback with equal-snapshot deduplication.

Adds Open, Handled, and Cancel link policies, keeps protocol clients internal, and removes client injection from normal presentation. Both demo presentation modes and their documentation use the new callbacks. Adapter state is scoped to each presentation, queued callbacks stop after teardown or failure, and completion preserves unrelated preloads.

This is an intentional prerelease API change: failure callbacks now receive CheckoutFailureEvent, and protocol-client registration is replaced by Kit callbacks. The README includes migration guidance and the public API baseline is updated. React Native will need a coordinated callback migration when it adopts the next published Android SDK.

Validation passed: 496 library tests, 47 protocol tests, 98 demo tests, Android library and demo builds (debug and release), static analysis, lint, and both API baseline checks.

@markmur
markmur requested a review from a team as a code owner September 14, 2026 12:16
@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Sep 14, 2026
@markmur markmur self-assigned this Sep 14, 2026
@bitrise

bitrise Bot commented Sep 14, 2026

Copy link
Copy Markdown

Install this build

Open Tophat, select your target device, then click Install. Links open on the Mac running Tophat.

SDK Install
Kotlin Install with Tophat

Checkout Kit E2E results

Status Tags Target Platform OS version tag Device
launch, checkout-presentation, checkout-completion, buyer-identity, preload kotlin android latest Google Pixel 9
Android 17.0

@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown

Package Size

Platform Artifact Base Head Delta
Android release AAR 366.6 KiB 398.5 KiB +32.0 KiB
Android file breakdown
File Base Head Delta
classes.jar 390.5 KiB 424.9 KiB +34.4 KiB
res/layout/checkout_view_content.xml 2.6 KiB 2.6 KiB 0 B
res/layout/checkout_sheet_content.xml 2.0 KiB 2.0 KiB 0 B
res/values/values.xml 1.3 KiB 1.3 KiB 0 B
R.txt 1.2 KiB 1.2 KiB 0 B
AndroidManifest.xml 922 B 922 B 0 B
proguard.txt 798 B 798 B 0 B
res/drawable/close.xml 431 B 431 B 0 B
res/menu/checkout_menu.xml 354 B 354 B 0 B
META-INF/com/android/build/gradle/aar-metadata.properties 157 B 157 B 0 B

Measured from the PR base SHA and PR head SHA. The file breakdown shows uncompressed sizes within each package artifact, so individual files do not sum to the compressed artifact total. This comment reports package artifact sizes only; it is not a final app binary-size report.

@markmur
markmur force-pushed the prototype/protocol-agnostic-events branch from 67f6e15 to 6bcf2dd Compare September 14, 2026 17:23
@markmur
markmur force-pushed the protocol-agnostic-events-android branch from 01d1ed8 to 1eefb47 Compare September 14, 2026 17:23
@markmur
markmur force-pushed the prototype/protocol-agnostic-events branch from 6bcf2dd to 47abfe1 Compare September 15, 2026 12:19
@markmur
markmur force-pushed the protocol-agnostic-events-android branch 2 times, most recently from 38ab010 to c3304f1 Compare September 15, 2026 13:19
@markmur
markmur force-pushed the prototype/protocol-agnostic-events branch from 47abfe1 to 8e3dbfb Compare September 15, 2026 13:19

@tiagocandido tiagocandido 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.

One README issue I could not attach inline (unchanged line outside the diff): line ~202 still says "callbacks and protocol client are fixed when the view is created" — the protocol client is gone in this PR; trim to just callbacks.

```

`ec.window.open_request` is handled by your registered `CheckoutProtocol.windowOpen` handler if you provide one. Otherwise, Checkout Kit opens web links in Android Custom Tabs and non-web links through Android intents.
`onStart` and `onUpdate` observe checkout state; they do not send mutations to the checkout running in the WebView.

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.

Preload consumes ec.start with the noop listener bound and it is not replayed, so with preloading on by default onStart mostly will not fire — is that intended? If yes, worth saying so here and in the preload section; if not, replay the latest snapshot when a real listener binds.

public class CheckoutCompleteEvent internal constructor(public val checkout: Checkout)

/** The payload delivered when checkout cannot continue. */
public class CheckoutFailureEvent internal constructor(public val error: CheckoutException)

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.

Is internal constructor deliberate? Kotlin consumers cannot construct these for tests (the RN wrapper does exactly that with CheckoutException today), Java bypasses it anyway, and Swift's events are public. I'd make them public.

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.

There are obviously differences between the two types (e.g. number of constructor args).. But the comments are pulling constructor visibility in opposite directions..

Make Checkout's constructor to reduce public API surface, and make event constructors public for testability.

It feels possible that test fixtures might also want to construct a Checkout instance, (RN doesn't today, and it does construct events - but maybe that's more of a current impl detail).

Maybe it's worth thinking about what our policy should be for Kit-emitted models - are they SDK-owned/read-only outputs. Or are they public value types consumers should be able to construct for fixtures

*/
@Serializable(with = CheckoutSerializer::class)
@Suppress("LongParameterList")
public data class Checkout(

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 public data class means copy and the positional constructor are in lib.api, so every future field is a binary-breaking change. Since only the SDK produces snapshots, I'd make the constructor internal — also squares oddly with the events being locked down.

override fun deserialize(decoder: Decoder): Checkout {
val input = decoder as? JsonDecoder
?: throw SerializationException("Checkout can only be deserialized from JSON")
val fields = input.decodeJsonElement().jsonObject

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.

.jsonObject throws IllegalArgumentException on a non-object, but callers are told to catch SerializationException. as? JsonObject ?: throw SerializationException(...) keeps it consistent.

@markmur
markmur force-pushed the prototype/protocol-agnostic-events branch from 8e3dbfb to 0775be4 Compare September 16, 2026 12:11
@markmur
markmur force-pushed the protocol-agnostic-events-android branch from c3304f1 to 157560e Compare September 16, 2026 12:11
@markmur
markmur force-pushed the prototype/protocol-agnostic-events branch from 5a48d81 to 0f3715c Compare September 18, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

#gsd:50662 Rebase Checkout Kit on UCP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants