Skip to content

Centralize Kotlin checkout message ingress validation - #645

Open
markmur wants to merge 5 commits into
swift-message-rejection-lifecyclefrom
kotlin-message-rejection-lifecycle
Open

Centralize Kotlin checkout message ingress validation#645
markmur wants to merge 5 commits into
swift-message-rejection-lifecyclefrom
kotlin-message-rejection-lifecycle

Conversation

@markmur

@markmur markmur commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

What changes are you making?

This PR brings the warning-and-drop message ingress approach from #644 to Kotlin. It is stacked on the Swift implementation so the team can compare this two-PR stack directly with the diagnostics subscription approach in #642 and #643.

It removes onMessageRejected from Configuration without adding a replacement callback or a coroutines dependency.

Incoming AndroidX WebKit messages now pass through an internal CheckoutMessageIngressPolicy before reaching the checkout protocol client:

val incomingMessage = IncomingCheckoutMessage(
    origin = sourceOrigin,
    isMainFrame = isMainFrame,
)

when (val decision = ingressPolicy.evaluate(incomingMessage)) {
    CheckoutMessageIngressPolicy.Decision.Accepted -> receiveMessage(message)
    is CheckoutMessageIngressPolicy.Decision.Rejected -> {
        handleMessageRejection(decision.rejection)
    }
}

The policy owns the transport admission decision using AndroidX WebKit's authenticated source origin and frame metadata. This keeps trust evaluation at the native WebView boundary and ensures the protocol client only receives admitted checkout messages.

Rejection behavior

A rejected message means that specific input was not trusted. It does not mean the checkout session failed: checkout may be displaying an external surface that also posts messages, and unrelated page activity must not be able to terminate an otherwise healthy checkout.

private fun handleMessageRejection(rejection: CheckoutMessageRejection) {
    log.w(
        LOG_TAG,
        "Rejected ECP WebMessage from ${rejection.origin}: ${rejection.reason.logDescription}",
    )
}
  • Rejected child-frame and origin messages are logged at warning level and dropped.
  • Rejected messages never reach the checkout protocol client.
  • Rejection does not transition PreloadState to Failed or evict the preload.
  • Rejection does not create a CheckoutException or invoke onFail / onCheckoutFailed.
  • The rejection model remains internal because this approach does not expose a public diagnostics stream.

Consumer API

Consumers only configure the origins they trust. The existing lifecycle APIs remain reserved for actual checkout failures:

ShopifyCheckoutKit.configure {
    it.allowedMessageOrigins = setOf(
        "https://checkout.example.com",
        "https://*.example.com",
    )
    it.logLevel = LogLevel.WARN
}

There is no message-rejection subscription in this approach. Consumers that need structured rejection telemetry can compare this PR with the diagnostics API in #643.


Before you merge

Important

  • I've added tests to support my implementation
  • I have read and agree with the Contribution Guidelines
  • I have read and agree with the Code of Conduct
  • I've updated the relevant platform README (platforms/swift/README.md and/or platforms/android/README.md)

@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Aug 14, 2026

markmur commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@markmur
markmur force-pushed the kotlin-message-rejection-lifecycle branch 2 times, most recently from 0db7a62 to 41881d6 Compare August 14, 2026 11:39
@markmur markmur changed the title Route Kotlin message rejection through lifecycle failures Centralize Kotlin checkout message ingress validation Aug 14, 2026
@markmur
markmur force-pushed the swift-message-rejection-lifecycle branch from 63d2a15 to ac84d8f Compare August 14, 2026 11:44
@markmur
markmur force-pushed the kotlin-message-rejection-lifecycle branch from 41881d6 to 94962ec Compare August 14, 2026 11:44
@markmur
markmur marked this pull request as ready for review August 14, 2026 11:47
@markmur
markmur requested a review from a team as a code owner August 14, 2026 11:47
@github-actions

Copy link
Copy Markdown

Package Size

Platform Artifact Base Head Delta
Android release AAR 281.8 KiB 290.2 KiB +8.4 KiB
Android file breakdown
File Base Head Delta
classes.jar 297.7 KiB 307.3 KiB +9.7 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.

@bitrise

bitrise Bot commented Aug 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 Suite Target Platform OS version tag Device
kotlin-android kotlin android latest Google Pixel 9
Android 17.0

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.

1 participant