-
Notifications
You must be signed in to change notification settings - Fork 3
New SDE Sample Queries #691
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@toppare Here's the most complex new query that I'd love your feedback on (the others are simplifications of this same base query)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can ignore the lines subtracted / added notes from Github. They were just indentation changes for consistency that I made in a later commit, the whole query is new as of this PR.
[revenuecat_data_table] | ||
WHERE (start_time BETWEEN [targeted_start_date] and [targeted_end_date] | ||
OR effective_end_time BETWEEN [targeted_start_date] and [targeted_end_date]) | ||
AND is_trial_period = 'false' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an important filter, better to add some comments here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which one? AND is_trial_period = 'false'
? If so I added this: /* Exclude trials, which do not contribute to MRR */
But I feel like you may be referring to a different filter that wasn't explicit enough
code_blocks/🔌 Integrations & Events/scheduled-data-exports_11.pgsql
Outdated
Show resolved
Hide resolved
code_blocks/🔌 Integrations & Events/scheduled-data-exports_11.pgsql
Outdated
Show resolved
Hide resolved
CASE | ||
WHEN expires_date IS NOT NULL | ||
AND is_trial_period = 'false' | ||
AND (is_trial_conversion = 'false' AND renewal_number = 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand why we need is_trial_conversion = 'false'
isn't it enough to put renewal_number=1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch -- this query returns 0:
SELECT COUNT(*) FROM transactions
WHERE renewal_number = 1
AND is_trial_conversion = true
AND is_sandbox <> true
I'll update it (there are many small changes like this that we should be able to bring back into the real Actives cube to make that simpler as well)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can simplify the code like below:
case when is_trial_conversion=true or renewal_number = 1 then 'new_mrr'
else 'renewal_mrr'
and we can be confident that all cases are covered
code_blocks/🔌 Integrations & Events/scheduled-data-exports_12.pgsql
Outdated
Show resolved
Hide resolved
code_blocks/🔌 Integrations & Events/scheduled-data-exports_11.pgsql
Outdated
Show resolved
Hide resolved
code_blocks/🔌 Integrations & Events/scheduled-data-exports_11.pgsql
Outdated
Show resolved
Hide resolved
code_blocks/🔌 Integrations & Events/scheduled-data-exports_11.pgsql
Outdated
Show resolved
Hide resolved
code_blocks/🔌 Integrations & Events/scheduled-data-exports_11.pgsql
Outdated
Show resolved
Hide resolved
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 Subscriptions as of a specified 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 DATE_DIFF('s', start_time, 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. -- Active Subscriptions Movement within a specified date range
WITH
filtered_subscriptipon_transactions AS (
SELECT
*
FROM [revenuecat_data_table]
/* Filter down to the date range that you want to measure MRR Movement for */
WHERE (start_time BETWEEN [targeted_start_date] and [targeted_end_date]
OR effective_end_time BETWEEN [targeted_start_date] and [targeted_end_date])
/* Exclude trials, which do not contribute to MRR */
AND is_trial_period = 'false'
AND DATE_DIFF('s', start_time, end_time)::float > 0
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'),
actives AS (
SELECT
DATE(start_time) AS date,
COUNT(
CASE
WHEN renewal_number = 1
OR is_trial_conversion = 'true'
THEN 1
ELSE NULL
END) AS num_new_actives,
COUNT(
CASE
WHEN renewal_number > 1
AND is_trial_conversion = 'false'
THEN 1
ELSE NULL
END
) AS num_renewals
FROM filtered_subscriptipon_transactions
GROUP BY 1),
expirations AS (
SELECT
DATE(effective_end_time) AS date,
COUNT(*) AS num_expirations
FROM filtered_subscriptipon_transactions
GROUP BY 1)
SELECT
COALESCE(a.date, e.date) AS date,
COALESCE(a.num_new_actives, 0) AS new_actives,
COALESCE(a.num_renewals, 0) AS num_renewals,
COALESCE(e.num_expirations, 0) AS num_expirations,
num_expirations - num_renewals AS churned_actives,
FROM actives a
FULL JOIN expirations e ON a.date = e.date
WHERE a.date BETWEEN [targeted_start_date] AND [targeted_end_date]
AND e.date BETWEEN [targeted_start_date] AND [targeted_end_date] -- MRR as of a specified date
SELECT
SUM(
CASE WHEN effective_end_time IS NOT NULL THEN
CASE
/* Handle cases where product_duration cannot be used for the transaction first */
WHEN (is_in_intro_offer_period = 'true' OR product_duration IS NULL) THEN
CASE
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 0 AND 1
THEN (30 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) = 3
THEN (10 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 6 AND 8
THEN (4 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 12 AND 16
THEN (2 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 27 AND 33
THEN (1 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 58 AND 62
THEN (0.5 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 88 AND 95
THEN (0.333333 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 179 AND 185
THEN (0.1666666 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 363 AND 375
THEN (0.08333 * price)::DECIMAL(18,2)
ELSE ((28 / (DATE_DIFF('s', start_time, end_time)::float / (24 * 3600))) * price)::DECIMAL(18,2)
END
/* Then handle cases where product_duration can be used */
WHEN product_duration = 'P1D'
THEN (30 * price)::DECIMAL(18,2)
WHEN product_duration = 'P3D'
THEN (10 * price)::DECIMAL(18,2)
WHEN product_duration = 'P7D'
THEN (4 * price)::DECIMAL(18,2)
WHEN product_duration = 'P1W'
THEN (4 * price)::DECIMAL(18,2)
WHEN product_duration = 'P2W'
THEN (2 * price)::DECIMAL(18,2)
WHEN product_duration = 'P4W'
THEN (1 * price)::DECIMAL(18,2)
WHEN product_duration = 'P1M'
THEN (1 * price)::DECIMAL(18,2)
WHEN product_duration = 'P2M'
THEN (0.5 * price)::DECIMAL(18,2)
WHEN product_duration = 'P3M'
THEN (0.333333 * price)::DECIMAL(18,2)
WHEN product_duration = 'P6M'
THEN (0.1666666 * price)::DECIMAL(18,2)
WHEN product_duration = 'P12M'
THEN (0.08333 * price)::DECIMAL(18,2)
WHEN product_duration = 'P1Y'
THEN (0.08333 * price)::DECIMAL(18,2)
ELSE ((28 / (DATE_DIFF('s', start_time, end_time)::float / (24 * 3600))) * price)::DECIMAL(18,2)
END
END
) AS active_mrr
FROM [revenuecat_data_table]
/* Filter down to the date range that you want to measure MRR Movement for */
WHERE date(effective_end_time) > '2024-02-06'
AND date(start_time) <= '2024-02-06'
/* Exclude trials, which do not contribute to MRR */
AND is_trial_period = 'false'
AND DATE_DIFF('s', start_time, end_time)::float > 0
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true' -- MRR Movement for a specified date range
WITH
filtered_subscriptipon_transactions AS (
SELECT
*,
CASE WHEN effective_end_time IS NOT NULL THEN
CASE
/* Handle cases where product_duration cannot be used for the transaction first */
WHEN (is_in_intro_offer_period = 'true' OR product_duration IS NULL) THEN
CASE
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 0 AND 1
THEN (30 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) = 3
THEN (10 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 6 AND 8
THEN (4 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 12 AND 16
THEN (2 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 27 AND 33
THEN (1 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 58 AND 62
THEN (0.5 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 88 AND 95
THEN (0.333333 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 179 AND 185
THEN (0.1666666 * price)::DECIMAL(18,2)
WHEN DATE_DIFF(day, start_time, end_time) BETWEEN 363 AND 375
THEN (0.08333 * price)::DECIMAL(18,2)
ELSE ((28 / (DATE_DIFF('s', start_time, end_time)::float / (24 * 3600))) * price)::DECIMAL(18,2)
END
/* Then handle cases where product_duration can be used */
WHEN product_duration = 'P1D' THEN (30 * price)::DECIMAL(18,2)
WHEN product_duration = 'P3D' THEN (10 * price)::DECIMAL(18,2)
WHEN product_duration = 'P7D' THEN (4 * price)::DECIMAL(18,2)
WHEN product_duration = 'P1W' THEN (4 * price)::DECIMAL(18,2)
WHEN product_duration = 'P2W' THEN (2 * price)::DECIMAL(18,2)
WHEN product_duration = 'P4W' THEN (1 * price)::DECIMAL(18,2)
WHEN product_duration = 'P1M' THEN (1 * price)::DECIMAL(18,2)
WHEN product_duration = 'P2M' THEN (0.5 * price)::DECIMAL(18,2)
WHEN product_duration = 'P3M' THEN (0.333333 * price)::DECIMAL(18,2)
WHEN product_duration = 'P6M' THEN (0.1666666 * price)::DECIMAL(18,2)
WHEN product_duration = 'P12M' THEN (0.08333 * price)::DECIMAL(18,2)
WHEN product_duration = 'P1Y' THEN (0.08333 * price)::DECIMAL(18,2)
ELSE ((28 / (DATE_DIFF('s', start_time, end_time)::float / (24 * 3600))) * price)::DECIMAL(18,2)
END
END AS transaction_mrr
FROM [revenuecat_data_table]
/* Filter down to the date range that you want to measure MRR Movement for */
WHERE (start_time BETWEEN [targeted_start_date] and [targeted_end_date]
OR effective_end_time BETWEEN [targeted_start_date] and [targeted_end_date])
/* Exclude trials, which do not contribute to MRR */
AND is_trial_period = 'false'
AND DATE_DIFF('s', start_time, end_time)::float > 0
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'),
actives AS (
SELECT
DATE(start_time) AS date,
SUM(
CASE
WHEN renewal_number = 1
OR is_trial_conversion = 'true'
THEN transaction_mrr
ELSE null
END
) AS new_mrr,
SUM(
CASE
WHEN renewal_number > 1
AND is_trial_conversion = 'false'
THEN transaction_mrr
ELSE null
END
) AS renewal_mrr
FROM filtered_subscriptipon_transactions
GROUP BY 1),
expirations AS (
SELECT
DATE(effective_end_time) AS date,
SUM(transaction_mrr) AS expired_mrr
FROM filtered_subscriptipon_transactions
GROUP BY 1)
SELECT
COALESCE(a.date, e.date) AS date,
COALESCE(a.new_mrr, 0) AS new_mrr,
COALESCE(a.renewal_mrr, 0) as renewal_mrr,
COALESCE(e.expired_mrr, 0) as expired_mrr,
expired_mrr - renewal_mrr as churned_mrr
FROM actives a
FULL JOIN expirations e ON a.date = e.date
WHERE a.date BETWEEN [targeted_start_date] AND [targeted_end_date]
AND e.date BETWEEN [targeted_start_date] AND [targeted_end_date] -- 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 DATE_DIFF('s', start_time, 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. -- Revenue generated on a specified date
SELECT
SUM(purchase_price_in_usd) as gross_revenue,
SUM(price_in_usd) as revenue_net_of_refunds, /* "Total Revenue" in the Revenue Chart */
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 DATE_DIFF('s', start_time, 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]
WHERE date(effective_end_time) > [targeted_date]
AND date(start_time) <= [targeted_date]
AND is_trial_period = 'false'
AND DATE_DIFF('s', start_time, 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 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]
WHERE is_trial_period = 'false'
AND ownership_type != 'FAMILY_SHARED'
AND store != 'promotional'
AND is_sandbox != 'true'
AND was_refunded = 'false'
AND price > 0),
first_purchase_dates AS
(SELECT
rc_original_app_user_id,
MIN(start_time) as first_purchase_date
FROM filtered_transactions
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
Two general changes in here:
Background is in Slack here.
TL;DR Customer is looking to reproduce Charts and is expecting us to have QA'ed queries to do so with so they can rely on them. I've done that myself for all of these queries and was able to get numbers that match up perfectly for individual data points, and are very close for aggregations (can reasonably be chalked up to freshness differences between sources).
The only exception to that is my MRR query is slightly off right now after my last round of reformatting, so I'm going to QA that further before setting this as ready for review.
UPDATE: MRR reconciles perfectly for days > 1 day ago, so I'm very confident this is a data freshness problem now.
Changes introduced
Linear ticket (if any)
Additional comments