-
Notifications
You must be signed in to change notification settings - Fork 3
Edited the SDEs docs, specifically for "Next export start time" and data freshness #685
base: main
Are you sure you want to change the base?
Conversation
just changed the language to be more clear
Previewstemp/scheduled-data-exports.md See contents
RevenueCat can automatically send data deliveries of all of your apps' transaction data to various cloud storage providers. These are in the form of gzip compressed .csv files delivered daily. Setup Instructions
Version Change LogTransaction FormatApplicable to the latest version
[block:parameters] *Available only on our most recent export version A note on transaction dataAll transaction data is based on the store receipts that RevenueCat has received. Receipts often have inconsistencies and quirks which may need to be considered. For example:
We try to normalize or at least annotate these quirks as much as possible, but by and large we consider receipts as the sources of truth, so any inconsistencies in the transaction data can always be traced back to the receipt. Updating to the latest versionIf you're on an older version of our exports, updating is easy:
[block:image]
Sample queries for RevenueCat measuresYou can use the following sample queries (written in Postgresql) as starting points for reproducing common RevenueCat measures. -- Active Trials as of your [targeted_date]
SELECT
COUNT(*)
FROM
[revenuecat_data_table]
WHERE date(effective_end_time) > [targeted_date]
AND date(start_time) <= [targeted_date]
AND is_trial_period = 'true'
AND (effective_end_time IS NULL OR DATE_DIFF('s', start_time, effective_end_time)::float > 0)
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
-- The RevenueCat Active Trials chart excludes
-- promotional transactions and transactions resulting from family sharing
-- since they do not reflect auto-renewing future payments. -- Active Subscriptions as of your [targeted_date]
SELECT
COUNT(*)
FROM
[revenuecat_data_table]
WHERE date(effective_end_time) > [targeted_date]
AND date(start_time) <= [targeted_date]
AND is_trial_period = 'false'
AND (effective_end_time IS NULL OR DATE_DIFF('s', start_time, effective_end_time)::float > 0)
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
-- The RevenueCat Active Subscriptions chart excludes trials,
-- promotional transactions, and transactions resulting from family sharing
-- since they do not reflect auto-renewing future payments. -- Revenue generated on [targeted_date]
SELECT
SUM(purchase_price_in_usd) as total_revenue,
SUM(price_in_usd) as total_revenue_net_of_refunds,
SUM(price_in_usd * (1 - tax_percentage - commission_percentage)) as proceeds
FROM
[revenuecat_data_table]
WHERE date(start_time) = [targeted_date]
AND is_trial_period = 'false'
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
-- Transactions which have been refunded can be identified through the refunded_at field. Sample queries for customized measuresScheduled Data Exports are a powerful way to add your own customizations on top of the core measures provided by RevenueCat. Check out the following sample queries (written in Postgresql) for some ideas. -- How many Active Subscriptions do I have with a given custom attribute value?
SELECT
COUNT(*)
FROM
[revenuecat_data_table] rc
WHERE date(effective_end_time) > [targeted_date]
AND date(start_time) <= [targeted_date]
AND is_trial_period = 'false'
AND (effective_end_time IS NULL OR DATE_DIFF('s', start_time, effective_end_time)::float > 0)
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
AND json_extract_path_text(custom_subscriber_attributes, '[custom_attribute_key].value') = [custom_attribute_value] -- What is my split of Active Subs by auto renew status?
SELECT
CASE
WHEN unsubscribe_detected_at IS NOT NULL THEN 'Set to cancel'
ELSE 'Set to renew'
END) as auto_renew_status,
COUNT(*) as active_subscriptions
FROM
[revenuecat_data_table] rc
WHERE date(effective_end_time) > [targeted_date]
AND date(start_time) <= [targeted_date]
AND is_trial_period = 'false'
AND (effective_end_time IS NULL OR DATE_DIFF('s', start_time, effective_end_time)::float > 0)
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
GROUP BY 1 -- What is my weekly revenue, where Monday is set as the start day of the week?
SELECT
date_trunc('week', start_time) as week,
SUM(price_in_usd) as total_revenue
FROM
[revenuecat_data_table]
WHERE date(start_time) BETWEEN [targeted_period_start_date] AND [targeted_period_end_date]
AND is_trial_period = 'false'
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
GROUP BY week -- What is my Realized LTV of each monthly subscription cohort, segmented by whether they were offered a trial?
WITH
(SELECT
MIN(start_time) as subscription_start_time,
original_store_transaction_id,
MAX(is_trial_period) as had_a_trial,
SUM(price_in_usd) as realized_ltv
FROM
[revenuecat_data_table]
WHERE date(start_time) > [targeted_period_start_date]
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
GROUP BY original_store_transaction_id) as subscriptions
SELECT
to_char(first_start_time, 'YYYY-MM') as subscription_start_month,
had_a_trial,
COUNT() as subscriptions,
SUM(realized_ltv) as realized_ltv,
SUM(realized_ltv) / COUNT() as realized_ltv_per_subscription
FROM
subscriptions -- What portion of my Active Trials are in a grace period?
SELECT
CASE
WHEN grace_period_end_time IS NOT NULL THEN 'in_grace_period'
ELSE 'in_trial_period'
END as period_type,
COUNT(*) as active_trials
FROM
[revenuecat_data_table]
WHERE date(effective_end_time) > [targeted_date]
AND date(start_time) <= [targeted_date]
AND is_trial_period = 'true'
AND (effective_end_time IS NULL OR DATE_DIFF('s', start_time, effective_end_time)::float > 0)
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
GROUP BY period_type -- What is my Realized LTV per Paying Customer cohorted by First Purchase Date?
WITH filtered_transactions AS
(SELECT *
FROM [revenuecat_data_table] rc
WHERE is_trial_period = 'false'
AND was_refunded = 'false'
AND ownership_type = 'PURCHASED'
AND is_sandbox != 'true'
AND store != 'promotional'
AND price > 0),
first_purchase_dates AS
(SELECT
rc_original_app_user_id,
MIN(start_time) as first_purchase_date
FROM filtered_transactions ft
GROUP BY 1)
SELECT
DATE(fpd.first_purchase_date) AS first_purchase_date,
COUNT(DISTINCT rc_original_app_user_id) AS paying_customers,
SUM(CASE WHEN DATEADD(day, 7, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)::DECIMAL(18,2) AS total_ltv_7_days,
SUM(CASE WHEN DATEADD(day, 30, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)::DECIMAL(18,2) AS total_ltv_30_days,
SUM(CASE WHEN DATEADD(month, 6, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)::DECIMAL(18,2) AS total_ltv_6_months,
SUM(CASE WHEN DATEADD(month, 12, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)::DECIMAL(18,2) AS total_ltv_12_months,
SUM(CASE WHEN DATEADD(month, 24, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)::DECIMAL(18,2) AS total_ltv_24_months,
SUM(price_in_usd)::DECIMAL(18,2) AS total_ltv_unbounded,
(SUM(CASE WHEN DATEADD(day, 7, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)/COUNT(DISTINCT rc_original_app_user_id))::DECIMAL(18,2) AS avg_ltv_7_days,
(SUM(CASE WHEN DATEADD(day, 30, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)/COUNT(DISTINCT rc_original_app_user_id))::DECIMAL(18,2) AS avg_ltv_30_days,
(SUM(CASE WHEN DATEADD(month, 6, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)/COUNT(DISTINCT rc_original_app_user_id))::DECIMAL(18,2) AS avg_ltv_6_months,
(SUM(CASE WHEN DATEADD(month, 12, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)/COUNT(DISTINCT rc_original_app_user_id))::DECIMAL(18,2) AS avg_ltv_12_months,
(SUM(CASE WHEN DATEADD(month, 23, first_purchase_date) > start_time THEN price_in_usd ELSE 0 END)/COUNT(DISTINCT rc_original_app_user_id))::DECIMAL(18,2) AS avg_ltv_24_months,
(SUM(price_in_usd)/COUNT(DISTINCT rc_original_app_user_id))::DECIMAL(18,2) AS avg_ltv_unbounded
FROM filtered_transactions ft
LEFT JOIN first_purchase_dates fpd
ON fpd.rc_original_app_user_id = ft.rc_original_app_user_id
GROUP BY 1 See contentsWhat is RevenueCat?RevenueCat provides a backend and a wrapper around StoreKit and Google Play Billing to make implementing in-app purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about how RevenueCat fits into your app or you can sign up free to start building. InstallationWe provide 2 ways to install our SDK: via Unity Package Manager (UPM) in the OpenUPM registry, or as a Option 1 (recommended): Install using OpenUPM
[block:callout] [block:callout] Configure a Main Gradle TemplateGo to Project -> Build Settings -> Player Settings -> Android tab -> Publishing Settings, and check "Custom Base Gradle Template", then close that window. Go to Assets -> External Dependency Manager -> Android Resolver -> Settings, then check "Patch mainTemplate.gradle" Option 2: Import the Purchases Unity packageDownload the latest version of Purchases.unitypackage. Import the downloaded unitypackage to your Unity project. Make sure the [block:callout] Configure a Main Gradle TemplateGo to Project -> Build Settings -> Player Settings -> Android tab -> Publishing Settings, and check "Custom Base Gradle Template", then close that window. Go to Assets -> External Dependency Manager -> Android Resolver -> Settings, then check "Patch mainTemplate.gradle" Create a GameObject with the Purchases behaviorThe Purchases package will include a MonoBehavior called Purchases. This will be your access point to RevenueCat from inside Unity. It should be instantiated once and kept as a singleton. You can use properties to configure your API Key, app user ID (if you have one), and product identifiers you want to fetch. Link StoreKit (iOS only)
Subclass Purchases.Listener MonoBehaviorThe Purchases behavior takes one additional parameter, a GameObject with a Purchases.Listener component. This will be where you handle purchase events, and updated subscriber information from RevenueCat. Here is a simple example: using System;
using System.Collections.Generic;
using UnityEngine;
public class PurchasesListener : Purchases.UpdatedCustomerInfoListener
{
public override void CustomerInfoReceived(Purchases.CustomerInfo customerInfo)
{
// display new CustomerInfo
}
private void Start()
{
var purchases = GetComponent<Purchases>();
purchases.SetDebugLogsEnabled(true);
purchases.GetOfferings((offerings, error) =>
{
if (error != null)
{
// show error
}
else
{
// show offering
}
});
}
public void BeginPurchase(Purchases.Package package)
{
var purchases = GetComponent<Purchases>();
purchases.PurchasePackage(package, (productIdentifier, customerInfo, userCancelled, error) =>
{
if (!userCancelled)
{
if (error != null)
{
// show error
}
else
{
// show updated Customer Info
}
}
else
{
// user cancelled, don't show an error
}
});
}
void RestoreClicked()
{
var purchases = GetComponent<Purchases>();
purchases.RestorePurchases((customerInfo, error) =>
{
if (error != null)
{
// show error
}
else
{
// show updated Customer Info
}
});
}
} Unity EditorRunning the Purchases SDK is unsupported in the Unity Editor at this time, and may result in Proguard rulesIf you have enabled Minify in Unity, make sure to add these custom rules to your
Installation with Unity IAP side by side[block:callout] Purchases Unity 5.0.0+side by side with Unity IAP 4.8.0 This version is only compatible with version 4.8.0 and above of Unity IAP which are the ones that include BillingClient 5. To install download If using RevenueCat alongside Unity IAP 2.2.0+ or other plugin that includes the Android BillingClient library you will be getting an error when compiling that warns about some BillingClient classes being duplicated. The easiest way to remove the error would be to tell Gradle to not include the billingclient library that Unity IAP is already including. In order to do that, make sure you have Modify the dependencies {
...
// ** ADD THIS **
configurations.all {
exclude group: 'com.android.billingclient', module: 'billing'
}
} Perform a clean up of the resolved dependencies using the Also make sure to perform a resolve, so External Dependency Manager adds the right dependencies to the generated [block:callout] Troubleshooting "ClassNotFoundException" errors at Runtime in AndroidWhen exporting your project to Android, in the Build Settings window, make sure you uncheck the Installing old versions of the pluginPurchases Unity 4.2.0+side by side with Unity IAP 4.4.0 < 4.8.0 Download Purchases Unity 4.0.0 and 4.1.0side by side with Unity IAP 3.3.0 < 4.4.0 Download Next Steps
|
Motivation / Description
made these changes as they came up in Zendesk ticket 36055 and I thought they reasonably could be asked again
Changes introduced
Linear ticket (if any)
Additional comments