react-native-adapty
Version:
Adapty React Native SDK
1,062 lines (914 loc) • 32.8 kB
text/typescript
import { Platform } from 'react-native';
import { $bridge } from '@/bridge';
import { LogContext, Log, LogScope } from '@/logger';
import type { Def, Req } from '@/types/schema';
import { AdaptyPaywallCoder } from '@/coders/adapty-paywall';
import { AdaptyPaywallProductCoder } from '@/coders/adapty-paywall-product';
import { AdaptyProfileParametersCoder } from '@/coders/adapty-profile-parameters';
import type * as Model from '@/types';
import * as Input from '@/types/inputs';
import { AddListenerFn, MethodName } from '@/types/bridge';
import { AdaptyType } from '@/coders/parse';
import version from '@/version';
import { AdaptyUiMediaCacheCoder } from '@/coders/adapty-ui-media-cache';
import { AdaptyUiMediaCache } from '@/ui/types';
import { RefundPreference } from '@/types';
/**
* Entry point for the Adapty SDK.
* All Adapty methods are available through this class.
* @public
*/
export class Adapty {
private resolveHeldActivation?: (() => Promise<void>) | null = null;
private activating: Promise<void> | null = null;
private nonWaitingMethods: MethodName[] = [
'activate',
'is_activated',
'get_paywall_for_default_audience',
];
private defaultMediaCache: AdaptyUiMediaCache = {
memoryStorageTotalCostLimit: 100 * 1024 * 1024,
memoryStorageCountLimit: 2147483647,
diskStorageSizeLimit: 100 * 1024 * 1024,
};
// Middleware to call native handle
async handle<T>(
method: MethodName,
params: string,
resultType: AdaptyType,
ctx: LogContext,
log: LogScope,
): Promise<T> {
/*
* If resolveHeldActivation is defined,
* wait until it is resolved before calling native methods
*
* Not applicable for activate method ofc
*/
if (
this.resolveHeldActivation &&
!this.nonWaitingMethods.includes(method)
) {
log.wait({});
await this.resolveHeldActivation();
this.resolveHeldActivation = null;
log.waitComplete({});
}
/*
* wait until activate call is resolved before calling native methods
* Not applicable for activate method ofc
*/
if (
this.activating &&
(!this.nonWaitingMethods.includes(method) || method === 'is_activated')
) {
log.wait({});
await this.activating;
log.waitComplete({});
this.activating = null;
}
try {
const result = await $bridge.request(method, params, resultType, ctx);
log.success(result);
return result as T;
} catch (error) {
/*
* Success because error was handled validly
* It is a developer task to define which errors must be logged
*/
log.success({ error });
throw error;
}
}
/**
* Adds a event listener for native event
*/
addEventListener: AddListenerFn = (event, callback) => {
if (event !== 'onLatestProfileLoad') {
throw new Error('Only onLatestProfileLoad event is supported');
}
return $bridge.addEventListener('did_load_latest_profile', callback);
};
/**
* Removes all attached event listeners
*/
removeAllListeners() {
return $bridge.removeAllEventListeners();
}
/**
* Initializes the Adapty SDK.
*
* @remarks
* This method must be called in order for the SDK to work.
* It is preffered to call it as early as possible in the app lifecycle,
* so background activities can be performed and cache can be updated.
*
* @example
* ## Basic usage in your app's entry point
* ```ts
* adapty.activate('YOUR_API_KEY'); // <-- pass your API key here (required)
* ```
*
* ## Usage with your user identifier from your system
* ```ts
* adapty.activate('YOUR_API_KEY', { // <-- pass your API key here (required)
* customerUserId: 'YOUR_USER_ID' // <-- pass your user identifier here (optional)
* });
* ```
*
* @param {string} apiKey - You can find it in your app settings
* in {@link https://app.adapty.io/ | Adapty Dashboard} App settings > General.
* @param {Input.ActivateParamsInput} params - Optional parameters of type {@link ActivateParamsInput}.
* @returns {Promise<void>} A promise that resolves when the SDK is initialized.
*
* @throws {@link AdaptyError}
* Usually throws if the SDK is already activated or if the API key is invalid.
*/
public async activate(
apiKey: string,
params: Input.ActivateParamsInput = {},
): Promise<void> {
// call before log ctx calls, so no logs are lost
const logLevel = params.logLevel;
Log.logLevel = logLevel || null;
const ctx = new LogContext();
const log = ctx.call({ methodName: 'activate' });
log.start({ apiKey, params });
const config: Def['AdaptyConfiguration'] = {
api_key: apiKey,
cross_platform_sdk_name: 'react-native',
cross_platform_sdk_version: version,
};
if (params.customerUserId) {
config['customer_user_id'] = params.customerUserId;
}
config['observer_mode'] = params.observerMode ?? false;
config['ip_address_collection_disabled'] =
params.ipAddressCollectionDisabled ?? false;
if (logLevel) {
config['log_level'] = logLevel;
}
config['server_cluster'] = params.serverCluster ?? 'default';
if (params.backendBaseUrl) {
config['backend_base_url'] = params.backendBaseUrl;
}
if (params.backendFallbackBaseUrl) {
config['backend_fallback_base_url'] = params.backendFallbackBaseUrl;
}
if (params.backendConfigsBaseUrl) {
config['backend_configs_base_url'] = params.backendConfigsBaseUrl;
}
if (params.backendProxyHost) {
config['backend_proxy_host'] = params.backendProxyHost;
}
if (params.backendProxyPort) {
config['backend_proxy_port'] = params.backendProxyPort;
}
config['activate_ui'] = params.activateUi ?? true;
const coder = new AdaptyUiMediaCacheCoder();
config['media_cache'] = coder.encode(
params.mediaCache ?? this.defaultMediaCache,
);
if (Platform.OS === 'ios') {
config['apple_idfa_collection_disabled'] =
params.ios?.idfaCollectionDisabled ?? false;
}
if (Platform.OS === 'android') {
config['google_adid_collection_disabled'] =
params.android?.adIdCollectionDisabled ?? false;
}
const methodKey = 'activate';
const body = JSON.stringify({
method: methodKey,
configuration: config,
} satisfies Req['Activate.Request']);
const activate = async () => {
this.activating = this.handle<void>(methodKey, body, 'Void', ctx, log);
await this.activating;
};
if (!params.__debugDeferActivation) {
return activate();
}
/*
* Deferring activation solves annoying simulator authentication,
* by postponing the moment, when simulator will use StoreKit
*/
return new Promise<void>(unlock => {
// do not resolve promise, only resolveHeldActivation must resolve
this.resolveHeldActivation = async () => {
const result = await activate();
unlock(result);
};
});
}
/**
* Fetches the paywall by the specified placement.
*
* @remarks
* With Adapty, you can remotely configure the products and offers in your app
* by simply adding them to paywalls – no need for hardcoding them.
* The only thing you hardcode is the placement ID.
* This flexibility allows you to easily update paywalls, products, and offers,
* or run A/B tests, all without the need for a new app release.
*
* @param {string} placementId - The identifier of the desired placement.
* This is the value you specified when you created the placement
* in the Adapty Dashboard.
* @param {string | undefined} [locale] - The locale of the desired paywall.
* @param {Input.GetPaywallParamsInput} [params] - Additional parameters for retrieving paywall.
* @returns {Promise<Model.AdaptyPaywall>}
* A promise that resolves with a requested paywall.
*
* @throws {@link AdaptyError}
* Throws an error:
* 1. if the paywall with the specified ID is not found
* 2. if your bundle ID does not match with your Adapty Dashboard setup
*/
public async getPaywall(
placementId: string,
locale?: string,
params: Input.GetPaywallParamsInput = {
fetchPolicy: Input.FetchPolicy.ReloadRevalidatingCacheData,
loadTimeoutMs: 5000,
},
): Promise<Model.AdaptyPaywall> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'getPaywall' });
log.start({ placementId, locale, params });
const methodKey = 'get_paywall';
const data: Req['GetPaywall.Request'] = {
method: methodKey,
placement_id: placementId,
load_timeout: (params.loadTimeoutMs ?? 5000) / 1000,
};
if (locale) {
data['locale'] = locale;
}
if (params.fetchPolicy !== 'return_cache_data_if_not_expired_else_load') {
data['fetch_policy'] = {
type:
params.fetchPolicy ?? Input.FetchPolicy.ReloadRevalidatingCacheData,
} satisfies Def['AdaptyPaywall.FetchPolicy'];
} else {
data['fetch_policy'] = {
type: params.fetchPolicy,
max_age: params.maxAgeSeconds,
} satisfies Def['AdaptyPaywall.FetchPolicy'];
}
const body = JSON.stringify(data);
const result = await this.handle<Model.AdaptyPaywall>(
methodKey,
body,
'AdaptyPaywall',
ctx,
log,
);
return result;
}
/**
* Fetches the paywall of the specified placement for the **All Users** audience.
*
* @remarks
* With Adapty, you can remotely configure the products and offers in your app
* by simply adding them to paywalls – no need for hardcoding them.
* The only thing you hardcode is the placement ID.
* This flexibility allows you to easily update paywalls, products, and offers,
* or run A/B tests, all without the need for a new app release.
*
* However, it’s crucial to understand that the recommended approach is to fetch the paywall
* through the placement ID by the {@link getPaywall} method.
* The `getPaywallForDefaultAudience` method should be a last resort due to its significant drawbacks.
* See docs for more details
*
* @param {string} placementId - The identifier of the desired placement.
* This is the value you specified when you created the placement
* in the Adapty Dashboard.
* @param {string | undefined} [locale] - The locale of the desired paywall.
* @param {Input.GetPaywallForDefaultAudienceParamsInput} [params] - Additional parameters for retrieving paywall.
* @returns {Promise<Model.AdaptyPaywall>}
* A promise that resolves with a requested paywall.
*
* @throws {@link AdaptyError}
* Throws an error:
* 1. if the paywall with the specified ID is not found
* 2. if your bundle ID does not match with your Adapty Dashboard setup
*/
public async getPaywallForDefaultAudience(
placementId: string,
locale?: string,
params: Input.GetPaywallForDefaultAudienceParamsInput = {
fetchPolicy: Input.FetchPolicy.ReloadRevalidatingCacheData,
},
): Promise<Model.AdaptyPaywall> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'getPaywallForDefaultAudience' });
log.start({ placementId, locale, params });
const methodKey = 'get_paywall_for_default_audience';
const data: Req['GetPaywallForDefaultAudience.Request'] = {
method: methodKey,
placement_id: placementId,
};
if (locale) {
data['locale'] = locale;
}
if (params.fetchPolicy !== 'return_cache_data_if_not_expired_else_load') {
data['fetch_policy'] = {
type:
params.fetchPolicy ?? Input.FetchPolicy.ReloadRevalidatingCacheData,
} satisfies Def['AdaptyPaywall.FetchPolicy'];
} else {
data['fetch_policy'] = {
type: params.fetchPolicy,
max_age: params.maxAgeSeconds,
} satisfies Def['AdaptyPaywall.FetchPolicy'];
}
const body = JSON.stringify(data);
const result = await this.handle<Model.AdaptyPaywall>(
methodKey,
body,
'AdaptyPaywall',
ctx,
log,
);
return result;
}
/**
* Fetches a list of products associated with a provided paywall.
*
* @example
* ```ts
* const paywall = await adapty.getPaywall('paywall_id');
* const products = await adapty.getPaywallProducts(paywall);
* ```
*
* @param {Model.AdaptyPaywall} paywall - a paywall to fetch products for. You can get it using {@link Adapty.getPaywall} method.
* @returns {Promise<Model.AdaptyPaywallProduct[]>} A promise that resolves with a list
* of {@link Model.AdaptyPaywallProduct} associated with a provided paywall.
* @throws {@link AdaptyError}
*/
public async getPaywallProducts(
paywall: Model.AdaptyPaywall,
): Promise<Model.AdaptyPaywallProduct[]> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'getPaywallProducts' });
log.start({ paywall });
const coder = new AdaptyPaywallCoder();
const methodKey = 'get_paywall_products';
const data: Req['GetPaywallProducts.Request'] = {
method: methodKey,
paywall: coder.encode(paywall),
};
const body = JSON.stringify(data);
const result = await this.handle<any>(
methodKey,
body,
'Array<AdaptyPaywallProduct>',
ctx,
log,
);
return result;
}
/**
* Fetches a user profile.
*
* Allows you to define the level of access,
* as well as other parameters.
*
* @remarks
* The getProfile method provides the most up-to-date result
* as it always tries to query the API.
* If for some reason (e.g. no internet connection),
* the Adapty SDK fails to retrieve information from the server,
* the data from cache will be returned.
* It is also important to note
* that the Adapty SDK updates {@link Model.AdaptyProfile} cache
* on a regular basis, in order
* to keep this information as up-to-date as possible.
*
* @returns {Promise<Model.AdaptyProfile>}
* @throws {@link AdaptyError}
*/
public async getProfile(): Promise<Model.AdaptyProfile> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'getProfile' });
log.start({});
const methodKey = 'get_profile';
const body = JSON.stringify({
method: methodKey,
} satisfies Req['GetProfile.Request']);
const result = await this.handle<Model.AdaptyProfile>(
methodKey,
body,
'AdaptyProfile',
ctx,
log,
);
return result;
}
/**
* Logs in a user with a provided customerUserId.
*
* If you don't have a user id on SDK initialization,
* you can set it later at any time with this method.
* The most common cases are after registration/authorization
* when the user switches from being an anonymous user to an authenticated user.
*
* @param {string} customerUserId - unique user id
* @throws {@link AdaptyError}
*/
public async identify(customerUserId: string): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'identify' });
log.start({ customerUserId });
const methodKey = 'identify';
const data: Req['Identify.Request'] = {
method: methodKey,
customer_user_id: customerUserId,
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Logs a paywall view event.
*
* Adapty helps you to measure the performance of the paywalls.
* We automatically collect all the metrics related to purchases except for paywall views.
* This is because only you know when the paywall was shown to a customer.
*
* @remarks
* Whenever you show a paywall to your user,
* call this function to log the event,
* and it will be accumulated in the paywall metrics.
*
* @example
* ```ts
* const paywall = await adapty.getPaywall('paywall_id');
* // ...after opening the paywall
* adapty.logShowPaywall(paywall);
* ```
*
* @param {Model.AdaptyPaywall} paywall - object that was shown to the user.
* @returns {Promise<void>} resolves when the event is logged
*/
public async logShowPaywall(paywall: Model.AdaptyPaywall): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'logShowPaywall' });
log.start({ paywall });
const coder = new AdaptyPaywallCoder();
const methodKey = 'log_show_paywall';
const data: Req['LogShowPaywall.Request'] = {
method: methodKey,
paywall: coder.encode(paywall),
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
public async openWebPaywall(
paywallOrProduct: Model.AdaptyPaywall | Model.AdaptyPaywallProduct,
): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'openWebPaywall' });
log.start({ paywallOrProduct });
const methodKey = 'open_web_paywall';
const data: Req['OpenWebPaywall.Request'] = {
method: methodKey,
...('vendorProductId' in paywallOrProduct
? { product: new AdaptyPaywallProductCoder().encode(paywallOrProduct) }
: { paywall: new AdaptyPaywallCoder().encode(paywallOrProduct) }),
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
public async createWebPaywallUrl(
paywallOrProduct: Model.AdaptyPaywall | Model.AdaptyPaywallProduct,
): Promise<string> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'create_web_paywall_url' });
log.start({ paywallOrProduct });
const methodKey = 'create_web_paywall_url';
const data: Req['CreateWebPaywallUrl.Request'] = {
method: methodKey,
...('vendorProductId' in paywallOrProduct
? { product: new AdaptyPaywallProductCoder().encode(paywallOrProduct) }
: { paywall: new AdaptyPaywallCoder().encode(paywallOrProduct) }),
};
const body = JSON.stringify(data);
const result = await this.handle<string>(
methodKey,
body,
'String',
ctx,
log,
);
return result;
}
/**
* Logs an onboarding screen view event.
*
* In order for you to be able to analyze user behavior
* at this critical stage without leaving Adapty,
* we have implemented the ability to send dedicated events
* every time a user visits yet another onboarding screen.
*
* @remarks
* Even though there is only one mandatory parameter in this function,
* we recommend that you think of names for all the screens,
* as this will make the work of analysts
* during the data examination phase much easier.
*
* @example
* ```ts
* adapty.logShowOnboarding(1, 'onboarding_name', 'screen_name');
* ```
*
* @param {number} screenOrder - The number of the screen that was shown to the user.
* @param {string} [onboardingName] - The name of the onboarding.
* @param {string} [screenName] - The name of the screen.
* @returns {Promise<void>} resolves when the event is logged
* @throws {@link AdaptyError}
*/
public async logShowOnboarding(
screenOrder: number,
onboardingName?: string,
screenName?: string,
): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'logShowOnboarding' });
log.start({ screenOrder, onboardingName, screenName });
const methodKey = 'log_show_onboarding';
const data: Req['LogShowOnboarding.Request'] = {
method: methodKey,
params: {
onboarding_screen_order: screenOrder,
onboarding_name: onboardingName,
onboarding_screen_name: screenName,
},
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Logs out the current user.
* You can then login the user using {@link Adapty.identify} method.
*
* @throws {@link AdaptyError}
*/
public async logout(): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'logout' });
log.start({});
const methodKey = 'logout';
const body = JSON.stringify({
method: methodKey,
} satisfies Req['Logout.Request']);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Performs a purchase of the specified product.
*
* All available promotions will be applied automatically.
*
* @remarks
* Successful purchase will also result in a call to the `'onLatestProfileLoad'` listener.
* You can use {@link Adapty.addEventListener} to subscribe to this event and handle
* the purchase result outside of this thread.
*
* @param {Model.AdaptyPaywallProduct} product - The product to be purchased.
* You can get the product using {@link Adapty.getPaywallProducts} method.
* @param {Input.MakePurchaseParamsInput} [params] - Additional parameters for the purchase.
* @returns {Promise<Model.AdaptyPurchaseResult>} A Promise that resolves to the {@link Model.AdaptyPurchaseResult} object
* containing details about the purchase. If the result is `'success'`, it also includes the updated user's profile.
* @throws {AdaptyError} If an error occurs during the purchase process
* or while decoding the response from the native SDK.
*
* @example
* ```ts
* try {
* const paywall = await adapty.getPaywall('onboarding');
* const products = await adapty.getPaywallProducts(paywall);
* const product = products[0];
*
* const profile = await adapty.makePurchase(product);
* // successful, canceled, or pending purchase
* } catch (error) {
* // handle error
* }
* ```
*/
public async makePurchase(
product: Model.AdaptyPaywallProduct,
params: Input.MakePurchaseParamsInput = {},
): Promise<Model.AdaptyPurchaseResult> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'makePurchase' });
log.start({ product, params });
const coder = new AdaptyPaywallProductCoder();
const encoded = coder.encode(product);
const productInput = coder.getInput(encoded);
const methodKey = 'make_purchase';
const data: Req['MakePurchase.Request'] = {
method: methodKey,
product: productInput,
};
if (params.android && Platform.OS === 'android') {
data['subscription_update_params'] = {
replacement_mode: params.android.prorationMode,
old_sub_vendor_product_id: params.android.oldSubVendorProductId,
};
if (params.android.isOfferPersonalized) {
data['is_offer_personalized'] = params.android.isOfferPersonalized;
}
}
const body = JSON.stringify(data);
const result = await this.handle<Model.AdaptyPurchaseResult>(
methodKey,
body,
'AdaptyPurchaseResult',
ctx,
log,
);
return result;
}
/**
* Opens a native modal screen to redeem Apple Offer Codes.
*
* @remarks
* iOS 14+ only.
*/
public async presentCodeRedemptionSheet(): Promise<void> {
if (Platform.OS === 'android') {
return Promise.resolve();
}
const ctx = new LogContext();
const log = ctx.call({ methodName: 'presentCodeRedemptionSheet' });
log.start({});
const methodKey = 'present_code_redemption_sheet';
const body = JSON.stringify({
method: methodKey,
} satisfies Req['PresentCodeRedemptionSheet.Request']);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Sets the variation ID of the purchase.
*
* In Observer mode, Adapty SDK doesn't know, where the purchase was made from.
* If you display products using our Paywalls or A/B Tests,
* you can manually assign variation to the purchase.
* After doing this, you'll be able to see metrics in Adapty Dashboard.
*
* @param {string} transactionId - `transactionId` property of {@link Model.AdaptySubscription}
* @param {string} variationId - `variationId` property of {@link Model.AdaptyPaywall}
* @throws {@link AdaptyError}
*/
public async reportTransaction(
transactionId: string,
variationId?: string,
): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'reportTransaction' });
log.start({ variationId, transactionId });
const methodKey = 'report_transaction';
const data: Req['ReportTransaction.Request'] = {
method: methodKey,
transaction_id: transactionId,
};
if (variationId) {
data['variation_id'] = variationId;
}
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Restores user purchases and updates the profile.
*
* @returns {Promise<Model.AdaptyProfile>} resolves with the updated profile
* @throws {@link AdaptyError} if an error occurs during the restore process or while decoding the response
*/
public async restorePurchases(): Promise<Model.AdaptyProfile> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'restorePurchases' });
log.start({});
const methodKey = 'restore_purchases';
const body = JSON.stringify({
method: methodKey,
} satisfies Req['RestorePurchases.Request']);
const result = await this.handle<Model.AdaptyProfile>(
methodKey,
body,
'AdaptyProfile',
ctx,
log,
);
return result;
}
/**
* Sets the fallback paywalls.
*
* Fallback paywalls will be used if the SDK fails
* to fetch the paywalls from the dashboard.
* It is not designed to be used for the offline flow,
* as products are not cached in Adapty.
*
* @returns {Promise<void>} resolves when fallback paywalls are saved
*/
public async setFallbackPaywalls(
paywallsLocation: Input.FallbackPaywallsLocation,
): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'setFallbackPaywalls' });
const paywallsLocationJson = Platform.select({
ios: paywallsLocation.ios.fileName,
android:
'relativeAssetPath' in paywallsLocation.android
? `${paywallsLocation.android.relativeAssetPath}a`
: `${paywallsLocation.android.rawResName}r`,
});
log.start({ paywallsLocationJson });
const methodKey = 'set_fallback_paywalls';
const data: Req['SetFallbackPaywalls.Request'] = {
method: methodKey,
asset_id: paywallsLocationJson ?? '',
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
public async setIntegrationIdentifier(
key: string,
value: string,
): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'setIntegrationIdentifier' });
log.start({ key });
const methodKey = 'set_integration_identifiers';
const data: Req['SetIntegrationIdentifier.Request'] = {
method: methodKey,
key_values: { [key]: value },
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Sets the preferred log level.
*
* By default, the log level is set to `error`.
*
* @remarks
* There are four levels available:
* `error`: only errors will be logged
* `warn`: messages from the SDK that do not cause critical errors, but are worth paying attention to
* `info`: various information messages, such as those that log the lifecycle of various modules
* `verbose`: any additional information that may be useful during debugging, such as function calls, API queries, etc.
*
* @param {Input.LogLevel} logLevel - new preferred log level
* @returns {Promise<void>} resolves when the log level is set
* @throws {@link AdaptyError} if the log level is invalid
*/
public async setLogLevel(logLevel: Input.LogLevel): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'setLogLevel' });
log.start({ logLevel });
Log.logLevel = logLevel;
const methodKey = 'set_log_level';
const data: Req['SetLogLevel.Request'] = {
method: methodKey,
value: logLevel,
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Updates an attribution data for the current user.
*
* @example
* ```ts
* const attribution = {
* 'Adjust Adid': 'adjust_adid',
* 'Adjust Network': 'adjust_network',
* 'Adjust Campaign': 'adjust_campaign',
* 'Adjust Adgroup': 'adjust_adgroup',
* };
*
* adapty.updateAttribution(attribution, 'adjust');
* ```
*
* @param {Record<string, any>} attribution - An object containing attribution data.
* @param {string} source - The source of the attribution data.
* @returns {Promise<void>} A promise that resolves when the attribution data is updated.
*
* @throws {@link AdaptyError} Throws if parameters are invalid or not provided.
*/
public async updateAttribution(
attribution: Record<string, any>,
source: string,
): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'updateAttribution' });
log.start({ attribution, source });
const methodKey = 'update_attribution_data';
const data: Req['UpdateAttributionData.Request'] = {
method: methodKey,
attribution: JSON.stringify(attribution),
source: source,
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
public async updateCollectingRefundDataConsent(
consent: boolean,
): Promise<void> {
if (Platform.OS === 'android') {
return Promise.resolve();
}
const ctx = new LogContext();
const log = ctx.call({
methodName: 'update_collecting_refund_data_consent',
});
log.start({ consent });
const methodKey = 'update_collecting_refund_data_consent';
const data: Req['UpdateCollectingRefundDataConsent.Request'] = {
method: methodKey,
consent: consent,
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
public async updateRefundPreference(
refundPreference: RefundPreference,
): Promise<void> {
if (Platform.OS === 'android') {
return Promise.resolve();
}
const ctx = new LogContext();
const log = ctx.call({ methodName: 'update_refund_preference' });
log.start({ refundPreference });
const methodKey = 'update_refund_preference';
const data: Req['UpdateRefundPreference.Request'] = {
method: methodKey,
refund_preference: refundPreference,
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
/**
* Updates a profile for the current user.
*
* @param {Model.AdaptyProfileParameters} params — an object of parameters to update
* @throws {@link AdaptyError} If parameters are invalid or there is a network error.
*
* @example
* ```ts
* const profile = {
* email: 'foo@example.com',
* phone: '+1234567890',
* };
*
* await adapty.updateProfile(profile);
* ```
*/
public async updateProfile(
params: Partial<Model.AdaptyProfileParameters>,
): Promise<void> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'updateProfile' });
log.start({ params });
const coder = new AdaptyProfileParametersCoder();
const methodKey = 'update_profile';
const data: Req['UpdateProfile.Request'] = {
method: methodKey,
params: coder.encode(params),
};
const body = JSON.stringify(data);
const result = await this.handle<void>(methodKey, body, 'Void', ctx, log);
return result;
}
public async isActivated(): Promise<boolean> {
const ctx = new LogContext();
const log = ctx.call({ methodName: 'isActivated' });
log.start({});
const methodKey = 'is_activated';
const body = JSON.stringify({
method: methodKey,
} satisfies Req['IsActivated.Request']);
const result = await this.handle<boolean>(
methodKey,
body,
'Boolean',
ctx,
log,
);
return result;
}
}