Validate and log purchase
Learn how to validate and log purchases.
The validateAndLogInAppPurchase
method is part of the receipt validation flow, which enables your app to validate in-app purchase events generated by the App Store.
📘Note
The function
validateAndLogInAppPurchase
can be replaced by the fully automatic purchase SDK connector (a premium service). To learn how to integrate the connector, see on Github iOS purchase SDK connector.
The method is currently implemented in two versions.
- validateAndLogInAppPurchase - BETA supported from SDK v.6.14.1
- validateAndLogInAppPurchase - Legacy
Implement validateAndLogInAppPurchase (BETA)
The validateAndLogInAppPurchase method (currently in BETA) sends the purchase details to AppsFlyer for validation. After AppsFlyer validates the purchase with the App Store, the method returns the response to a completion handler block.
To implement the method, perform the following steps:
- Query the App Store for the object of the in-app purchase event.
- Initialize an AFSDKPurchaseDetails instance and set it with the purchase type, token, product ID, price, and currency details retrieved from the Purchase object.
- If you want to add additional details to the purchase in-app event, populate a dictionary with key-value pairs.
- Invoke
validateAndLogInAppPurchase
with the following arguments:- The
AFSDKPurchaseDetails
object you created in step 2. - The dictionary with the additional details you created in step 3.
- The
- Add your logic for handling the failure, success, or error responses to a completion handler block in the function body.
If the validation is successful, an af_purchase
event is logged with the values provided to validateAndLogInAppPurchase
.
📘Note
validateAndLogInAppPurchase
generates anaf_purchase
in-app event upon successful validation. Sending this event yourself will cause duplicate event reporting.
Code example
func sampleCodeValidateAndLog(){
let productId = "my-product-id"
let price = "1.99"
let currency = "USD"
let transactionId = "12345-transaction-id"
// Create a new instance of AFSDKPurchaseDetails with the required information
let purchaseDetails = AFSDKPurchaseDetails(productId: productId, price: price, currency: currency, transactionId: transactionId)
let extraEventValues: [String: Any]? = [
"firstExtraEventValue": "something",
"secondExtraEventValue": "nice",
]
AppsFlyerLib.shared().validateAndLog(inAppPurchase: purchaseDetails, extraEventValues: extraEventValues) { result in
// This block is executed when the logging and validation operation is complete.
// Process the 'result' as needed
switch result!.status {
case .success:
print("Purchase validation and logging succeeded.")
if let resultData = result?.result {
// Process successful result data
print("Validation successful data: \(resultData)")
}
case .failure:
print("Purchase was not validated.")
if let errorData = result?.errorData {
// Process failure error data
print("Validation failed data: \(errorData)")
}
case .error:
print("An error occurred during the validation and logging operation.")
if let error = result?.error {
// Process NSError
print("Error: \(error)")
}
}
}
}
Subscription purchase that failed validation
{
"result": false,
"status": "failure",
"product_id": "my-product-id",
"price": "1.99",
"currency": "USD",
"transaction_id": "12345-transaction-id",
"error": {
"code": 100,
"message": "Invalid purchase token or product id."
},
"additional_parameters": {
"firstExtraEventValue": "something",
"secondExtraEventValue": "nice"
}
}
Testing purchase validation in Sandbox mode
To test purchase validation using a sandboxed environment, add the following code:
[AppsFlyerLib shared].useReceiptValidationSandbox = YES;
AppsFlyerLib.shared().useReceiptValidationSandbox = true
Note
This code must be removed from your production builds.
Validating an in-app purchase automatically generates and sends an in-app purchase event to AppsFlyer. Its eventValues
will look something like this:
{
"some_parameter": "some_value", // from additional_event_values
"af_currency": "USD", // from currency
"af_content_id" :"test_id", // from purchase
"af_revenue": "10", // from revenue
"af_quantity": "1", // from purchase
"af_validated": true // flag that AF verified the purchase
}
validateAndLogInAppPurchase (LEGACY)
Purchase validation using validateAndLogInAppPurchase
validateAndLoginInAppPurchase
takes these arguments:
- (void) validateAndLogInAppPurchase:(NSString *) productIdentifier,
price:(NSString *) price
currency:(NSString *) currency
transactionId:(NSString *) tranactionId
additionalParameters:(NSDictionary *) params
success:(void (^)(NSDictionary *response)) successBlock
failure:(void (^)(NSError *error, id reponse)) failedBlock;
validateAndLog(inAppPurchase: String?,
price: String?,
currency: String?,
transactionId: String?,
additionalParameters: [AnyHashable : Any]?,
success: ([AnyHashable : Any]) -> Void)?,
failure: ((Error?, Any?) -> Void)?)
Upon successful validation, a NSDictionary
is returned with the receipt validation data (provided by Apple servers).
Note
Calling
validateAndLogInAppPurchase
generates anaf_purchase
in-app event upon successful validation. Sending this event yourself creates duplicate event reporting.
Example: Validate in-app purchase
[[AppsFlyerLib shared] validateAndLogInAppPurchase:@"ProductIdentifier" price:@"price"
currency:@"USD"
transactionId:@"transactionID"
additionalParameters:@{@"test": @"val" , @"test1" : @"val 1"}
success:^(NSDictionary *result){
NSLog(@"Purchase succeeded And verified! response: %@", result[@"receipt"]);
} failure:^(NSError *error, id response) {
NSLog(@"response = %@", response);
if([response isKindOfClass:[NSDictionary class]]) {
if([response[@"status"] isEqualToString:@"in_app_arr_empty"]){
// retry with 'SKReceiptRefreshRequest' because
// Apple has returned an empty response
// <YOUR CODE HERE>
}
} else {
//handle other errors
return;
}
}];
AppsFlyerLib.shared().validateAndLogInAppPurchase (
inAppPurchase: "productIdentifier",
price: "price",
currency: "currency",
transactionId: "transactionId",
additionalParameters: [:],
success: {
guard let dictionary = $0 as? [String:Any] else { return }
dump(dictionary)
},
failure: { error, result in
guard let emptyInApp = result as? [String:Any],
let status = emptyInApp["status"] as? String,
status == "in_app_arr_empty" else {
// Try to handle other errors
return
}
})
Updated 3 months ago