1010#import " ADJUtil.h"
1111#import " ADJLogger.h"
1212#import " ADJAdjustFactory.h"
13- #import " ADJBackoffStrategy.h"
1413#import " ADJUserDefaults.h"
1514#import " ADJPackageBuilder.h"
1615#import " ADJPurchaseVerificationResult.h"
@@ -24,12 +23,11 @@ @interface ADJPurchaseVerificationHandler()
2423@property (nonatomic , strong ) ADJRequestHandler *requestHandler;
2524
2625@property (nonatomic , assign ) BOOL paused;
27- @property (nonatomic , strong ) ADJBackoffStrategy *backoffStrategy ;
26+ @property (nonatomic , assign ) BOOL isSendingPurchaseVerificationPackage ;
2827
2928@property (nonatomic , weak ) id <ADJLogger> logger;
3029@property (nonatomic , weak ) id <ADJActivityHandler> activityHandler;
3130
32- @property (nonatomic , assign ) NSInteger lastPackageRetriesCount;
3331@property (nonatomic , strong ) NSNumber *lastPackageRetryInMilli;
3432
3533@end
@@ -48,12 +46,12 @@ - (id)initWithActivityHandler:(id<ADJActivityHandler>)activityHandler
4846
4947 self.internalQueue = dispatch_queue_create (kInternalQueueName , DISPATCH_QUEUE_SERIAL);
5048 self.logger = ADJAdjustFactory.logger ;
51- self.lastPackageRetriesCount = 0 ;
5249
53- self.requestHandler = [[ADJRequestHandler alloc ] initWithResponseCallback: self
54- urlStrategy: urlStrategy
55- requestTimeout: [ADJAdjustFactory requestTimeout ]
56- adjustConfiguration: activityHandler.adjustConfig];
50+ self.requestHandler =
51+ [[ADJRequestHandler alloc ] initWithResponseCallback: self
52+ urlStrategy: urlStrategy
53+ requestTimeout: [ADJAdjustFactory verifyRequestTimeout ]
54+ adjustConfiguration: activityHandler.adjustConfig];
5755
5856 [ADJUtil launchInQueue: self .internalQueue
5957 selfInject: self
@@ -68,6 +66,8 @@ - (void)pauseSending {
6866 selfInject: self
6967 block: ^(ADJPurchaseVerificationHandler *selfI) {
7068 selfI.paused = YES ;
69+ selfI.isSendingPurchaseVerificationPackage = NO ;
70+ selfI.lastPackageRetryInMilli = nil ;
7171 }];
7272}
7373
@@ -114,9 +114,10 @@ - (void)teardown {
114114
115115 self.internalQueue = nil ;
116116 self.logger = nil ;
117- self.backoffStrategy = nil ;
118117 self.packageQueue = nil ;
119118 self.activityHandler = nil ;
119+ self.isSendingPurchaseVerificationPackage = NO ;
120+ self.lastPackageRetryInMilli = nil ;
120121}
121122
122123#pragma mark - Private & helper methods
@@ -126,7 +127,8 @@ - (void)initI:(ADJPurchaseVerificationHandler *)selfI
126127 startsSending : (BOOL )startsSending {
127128 selfI.activityHandler = activityHandler;
128129 selfI.paused = !startsSending;
129- selfI.backoffStrategy = [ADJAdjustFactory sdkClickHandlerBackoffStrategy ];
130+ selfI.isSendingPurchaseVerificationPackage = NO ;
131+ selfI.lastPackageRetryInMilli = nil ;
130132 selfI.packageQueue = [NSMutableArray array ];
131133}
132134
@@ -143,63 +145,47 @@ - (void)sendNextPurchaseVerificationPackageI:(ADJPurchaseVerificationHandler *)s
143145 [selfI.logger debug: @" Purchase verification handler is paused" ];
144146 return ;
145147 }
146- NSUInteger queueSize = selfI.packageQueue .count ;
147- if (queueSize == 0 ) {
148+ if (selfI.isSendingPurchaseVerificationPackage ) {
149+ [selfI.logger debug: @" Purchase verification handler is already sending a package" ];
150+ return ;
151+ }
152+ if (selfI.packageQueue .count == 0 ) {
148153 return ;
149154 }
150155 if ([selfI.activityHandler isGdprForgotten ]) {
151- [selfI.logger debug: @" purchase_verification request won't be fired for forgotten user" ];
156+ [selfI.logger debug: @" purchase_verification request won't be sent for GDPR forgotten user" ];
157+ return ;
158+ }
159+
160+ // check if we need to wait for backend-requested retry_in delay
161+ NSNumber *waitTime = [selfI waitTimeTimeInterval ];
162+ if (waitTime != nil ) {
163+ dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t )([waitTime doubleValue ] * NSEC_PER_SEC)),
164+ selfI.internalQueue , ^{
165+ // clear the retry delay after waiting
166+ selfI.lastPackageRetryInMilli = nil ;
167+ [selfI sendNextPurchaseVerificationPackage ];
168+ });
152169 return ;
153170 }
154171
172+ // get the package but keep it in the queue until processing is complete
155173 ADJActivityPackage *purchaseVerificationPackage = [self .packageQueue objectAtIndex: 0 ];
156- [self .packageQueue removeObjectAtIndex: 0 ];
157174
158175 if (![purchaseVerificationPackage isKindOfClass: [ADJActivityPackage class ]]) {
159176 [selfI.logger error: @" Failed to read purchase_verification package" ];
177+ // remove the bad package to prevent infinite loop
178+ [selfI.packageQueue removeObjectAtIndex: 0 ];
179+ selfI.isSendingPurchaseVerificationPackage = NO ;
160180 [selfI sendNextPurchaseVerificationPackage ];
161181 return ;
162182 }
163183
164- dispatch_block_t work = ^{
165- [selfI.requestHandler sendPackageByPOST: purchaseVerificationPackage
166- sendingParameters: nil ];
167- [selfI sendNextPurchaseVerificationPackage ];
168- };
169-
170- NSNumber *waitTimeSecondsDouble = [selfI waitTimeTimeInterval ];
171-
172- if (waitTimeSecondsDouble != nil ) {
173- dispatch_after (dispatch_time (DISPATCH_TIME_NOW,
174- (int64_t )(waitTimeSecondsDouble.doubleValue * NSEC_PER_SEC)),
175- self.internalQueue , work);
176- } else {
177- work ();
178- }
179- }
180- - (NSNumber *)waitTimeTimeInterval {
181- if (self.lastPackageRetriesCount > 0 ) {
182- NSTimeInterval waitTime = [ADJUtil waitingTime: self .lastPackageRetriesCount
183- backoffStrategy: self .backoffStrategy];
184-
185- [self .logger verbose:
186- @" Waiting for %@ seconds before retrying purchase_verification for the %d time" ,
187- [ADJUtil secondsNumberFormat: waitTime], self .lastPackageRetriesCount];
188-
189- return @(waitTime);
190- }
191-
192- if (self.lastPackageRetryInMilli != nil ) {
193- NSTimeInterval waitTime = [self .lastPackageRetryInMilli intValue ] / 1000.0 ;
194-
195- [self .logger verbose:
196- @" Waiting for %@ seconds before retrying purchase_verification with retry_in" ,
197- [ADJUtil secondsNumberFormat: waitTime]];
198-
199- return @(waitTime);
200- }
184+ // set flag to indicate we're sending a package
185+ selfI.isSendingPurchaseVerificationPackage = YES ;
201186
202- return nil ;
187+ [selfI.requestHandler sendPackageByPOST: purchaseVerificationPackage
188+ sendingParameters: nil ];
203189}
204190
205191- (void )updatePackagesTrackingI : (ADJPurchaseVerificationHandler *)selfI
@@ -220,50 +206,65 @@ - (void)updatePackagesTrackingI:(ADJPurchaseVerificationHandler *)selfI
220206}
221207
222208- (void )responseCallback : (ADJResponseData *)responseData {
209+ // reset flag to indicate we're done processing this package
210+ self.isSendingPurchaseVerificationPackage = NO ;
211+
223212 if (!responseData.jsonResponse ) {
224213 [self .logger error:
225214 @" Could not get purchase_verification JSON response with message: %@ " , responseData.message];
226215 ADJPurchaseVerificationResult *verificationResult = [[ADJPurchaseVerificationResult alloc ] init ];
227216 verificationResult.verificationStatus = @" not_verified" ;
228217 verificationResult.code = 102 ;
229218 verificationResult.message = responseData.message ;
230- responseData.purchaseVerificationPackage .purchaseVerificationCallback (verificationResult);
231- }
232- // Check if any package response contains information that user has opted out.
233- // If yes, disable SDK and flush any potentially stored packages that happened afterwards.
234- if (responseData.trackingState == ADJTrackingStateOptedOut) {
235- self.lastPackageRetriesCount = 0 ;
219+ ((ADJPurchaseVerificationResponseData *)responseData).error = verificationResult;
220+ } else {
221+ // check if any package response contains information that user has opted out.
222+ // if yes, disable SDK and flush any potentially stored packages that happened afterwards.
223+ if (responseData.trackingState == ADJTrackingStateOptedOut) {
224+ [self .activityHandler setTrackingStateOptedOut ];
225+ return ;
226+ }
227+
228+ // check if backend requested retry_in delay
229+ if (responseData.retryInMilli != nil ) {
230+ self.lastPackageRetryInMilli = responseData.retryInMilli ;
231+ [self .logger error: @" Retrying purchase_verification package with retry in %d ms" ,
232+ [responseData.retryInMilli intValue ]];
233+
234+ // package stays in queue - schedule retry
235+ [self sendNextPurchaseVerificationPackage ];
236+ return ;
237+ }
238+
239+ // reset retry counter after successful response
236240 self.lastPackageRetryInMilli = nil ;
237- [self .activityHandler setTrackingStateOptedOut ];
238- return ;
239241 }
240242
241- if ([ self retryPackageWithResponse: responseData]) {
242- [ self sendPurchaseVerificationPackage: responseData.purchaseVerificationPackage];
243- return ;
243+ // processing is complete - remove the package from queue
244+ if (self. packageQueue . count > 0 ) {
245+ [ self .packageQueue removeObjectAtIndex: 0 ] ;
244246 }
245247
246- self.lastPackageRetriesCount = 0 ;
247- self.lastPackageRetryInMilli = nil ;
248+ // finish package tracking without retrying / backoff
248249 [self .activityHandler finishedTracking: responseData];
250+
251+ // process next package in queue if any
252+ [self sendNextPurchaseVerificationPackage ];
249253}
250254
251- - (BOOL )retryPackageWithResponse : (ADJResponseData *)responseData {
252- if (responseData.jsonResponse == nil ) {
253- self.lastPackageRetriesCount ++;
254- [self .logger error: @" Retrying purchase_verification package for the %d time" ,
255- self .lastPackageRetriesCount];
256- return YES ;
257- }
255+ - (NSNumber *)waitTimeTimeInterval {
256+ // handle backend-requested retry_in delay
257+ if (self.lastPackageRetryInMilli != nil ) {
258+ NSTimeInterval waitTime = [self .lastPackageRetryInMilli intValue ] / 1000.0 ;
258259
259- if (responseData. retryInMilli != nil ) {
260- self. lastPackageRetryInMilli = responseData. retryInMilli ;
261- [ self .logger error: @" Retrying purchase_verification package with retry in %d ms " ,
262- [responseData.retryInMilli intValue ]];
263- return YES ;
260+ [ self .logger verbose:
261+ @" Waiting for %@ seconds before retrying purchase_verification with retry_in " ,
262+ [ADJUtil secondsNumberFormat: waitTime]];
263+
264+ return @(waitTime) ;
264265 }
265266
266- return NO ;
267+ return nil ;
267268}
268269
269270@end
0 commit comments