Skip to content

Commit

Permalink
Merge pull request #79 from qonversion/release/6.0.0
Browse files Browse the repository at this point in the history
Release/6.0.0
  • Loading branch information
suriksarkisyan committed Jul 9, 2024
2 parents 252a998 + f024f05 commit 5ca456a
Show file tree
Hide file tree
Showing 17 changed files with 242 additions and 94 deletions.
2 changes: 1 addition & 1 deletion plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-qonversion",
"version": "5.5.0",
"version": "6.0.0",
"description": "Qonversion Cordova Plugin",
"cordova": {
"id": "cordova-plugin-qonversion",
Expand Down
5 changes: 3 additions & 2 deletions plugin/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<js-module src="www/Offerings.js" name="Offerings" />
<js-module src="www/Product.js" name="Product" />
<js-module src="www/ProductInAppDetails.js" name="ProductInAppDetails" />
<js-module src="www/ProductInstallmentPlanDetails.js" name="ProductInstallmentPlanDetails" />
<js-module src="www/ProductOfferDetails.js" name="ProductOfferDetails" />
<js-module src="www/ProductPrice.js" name="ProductPrice" />
<js-module src="www/ProductPricingPhase.js" name="ProductPricingPhase" />
Expand Down Expand Up @@ -54,7 +55,7 @@
<param name="android-package" value="com.qonversion.android.sdk.QonversionPlugin"/>
</feature>
</config-file>
<framework src="io.qonversion.sandwich:sandwich:4.5.0" />
<framework src="io.qonversion.sandwich:sandwich:5.0.2" />
<source-file src="src/android/QonversionPlugin.java" target-dir="src/com/qonversion/android/sdk" />
<source-file src="src/android/EntitiesConverter.java" target-dir="src/com/qonversion/android/sdk" />
<source-file src="src/android/Utils.java" target-dir="src/com/qonversion/android/sdk" />
Expand All @@ -72,7 +73,7 @@
<source url="https://github.com/CocoaPods/Specs.git"/>
</config>
<pods use-frameworks="true">
<pod name="QonversionSandwich" spec="4.5.0" />
<pod name="QonversionSandwich" spec="5.0.2" />
</pods>
</podspec>
</platform>
Expand Down
36 changes: 7 additions & 29 deletions plugin/src/android/QonversionPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.List;
import java.util.Map;

import io.qonversion.sandwich.PurchaseResultListener;
import io.qonversion.sandwich.QonversionEventsListener;
import io.qonversion.sandwich.QonversionSandwich;
import io.qonversion.sandwich.ResultListener;
Expand All @@ -29,8 +28,6 @@ public class QonversionPlugin extends AnnotatedCordovaPlugin implements Qonversi

private QonversionSandwich qonversionSandwich;

private static final String ERROR_CODE_PURCHASE_CANCELLED_BY_USER = "PURCHASE_CANCELLED_BY_USER";

private @Nullable CallbackContext entitlementsUpdateDelegate = null;

@Override
Expand Down Expand Up @@ -84,7 +81,7 @@ public void initializeSdk(

@PluginAction(thread = ExecutionThread.UI, actionName = "purchase", isAutofinish = false)
public void purchase(String productId, @Nullable String offerId, @Nullable Boolean applyOffer, CallbackContext callbackContext) {
qonversionSandwich.purchase(productId, offerId, applyOffer, getPurchaseResultListener(callbackContext));
qonversionSandwich.purchase(productId, offerId, applyOffer, Utils.getResultListener(callbackContext));
}

@PluginAction(thread = ExecutionThread.UI, actionName = "updatePurchase", isAutofinish = false)
Expand All @@ -102,7 +99,7 @@ public void updatePurchase(
applyOffer,
oldProductId,
updatePolicyKey,
getPurchaseResultListener(callbackContext)
Utils.getResultListener(callbackContext)
);
}

Expand Down Expand Up @@ -205,6 +202,11 @@ public void detachUserFromRemoteConfiguration(String remoteConfigurationId, Call
qonversionSandwich.detachUserFromRemoteConfiguration(remoteConfigurationId, Utils.getEmptyResultListener(callbackContext));
}

@PluginAction(thread = ExecutionThread.WORKER, actionName = "isFallbackFileAccessible", isAutofinish = false)
public void isFallbackFileAccessible(CallbackContext callbackContext) {
qonversionSandwich.isFallbackFileAccessible(Utils.getResultListener(callbackContext));
}

@PluginAction(thread = ExecutionThread.WORKER, actionName = "syncPurchases", isAutofinish = false)
public void syncPurchases(CallbackContext callbackContext) {
qonversionSandwich.syncPurchases();
Expand Down Expand Up @@ -238,28 +240,4 @@ public void onEntitlementsUpdated(@NonNull Map<String, ?> map) {
}
}
}

private PurchaseResultListener getPurchaseResultListener(CallbackContext callbackContext) {
return new PurchaseResultListener() {
@Override
public void onSuccess(@NonNull Map<String, ?> map) {
try {
final JSONObject payload = EntitiesConverter.convertMapToJson(map);
callbackContext.success(payload);
} catch (JSONException e) {
e.printStackTrace();
callbackContext.error(e.getMessage());
}
}

@Override
public void onError(@NonNull SandwichError error, boolean isCancelled) {
if (isCancelled) {
Utils.rejectWithError(error, callbackContext, ERROR_CODE_PURCHASE_CANCELLED_BY_USER);
} else {
Utils.rejectWithError(error, callbackContext);
}
}
};
}
}
1 change: 1 addition & 0 deletions plugin/src/ios/CDVQonversionPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)detachUserFromExperiment:(CDVInvokedUrlCommand *)command;
- (void)attachUserToRemoteConfiguration:(CDVInvokedUrlCommand *)command;
- (void)detachUserFromRemoteConfiguration:(CDVInvokedUrlCommand *)command;
- (void)isFallbackFileAccessible:(CDVInvokedUrlCommand *)command;

@end

Expand Down
20 changes: 12 additions & 8 deletions plugin/src/ios/CDVQonversionPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#import "CDVQonversionPlugin.h"
@import QonversionSandwich;

static NSString *const kErrorCodePurchaseCancelledByUser = @"PURCHASE_CANCELLED_BY_USER";

@interface CDVQonversionPlugin () <QonversionEventListener>

@property (nonatomic, strong) QonversionSandwich *qonversionSandwich;
Expand Down Expand Up @@ -221,6 +219,17 @@ - (void)detachUserFromRemoteConfiguration:(CDVInvokedUrlCommand *)command {
}];
}

- (void)isFallbackFileAccessible:(CDVInvokedUrlCommand *)command {
__block __weak CDVQonversionPlugin *weakSelf = self;
[self.qonversionSandwich isFallbackFileAccessibleWithCompletion:^(NSDictionary<NSString *,id> * _Nullable result, SandwichError * _Nullable error) {
if (error) {
[weakSelf returnCordovaResult:nil error:error command:command];
} else {
[weakSelf returnCordovaResult:result error:nil command:command];
}
}];
}

- (void)identify:(CDVInvokedUrlCommand *)command {
__block __weak CDVQonversionPlugin *weakSelf = self;
NSString *identityId = [command argumentAtIndex:0];
Expand Down Expand Up @@ -279,12 +288,7 @@ - (void)returnCordovaResult:(NSDictionary *)result
errorInfo[@"domain"] = error.domain;
errorInfo[@"description"] = error.details;
errorInfo[@"additionalMessage"] = error.additionalMessage;
NSNumber *isCancelled = error.additionalInfo[@"isCancelled"];
if (isCancelled.boolValue) {
errorInfo[@"code"] = kErrorCodePurchaseCancelledByUser;
} else {
errorInfo[@"code"] = error.code;
}
errorInfo[@"code"] = error.code;
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:errorInfo];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
Expand Down
112 changes: 94 additions & 18 deletions plugin/src/plugin/Mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
TransactionType,
TransactionOwnershipType,
TransactionEnvironment,
EntitlementGrantType
EntitlementGrantType,
QonversionErrorCode
} from "./enums";
import {IntroEligibility} from "./IntroEligibility";
import {Offering} from "./Offering";
Expand Down Expand Up @@ -47,6 +48,7 @@ import {ProductOfferDetails} from "./ProductOfferDetails";
import {ProductInAppDetails} from "./ProductInAppDetails";
import {ProductPrice} from "./ProductPrice";
import {ProductPricingPhase} from "./ProductPricingPhase";
import {ProductInstallmentPlanDetails} from "./ProductInstallmentPlanDetails";

export type QProduct = {
id: string;
Expand Down Expand Up @@ -79,6 +81,7 @@ type QProductStoreDetails = {
isInApp: boolean,
isSubscription: boolean,
isPrepaid: boolean,
isInstallment: boolean,
}

type QSubscriptionPeriod = {
Expand All @@ -98,13 +101,19 @@ type QProductPricingPhase = {
isBasePlan: boolean,
}

type QProductInstallmentPlanDetails = {
commitmentPaymentsCount: number;
subsequentCommitmentPaymentsCount: number;
}

type QProductOfferDetails = {
basePlanId: string,
offerId?: string | null,
offerToken: string,
tags: string[],
pricingPhases: QProductPricingPhase[],
basePlan?: QProductPricingPhase | null,
installmentPlanDetails?: QProductInstallmentPlanDetails | null,
trialPhase?: QProductPricingPhase | null,
introPhase: QProductPricingPhase | null,
hasTrial: boolean,
Expand Down Expand Up @@ -237,6 +246,10 @@ type QAutomationsEvent = {
timestamp: number;
};

export type QEmptySuccessResult = {
success: boolean;
};

export type QUser = {
qonversionId: string;
identityId?: string | null;
Expand Down Expand Up @@ -761,27 +774,41 @@ class Mapper {
return result;
}

static convertProductOfferDetails(defaultOfferDetail: QProductOfferDetails): ProductOfferDetails {
let basePlan = Mapper.convertProductPricingPhase(defaultOfferDetail.basePlan);
let trialPhase = Mapper.convertProductPricingPhase(defaultOfferDetail.trialPhase);
let introPhase = Mapper.convertProductPricingPhase(defaultOfferDetail.introPhase);
static convertProductInstallmentPlanDetails(installmentPlanDetails: QProductInstallmentPlanDetails | null | undefined): ProductInstallmentPlanDetails | null {
if (!installmentPlanDetails) {
return null;
}

return new ProductInstallmentPlanDetails(
installmentPlanDetails.commitmentPaymentsCount,
installmentPlanDetails.subsequentCommitmentPaymentsCount,
);
}

static convertProductOfferDetails(offerDetails: QProductOfferDetails): ProductOfferDetails {
let basePlan = Mapper.convertProductPricingPhase(offerDetails.basePlan);
let trialPhase = Mapper.convertProductPricingPhase(offerDetails.trialPhase);
let introPhase = Mapper.convertProductPricingPhase(offerDetails.introPhase);

let installmentPlanDetails = Mapper.convertProductInstallmentPlanDetails(offerDetails.installmentPlanDetails);

let pricingPhases = defaultOfferDetail.pricingPhases.map(
let pricingPhases = offerDetails.pricingPhases.map(
pricingPhase => Mapper.convertProductPricingPhase(pricingPhase)
).filter(Boolean) as ProductPricingPhase[];

return new ProductOfferDetails(
defaultOfferDetail.basePlanId,
defaultOfferDetail.offerId ?? null,
defaultOfferDetail.offerToken,
defaultOfferDetail.tags,
offerDetails.basePlanId,
offerDetails.offerId ?? null,
offerDetails.offerToken,
offerDetails.tags,
pricingPhases,
basePlan,
installmentPlanDetails,
introPhase,
trialPhase,
defaultOfferDetail.hasTrial,
defaultOfferDetail.hasIntro,
defaultOfferDetail.hasTrialOrIntro,
offerDetails.hasTrial,
offerDetails.hasIntro,
offerDetails.hasTrialOrIntro,
);
}

Expand Down Expand Up @@ -846,6 +873,7 @@ class Mapper {
productStoreDetails.isInApp,
productStoreDetails.isSubscription,
productStoreDetails.isPrepaid,
productStoreDetails.isInstallment,
);
}

Expand Down Expand Up @@ -963,18 +991,21 @@ class Mapper {
payload["type"],
payload["value"],
this.convertQonversionError(payload["error"])
)
);
}

static convertQonversionError(
payload: Record<string, string> | undefined
): QonversionError | undefined {
return payload ? new QonversionError(
payload["code"],
if (!payload) return undefined;

const code = this.convertErrorCode(payload["code"]);
return new QonversionError(
code,
payload["description"],
payload["additionalMessage"],
payload["domain"],
) : undefined;
);
}

static convertAutomationsEvent(
Expand All @@ -983,7 +1014,7 @@ class Mapper {
return new AutomationsEvent(
automationsEvent.type,
automationsEvent.timestamp
)
);
}

static convertUserInfo(user: QUser): User {
Expand Down Expand Up @@ -1051,6 +1082,51 @@ class Mapper {
return ExperimentGroupType.UNKNOWN;
}
}

static convertErrorCode(code: string): QonversionErrorCode {
switch (code) {
case QonversionErrorCode.UNKNOWN: return QonversionErrorCode.UNKNOWN;
case QonversionErrorCode.API_RATE_LIMIT_EXCEEDED: return QonversionErrorCode.API_RATE_LIMIT_EXCEEDED;
case QonversionErrorCode.APPLE_STORE_ERROR: return QonversionErrorCode.APPLE_STORE_ERROR;
case QonversionErrorCode.BACKEND_ERROR: return QonversionErrorCode.BACKEND_ERROR;
case QonversionErrorCode.BILLING_UNAVAILABLE: return QonversionErrorCode.BILLING_UNAVAILABLE;
case QonversionErrorCode.CLIENT_INVALID: return QonversionErrorCode.CLIENT_INVALID;
case QonversionErrorCode.CLOUD_SERVICE_NETWORK_CONNECTION_FAILED: return QonversionErrorCode.CLOUD_SERVICE_NETWORK_CONNECTION_FAILED;
case QonversionErrorCode.CLOUD_SERVICE_PERMISSION_DENIED: return QonversionErrorCode.CLOUD_SERVICE_PERMISSION_DENIED;
case QonversionErrorCode.CLOUD_SERVICE_REVOKED: return QonversionErrorCode.CLOUD_SERVICE_REVOKED;
case QonversionErrorCode.FAILED_TO_RECEIVE_DATA: return QonversionErrorCode.FAILED_TO_RECEIVE_DATA;
case QonversionErrorCode.FEATURE_NOT_SUPPORTED: return QonversionErrorCode.FEATURE_NOT_SUPPORTED;
case QonversionErrorCode.FRAUD_PURCHASE: return QonversionErrorCode.FRAUD_PURCHASE;
case QonversionErrorCode.INCORRECT_REQUEST: return QonversionErrorCode.INCORRECT_REQUEST;
case QonversionErrorCode.INTERNAL_ERROR: return QonversionErrorCode.INTERNAL_ERROR;
case QonversionErrorCode.INVALID_CLIENT_UID: return QonversionErrorCode.INVALID_CLIENT_UID;
case QonversionErrorCode.INVALID_CREDENTIALS: return QonversionErrorCode.INVALID_CREDENTIALS;
case QonversionErrorCode.INVALID_STORE_CREDENTIALS: return QonversionErrorCode.INVALID_STORE_CREDENTIALS;
case QonversionErrorCode.LAUNCH_ERROR: return QonversionErrorCode.LAUNCH_ERROR;
case QonversionErrorCode.NETWORK_CONNECTION_FAILED: return QonversionErrorCode.NETWORK_CONNECTION_FAILED;
case QonversionErrorCode.OFFERINGS_NOT_FOUND: return QonversionErrorCode.OFFERINGS_NOT_FOUND;
case QonversionErrorCode.PAYMENT_INVALID: return QonversionErrorCode.PAYMENT_INVALID;
case QonversionErrorCode.PAYMENT_NOT_ALLOWED: return QonversionErrorCode.PAYMENT_NOT_ALLOWED;
case QonversionErrorCode.PLAY_STORE_ERROR: return QonversionErrorCode.PLAY_STORE_ERROR;
case QonversionErrorCode.PRIVACY_ACKNOWLEDGEMENT_REQUIRED: return QonversionErrorCode.PRIVACY_ACKNOWLEDGEMENT_REQUIRED;
case QonversionErrorCode.PRODUCT_ALREADY_OWNED: return QonversionErrorCode.PRODUCT_ALREADY_OWNED;
case QonversionErrorCode.PRODUCT_NOT_FOUND: return QonversionErrorCode.PRODUCT_NOT_FOUND;
case QonversionErrorCode.PRODUCT_NOT_OWNED: return QonversionErrorCode.PRODUCT_NOT_OWNED;
case QonversionErrorCode.PROJECT_CONFIG_ERROR: return QonversionErrorCode.PROJECT_CONFIG_ERROR;
case QonversionErrorCode.PURCHASE_CANCELED: return QonversionErrorCode.PURCHASE_CANCELED;
case QonversionErrorCode.PURCHASE_INVALID: return QonversionErrorCode.PURCHASE_INVALID;
case QonversionErrorCode.PURCHASE_PENDING: return QonversionErrorCode.PURCHASE_PENDING;
case QonversionErrorCode.PURCHASE_UNSPECIFIED: return QonversionErrorCode.PURCHASE_UNSPECIFIED;
case QonversionErrorCode.RECEIPT_VALIDATION_ERROR: return QonversionErrorCode.RECEIPT_VALIDATION_ERROR;
case QonversionErrorCode.REMOTE_CONFIGURATION_NOT_AVAILABLE: return QonversionErrorCode.REMOTE_CONFIGURATION_NOT_AVAILABLE;
case QonversionErrorCode.RESPONSE_PARSING_FAILED: return QonversionErrorCode.RESPONSE_PARSING_FAILED;
case QonversionErrorCode.STORE_PRODUCT_NOT_AVAILABLE: return QonversionErrorCode.STORE_PRODUCT_NOT_AVAILABLE;
case QonversionErrorCode.UNAUTHORIZED_REQUEST_DATA: return QonversionErrorCode.UNAUTHORIZED_REQUEST_DATA;
case QonversionErrorCode.UNKNOWN_CLIENT_PLATFORM: return QonversionErrorCode.UNKNOWN_CLIENT_PLATFORM;
}

return QonversionErrorCode.UNKNOWN;
}
}

export default Mapper;
26 changes: 26 additions & 0 deletions plugin/src/plugin/ProductInstallmentPlanDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* This class represents the details about the installment plan for a subscription product.
*/
export class ProductInstallmentPlanDetails {
/**
* Committed payments count after a user signs up for this subscription plan.
*/
commitmentPaymentsCount: number;

/**
* Subsequent committed payments count after this subscription plan renews.
*
* Returns 0 if the installment plan doesn't have any subsequent commitment,
* which means this subscription plan will fall back to a normal
* non-installment monthly plan when the plan renews.
*/
subsequentCommitmentPaymentsCount: number;

constructor(
commitmentPaymentsCount: number,
subsequentCommitmentPaymentsCount: number
) {
this.commitmentPaymentsCount = commitmentPaymentsCount;
this.subsequentCommitmentPaymentsCount = subsequentCommitmentPaymentsCount;
}
}
Loading

0 comments on commit 5ca456a

Please sign in to comment.