Server-to-Server Integration Guide

Server to Server Integration Guide

Server-to-Server Use Case

As an alternative to the Singular SDK, which has to be embedded within your apps, Singular also provides a REST API that can be used to build a full integration that runs from your own server without the Singular SDK. This guide explains the concepts behind building a complete Server-to-Server integration and highlights all the advanced features.

Hybrid Use Case

As an alternative to a 100% Server-to-Server Integration, you may utilize a hybrid approach using the Singular SDK to handle Session management and all the advanced features and data collection requirements. In addition, leveraging the Server-to-Server EVENT API to send Events from your backend server.

Hybrid Use Case Details

Hybrid Use Case Details

When sending out-of-app mobile events or website event data from your server to Singular, it’s essential to include the corresponding identifier from the Singular Mobile SDK or Web SDK. This ensures that the event activity is accurately attributed to the correct device.

Data Flow

s2s_hybrid_device_data_capture.png

There are two main methods to retrieve this device data:

  1. Client-Managed Data Flow: This option is to capture all the required data points on the client and pass them to your server via your own API. This approach allows you to collect device identifiers, event data, and other necessary attributes directly from the client-side (e.g., mobile app or website) and forward them to your server, where you can process and send them to Singular using the Server-to-Server EVENT API.
  2. Postback: This option is to configure the Singular Internal BI postback which allows Singular to send a postback to your designated endpoint with a JSON payload. This postback is delivered in real-time after an install, re-engagement, and/or in-app events occur. The payload will include all necessary data points required for successfully sending out-of-app server-side events. For detailed instructions on setting up the Internal BI postback, click [HERE].

Both options may require some server-side logic to maintain a device graph. If the Singular SDK detects a change in the device identifier, it is critical that your server is updated accordingly to ensure accurate tracking and attribution.

To implement the first option, where you capture device identifiers and parameters from the app and send them to your server for use with Singular API Endpoints, review the following articles for guidance:

Review the general guidelines below to follow the general guidelines of a Server-to-Server configuration. With a Hybrid use-case you can omit the SESSION Endpoint as the Singular SDK will manage this for you.

Key Points

  • Flexibility: Full control over data collection and transmission.
  • Feature Parity: Supports all SDK functionality when proper data is provided.
  • Integration Path: Client → Your Server → Singular API
  • Real-time processing: One request at a time, no batch processing.
  • Sequential data flow: Events must be processed in chronological order.
  • Data Deduplication: Singular does not deduplicate received data. It is recommended to send one(1) successful Request and save logs in the event a Request should be replayed.
  • Data Validation: Device-level data is permanent and cannot be deleted once ingested. Implement thorough data validation before sending data to Singular to ensure accuracy.

Requirements

While the SDK automatically collects device and app data, the S2S approach requires you to:

  1. Collect required data points from your app.
  2. Forward this data to your server and store it in a device graph.
  3. Send Session requests to Singular via the SESSION API Endpoint.
  4. Pass the Singular response back to the app.
  5. Forward Event requests to Singular via the EVENT API Endpoint.

REST API Endpoints


Session Endpoint

GET https://s2s.singular.net/api/v1/launch

Session Endpoint API Reference


Event Endpoint

GET https://s2s.singular.net/api/v1/evt

Event Endpoint API Reference


Getting Started

To successfully integrate with Singular's REST API for tracking app sessions and events, you need to implement a comprehensive data pipeline that consists of four key phases.

  1. Establish a robust data collection strategy within your app that captures and stores relevant user interactions and the Singular required data points in your server-side database.
  2. Create a real-time data streaming mechanism that instantly forwards the captured events from your server to Singular's REST API endpoints.
  3. Implement proper response handling to process Singular's API responses, ensuring successful data transmission and appropriate error management.
  4. Conduct thorough testing across all data flows to validate the accuracy and reliability of the integration.

This systematic approach ensures seamless data transmission while maintaining data integrity and real-time synchronization between your application and Singular's analytics platform.

Data Collection

Data Collection

Singular requires specific data points on each endpoint, which are neccessary to provide the feature functionality of the Singular platform. All required parameters are required and if omitted will result in data or functional discrepancies.

Tip: When you collect client-side data to to be sent back to your server, for use with Singular APIs, be sure to wait for asynchronous functions to return and handle various edge cases. This is a common issue that can cause missing data and partial attribution.

With the required data points captured and provided to your server, you are ready to stream session and event requests to Singular.

Real-time data streaming

Real-time data streaming

Real-time data streaming is crucial for maintaining accurate attribution and campaign performance measurement. When implementing server-side tracking, the timing and sequence of data transmission directly impacts the quality of analytics and optimization capabilities provided by Singular.

Critical Timing Considerations

  • Attribution Accuracy: Delayed session reporting can severely impact attribution accuracy, as the system requires precise temporal data to correctly associate user actions with marketing campaigns.
  • SKAdNetwork Implications: The time-sensitive nature of iOS SKAdNetwork conversion values makes real-time data streaming particularly critical. With a strict on-device timer window for conversion value updates, any delay in event reporting can lead to missed opportunities to update conversion values, resulting in incomplete or inaccurate campaign performance data.

Some best practices to keep in mind when building your data stream:

With the real-time stream configured, move on to response handling.

Response handling

Response handling

Response handling is another critical component that bridges server-side API interactions with client-side functionality. This bidirectional communication ensures that valuable response data from Singular's API reaches the mobile application, enabling key features like deferred deep linking and conversion value updates.

Key Response Types

  • Deferred Deep Links: When a new session is initiated, the API response may contain pending deep link data that needs immediate relay to the app for proper user routing and experience personalization.
  • Conversion Values: For iOS campaigns, the conversion endpoint provides updated SKAdNetwork conversion values that must be promptly forwarded to the app to maintain accurate campaign measurement.

Some best practices to keep in mind when building your response handling flow:

  • Implement response handling on client server.
  • Parse and validate Singular API responses.
  • Forward relevant response data to client app. Essential for iOS SKAdNetwork conversion value updates.
  • Implement client-side response processing.
  • Handle errors gracefully with proper status codes.
  • Log failed responses for retry mechanisms.

Now that response handling is complete, it is time to test the data pipeline and validate that all data flows function as expected and provide the correct data.

Testing data flows

Testing data flows

The testing phase is critical to a successful deployment. The most basic integration with Singular involves notifying Singular when a User session occurs, allowing Singular to trigger several internal processes:

  • If it's the first session for the app on the specific device, Singular recognizes a new install and triggers the install attribution process.
  • If the session qualifies as a re-engagement session, Singular triggers the re-engagement attribution process (learn more in the Re-engagement FAQ).
  • Otherwise, Singular marks it as a session, which is used to track user activity and retention metrics.

The timing of a session request and subsequent event requests to the Singular servers is critical:

  1. A single session must be received prior to any events.
    For example, the Singular SDK will trigger a session on the App Open when a user starts using the app, then in-app events may be sent following the session. If the user places the app in the background for an extended period of time (greater than 1 minute) the session would time-out. Another session would be sent when the app is brought back to the foreground. It is recommended to use App lifecycle events and a timer to help manage your session management and regulate the session requests to Singular.
  2. Events occuring in the app must be sent in real-time and after their respective session.
  • Test the Session data flow and validate that the first session and subsequent sessions have their respective data points and correct values.
  • Confirm that events are only received after the session is reported to Singular. If events are received prior to the session, then an Organic attribution will be fabricated for the device which may cause undesired results in reporting.
  • Confirm that the session response is handled and passed back to the client app. This is critical when supporting deferred deeplinks with Singular campaigns.

Success:

  • You have validated the Data Collection and Storage of required data points!
  • You have validated the Real-time Data Streaming to Singular!
  • You have validated the Response Handling & Logging of requests to Singular!
  • You have validated all testing data flows!

Advanced Options

To leverage the advanced options of Singular's Server-to-Server (S2S) integration, you need to enhance the Session Notification Endpoint with additional parameters.

This process involves:

  1. Identifying the desired advanced features.
  2. Locating the corresponding special parameters.
  3. Incorporating these parameters into the existing endpoint configuration.

Review the available advanced options below:

Additional Attribution Handling

Supporting Apple Search Ads Campaigns

Attribution for Apple Search Ads Campaigns (iOS)

Apple Search Ads is considered a Self-Attributing Network (SAN).  As of iOS 14.3, the Apple Search Ads integration is supported through two iOS frameworks:

We recommend that both the iAd and AdServices frameworks are implemented until the iAd framework is deprecated at a future date.  Since AdServices is still a new Apple service, Singular will utilize both services but prioritize AdServices over iAd signals for attribution and reporting. 

For more information, see our Apple Search Ads integration documentation.

Implementing Apple Search Ads via iAd (iOS 14.2 and below)

1. Retrieving the Attribution Data:

To retrieve the attribution data, use the Apple Search Ads iAd API. Calling requestAttributionDetails(_:): returns a JSON object containing the attribution data.

For example:

Objective-C
#import <iAd/iAd.h>
  
    Class ADClientClass = NSClassFromString(@"ADClient");
  
    if (ADClientClass) {
        id sharedClient = [ADClientClass performSelector:@selector(sharedClient)];
        
        if ([sharedClient respondsToSelector:@selector(requestAttributionDetailsWithBlock:)]) {
            [sharedClient requestAttributionDetailsWithBlock:^(NSDictionary *attributionDetails, NSError *error) {
                if (attributionDetails && attributionDetails.count > 0) {
                    // REPORT attributionDetails FROM YOUR APP TO YOUR SERVER

                }
            }];
        }
    }
Warning: Often, users who click on a Search Ads ad download and open the app immediately. If there is any latency in communications, an MMP such as Singular may not be able to receive and process the ad click in time before the app is opened and calls the Search Ads Attribution API. To prevent this, Apple recommends:
  1. Setting a delay of a few seconds before retrieving attribution data.
  2. Implementing retry logic if the response is False or an error code (0, 2, 3). Call the Apple Attribution API again 2 seconds later.

2. Sending the Attribution Data to Singular:

To share the attribution data with Singular, use the Event Notification endpoint to report an event with the reserved event name __iAd_Attribution__. Pass the JSON object you retrieved in the previous step as the value of the parameter, as in the example below.

Python HTTP
import requests
  import json
  
  SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
  EVENT_URL = 'https://s2s.singular.net/api/v1/evt'
  
  # !!! REPLACE WITH COLLECTED VALUE FROM APP !!!
  apple_attribution_data = {
    u'Version3.1': {
        u'iad-adgroup-id': u'1234567',
        u'iad-adgroup-name': u'Ad Group Name',
        u'iad-attribution': u'true',
        u'iad-campaign-id': u'1234567',
        u'iad-campaign-name': u'Search Campaign',
        u'iad-click-date': u'2016-05-21T12:19:31Z',
        u'iad-conversion-date': u'2016-05-21T12:19:41Z',
        u'iad-keyword': u'ballon',
        u'iad-lineitem-id': u'1234567',
        u'iad-lineitem-name': u'Line Item Name',
        u'iad-org-name': u'Cool Company',
        u'iad-purchase-date': u'2016-05-21T12:19:41Z'
    }
  }
  
  params = {
    'n': '__iAd_Attribution__',
    'e': json.dumps(apple_attribution_data),
    'a': SDK_KEY,
    'p': 'iOS',
    'i': 'com.singular.app',
    'ip': '10.1.2.3',
    've': '9.2',
    'mo': 'iPhone9%2C4',
    'lc': 'en_US',
    'idfa': '8ECD7512-2864-440C-93F3-A3CABE62525B',
    'idfv': '38548D9F-3F73-4D4B-8545-9A920CC89191',
    'utime': 1483228800
  }
  
  result = requests.get(EVENT_URL, params=params)
  print result.json()

Notes:

  • For iOS 13+, you have to send the __iAd_Attribution__ event immediately after the first session after install or re-install. Otherwise, the Apple Search Ads data will not be considered for attribution.
  • In iOS 14+, Apple Search Ads attribution responses are only available under certain conditions and not available if AppTrackingTransparency status is ATTrackingManager.AuthorizationStatus.denied

Implementing Apple Search Ads via AdServices (iOS 14.3 and above)

1. Retrieving the attribution token:

Retrieve the attribution token using attributionToken() as soon the app initializes for the first time after an install or re-install. 

For example:

Objective-C
#import <AdServices/AdServices.h> 

NSError *error = nil;
Class AAAttributionClass = NSClassFromString(@"AAAttribution");
if (AAAttributionClass) {
    NSString *attributionToken = [AAAttributionClass attributionTokenWithError:&error];
    if (!error && attributionToken) {
        // Handle attributionToken

    }
}

Notes:

  • The attribution token is generated on the device.
  • After generating, the token is cached for 5 minutes on the device.  After 5 minutes, a new token is generated if attributionToken() is called.
  • The generated token is valid for 24 hours.

2. Send the attribution token to Singular:

URL encode the token and send the token to Singular via the session notification endpoint, appended to the &attribution_token= parameter.   This token should be sent on the first session after every install and re-install to enable Singular to track Apple Search Ads downloads and re-downloads.   

Supporting Google Play Install Referrer

Important: Sending the Google Play Install Referrer (Android)

The install referrer contains information about who sent a user to the Google Play Store. When the install referrer is available to Singular, it provides the most accurate way to attribute installs. Retrieve this value and pass it along to Singular on the first session notification call. It is required for some important Singular features, such as receiving Facebook data in our User-Level Exports, sharing it with Data Destinations, and sending postbacks.

Google Play collects referrer information when a user arrives at the store. If the user later installs the app they were directed to, Google Play makes the information available to the app. For more information, see Google's developer documentation.

To share the install referrer with Singular:

  1. When the app is opened for the first time, retrieve the install referrer using the Play Install Referrer API.
  2. Report a session to Singular using the Session Notification endpoint including the parameter install_ref. This parameter is JSON-encoded and has the following attributes:

    Attribute Description
    referrer
    The referrer value as retrieved from the Play Install Referrer API. This is a JSON object, so make sure to encode it as a string.
    referrer_source
    Specify "service".
    clickTimestampSeconds
    The click timestamp as received from the Play Install Referrer API (e.g., "1550420123").
    installBeginTimestampSeconds
    The time that the install started, as received from the Play Install Referrer API.
    current_device_time
    The time in the current device, in milliseconds (e.g., "1550420454906").

The following is sample code for reporting the install referrer event:

Python HTTP
import requests
  import json
  
  SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
  LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'
  
  referrer_values = {
        "referrer": "tracking_id%3D123456789&utm_source%3Dmdotm%26utm_medium%3Dbanner%26utm_campaign%3Dcampaign",
        "referrer_source" : "service",
        "clickTimestampSeconds" : 1550420123,
        "installBeginTimestampSeconds" : 1550420123,
        "current_device_time" : 1550420454906
      }

referrer_values = json.dumps(referrer_values, separators=(',',':'))
  
   params = {
       'a': SDK_KEY,
       'p': 'Android',
       'i': 'com.singular.app',
       'ip': '10.1.2.3',
       've': '9.2',
       'ma': 'samsung',
       'mo': 'SM-G935F',
       'lc': 'en_US',
       'aifa': '8ecd7512-2864-440c-93f3-a3cabe62525b',
       'andi': 'fc8d449516de0dfb',
       'utime': 1483228800,
       'dnt': 0,
       'install':'true',
       'n': 'MyCoolApp',
       'c': 'wifi',
       'cn': 'Comcast',
       'bd': 'Build/13D15',
       'fcm':'bk3RNwTe3H0CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1',
       'app_v':'1.2.3',
       'openuri':'myapp%3A%2F%2Fhome%2Fpage%3Fqueryparam1%3Dvalue1',
       'ddl_enabled':'false',
       'install_source': 'com.android.vending',
       'install_time': 1510040127,
       'update_time': 1510090877,
      'custom_user_id': '123456789abcd',
      'install_ref' : referrer_values
   }
  
   result = requests.get(LAUNCH_URL, params=params)
   print result.json()
Supporting Meta Install Referrer Attribution

Meta Install Referrer Attribution (Android)

"Meta Referrer" is an Android-specific measurement solution introduced by Facebook to allow advertisers access to granular user-level attribution data for Android app installs (see Facebook's data policies). It is comprised of implementing both "Google Play Install Referrer" (see "Passing Google Install Referrer") and "Meta Install Referrer" technologies for app install measurement. Read more about Meta Referrer in the FAQ on the topic.

To share Meta Install Referrer information with Singnular:

  1. When the app is opened for the first time, retrieve the Meta install referrer according to Meta's documentation.
  2. Report a session to Singular using the Session Notification endpoint including the parameter meta_ref. This parameter is JSON-encoded and has the following attributes:

    Attribute Description
    is_ct
    The "is_ct" as received from the Meta Install Referrer. (e.g. 0 or 1)
    install_referrer
    The "install_referrer" as received from the Meta Install Referrer
    actual_timestamp
    The "actual_timestamp" as received from the Meta Install Referrer (e.g., 1693978124).

Singular Link Support

Deep links are clickable links that lead users to specific content inside an app. When a user clicks a deep link on a device that has the app installed, the app opens and shows a specific product or experience.

Singular tracking links can include deep linking as well as deferred deep linking (see our Deep Linking FAQ and the Singular Links FAQ for more information).

Enabling Deep Linking

Enabling Deep Linking

Deep Linking Prerequisites

The client App must be configured to recognize the Singular Link as an iOS Universal Link or an Android App Link. Follow the Singular Links Prerequisites guide to enable iOS and Android deeplinking.

Implementing Deep Links

When the app is opened through a deep link, capture and add the openURL to the Session Notification request sent to Singular by append the URL value to the openuri parameter. This is required when using Singular Links.

The client App code must be updated to parse and handle the Deeplink Parameters from the Singular Links URL. In the event of a successful deep link caused by a Singular Link, the following parameters may be present on the openURL:

_dl, _ios_dl, _android_dl, _p

 

  • _ios_dl and _android_dl will be present on a link when the link was generated to deep link on both iOS AND Android with different deeplink values for each platform.
  • If deep link values are the same for iOS AND Android, only _dl will be present.
  • If additional data is passed in the passthrough parameter the _p will be present.

Deep Link Handler Code Sample

The App must have handler code capable of parsing the openURL and handling it appropriately. Find the example below to demonstrate how you might parse the Singular Link parameters.

iOS - Swift Android - Kotlin
class DeepLinkHandler {
  func handleURL(_ url: URL) {
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
      return
    }

    var params: [String: String] = [:]

    // Parse query parameters

    if let queryItems = components.queryItems {
      for item in queryItems {
        switch item.name {
        case "_dl":
          params["deeplink"] = item.value
        case "_ios_dl":
          params["ios_deeplink"] = item.value
        case "_p":
          params["passthrough"] = item.value
        default:
          break
        }
      }
    }

    // Handle the parsed parameters

    processDeepLinkParameters(params)
  }

  private func processDeepLinkParameters(_ params: [String: String]) {
    // Process the parameters as needed

    print("Processed parameters: \(params)")
  }
}

// In SceneDelegate or AppDelegate

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  guard let url = URLContexts.first?.url else { return }
  let handler = DeepLinkHandler()
  handler.handleURL(url)
}

Deferred Deep Link Support

When the app is opened for the first time since the install, enable the deferred deep linking flow by adding the following parameters when you report the Session to Singular:

  • install=true
  • ddl_enabled=true

Singular looks to see if the app was installed through a tracking link that included a deferred deep link. If it was, the Session request returns the following values in the response to your server:

  • deferred_deeplink - the deep link address. This is what you need to parse to show the users the right product or experience.
  • deferred_passthrough - any passthrough parameters added to the deep link.

The client App should include code to handle the Deferred Deeplink, and Passthrough Parameters provided in the response from the Singular SESSION API and handle the data appropriately. The response JSON from the SESSION notification with a deferred deeplink value will look like:

{
 "deferred_deeplink":"myapp://deferred-deeplink",
 "status":"ok",
 "deferred_passthrough":"passthroughvalue"
}

Using Dynamic Passthrough Parameters

Singular tracking links can include dynamic passthrough parameters (learn more). If your organization has set up dynamic passthrough parameters for a link, the deep link URL includes the _p parameter followed by a URL-encoded JSON string value or an unstructured string which you can use to show the user the appropriate content or experience.

Resolving Short Links

When the App is opened from a shortened Singular Link, the following parameter should be included on the launch request to notify the Singular Session endpoint that the openURL sent in openuri parameter should be resolved to the Singular long link.

  • singular_link_resolve_required=true

Singular will return the unshortened long link and the App Developer can parse the deep link and passthrough parameters in the link handler, as previously noted.

Sample Response

{
   "status":"ok",
   "resolved_singular_link":"https://myapp.sng.link/A59c0/nha7?_dl=myapp%3A%2F%2Fdeeplink&_ddl=myapp%3A%2F%2Fdeferred-deeplink&_p=passthroughvalue"
}

Cross-device Support

When implementing Singular's cross-device solution or associating users with device-level sessions, it's crucial to leverage the custom_user_id parameter effectively. This parameter should contain your internal User ID, but it's imperative to adhere to data privacy policies by avoiding the inclusion of personally identifiable information (PII).

Best practices involve using a hashed value derived from a username, email address, or a randomly generated string that serves as your app's unique user identifier. By implementing this approach, you enable Singular to utilize the custom_user_id for comprehensive cross-device reporting, user-level data exports, and Internal BI postbacks (when configured), thereby enhancing the granularity and value of your analytics data while maintaining user privacy.


Reporting Revenue

Singular can collect data about revenue gained through the app to help analyze the performance and ROI of your campaigns. Singular will make the data available to you in reports, export logs, and postbacks.

To track revenue events, use the same Event Notification endpoint that you use for all events, but include the Revenue parameters.

  • is_revenue_event This marks the event as a revenue event. You can skip this parameter if the event name is __iap__ or the amount is greater than zero.
  • purchase_receipt This is an object returned from Android's or iOS's In-App Purchase (IAP) process. We highly recommend passing it along to Singular to give Singular full details about the transaction and enrich your Singular reports with data.
  • receipt_signature (Android only) We highly recommend passing this bit of data along to Singular to validate the transaction and fight in-app fraud.
  • amt This is the Revenue Amount as a Double (example: "amt=1.99").
  • cur This is the ISO 4217 currency code, (example: "cur=USD").

Use the Implement Subscription State Management in App Client Code as a guide to get the purchase object from Google's Play Billing Library and/or Apple's StoreKit. You can also query subscription information and manage the subscription state directly on the device. Pass these details back to your server for inclusion on the Event request to Singular.

Sample Revenue Event Request

Sample Custom Revenue Event

Python HTTP cURL
import requests

params = {
    "a": "sdk_key_here",
    "p": "Android",
    "i": "com.singular.app",
    "ip": "10.1.2.3",
    "ve": "9.2",
    "aifa": "8ecd7512-2864-440c-93f3-a3cabe62525b",
    "asid": "edee92a2-7b2f-45f4-a509-840f170fc6d9",
    "n": "RevenueEventName",
    "amt": "2.50",
    "cur": "USD",
    "is_revenue_event": "true",
    "purchase_receipt": {
        'orderId"': "GPA.1234",
        "packageName": "com.example",
        "productId": "com.example.product",
        "purchaseTime": 1417113074914,
        "purchaseState": 0,
        "purchaseToken": "hakfcimbkargpM",
    },
    "receipt_signature": "TyVJfHg8OAoW7W4wuJtasr5agEDMnNXvhfrw==",
    "purchase_product_id": "com.example.product",
    "purchase_transaction_id": "GPA.1234-1234-1234-12345",
}

response = requests.get("https://s2s.singular.net/api/v1/evt", params=params)
print(response.json())

Tracking Uninstalls

Singular can track uninstalls using the device's Silent Push Notifications. To enable it, you will need to send the device's push token to the Singular server along with every session notification.

  1. Be sure to follow our guides to Setup Uninstall Tracking in the Singular Platform and for your App:

  2. For iOS append the APNS Token to the apns_token parameter. For Android append the FCM Token to the fcm parameter.

Retrieving the iOS Install Receipt

When reporting a session for an iOS app, you should pass the install receipt in the install_receipt parameter.

How to retrieve iOS Install Receipt

To retrieve this value, add the following code to your app:

Objective-CSwift
// ReceiptManager.h

@interface ReceiptManager : NSObject
+ (nullable NSString *)getInstallReceipt;
@end

// ReceiptManager.m

#import <StoreKit/StoreKit.h>

@implementation ReceiptManager

+ (nullable NSString *)getInstallReceipt {
    if (@available(iOS 18.0, *)) {
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        __block NSString *result = nil;
        
        [SKPaymentQueue.defaultQueue addTransactionObserver:[[SKPaymentTransactionObserver alloc] init]];
        
        if (@available(iOS 18.0, *)) {
            [AppTransaction.shared fetchWithCompletionHandler:^(AppTransaction *transaction, NSError *error) {
                if (error) {
                    NSLog(@"Failed to get app transaction: %@", error.localizedDescription);
                } else {
                    result = transaction.jwsRepresentation;
                }
                dispatch_semaphore_signal(semaphore);
            }];
        }
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        return result;
        
    } else {
        NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
        if (!receiptURL) {
            NSLog(@"Receipt URL not found");
            return nil;
        }
        
        NSError *error = nil;
        NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL 
                                                   options:NSDataReadingUncached 
                                                     error:&error];
        
        if (error) {
            NSLog(@"Failed to read receipt: %@", error.localizedDescription);
            return nil;
        }
        
        return [receiptData base64EncodedStringWithOptions:0];
    }
}

@end

Complying with Data Privacy Laws

Singular provides privacy-safeguarding functionality to help you cooperate with any partners who may be complying with consumer privacy laws such as GDPR and CCPA. These partners want to be notified if the end-user has consented to share their private information.

If you have implemented a way to ask users for consent to share their information, use the data_sharing_options parameter to notify Singular of the user's choice:

  • Pass "limit_data_sharing":false to indicate that the user consented (opted-in) to share their information.
  • Pass "limit_data_sharing":true if the user refused.

Singular uses limit_data_sharing in "User Privacy Postbacks" as well as passing this information on to partners who require it in order to comply with relevant regulations. See "User Privacy and Limit Data Sharing" for more information.

Note:

  • The data_sharing_options parameter is optional, but there may be attribution information that the partner will share with Singular only if specifically notified that the user has opted-in.