Conversation
Replace @SerialName-based serialization with a custom case-insensitive
serializer so both lowercase Stripe values ("trial") and uppercase Play
Store values ("TRIAL") deserialize correctly. Add missing CODE and
WINBACK variants to match iOS OfferType parity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add support for local resources
…sensitive-serializer Fix LatestPeriodType case-insensitive deserialization for Stripe
superwall/src/main/java/com/superwall/sdk/paywall/view/webview/LocalResourceHandler.kt
Show resolved
Hide resolved
superwall/src/main/java/com/superwall/sdk/paywall/view/webview/LocalResourceHandler.kt
Show resolved
Hide resolved
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
2.7.7
Enhancements
Superwall.instance.localResourcesFixes
Checklist
CHANGELOG.mdfor any breaking changes, enhancements, or bug fixes.ktlintin the main directory and fixed any issues.Greptile Summary
This PR releases version 2.7.7, delivering two changes: (1) a new local resource loading system for paywall WebViews via
swlocal://custom URLs, and (2) a serialization fix for Stripe subscription period/state types that were always deserializing toUNKNOWN.Key changes:
LocalResourceHandler(new): interceptsswlocal://URLs inshouldInterceptRequestand serves assets from either afile:///content://Urior an Android resource ID (R.raw.*,R.drawable.*). Includes CORS headers, proper MIME type resolution, and structured error responses (400/404/500).Superwall.localResources(new public API): aMap<String, PaywallResource>that developers populate to register local assets by ID. The WebView lazily creates the handler and reads this map at request time.LatestPeriodType/LatestSubscriptionState: the old customKSerializerused case-insensitive matching against enum names (e.g."GRACE_PERIOD"), but the API sends lowercase snake_case ("grace_period"), so every value was silently falling back toUNKNOWN. Both enums are now annotated with lowercase@SerialNamevalues that match the actual API contract.coerceInputValues = trueis added toSubscriptionServiceas a forward-compatibility safety net.LatestPeriodTypevariants (CODE,WINBACK) and their string mappings inReceiptManager.determinePeriodType().LocalResourceHandlerTestcovering all response code paths, MIME type fallback, and CORS header presence.Confidence Score: 4/5
LocalResourceHandleris well-structured with proper error handling and a solid Robolectric test suite. Two minor style issues exist inLocalResourceHandler: the MIME type extraction uses a slightly fragile path throughMimeTypeMap.getFileExtensionFromUrlfor resource entry names, and the success response passesnullencoding. Neither is a runtime blocker given the primary binary use-case (images/videos). No breaking changes to existing public APIs.LocalResourceHandler.kthas two minor style suggestions worth addressing before merge.Important Files Changed
swlocal://URL interception and local resource serving. Well-structured with proper error responses (400/404/500). Two minor style issues: extension is redundantly re-extracted viaMimeTypeMap.getFileExtensionFromUrl, and the success response passesnullencoding which could affect text resources.shouldInterceptRequestoverride that delegatesswlocal://URLs toLocalResourceHandlerwhile passing all other requests to the parent. Integration is clean and correct.@SerialNamevalues from uppercase (TRIAL/SUBSCRIPTION/etc.) to lowercase snake_case matching the actual API values. Adds two new variants:CODEandWINBACK.KSerializer(which used case-insensitive.namematching, effectively always returning UNKNOWN for lowercase API values) with standard@SerialNameannotations mapping to the actual lowercase strings the API sends. Correct fix for the deserialization bug.coerceInputValues = trueto the JSON parser as a safety net for future unknown enum values. Correctly complements the@SerialNamerefactoring in the receipt models.LocalResourceHandlerand passes it to bothDefaultWebviewClientandWebviewFallbackClient. Thelazydelegate ensures the handler is created only when the WebView is first used. Clean integration.localResources: Map<String, PaywallResource>property with KDoc and usage example. Property is a simplevardefaulting toemptyMap(), safe for concurrent read access.CODEandWINBACKcases todeterminePeriodType()matching the two newLatestPeriodTypevariants. No issues.localResourceHandlerparameter and forwards it to theDefaultWebviewClientsuperclass. Parameter ordering matches the parent constructor correctly.Sequence Diagram
sequenceDiagram participant PW as Paywall HTML participant WV as SWWebView participant DWC as DefaultWebviewClient participant LRH as LocalResourceHandler participant SW as Superwall.localResources participant CR as ContentResolver / Resources PW->>WV: GET swlocal://hero-video WV->>DWC: shouldInterceptRequest(url) DWC->>LRH: isLocalResourceUrl(url) → true DWC->>LRH: handleRequest(url) LRH->>SW: localResources()[resourceId] SW-->>LRH: PaywallResource.FromUri(uri) or FromResources(resId) alt FromUri LRH->>CR: contentResolver.openInputStream(uri) CR-->>LRH: InputStream else FromResources LRH->>CR: resources.openRawResource(resId) CR-->>LRH: InputStream end LRH-->>DWC: WebResourceResponse(200, mimeType, CORS headers, stream) DWC-->>WV: WebResourceResponse WV-->>PW: Asset dataLast reviewed commit: a15c74a