Skip to content

Commit 8e9f76a

Browse files
committed
Split Android checkout protocol module
Assisted-By: devx/6488e3d0-f47f-4171-a1c4-d2432b2a653c
1 parent 176e7bf commit 8e9f76a

49 files changed

Lines changed: 3612 additions & 2786 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
```
44
platforms/
55
swift/ # iOS Swift Package and CocoaPods sources
6-
android/ # Android library and sample apps
6+
android/ # Android library, embedded checkout protocol artifact, and sample apps
77
react-native/ # React Native wrapper
88
web/ # Web component package and sample app
99
protocol/ # cross-platform communication layer based on UCP
@@ -58,7 +58,7 @@ It does **not** refer to the React Native wrapper platform folders:
5858
### What `--local` does
5959
6060
- For React Native iOS, `--local` wires CocoaPods to the in-repo `platforms/swift/` sources via a local path instead of a released pod version.
61-
- For React Native Android, `--local` publishes/uses the in-repo `platforms/android/` SDK through Maven Local so Gradle resolves the local SDK artifact instead of a released Maven version.
61+
- For React Native Android, `--local` publishes/uses the in-repo `platforms/android/` SDK through Maven Local so Gradle resolves the local `com.shopify:checkout-kit` and `com.shopify:embedded-checkout-protocol` artifacts instead of released Maven versions.
6262
6363
### When to use it
6464
@@ -79,11 +79,11 @@ dev rn ios --local
7979
dev rn android --local
8080
8181
# React Native Android unit tests using local platforms/android via Maven Local
82-
# `dev rn test android` publishes platforms/android/lib to ~/.m2 first, then runs the RN module tests.
82+
# `dev rn test android` publishes the Android SDK artifacts to ~/.m2 first, then runs the RN module tests.
8383
dev rn test android
8484
```
8585
86-
For ad-hoc Android Gradle test commands, publish the local Android SDK first and set `USE_LOCAL_SDK=1` so the React Native module resolves `com.shopify:checkout-kit:1.0.0` from Maven Local instead of the unreleased placeholder artifact:
86+
For ad-hoc Android Gradle test commands, publish the local Android SDK first and set `USE_LOCAL_SDK=1` so the React Native sample resolves the local `com.shopify:checkout-kit` and `com.shopify:embedded-checkout-protocol` artifacts from Maven Local:
8787

8888
```bash
8989
cd platforms/react-native
@@ -92,6 +92,8 @@ cd sample/android
9292
USE_LOCAL_SDK=1 ./gradlew :shopify_checkout-kit-react-native:testDebugUnitTest
9393
```
9494

95+
The React Native Android sample uses exclusive Maven Local resolution for those two `com.shopify` modules when `USE_LOCAL_SDK=1`. Keep that filtering in the sample Gradle build; duplicating exclusive repository filters for the same modules elsewhere can break dependency resolution.
96+
9597
## Sensitive configuration
9698

9799
Treat storefront environment and generated sample app configuration values as

dev.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,20 +227,20 @@ commands:
227227
open -a "Android Studio" "$project"
228228
229229
api:
230-
desc: Validate or update the public API baseline (lib/api/lib.api)
230+
desc: Validate or update the public Android API baselines
231231
run: |
232232
echo "Usage: dev android api {check|dump}"
233233
echo ""
234-
echo " check Verify public API matches the committed baseline"
235-
echo " dump Regenerate the baseline after intentional public API changes"
234+
echo " check Verify public APIs match the committed baselines"
235+
echo " dump Regenerate the baselines after intentional public API changes"
236236
exit 1
237237
subcommands:
238238
check:
239-
desc: Verify public API matches the committed baseline
240-
run: cd platforms/android && ./gradlew :lib:apiCheck
239+
desc: Verify public APIs match the committed baselines
240+
run: cd platforms/android && ./gradlew apiCheck
241241
dump:
242-
desc: Regenerate the baseline after intentional public API changes
243-
run: cd platforms/android && ./gradlew :lib:apiDump
242+
desc: Regenerate the baselines after intentional public API changes
243+
run: cd platforms/android && ./gradlew apiDump
244244

245245
# Swift
246246
swift:

platforms/android/AGENTS.md

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
1-
# CLAUDE.md
1+
# AGENTS.md
22

3-
Guidance for Claude Code when working in this repository.
3+
Guidance for AI agents when working in the Android platform.
44

55
## Project overview
66

7-
Shopify Checkout Kit for Android is a published AAR library (`com.shopify:checkout-kit`) that presents Shopify checkouts as a native, dialog-hosted WebView in consumer apps. It is consumed by third-party Android apps via Maven Central, so changes to the library's public surface have real consumer impact and real reversal cost once released.
7+
Shopify Checkout Kit for Android publishes AARs that are consumed by third-party Android apps via Maven Central, so changes to public surfaces have real consumer impact and real reversal cost once released.
88

9-
Two modules matter:
9+
The main modules are:
1010

11-
- **`lib/`** — the library itself. Everything here ships to consumers.
12-
- **`samples/CheckoutKitAndroidDemo/`** — a demo app that consumes `lib/` as a source dependency. Changes here never reach consumers; this module is for internal testing and developer onboarding.
11+
- **`lib/`** — the Checkout Kit library, published as `com.shopify:checkout-kit`. It presents Shopify checkouts as a native, dialog-hosted WebView in consumer apps.
12+
- **`universal-commerce-protocol/`** — the Embedded Checkout Protocol artifact, published as `com.shopify:embedded-checkout-protocol`. The Gradle project path is still `:universal-commerce-protocol`, and the Kotlin package is `com.shopify.ucp.embedded.checkout`.
13+
- **`samples/CheckoutKitAndroidDemo/`** — a demo app that consumes the Android modules as source dependencies. Changes here never reach consumers; this module is for internal testing and developer onboarding.
1314

14-
The sample is a separate Gradle composite (`samples/CheckoutKitAndroidDemo/settings.gradle`) that includes `:lib` from `../../lib`. The sample's `gradle.properties` and Gradle wrapper are independent of the root's.
15+
The sample is a separate Gradle composite (`samples/CheckoutKitAndroidDemo/settings.gradle`) that includes `:lib` and `:universal-commerce-protocol` as source dependencies. The sample's `gradle.properties` and Gradle wrapper are independent of the root's.
1516

1617
## Where to make changes
1718

18-
- Library source: `lib/src/main/java/com/shopify/checkoutkit/`. Flat package at the top level, including generated protocol models.
19+
- Checkout Kit source: `lib/src/main/java/com/shopify/checkoutkit/`.
20+
- Embedded Checkout Protocol source, generated models, and generated event catalog: `universal-commerce-protocol/src/main/java/com/shopify/ucp/embedded/checkout/`.
1921
- Library tests: `lib/src/test/java/com/shopify/checkoutkit/`. "No test, no merge" is a listed reject criterion in the repo-root `.github/CONTRIBUTING.md`.
22+
- Protocol tests: `universal-commerce-protocol/src/test/java/com/shopify/ucp/embedded/checkout/`.
2023
- Java interop is a first-class concern — the library is commonly consumed from Java code. `lib/src/test/java/com/shopify/checkoutkit/InteropTest.java` exercises the public API from Java specifically; treat breakage there as a consumer-facing issue.
2124

2225
## Key components
@@ -25,7 +28,11 @@ The sample is a separate Gradle composite (`samples/CheckoutKitAndroidDemo/setti
2528
- **`CheckoutDialog.kt`** — the dialog that hosts the WebView, including the progress indicator and checkout error coordination.
2629
- **`CheckoutWebView.kt`** — primary WebView. Instruments page loads and attaches the ECP JavaScript interface.
2730
- **`BaseWebView.kt`** — abstract base class. Any new WebView variant must extend this so shared configuration (user agent suffix, WebChromeClient hooks, navigation error handling) is consistent.
28-
- **`EmbeddedCheckoutProtocol.kt`** — the Embedded Checkout Protocol JavaScript interface. Handles `ec.ready`, ECP notifications, and request/response delegations.
31+
- **`CheckoutProtocol.kt`** — the curated consumer-facing Checkout Kit protocol API. This is where supported events/delegations are intentionally exposed.
32+
- **`EmbeddedCheckoutProtocolBridge.kt`** — the internal JavaScript interface attached to the WebView. Handles `ec.ready`, ECP notifications, and request/response delegations.
33+
- **`universal-commerce-protocol/src/main/java/com/shopify/ucp/embedded/checkout/EmbeddedCheckoutProtocol.kt`** — the generated low-level Embedded Checkout Protocol event catalog.
34+
- **`universal-commerce-protocol/src/main/java/com/shopify/ucp/embedded/checkout/ProtocolCodec.kt`** — hand-written JSON-RPC request/response helpers.
35+
- **`universal-commerce-protocol/src/main/java/com/shopify/ucp/embedded/checkout/Descriptors.kt`** — reusable protocol descriptor and codec types.
2936
- **`Configuration.kt`** — runtime config container (color scheme, log level).
3037
- **`CheckoutListener.kt`** + **`DefaultCheckoutListener`** — consumer-implemented lifecycle interface (failure, cancellation, permission prompts, file chooser). Changes here are consumer API changes.
3138
- **`CheckoutPresentation.kt`** — Kotlin-first builder for per-presentation callbacks (`onFail`, `onCancel`, browser/system hooks, ECP `connect(...)`). Builds a `DefaultCheckoutListener` internally.
@@ -39,31 +46,38 @@ The sample is a separate Gradle composite (`samples/CheckoutKitAndroidDemo/setti
3946

4047
## Conventions
4148

42-
- **`-Xexplicit-api=strict`** is on (`lib/build.gradle`). Every public class, method, field, and property must have an explicit visibility modifier. "Accidentally public" is not a thing here. This is a consumer-protection rule — if you see a public-by-default declaration, it was deliberate.
49+
- **`-Xexplicit-api=strict`** is on for both published Android modules. Every public class, method, field, and property must have an explicit visibility modifier. "Accidentally public" is not a thing here. This is a consumer-protection rule — if you see a public-by-default declaration, it was deliberate.
4350
- **Max line length: 140** (detekt-enforced). Detekt config: `lib/detekt.config.yml`.
4451
- **Library JVM target: 11.** Consumers must build with JDK 11+ to consume the AAR. Raising further is a major-version discussion.
45-
- **Library Kotlin `apiVersion` / `languageVersion` are pinned at 2.0.** Set in `lib/build.gradle` so the AAR's bytecode stays consumable by Kotlin 2.0+ projects even though the compiler itself is on a newer 2.x. Bumping this pin is the consumer-facing breaking change, not bumping the compiler - treat it as a planned major-version event.
46-
- **Prefer generated protocol models.** Before adding hand-written protocol DTOs, check the generated models in `lib/src/main/java/com/shopify/checkoutkit/Models.kt` and the OpenRPC schema. Use generated UCP/ECP types for wire payloads; reserve local DTOs for Android-internal transport helpers that are not represented in the schema.
52+
- **Library Kotlin `apiVersion` / `languageVersion` are pinned at 2.0.** Set through `gradle/android-library-versions.gradle` so the AARs stay consumable by Kotlin 2.0+ projects even though the compiler itself is on a newer 2.x. Bumping this pin is the consumer-facing breaking change, not bumping the compiler - treat it as a planned major-version event.
53+
- **SDK/toolchain compatibility values live in `gradle/android-library-versions.gradle`.** Dependency, plugin, and Android artifact versions live in `gradle/libs.versions.toml`.
54+
- **Protocol vs kit boundary:** the protocol artifact should own generated raw wire names, generated models, thin descriptors, and encoding/decoding helpers. Checkout Kit should own curation, default behavior, WebView integration, and the higher-level consumer API.
55+
- **Prefer generated protocol models.** Before adding hand-written protocol DTOs, check the generated models in `universal-commerce-protocol/src/main/java/com/shopify/ucp/embedded/checkout/Models.kt` and the OpenRPC schema. Use generated UCP/ECP types for wire payloads; reserve local DTOs for Android-internal transport helpers that are not represented in the schema.
4756

4857
## Public API surface
4958

50-
The library's public API is captured in `lib/api/lib.api` (managed by the [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) Gradle plugin). Every PR is gated by `./gradlew :lib:apiCheck` in CI — the build fails if the compiled public API diverges from the committed baseline.
59+
The Android public APIs are captured by the [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) Gradle plugin:
60+
61+
- `lib/api/lib.api` for `com.shopify:checkout-kit`.
62+
- `universal-commerce-protocol/api/universal-commerce-protocol.api` for `com.shopify:embedded-checkout-protocol`.
63+
64+
Every PR is gated by `./gradlew apiCheck` in CI — the build fails if the compiled public API diverges from the committed baselines.
5165

5266
If a change intentionally modifies public API (adding, removing, or changing any public class, method, field, or property):
5367

54-
1. Run `./gradlew :lib:apiDump` (or `dev android api dump`) to regenerate the baseline.
55-
2. Review the diff in `lib/api/lib.api` — it's the single best indicator of consumer impact, and reviewers will focus on it.
68+
1. Run `./gradlew apiDump` (or `dev android api dump`) to regenerate the baselines.
69+
2. Review the `.api` diffs — they are the single best indicator of consumer impact, and reviewers will focus on them.
5670
3. Commit the updated `.api` file in the same PR as the code change.
5771

5872
If `apiCheck` fails and you did *not* intend to change public API, the diff tells you what inadvertently leaked out. Fix the leak rather than updating the baseline — you've accidentally shifted the consumer contract.
5973

6074
## Common commands
6175

62-
- Tests: `./gradlew :lib:test` (or `dev android test`)
63-
- API surface: `./gradlew :lib:apiCheck` / `./gradlew :lib:apiDump` (or `dev android api check` / `dev android api dump`)
76+
- Tests: `./gradlew test` (or `dev android test`)
77+
- API surface: `./gradlew apiCheck` / `./gradlew apiDump` (or `dev android api check` / `dev android api dump`)
6478
- Lint: `./gradlew detekt lintRelease` (or `dev android lint`)
6579
- Format: `./gradlew detekt --auto-correct` (or `dev android format`)
66-
- Full local verification: `./gradlew :lib:clean :lib:test :lib:detekt :lib:lintRelease :lib:assembleRelease`
80+
- Full local verification: `./gradlew clean test detekt lintRelease assembleRelease`
6781
- Sample app build (from `samples/CheckoutKitAndroidDemo/`): `./gradlew assembleDebug`
6882

6983
## Consumer requirements
@@ -75,13 +89,13 @@ Raising any of these is a consumer-facing breaking change and needs visible rele
7589
- Kotlin compiler / `apiVersion` / `languageVersion`
7690
- JVM target
7791

78-
**Transitive `androidx` bumps can silently raise the `compileSdk` floor** — review dependabot PRs with this in mind, and run `./gradlew :lib:apiCheck` and check `aarMetadata` output when bumping any `androidx.*` dependency.
92+
**Transitive `androidx` bumps can silently raise the `compileSdk` floor** — review dependabot PRs with this in mind, and run `./gradlew apiCheck` and check `aarMetadata` output when bumping any `androidx.*` dependency.
7993

8094
## Release process
8195

82-
Versions are bumped via:
96+
Published Android artifact versions are bumped via:
8397

84-
1. The `versionName` literal in `lib/build.gradle`.
98+
1. `gradle/libs.versions.toml` (`checkoutKitAndroid` and `embeddedCheckoutProtocolAndroid`).
8599
2. The install snippets in `README.md` (Gradle and Maven).
86100

87101
Android releases are tagged `android/X.Y.Z` (Swift releases use bare `X.Y.Z`). The publish workflow filters on the `android/` prefix — without it, nothing publishes on the Android side.

platforms/android/build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Top-level build file where you can add configuration options common to all sub-projects/modules.
22
plugins {
3-
id 'com.android.library' version '9.1.1' apply false
4-
id 'org.jetbrains.kotlin.android' version '2.3.21' apply false
5-
id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.21' apply false
6-
id 'io.gitlab.arturbosch.detekt' version '1.23.8' apply false
7-
id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.18.1'
3+
alias(libs.plugins.android.library) apply false
4+
alias(libs.plugins.kotlin.android) apply false
5+
alias(libs.plugins.kotlin.serialization) apply false
6+
alias(libs.plugins.detekt) apply false
7+
alias(libs.plugins.binary.compatibility.validator)
88
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Consumer-facing compatibility values shared by the published Android artifacts.
2+
// Keep these centralized so checkout-kit and embedded-checkout-protocol do not drift.
3+
ext.androidPublishedArtifactCompatibility = [
4+
compileSdk: 36,
5+
minSdk: 23,
6+
minCompileSdk: 35,
7+
javaVersion: "11",
8+
kotlinJvmTarget: "JVM_11",
9+
kotlinApiVersion: "KOTLIN_2_0",
10+
kotlinLanguageVersion: "KOTLIN_2_0",
11+
]

0 commit comments

Comments
 (0)