Skip to content

Add decode logging parity for Web + Swift + Android#437

Merged
markmur merged 5 commits into
mainfrom
decode-error-logging-parity
Jul 14, 2026
Merged

Add decode logging parity for Web + Swift + Android#437
markmur merged 5 commits into
mainfrom
decode-error-logging-parity

Conversation

@markmur

@markmur markmur commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What changes are you making?

A payload that fails to decode was dropped silently on Web and Swift, and only at debug level on Android (suppressed at the default WARN) — so merchants lost the event with nothing to debug. This brings all three platforms to parity: a dropped payload now always leaves a log line.

Adds a chainable, public onDecodeError callback to the shared protocol client (TS + Swift) that each kit wires to its own logger. Android already has direct logger access in the kit, so it logs inline instead.

  • TS: new Client.onDecodeError(handler) + DecodeErrorContext; fired on both notification and request decode failures.
  • Swift: descriptor decode now throws (was try?-swallowed to nil), so the real DecodingError surfaces; new public onDecodeError captures it.
  • Web kit: wires the seam to a new unconditional #warn (prefix <shopify-checkout>:).
  • Swift kit: wires the seam to OSLogger.shared.error(...).
  • Android kit: both payload-decode catch sites bumped log.dlog.e (unconditional). Envelope-level failures stay at debug, matching the silent .unknown path elsewhere.

checkout-kit public API is unchanged. Swift EmbeddedCheckoutProtocol baseline changes are intended: decode param (Data) -> Payload?(Data) throws -> Payload, and onDecodeError / DecodeErrorHandler now public. First PR in a stack improving decode tolerance.


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)

@markmur
markmur requested a review from a team as a code owner July 13, 2026 13:39
@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Jul 13, 2026
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

React Native — Coverage Report

Lines Statements Branches Functions
Coverage: 92%
91.85% (327/356) 87.98% (183/208) 100% (86/86)

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

Web — Coverage Report

Lines Statements Branches Functions
Coverage: 100%
99.58% (238/239) 86.4% (108/125) 100% (65/65)

@markmur markmur self-assigned this Jul 13, 2026
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

Package Size

Platform Artifact Base Head Delta
Web npm tarball 63.4 KiB 63.8 KiB +428 B
React Native npm tarball 103.0 KiB 103.1 KiB +124 B
Android release AAR 222.6 KiB 222.8 KiB +237 B
Web file breakdown
File Base Head Delta
dist/index.js.map 180.8 KiB 182.0 KiB +1.2 KiB
dist/index.d.ts 41.2 KiB 41.2 KiB 0 B
dist/custom-elements.json 35.5 KiB 36.0 KiB +495 B
dist/index.js 28.7 KiB 29.2 KiB +469 B
README.md 17.2 KiB 17.2 KiB 0 B
package.json 2.8 KiB 2.8 KiB 0 B
LICENSE 1.1 KiB 1.1 KiB 0 B
React Native file breakdown
File Base Head Delta
node_modules/@shopify/checkout-kit-protocol/src/generated/Models.ts 85.4 KiB 85.4 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/Models.d.ts 53.0 KiB 53.0 KiB 0 B
ios/ShopifyCheckoutKit.swift 15.0 KiB 15.0 KiB 0 B
ios/AcceleratedCheckoutButtons.swift 14.0 KiB 14.0 KiB 0 B
lib/commonjs/index.js 13.1 KiB 13.1 KiB 0 B
src/components/AcceleratedCheckoutButtons.tsx 12.9 KiB 12.9 KiB 0 B
src/index.ts 12.5 KiB 12.5 KiB 0 B
lib/commonjs/components/AcceleratedCheckoutButtons.js 11.4 KiB 11.4 KiB 0 B
android/src/main/java/com/shopify/reactnative/checkoutkit/ShopifyCheckoutKitModule.java 11.1 KiB 11.1 KiB 0 B
lib/module/index.js 10.4 KiB 10.4 KiB 0 B
lib/commonjs/components/AcceleratedCheckoutButtons.js.map 10.3 KiB 10.3 KiB 0 B
lib/module/components/AcceleratedCheckoutButtons.js 10.2 KiB 10.2 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolNotifications.ts 9.5 KiB 9.5 KiB 0 B
lib/module/components/AcceleratedCheckoutButtons.js.map 9.1 KiB 9.1 KiB 0 B
src/index.d.ts 8.9 KiB 8.9 KiB 0 B
lib/module/index.js.map 8.2 KiB 8.2 KiB 0 B
src/present-dispatcher.ts 8.0 KiB 8.0 KiB 0 B
lib/commonjs/index.js.map 7.7 KiB 7.7 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolNotifications.d.ts 7.6 KiB 7.6 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolRenameMap.ts 7.2 KiB 7.2 KiB 0 B
…and 110 smaller files
Android file breakdown
File Base Head Delta
classes.jar 235.5 KiB 235.8 KiB +229 B
res/layout/checkout_sheet_content.xml 4.0 KiB 4.0 KiB 0 B
res/values/values.xml 1.2 KiB 1.2 KiB 0 B
R.txt 1.0 KiB 1.0 KiB 0 B
proguard.txt 798 B 798 B 0 B
AndroidManifest.xml 578 B 578 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 changed the title Add decode logging parity for Web + Swift Add decode logging parity for Web + Swift + Android Jul 13, 2026

markmur commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@markmur markmur mentioned this pull request Jul 13, 2026
11 tasks
Comment thread platforms/web/src/checkout.ts Outdated
@markmur
markmur force-pushed the decode-error-logging-parity branch from 48cc8eb to 6e6776f Compare July 13, 2026 16:09
@markmur
markmur force-pushed the decode-error-logging-parity branch from 296e415 to 0070c87 Compare July 14, 2026 08:22
@markmur
markmur merged commit 88df993 into main Jul 14, 2026
34 checks passed
@markmur
markmur deleted the decode-error-logging-parity branch July 14, 2026 10:02
markmur added a commit that referenced this pull request Jul 14, 2026
### What changes are you making?

Kotlin was the odd platform out: TS (`client.ts`) and Swift (`EmbeddedCheckoutProtocol+Client.swift`) ship a generic dispatch `Client` — with `onDecodeError` — inside their protocol packages, but Kotlin had no `Client` in the `embedded-checkout-protocol` artifact. The dispatch loop lived entirely in the kit's `CheckoutProtocol.Client`, which decoded, routed, and encoded inline. This PR closes that structural gap and exposes `onDecodeError` publicly, matching the parity #437 established for TS + Swift.

Stacked on #437.

**Protocol (`embedded-checkout-protocol`)**
- New generic `Client`: `on(NotificationDescriptor)`, `on(RequestDescriptor)`, chainable `onDecodeError`, and `process(message)`. It is thread-agnostic and synchronous — handlers run on the calling thread, so hosts wrap them for threading before registering.
- `onDecodeError` is `public` (Kotlin lands at TS's public level; it has no `package` visibility and `internal` in the artifact is invisible to the kit). A `SerializationException` while decoding a notification skips dispatch and fires the callback; a request with undecodable params fires the callback and answers with an invalid-params error; envelope-level failures stay silent (matching the `.unknown` path on the other platforms).
- `hasValidJsonRpcRequestId` moved into the artifact codec (now shared/public) since both the generic client and the kit need it.

**Kit (`checkout-kit`)**
- `CheckoutProtocol.Client` is now a thin wrapper over the generic client: it keeps Checkout Kit curation (only supported descriptors register), main-thread delivery of consumer handlers, and wires `onDecodeError` to `log.e` once. The duplicated decode/route/encode/catch code is gone. Public kit API is unchanged (`lib.api` untouched).

The `embedded-checkout-protocol.api` baseline gains `Client` and `hasValidJsonRpcRequestId`; that diff is intended.

### Usage

**Directly against the protocol artifact** (`com.shopify:embedded-checkout-protocol`) — for a custom transport/WebView integration:

```kotlin
import com.shopify.ucp.embedded.checkout.Client
import com.shopify.ucp.embedded.checkout.EmbeddedCheckoutProtocol

val client = Client()
    .onDecodeError { method, error ->
        Log.e("MyApp", "Failed to decode $method", error)   // you wire your own logging
    }
    .on(EmbeddedCheckoutProtocol.start) { params ->
        // raw protocol payload — you unwrap it yourself; runs on the calling thread
        println("started: ${params.checkout.id}")
    }
    .on(EmbeddedCheckoutProtocol.windowOpen) { request ->
        WindowOpenResult(/* … */)                            // request handler returns the response
    }

// you drive it from your own message pump; response is the JSON-RPC reply (or null)
val response: String? = client.process(rawJsonRpcMessage)
```

**Through Checkout Kit** (`com.shopify:checkout-kit`) — the managed path, wired into the kit's WebView automatically:

```kotlin
import com.shopify.checkoutkit.CheckoutProtocol

val client = CheckoutProtocol.Client()
    .on(CheckoutProtocol.start) { checkout ->
        // payload already unwrapped to `Checkout`, delivered on the main thread
        println("started: ${checkout.id}")
    }
    .on(CheckoutProtocol.windowOpen) { request ->
        WindowOpenResult(/* … */)
    }
// no onDecodeError needed — the kit wires it to its logger; no process() — the WebView bridge drives it
```

The kit adds, on top of the generic client: payload unwrapping (`Checkout` vs raw `CheckoutParams`), descriptor curation (only supported events register), main-thread handler delivery, decode-error logging, and WebView wiring. The protocol client is the unopinionated primitive underneath.

---

### Before you merge

> [!IMPORTANT]
>
> - [x] I've added tests to support my implementation
> - [x] I have read and agree with the [Contribution Guidelines](./CONTRIBUTING.md)
> - [x] I have read and agree with the [Code of Conduct](./CODE_OF_CONDUCT.md)
> - [ ] I've updated the relevant platform README (`platforms/swift/README.md` and/or `platforms/android/README.md`)

---

<details>
<summary>Releasing a new Swift version?</summary>

- [ ] I have bumped the version in `ShopifyCheckoutKit.podspec`
- [ ] I have bumped the version in `platforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swift`
- [ ] I have updated the SwiftPM/CocoaPods version snippets in `platforms/swift/README.md` (major version only)

</details>

<details>
<summary>Releasing a new Embedded Checkout Protocol version?</summary>

- [ ] I have bumped `embeddedCheckoutProtocolAndroid` in `platforms/android/gradle/libs.versions.toml`
- [ ] I have updated `protocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.api` if the public API changed

</details>

<details>
<summary>Releasing a new Android version?</summary>

- [ ] I have bumped `checkoutKitAndroid` in `platforms/android/gradle/libs.versions.toml`
- [ ] I have updated the Gradle/Maven version snippets in `platforms/android/README.md`

</details>

> [!TIP]
> See the [Contributing documentation](./CONTRIBUTING.md) for the full release process per platform.
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.

2 participants