AdMob SSV: add @_spi(Internal) poll endpoint on Purchases#6641
Conversation
c865a7e to
bbef629
Compare
da15696 to
be5f20b
Compare
bbef629 to
ec05f64
Compare
8799eb5 to
167727f
Compare
Generated by 🚫 Danger |
|
@RCGitBot please test |
4 builds increased size
RevenueCat 1.0 (1)
|
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 37.6 kB |
| DYLD.Exports | ⬆️ 2.7 kB |
| Code Signature | ⬆️ 2.4 kB |
| 📝 RevenueCat.AdsAPI.AdsAPI | ⬆️ 1.3 kB |
| RevenueCat.PostSubscriberAttributesOperation.PostSubscriberAttrib... | ⬇️ -628 B |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.local-source
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 24.5 kB (0.21%)
Total download size change: ⬆️ 9.7 kB (0.24%)
Largest size changes
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 3.4 kB |
| 📝 RevenueCat.GetAdMobSSVStatusOperation.getAdMobSSVStatus(completio... | ⬆️ 2.1 kB |
| RevenueCat.Result.parseResponse | ⬆️ 896 B |
| Code Signature | ⬆️ 624 B |
| 📝 RevenueCat.AdsAPI.AdsAPI | ⬆️ 608 B |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.cocoapods
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 58.0 kB (0.22%)
Total download size change: ⬆️ 14.2 kB (0.24%)
Largest size changes
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 25.6 kB |
| 📝 RevenueCat.GetAdMobSSVStatusOperation.getAdMobSSVStatus(completio... | ⬆️ 2.1 kB |
| Code Signature | ⬆️ 1.4 kB |
| RevenueCat.Result.parseResponse | ⬆️ 896 B |
| 📝 RevenueCat.AdsAPI.AdsAPI | ⬆️ 608 B |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.spm
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 19.8 kB (0.19%)
Total download size change: ⬆️ 9.6 kB (0.23%)
Largest size changes
| Item | Install Size Change |
|---|---|
| 📝 RevenueCat.GetAdMobSSVStatusOperation.getAdMobSSVStatus(completio... | ⬆️ 2.1 kB |
| RevenueCat.Result.parseResponse | ⬆️ 896 B |
| 📝 RevenueCat.AdsAPI.AdsAPI | ⬆️ 608 B |
| Swift._NativeDictionary.mutatingFind(isUnique) | ⬇️ -588 B |
| Other | ⬆️ 16.8 kB |
🛸 Powered by Emerge Tools
Comment trigger: Size diff threshold of 100.00kB exceeded
|
@RCGitBot please test |
c0c80de to
4ad4633
Compare
📸 Snapshot Test327 unchanged
🛸 Powered by Emerge Tools |
2e2c1f7 to
b45dfb4
Compare
b45dfb4 to
4ad4633
Compare
|
@RCGitBot please test |
ajpallares
left a comment
There was a problem hiding this comment.
Looking good! I left some comments for now
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f9a6cc2. Configure here.
Add a non-skip commit so required CircleCI statuses are reported on the current head.
Update HTTP request path tests to cover AdMob SSV signature and nonce behavior.
1256d78 to
d0216f6
Compare
|
@RCGitBot please test |
Renames the internal AdMobSSV* types and SPI introduced in #6641 to RewardVerification* so the wording stays adapter-agnostic. Also renames the backend URL path and the client-side metric to match, so the SPI is provider-agnostic end to end. Renames: - Sources/Ads/AdMobSSV/ -> Sources/Ads/RewardVerification/ - AdMobSSVPollStatus -> RewardVerificationPollStatus - RewardVerificationPollStatus.validated -> .verified - AdMobSSVStatusResponse -> RewardVerificationStatusResponse - RewardVerificationStatusResponse.Status.validated -> .verified (the decoder still accepts the legacy "validated" wire value during the rollout window; covered by a new decoder unit test) - AdMobSSVStatusCallback -> RewardVerificationStatusCallback - GetAdMobSSVStatusOperation -> GetRewardVerificationStatusOperation - AdsAPI.getAdMobSSVStatus -> getRewardVerificationStatus - Purchases.pollAdMobSSVStatus -> pollRewardVerificationStatus - HTTPRequest.Path.adMobSSVStatus -> .rewardVerificationStatus - URL path: /ads/admob/ssv/{tx} -> /ads/reward_verification/{tx} - Metric name: get_admob_ssv_status -> get_reward_verification_status - BackendErrorStrings.unknown_admob_ssv_status -> .unknown_reward_verification_status - Test files, test methods and snapshot artifacts renamed to match. - Xcode project file references updated in lockstep.
Renames the internal AdMobSSV* types and SPI introduced in #6641 to RewardVerification* so the wording stays adapter-agnostic. Also renames the backend URL path and the client-side metric to match, so the SPI is provider-agnostic end to end. Renames: - Sources/Ads/AdMobSSV/ -> Sources/Ads/RewardVerification/ - AdMobSSVPollStatus -> RewardVerificationPollStatus - RewardVerificationPollStatus.validated -> .verified - AdMobSSVStatusResponse -> RewardVerificationStatusResponse - RewardVerificationStatusResponse.Status.validated -> .verified (the decoder still accepts the legacy "validated" wire value during the rollout window; covered by a new decoder unit test) - AdMobSSVStatusCallback -> RewardVerificationStatusCallback - GetAdMobSSVStatusOperation -> GetRewardVerificationStatusOperation - AdsAPI.getAdMobSSVStatus -> getRewardVerificationStatus - Purchases.pollAdMobSSVStatus -> pollRewardVerificationStatus - HTTPRequest.Path.adMobSSVStatus -> .rewardVerificationStatus - URL path: /ads/admob/ssv/{tx} -> /ads/reward_verifications/{tx} - Metric name: get_admob_ssv_status -> get_reward_verification_status - BackendErrorStrings.unknown_admob_ssv_status -> .unknown_reward_verification_status - Test files, test methods and snapshot artifacts renamed to match. - Xcode project file references updated in lockstep.
Renames the internal AdMobSSV* types and SPI introduced in #6641 to RewardVerification* so the wording stays adapter-agnostic. Also renames the backend URL path and the client-side metric to match, so the SPI is provider-agnostic end to end. Renames: - Sources/Ads/AdMobSSV/ -> Sources/Ads/RewardVerification/ - AdMobSSVPollStatus -> RewardVerificationPollStatus - RewardVerificationPollStatus.validated -> .verified - AdMobSSVStatusResponse -> RewardVerificationStatusResponse - RewardVerificationStatusResponse.Status.validated -> .verified (the decoder still accepts the legacy "validated" wire value during the rollout window; covered by a new decoder unit test) - AdMobSSVStatusCallback -> RewardVerificationStatusCallback - GetAdMobSSVStatusOperation -> GetRewardVerificationStatusOperation - AdsAPI.getAdMobSSVStatus -> getRewardVerificationStatus - Purchases.pollAdMobSSVStatus -> pollRewardVerificationStatus - HTTPRequest.Path.adMobSSVStatus -> .rewardVerificationStatus - URL path: /ads/admob/ssv/{tx} -> /ads/reward_verifications/{tx} - Metric name: get_admob_ssv_status -> get_reward_verification_status - BackendErrorStrings.unknown_admob_ssv_status -> .unknown_reward_verification_status - Test files, test methods and snapshot artifacts renamed to match. - Xcode project file references updated in lockstep.
) * Rename internal AdMobSSV symbols, URL and metric to RewardVerification Renames the internal AdMobSSV* types and SPI introduced in #6641 to RewardVerification* so the wording stays adapter-agnostic. Also renames the backend URL path and the client-side metric to match, so the SPI is provider-agnostic end to end. Renames: - Sources/Ads/AdMobSSV/ -> Sources/Ads/RewardVerification/ - AdMobSSVPollStatus -> RewardVerificationPollStatus - RewardVerificationPollStatus.validated -> .verified - AdMobSSVStatusResponse -> RewardVerificationStatusResponse - RewardVerificationStatusResponse.Status.validated -> .verified (the decoder still accepts the legacy "validated" wire value during the rollout window; covered by a new decoder unit test) - AdMobSSVStatusCallback -> RewardVerificationStatusCallback - GetAdMobSSVStatusOperation -> GetRewardVerificationStatusOperation - AdsAPI.getAdMobSSVStatus -> getRewardVerificationStatus - Purchases.pollAdMobSSVStatus -> pollRewardVerificationStatus - HTTPRequest.Path.adMobSSVStatus -> .rewardVerificationStatus - URL path: /ads/admob/ssv/{tx} -> /ads/reward_verifications/{tx} - Metric name: get_admob_ssv_status -> get_reward_verification_status - BackendErrorStrings.unknown_admob_ssv_status -> .unknown_reward_verification_status - Test files, test methods and snapshot artifacts renamed to match. - Xcode project file references updated in lockstep. * Drop legacy `validated` wire-value compat in RewardVerification decoder The `RewardVerificationStatusResponse` decoder was accepting both `"verified"` and the legacy `"validated"` status from the backend as a backwards-compat hedge during rollout. The endpoint has not been shipped and the SDK has no public entry point that polls it yet, so there is no compatibility window to preserve — the decoder can require `"verified"` outright. While here, simplify the decoder to look up known cases via `Status(rawValue:)` instead of a string-literal switch, so adding a future case can't silently drift from the enum. Removes the corresponding `testDecodesLegacyValidatedWireValueAsVerified` test. Existing coverage for `verified`, `pending`, `failed`, and the `unknown` fallback (with the `Logger.warn` assertion) is unchanged. * Update Sources/Purchasing/Purchases/Purchases.swift Co-authored-by: Antonio Pallares <ajpallares@users.noreply.github.com> * test: assert warning is logged when decoding literal "unknown" status Address review feedback: cover the case where the backend sends the literal "unknown" wire value, which still falls through the unmapped-status branch and should emit a warning. --------- Co-authored-by: Antonio Pallares <ajpallares@users.noreply.github.com>





Checklist
purchases-androidand hybridsMotivation
Adapter modules that import the SDK with
@_spi(Internal) import RevenueCat(starting withRevenueCatAdMob) need a way to ask the backend whether AdMob's SSV postback for a given rewarded ad has been validated, is still pending, or was rejected.This PR adds the SDK-side polling primitive used by adapters after AdMob's
userDidEarnRewardHandlerfires.Description
Internal SPI surface (
@_spi(Internal)):Purchases.pollAdMobSSVStatus(clientTransactionID:) async throws -> AdMobSSVPollStatusAdMobSSVPollStatusenum cases:.validated,.pending,.failed,.unknownTaskdoes not cancel an in-flight HTTP requestBackend/networking additions:
AdsAPIentry point for ad-related backend callsHTTPRequest.Path.adMobSSVStatus(appUserID:clientTransactionID:)GetAdMobSSVStatusOperationimplemented asCacheableNetworkOperationto dedupe concurrent identical pollsappUserID + "\n" + clientTransactionIDto avoid collisionsshouldSendEtag = falsefor this polling endpoint so status transitions are not hidden by 304 responsesResponse behavior:
.unknown.unknownto adapters so adapter-level polling policy can decide how to handle itTesting
BackendGetAdMobSSVStatusTestscoverage for status decoding, HTTP wiring, errors, empty user ID handling, concurrent dedupe, and sequential reissue behaviorPurchasesAdMobSSVTestscoverage forPurchases.pollAdMobSSVStatusstatus mapping and error forwardingNote
Medium Risk
Touches core networking (
Backend,HTTPRequest.Path, shared error types) and introduces a new signed/verified backend endpoint, so regressions could affect request routing/caching behavior despite good test coverage.Overview
Adds an internal SPI polling primitive for AdMob SSV verification:
Purchases.pollAdMobSSVStatus(clientTransactionID:)returningAdMobSSVPollStatus(validated/pending/failed/unknown).Implements a new ads backend surface (
AdsAPI) andGET /v1/subscribers/{app_user_id}/ads/admob/ssv/{client_transaction_id}wiring viaHTTPRequest.Path.adMobSSVStatusandGetAdMobSSVStatusOperation, including deduping concurrent identical polls and graceful decoding/logging of unknown future status values.Extends error handling with
BackendError.missingClientTransactionIDand adds unit tests + snapshot baselines covering request formation, decoding, error cases, and dedupe behavior (plusMockAdsAPIandPurchasesAdMobSSVTests).Reviewed by Cursor Bugbot for commit d0216f6. Bugbot is set up for automated code reviews on this repo. Configure here.