From c32c038d17f0c4820d81e4004dae8e650422110d Mon Sep 17 00:00:00 2001 From: Cesar de la Vega <664544+vegaro@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:20:39 +0200 Subject: [PATCH] fix(ios): send one _makePurchase message per purchase attempt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit purchaseProduct and purchasePackage sent the response twice when the purchase failed: once inside the error branch and once from the unconditional send that follows the if/else. Only the first delivery reached the caller, because _makePurchase clears MakePurchaseCallback before invoking it, so the second message was silently dropped. The win-back offer handlers already had the correct single-send shape; this makes all four consistent. Also replaces `response[@"userCancelled"] = false` with `@NO` in the three places that used it. `false` is 0, so it boxed to nil, and assigning nil through an NSMutableDictionary subscript removes the key instead of storing it — the success payload was shipping without userCancelled at all. Unity read the missing key as false, so the effect was invisible, but the payload did not match what PurchaseResult expects. Co-Authored-By: Claude --- RevenueCat/Plugins/iOS/PurchasesUnityHelper.m | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/RevenueCat/Plugins/iOS/PurchasesUnityHelper.m b/RevenueCat/Plugins/iOS/PurchasesUnityHelper.m index 89142bc7..1b7a0fa4 100644 --- a/RevenueCat/Plugins/iOS/PurchasesUnityHelper.m +++ b/RevenueCat/Plugins/iOS/PurchasesUnityHelper.m @@ -160,10 +160,9 @@ - (void)purchaseProduct:(NSString *)productIdentifier response = [NSMutableDictionary new]; response[@"error"] = error.info; response[@"userCancelled"] = error.info[@"userCancelled"]; - [self sendJSONObject:response toMethod:MAKE_PURCHASE]; } else { response = [NSMutableDictionary dictionaryWithDictionary:responseDictionary]; - response[@"userCancelled"] = false; + response[@"userCancelled"] = @NO; } [self sendJSONObject:response toMethod:MAKE_PURCHASE]; }]; @@ -181,7 +180,6 @@ - (void)purchasePackage:(NSString *)packageIdentifier response = [NSMutableDictionary new]; response[@"error"] = error.info; response[@"userCancelled"] = error.info[@"userCancelled"]; - [self sendJSONObject:response toMethod:MAKE_PURCHASE]; } else { response = [NSMutableDictionary dictionaryWithDictionary:responseDictionary]; response[@"userCancelled"] = @NO; @@ -558,7 +556,7 @@ - (void)purchaseProductWithWinBackOffer:(NSString *)productIdentifier winBackOff response[@"userCancelled"] = error.info[@"userCancelled"]; } else { response = [NSMutableDictionary dictionaryWithDictionary:responseDictionary]; - response[@"userCancelled"] = false; + response[@"userCancelled"] = @NO; } [self sendJSONObject:response toMethod:PURCHASE_PRODUCT_WITH_WIN_BACK_OFFER]; }]; @@ -593,7 +591,7 @@ - (void)purchasePackageWithWinBackOffer:(NSString *)packageIdentifier presentedO response[@"userCancelled"] = error.info[@"userCancelled"]; } else { response = [NSMutableDictionary dictionaryWithDictionary:responseDictionary]; - response[@"userCancelled"] = false; + response[@"userCancelled"] = @NO; } [self sendJSONObject:response toMethod:PURCHASE_PACKAGE_WITH_WIN_BACK_OFFER]; }];