@@ -75,6 +75,7 @@ public class ActivityHandler implements IActivityHandler {
7575 private AdjustAttribution attribution ;
7676 private IAttributionHandler attributionHandler ;
7777 private ISdkClickHandler sdkClickHandler ;
78+ private IPurchaseVerificationHandler purchaseVerificationHandler ;
7879 private SessionParameters sessionParameters ;
7980 private InstallReferrer installReferrer ;
8081 private InstallReferrerHuawei installReferrerHuawei ;
@@ -102,6 +103,9 @@ public void teardown() {
102103 if (sdkClickHandler != null ) {
103104 sdkClickHandler .teardown ();
104105 }
106+ if (purchaseVerificationHandler != null ) {
107+ purchaseVerificationHandler .teardown ();
108+ }
105109 if (sessionParameters != null ) {
106110 if (sessionParameters .callbackParameters != null ) {
107111 sessionParameters .callbackParameters .clear ();
@@ -126,6 +130,7 @@ public void teardown() {
126130 adjustConfig = null ;
127131 attributionHandler = null ;
128132 sdkClickHandler = null ;
133+ purchaseVerificationHandler = null ;
129134 sessionParameters = null ;
130135 }
131136
@@ -383,6 +388,10 @@ public void finishedTrackingActivity(ResponseData responseData) {
383388 launchEventResponseTasks ((EventResponseData )responseData );
384389 return ;
385390 }
391+ // check if it's a purchase verification response
392+ if (responseData instanceof PurchaseVerificationResponseData ) {
393+ launchPurchaseVerificationResponseTasks ((PurchaseVerificationResponseData )responseData );
394+ }
386395 }
387396
388397 @ Override
@@ -538,6 +547,16 @@ public void run() {
538547 });
539548 }
540549
550+ @ Override
551+ public void launchPurchaseVerificationResponseTasks (final PurchaseVerificationResponseData purchaseVerificationResponseData ) {
552+ executor .submit (new Runnable () {
553+ @ Override
554+ public void run () {
555+ launchPurchaseVerificationResponseTasksI (purchaseVerificationResponseData );
556+ }
557+ });
558+ }
559+
541560 @ Override
542561 public void sendFirstPackages () {
543562 executor .submit (new Runnable () {
@@ -744,6 +763,16 @@ public AdjustAttribution getAttribution() {
744763 return attribution ;
745764 }
746765
766+ @ Override
767+ public void verifyPurchase (final AdjustPurchase purchase , final OnPurchaseVerificationFinishedListener callback ) {
768+ executor .submit (new Runnable () {
769+ @ Override
770+ public void run () {
771+ verifyPurchaseI (purchase , callback );
772+ }
773+ });
774+ }
775+
747776 public InternalState getInternalState () {
748777 return internalState ;
749778 }
@@ -911,6 +940,7 @@ public void run() {
911940 adjustConfig .basePath ,
912941 adjustConfig .gdprPath ,
913942 adjustConfig .subscriptionPath ,
943+ adjustConfig .purchaseVerificationPath ,
914944 deviceInfo .clientSdk );
915945 packageHandler = AdjustFactory .getPackageHandler (
916946 this ,
@@ -924,6 +954,7 @@ public void run() {
924954 adjustConfig .basePath ,
925955 adjustConfig .gdprPath ,
926956 adjustConfig .subscriptionPath ,
957+ adjustConfig .purchaseVerificationPath ,
927958 deviceInfo .clientSdk );
928959
929960 attributionHandler = AdjustFactory .getAttributionHandler (
@@ -937,13 +968,28 @@ public void run() {
937968 adjustConfig .basePath ,
938969 adjustConfig .gdprPath ,
939970 adjustConfig .subscriptionPath ,
971+ adjustConfig .purchaseVerificationPath ,
940972 deviceInfo .clientSdk );
941973
942974 sdkClickHandler = AdjustFactory .getSdkClickHandler (
943975 this ,
944976 toSendI (true ),
945977 sdkClickHandlerActivitySender );
946978
979+ IActivityPackageSender purchaseVerificationHandlerActivitySender =
980+ new ActivityPackageSender (
981+ adjustConfig .urlStrategy ,
982+ adjustConfig .basePath ,
983+ adjustConfig .gdprPath ,
984+ adjustConfig .subscriptionPath ,
985+ adjustConfig .purchaseVerificationPath ,
986+ deviceInfo .clientSdk );
987+
988+ purchaseVerificationHandler = AdjustFactory .getPurchaseVerificationHandler (
989+ this ,
990+ toSendI (true ),
991+ purchaseVerificationHandlerActivitySender );
992+
947993 if (isToUpdatePackagesI ()) {
948994 updatePackagesI ();
949995 }
@@ -1577,6 +1623,35 @@ public void run() {
15771623 handler .post (runnable );
15781624 }
15791625
1626+ private void launchPurchaseVerificationResponseTasksI (PurchaseVerificationResponseData purchaseVerificationResponseData ) {
1627+ // use the same handler to ensure that all tasks are executed sequentially
1628+ Handler handler = new Handler (adjustConfig .context .getMainLooper ());
1629+ JSONObject jsonResponse = purchaseVerificationResponseData .jsonResponse ;
1630+
1631+ // check and parse response data
1632+ AdjustPurchaseVerificationResult verificationResult ;
1633+ if (jsonResponse == null ) {
1634+ verificationResult = new AdjustPurchaseVerificationResult (
1635+ "not_verified" ,
1636+ 101 ,
1637+ purchaseVerificationResponseData .message );
1638+ } else {
1639+ verificationResult = new AdjustPurchaseVerificationResult (
1640+ UtilNetworking .extractJsonString (jsonResponse , "verification_status" ),
1641+ UtilNetworking .extractJsonInt (jsonResponse , "code" ),
1642+ UtilNetworking .extractJsonString (jsonResponse , "message" ));
1643+ }
1644+
1645+ // trigger purchase verification callback with the verification result
1646+ Runnable runnable = new Runnable () {
1647+ @ Override
1648+ public void run () {
1649+ purchaseVerificationResponseData .activityPackage .getPurchaseVerificationCallback ().onVerificationFinished (verificationResult );
1650+ }
1651+ };
1652+ handler .post (runnable );
1653+ }
1654+
15801655 private void prepareDeeplinkI (final Uri deeplink , final Handler handler ) {
15811656 if (deeplink == null ) {
15821657 return ;
@@ -1923,15 +1998,18 @@ private void pauseSendingI() {
19231998 // it's possible for the sdk click handler to be active while others are paused
19241999 if (!toSendI (true )) {
19252000 sdkClickHandler .pauseSending ();
2001+ purchaseVerificationHandler .pauseSending ();
19262002 } else {
19272003 sdkClickHandler .resumeSending ();
2004+ purchaseVerificationHandler .resumeSending ();
19282005 }
19292006 }
19302007
19312008 private void resumeSendingI () {
19322009 attributionHandler .resumeSending ();
19332010 packageHandler .resumeSending ();
19342011 sdkClickHandler .resumeSending ();
2012+ purchaseVerificationHandler .resumeSending ();
19352013 }
19362014
19372015 private boolean updateActivityStateI (long now ) {
@@ -2403,6 +2481,73 @@ private void trackSubscriptionI(final AdjustPlayStoreSubscription subscription)
24032481 packageHandler .sendFirstPackage ();
24042482 }
24052483
2484+ private void verifyPurchaseI (final AdjustPurchase purchase , final OnPurchaseVerificationFinishedListener callback ) {
2485+ if (callback == null ) {
2486+ logger .warn ("Purchase verification aborted because verification callback is null" );
2487+ return ;
2488+ }
2489+ // from this moment on we know that we can ping client callback in case of error
2490+ if (adjustConfig .urlStrategy != null &&
2491+ (adjustConfig .urlStrategy .equals (AdjustConfig .DATA_RESIDENCY_EU ) ||
2492+ adjustConfig .urlStrategy .equals (AdjustConfig .DATA_RESIDENCY_US ) ||
2493+ adjustConfig .urlStrategy .equals (AdjustConfig .DATA_RESIDENCY_TR ))) {
2494+ logger .warn ("Purchase verification not available for data residency users right now" );
2495+ return ;
2496+ }
2497+ if (!checkActivityStateI (activityState )) {
2498+ AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult (
2499+ "not_verified" ,
2500+ 102 ,
2501+ "Purchase verification aborted because SDK is still not initialized" );
2502+ callback .onVerificationFinished (result );
2503+ logger .warn ("Purchase verification aborted because SDK is still not initialized" );
2504+ return ;
2505+ }
2506+ if (!isEnabledI ()) {
2507+ AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult (
2508+ "not_verified" ,
2509+ 103 ,
2510+ "Purchase verification aborted because SDK is disabled" );
2511+ callback .onVerificationFinished (result );
2512+ logger .warn ("Purchase verification aborted because SDK is disabled" );
2513+ return ;
2514+ }
2515+ if (activityState .isGdprForgotten ) {
2516+ AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult (
2517+ "not_verified" ,
2518+ 104 ,
2519+ "Purchase verification aborted because user is GDPR forgotten" );
2520+ callback .onVerificationFinished (result );
2521+ logger .warn ("Purchase verification aborted because user is GDPR forgotten" );
2522+ return ;
2523+ }
2524+ if (purchase == null ) {
2525+ logger .warn ("Purchase verification aborted because purchase instance is null" );
2526+ AdjustPurchaseVerificationResult verificationResult =
2527+ new AdjustPurchaseVerificationResult (
2528+ "not_verified" ,
2529+ 105 ,
2530+ "Purchase verification aborted because purchase instance is null" );
2531+ callback .onVerificationFinished (verificationResult );
2532+ return ;
2533+ }
2534+
2535+ long now = System .currentTimeMillis ();
2536+ PackageBuilder packageBuilder = new PackageBuilder (adjustConfig , deviceInfo , activityState , sessionParameters , now );
2537+ ActivityPackage verificationPackage = packageBuilder .buildVerificationPackage (purchase , callback );
2538+ if (verificationPackage == null ) {
2539+ logger .warn ("Purchase verification aborted because verification package is null" );
2540+ AdjustPurchaseVerificationResult verificationResult =
2541+ new AdjustPurchaseVerificationResult (
2542+ "not_verified" ,
2543+ 106 ,
2544+ "Purchase verification aborted because verification package is null" );
2545+ callback .onVerificationFinished (verificationResult );
2546+ return ;
2547+ }
2548+ purchaseVerificationHandler .sendPurchaseVerificationPackage (verificationPackage );
2549+ }
2550+
24062551 private void gotOptOutResponseI () {
24072552 activityState .isGdprForgotten = true ;
24082553 writeActivityStateI ();
0 commit comments