Skip to content

Commit ad1b221

Browse files
committed
Add disableAppSetId API and new validateAndLogInAppPurchase (V2) API
1 parent 50d8ab8 commit ad1b221

6 files changed

Lines changed: 260 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
1+
## 6.17.21
2+
Release date: *2025-08-08*
3+
4+
- React Native >> Add new `disableAppSetId` API for Android platform
5+
- React Native >> Add new `validateAndLogInAppPurchase` API with AFPurchaseDetails support
6+
- React Native >> Update Android SDK to 6.17.2
7+
- React Native >> Update Plugin to v6.17.21
8+
9+
## 6.17.2
10+
Release date: *2025-07-30*
11+
12+
- React Native >> Add Purchase Connector and Swift AppDelegate support to Expo config plugin
13+
- React Native >> expo 53 and RN 0.79 support
14+
- React Native >> Add Expo Android Purchase Connector support
15+
- React Native >> Fix withAppsFlyerAndroid configuration
16+
- React Native >> Update Plugin to v6.17.2
17+
18+
## 6.17.1
19+
Release date: *2025-07-01*
20+
21+
- React Native >> Update iOS Purchase Connector to 6.17.1
22+
- React Native >> Update Plugin to v6.17.1
23+
24+
## 6.17.0
25+
Release date: *2025-06-15*
26+
27+
- React Native >> Add Purchase Connector support with StoreKit2 option
28+
- React Native >> Update iOS & Android SDKs to 6.17.0
29+
- React Native >> Update Plugin to v6.17.0
30+
131
## 6.16.2
232
Release date: *2025-03-19*
333

Docs/RN_API.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ The list of available methods for this plugin is described below.
3131
- [setSharingFilter](#setsharingfilter)
3232
- [setSharingFilterForPartners](#setsharingfilterforpartners)
3333
- [validateAndLogInAppPurchase](#validateandloginapppurchase)
34+
- [validateAndLogInAppPurchase(NEW)](#validateandloginapppurchasev2)
3435
- [updateServerUninstallToken](#updateserveruninstalltoken)
3536
- [sendPushNotificationData](#sendpushnotificationdata)
3637
- [addPushNotificationDeepLinkPath](#addpushnotificationdeeplinkpath)
@@ -44,6 +45,7 @@ The list of available methods for this plugin is described below.
4445
- [setCollectIMEI](#setcollectimei)
4546
- [setDisableNetworkData `setDisableNetworkData(disable)`](#setdisablenetworkdata-setdisablenetworkdatadisable)
4647
- [performOnDeepLinking](#performondeeplinking)
48+
- [disableAppSetId](#disableappsetid)
4749
- [iOS Only APIs](#ios-only-apis)
4850
- [disableCollectASA](#disablecollectasa)
4951
- [disableIDFVCollection](#disableidfvcollection)
@@ -627,6 +629,53 @@ appsFlyer.validateAndLogInAppPurchase(info, res => console.log(res), err => cons
627629

628630
---
629631

632+
## New In-App Purchase Validation API
633+
The new `validateAndLogInAppPurchase` API uses `AFPurchaseDetails` and `AFPurchaseType` enum for structured purchase validation.
634+
635+
### AFPurchaseType Enum
636+
637+
```javascript
638+
import { AFPurchaseType } from 'react-native-appsflyer';
639+
640+
AFPurchaseType.SUBSCRIPTION // "subscription"
641+
AFPurchaseType.ONE_TIME_PURCHASE // "one-time-purchase"
642+
```
643+
644+
### AFPurchaseDetails Interface
645+
646+
```typescript
647+
interface AFPurchaseDetails {
648+
purchaseType: AFPurchaseType; // Type of purchase
649+
transactionId: string; // Unique transaction identifier
650+
productId: string; // Product identifier
651+
}
652+
```
653+
654+
### Usage Example
655+
656+
```javascript
657+
import appsFlyer, { AFPurchaseType } from 'react-native-appsflyer';
658+
659+
const purchaseDetails = {
660+
purchaseType: AFPurchaseType.SUBSCRIPTION,
661+
transactionId: "google_play_purchase_token_123",
662+
productId: "com.example.app.premium_monthly"
663+
};
664+
665+
const additionalParams = {
666+
revenue: 9.99,
667+
currency: "USD"
668+
};
669+
670+
appsFlyer.validateAndLogInAppPurchase(
671+
purchaseDetails,
672+
additionalParams,
673+
(result) => console.log(result)
674+
);
675+
```
676+
677+
---
678+
630679
### updateServerUninstallToken
631680
`updateServerUninstallToken(token, callback)`
632681

@@ -948,6 +997,25 @@ if (Platform.OS == 'android') {
948997

949998
appsFlyer.startSdk(); // <--- Here we send launch
950999
```
1000+
1001+
### disableAppSetId
1002+
`disableAppSetId()`
1003+
1004+
**Disable the collection of AppSet ID.**<br/>
1005+
**Must be called before calling start.**<br/>
1006+
1007+
*Example:*
1008+
```javascript
1009+
if (Platform.OS == 'android') {
1010+
appsFlyer.disableAppSetId();
1011+
appsFlyer.initSdk({
1012+
devKey: 'K2***********99',
1013+
isDebug: false,
1014+
appId: '41*****44',
1015+
});
1016+
}
1017+
```
1018+
9511019
## iOS Only APIs
9521020

9531021
### disableCollectASA

android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,55 @@ public void onValidateInAppFailure(String error) {
776776
});
777777
}
778778

779+
@ReactMethod
780+
public void validateAndLogInAppPurchaseV2(ReadableMap purchaseDetails, ReadableMap additionalParameters, Callback callback) {
781+
try {
782+
String purchaseType = purchaseDetails.getString("purchaseType");
783+
String purchaseToken = purchaseDetails.getString("transactionId");
784+
String productId = purchaseDetails.getString("productId");
785+
786+
// Convert purchaseType string to AFPurchaseType enum
787+
AFPurchaseType afPurchaseType;
788+
if ("subscription".equals(purchaseType)) {
789+
afPurchaseType = AFPurchaseType.SUBSCRIPTION;
790+
} else {
791+
afPurchaseType = AFPurchaseType.ONE_TIME_PURCHASE;
792+
}
793+
794+
// Create AFPurchaseDetails object
795+
AFPurchaseDetails afPurchaseDetails = new AFPurchaseDetails(afPurchaseType, purchaseToken, productId);
796+
797+
// Convert additionalParameters to Map
798+
Map<String, String> additionalParamsMap = null;
799+
if (additionalParameters != null) {
800+
additionalParamsMap = RNUtil.toMap(additionalParameters);
801+
}
802+
803+
AppsFlyerLib.getInstance().validateAndLogInAppPurchase(
804+
afPurchaseDetails,
805+
additionalParamsMap,
806+
new AppsFlyerInAppPurchaseValidationCallback() {
807+
@Override
808+
public void onSuccess() {
809+
if (callback != null) {
810+
callback.invoke("In App Purchase Validation completed successfully!");
811+
}
812+
}
813+
814+
@Override
815+
public void onError(int errorCode, String errorMessage) {
816+
if (callback != null) {
817+
callback.invoke("Error: " + errorMessage);
818+
}
819+
}
820+
});
821+
} catch (Exception e) {
822+
if (callback != null) {
823+
callback.invoke("Error: " + e.getMessage());
824+
}
825+
}
826+
}
827+
779828
@ReactMethod
780829
public void sendPushNotificationData(ReadableMap pushPayload, Callback errorCallback) {
781830
JSONObject payload = RNUtil.readableMapToJson(pushPayload);
@@ -879,6 +928,11 @@ public void performOnDeepLinking() {
879928
}
880929
}
881930

931+
@ReactMethod
932+
public void disableAppSetId() {
933+
AppsFlyerLib.getInstance().disableAppSetId();
934+
}
935+
882936
@ReactMethod
883937
public void enableTCFDataCollection(Boolean enabled) {
884938
AppsFlyerLib.getInstance().enableTCFDataCollection(enabled);

index.d.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,22 @@ declare module "react-native-appsflyer" {
102102
additionalParameters?: object;
103103
}
104104

105+
export enum AFPurchaseType {
106+
SUBSCRIPTION = "subscription",
107+
ONE_TIME_PURCHASE = "one-time-purchase"
108+
}
109+
110+
export interface AFPurchaseDetails {
111+
purchaseType: AFPurchaseType;
112+
transactionId: string; //aka PurchaseToken
113+
productId: string;
114+
}
115+
116+
export interface AppsFlyerInAppPurchaseValidationCallback {
117+
onSuccess?: (result?: any) => void;
118+
onError?: (error?: any) => void;
119+
}
120+
105121
export interface SetEmailsOptions {
106122
emails?: string[];
107123
emailsCryptType: AF_EMAIL_CRYPT_TYPE | 0 | 3;
@@ -350,11 +366,23 @@ declare module "react-native-appsflyer" {
350366
latitude: number,
351367
successC?: SuccessCB
352368
): void;
369+
/**
370+
* [LEGACY WARNING] This is the legacy validateAndLogInAppPurchase API.
371+
*/
353372
validateAndLogInAppPurchase(
354373
purchaseInfo: InAppPurchase,
355374
successC: SuccessCB,
356375
errorC: ErrorCB
357376
): Response<string>;
377+
/**
378+
* [NEW API] This is the new validateAndLogInAppPurchase API with AFPurchaseDetails.
379+
*/
380+
validateAndLogInAppPurchase(
381+
purchaseDetails: AFPurchaseDetails,
382+
additionalParameters?: object,
383+
callback?: AppsFlyerInAppPurchaseValidationCallback
384+
): void;
385+
358386
updateServerUninstallToken(token: string, successC?: SuccessCB): void;
359387
sendPushNotificationData(pushPayload: object, errorC?: ErrorCB): void;
360388
setHost(hostPrefix: string, hostName: string, success: SuccessCB): void;
@@ -390,6 +418,7 @@ declare module "react-native-appsflyer" {
390418
setCollectAndroidID(isCollect: boolean, successC?: SuccessCB): void;
391419
setDisableNetworkData(disable: boolean): void;
392420
performOnDeepLinking(): void;
421+
disableAppSetId(): void;
393422
};
394423

395424
export default appsFlyer;

index.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -846,18 +846,26 @@ appsFlyer.disableCollectASA = (shouldDisable) => {
846846
};
847847

848848
/**
849-
* Receipt validation is a secure mechanism whereby the payment platform (e.g. Apple or Google) validates that an in-app purchase indeed occurred as reported.
850-
* Learn more - https://support.appsflyer.com/hc/en-us/articles/207032106-Receipt-validation-for-in-app-purchases
851-
* @param purchaseInfo ReadableMap includes: String publicKey, String signature, String purchaseData, String price, String currency, JSONObject additionalParameters.
852-
* @param successC Success callback
853-
* @param errorC Error callback
849+
* Validate and log in-app purchase with support for both legacy and new APIs.
850+
*
851+
* - **Legacy API**: validateAndLogInAppPurchase(purchaseInfo, successC, errorC)
852+
* - **New API**: validateAndLogInAppPurchase(purchaseDetails, additionalParameters, callback)
853+
*
854+
* The method automatically detects which API to use based on the parameters:
855+
* - If first parameter has 'purchaseType' property, uses new API
856+
* - Otherwise, uses legacy API
854857
*/
855-
appsFlyer.validateAndLogInAppPurchase = (purchaseInfo, successC, errorC) => {
856-
return RNAppsFlyer.validateAndLogInAppPurchase(
857-
purchaseInfo,
858-
successC,
859-
errorC
860-
);
858+
appsFlyer.validateAndLogInAppPurchase = (param1, param2, param3) => {
859+
// Detect which API to use based on the first parameter
860+
if (param1 && typeof param1 === 'object' && param1.purchaseType) {
861+
// New API: (purchaseDetails, additionalParameters, callback)
862+
console.log('[AppsFlyer] Using new validateAndLogInAppPurchase API with AFPurchaseDetails');
863+
return RNAppsFlyer.validateAndLogInAppPurchaseV2(param1, param2, param3);
864+
} else {
865+
// Legacy API: (purchaseInfo, successC, errorC)
866+
console.log('[AppsFlyer] Using legacy validateAndLogInAppPurchase API');
867+
return RNAppsFlyer.validateAndLogInAppPurchase(param1, param2, param3);
868+
}
861869
};
862870

863871
appsFlyer.setUseReceiptValidationSandbox = (isSandbox) => {
@@ -954,6 +962,14 @@ appsFlyer.performOnDeepLinking = () => {
954962
return RNAppsFlyer.performOnDeepLinking();
955963
};
956964

965+
/**
966+
* Disable the collection of AppSet ID.
967+
* This method is only relevant for Android platform.
968+
*/
969+
appsFlyer.disableAppSetId = () => {
970+
return RNAppsFlyer.disableAppSetId();
971+
};
972+
957973
/**
958974
* instruct the SDK to collect the TCF data from the device.
959975
* @param enabled: if the sdk should collect the TCF data. true/false

ios/RNAppsFlyer.m

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,10 @@ - (BOOL)isExpoApp {
565565
[[AppsFlyerLib shared] setDisableIDFVCollection:shouldDisable];
566566
}
567567

568+
RCT_EXPORT_METHOD(disableAppSetId) {
569+
NSLog(@"disableAppSetId is only relevant for Android platform");
570+
}
571+
568572
RCT_EXPORT_METHOD(setUseReceiptValidationSandbox: (BOOL)isSandbox) {
569573
[AppsFlyerLib shared].useReceiptValidationSandbox = isSandbox;
570574
}
@@ -600,6 +604,54 @@ - (BOOL)isExpoApp {
600604

601605
}
602606

607+
RCT_EXPORT_METHOD(validateAndLogInAppPurchaseV2: (NSDictionary*)purchaseDetails
608+
additionalParameters:(NSDictionary*)additionalParameters
609+
callback:(RCTResponseSenderBlock)callback) {
610+
611+
if (!purchaseDetails || [purchaseDetails isKindOfClass:[NSNull class]]) {
612+
if (callback) {
613+
callback(@[@"Error: Purchase details are required"]);
614+
}
615+
return;
616+
}
617+
618+
NSString* purchaseType = [purchaseDetails objectForKey:@"purchaseType"];
619+
NSString* productId = [purchaseDetails objectForKey:@"productId"];
620+
NSString* transactionId = [purchaseDetails objectForKey:@"transactionId"];
621+
622+
if (!purchaseType || !productId || !transactionId) {
623+
if (callback) {
624+
callback(@[@"Error: purchaseType, productId, and transactionId are required"]);
625+
}
626+
return;
627+
}
628+
629+
// Convert purchaseType string to enum
630+
AFSDKPurchaseType afPurchaseType;
631+
if ([purchaseType isEqualToString:@"subscription"]) {
632+
afPurchaseType = AFSDKPurchaseTypeSubscription;
633+
} else {
634+
afPurchaseType = AFSDKPurchaseTypeOneTimePurchase;
635+
}
636+
637+
// Create AFSDKPurchaseDetails object
638+
AFSDKPurchaseDetails* details = [[AFSDKPurchaseDetails alloc] initWithPurchaseType:afPurchaseType
639+
productId:productId
640+
transactionId:transactionId];
641+
642+
[[AppsFlyerLib shared] validateAndLogInAppPurchase:details
643+
extraEventValues:additionalParameters
644+
completionHandler:^(AFSDKValidateAndLogResult * _Nullable result) {
645+
if (callback) {
646+
if (result && result.isValid) {
647+
callback(@[@"In App Purchase Validation completed successfully!"]);
648+
} else {
649+
callback(@[@"Error: Purchase validation failed"]);
650+
}
651+
}
652+
}];
653+
}
654+
603655
RCT_EXPORT_METHOD(sendPushNotificationData: (NSDictionary*)pushPayload errorCallBack:(RCTResponseErrorBlock)errorCallBack) {
604656
[[AppsFlyerLib shared] handlePushNotification:pushPayload];
605657
}

0 commit comments

Comments
 (0)