From 1ea26bb865dcac1cbb1c1dd9b42c362efdf7ed48 Mon Sep 17 00:00:00 2001 From: Sohaib Ahmed Date: Thu, 3 Oct 2024 11:24:31 +0500 Subject: [PATCH] InAppBilling --- .gitignore | 9 +-- .../billing/dataClasses/PricingPhase.kt | 80 +++++++++++++++++++ .../billing/dataClasses/ProductDetail.kt | 58 +++++++------- .../hypersoft/billing/enums/RecurringMode.kt | 21 +++++ .../billing/repository/BillingRepository.kt | 58 +++++++++----- 5 files changed, 170 insertions(+), 56 deletions(-) create mode 100644 billing/src/main/java/com/hypersoft/billing/dataClasses/PricingPhase.kt create mode 100644 billing/src/main/java/com/hypersoft/billing/enums/RecurringMode.kt diff --git a/.gitignore b/.gitignore index aa724b7..9c14519 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,8 @@ *.iml .gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml +.idea .DS_Store +/local.properties /build /captures .externalNativeBuild diff --git a/billing/src/main/java/com/hypersoft/billing/dataClasses/PricingPhase.kt b/billing/src/main/java/com/hypersoft/billing/dataClasses/PricingPhase.kt new file mode 100644 index 0000000..f8ad0df --- /dev/null +++ b/billing/src/main/java/com/hypersoft/billing/dataClasses/PricingPhase.kt @@ -0,0 +1,80 @@ +package com.hypersoft.billing.dataClasses + +import com.hypersoft.billing.enums.RecurringMode + +/** + * Developer: Sohaib Ahmed + * Date: 10/3/2024 + * Profile: + * -> github.com/epegasus + * -> linkedin.com/in/epegasus + */ + + +/** + * @param + * @param planTitle: e.g. Weekly, Monthly, Yearly, etc + * @param currencyCode: e.g. USD, PKR, etc + * @param price: e.g. Rs 750.00 + * @param priceAmountMicros: e.g. 750000000 + * @param freeTrialPeriod: e.g. 3, 5, 7, etc + * @param billingPeriod + * - Weekly: P1W (One week) + * - Every 4 weeks: P4W (Four weeks) + * - Monthly: P1M (One month) + * - Every 2 months (Bimonthly): P2M (Two months) + * - Every 3 months (Quarterly): P3M (Three months) + * - Every 4 months: P4M (Four months) + * - Every 6 months (Semiannually): P6M (Six months) + * - Every 8 months: P8M (Eight months) + * - Yearly: P1Y (One year) + */ + +/** + * Data class representing the pricing phase for a subscription plan. + * + * @param recurringMode: The recurring mode of the pricing phase, which can be: + * - FREE: Represents a free phase of the subscription. + * - DISCOUNTED: Represents a discounted phase of the subscription for specific time period. + * - ORIGINAL: Represents the original price phase of the subscription. + * + * @param planTitle: The title of the subscription plan, e.g., "Weekly", "Monthly", "Yearly", etc. + * @param currencyCode: The ISO 4217 currency code, e.g., "USD" for US Dollars, "PKR" for Pakistani Rupee, etc. + * @param price: The formatted price string of the subscription, e.g., "Rs 750.00". + * @param priceAmountMicros: The price of the subscription in micros (1,000,000 micros = 1 unit of currency), e.g., "750000000" represents Rs 750.00. + * @param billingCycleCount: The number of billing cycles this phase lasts, e.g., 1 for one cycle. + * @param freeTrialPeriod: The length of the free trial period in days, e.g., 3, 5, 7, etc. + * + * @param billingPeriod: The duration of the billing period in ISO-8601 format, e.g., + * - Weekly: "P1W" (One week) + * - Every 4 weeks: "P4W" (Four weeks) + * - Monthly: "P1M" (One month) + * - Bimonthly: "P2M" (Two months) + * - Quarterly: "P3M" (Three months) + * - Every 4 months: "P4M" (Four months) + * - Semiannually: "P6M" (Six months) + * - Every 8 months: "P8M" (Eight months) + * - Yearly: "P1Y" (One year) + */ + +data class PricingPhase( + var recurringMode: RecurringMode, + var price: String, + var currencyCode: String, + var planTitle: String, + var billingCycleCount: Int, + var billingPeriod: String, + var priceAmountMicros: Long, + var freeTrialPeriod: Int, +) { + constructor() : this( + recurringMode = RecurringMode.ORIGINAL, + price = "", + currencyCode = "", + planTitle = "", + billingCycleCount = 0, + billingPeriod = "", + priceAmountMicros = 0, + freeTrialPeriod = 0, + ) +} \ No newline at end of file diff --git a/billing/src/main/java/com/hypersoft/billing/dataClasses/ProductDetail.kt b/billing/src/main/java/com/hypersoft/billing/dataClasses/ProductDetail.kt index 1f46b9b..8e086bd 100644 --- a/billing/src/main/java/com/hypersoft/billing/dataClasses/ProductDetail.kt +++ b/billing/src/main/java/com/hypersoft/billing/dataClasses/ProductDetail.kt @@ -9,49 +9,45 @@ package com.hypersoft.billing.dataClasses */ /** - * @param productId: Unique ID (Console's ID) for product - * @param planId: Unique ID (Console's ID) for plan - * @param productTitle: e.g. Gold Tier - * @param planTitle: e.g. Weekly, Monthly, Yearly, etc - * @param productType: e.g. InApp / Subs - * @param currencyCode: e.g. USD, PKR, etc - * @param price: e.g. Rs 750.00 - * @param priceAmountMicros: e.g. 750000000 - * @param freeTrialDays: e.g. 3, 5, 7, etc - * @param billingPeriod - * - Weekly: P1W (One week) - * - Every 4 weeks: P4W (Four weeks) - * - Monthly: P1M (One month) - * - Every 2 months (Bimonthly): P2M (Two months) - * - Every 3 months (Quarterly): P3M (Three months) - * - Every 4 months: P4M (Four months) - * - Every 6 months (Semiannually): P6M (Six months) - * - Every 8 months: P8M (Eight months) - * - Yearly: P1Y (One year) + * Data class representing the detailed information of a product and its associated pricing plans. + * + * @param productId: The unique identifier for the product, as defined in the console (e.g., Google Play Console). + * @param planId: The unique identifier for the subscription plan, as defined in the console. + * @param productTitle: The name or title of the product, e.g., "Gold Tier". + * @param productType: The type of product, either "InApp" (for in-app purchases) or "Subs" (for subscriptions). + * @param pricingDetails: A list of `PricingPhase` objects that contain pricing information for each phase of the product's billing cycles. + * + * Each `PricingPhase` contains: + * - price: The formatted price string, e.g., "Rs 750.00". + * - priceAmountMicros: The price of the subscription in micros (1,000,000 micros = 1 unit of currency). + * - currencyCode: ISO 4217 currency code, e.g., "USD" or "PKR". + * - planTitle: The title of the plan, e.g., "Weekly", "Monthly", "Yearly". + * - freeTrialPeriod: Number of days for the free trial, e.g., 3, 5, 7, etc. + * - billingPeriod: Duration of the billing period in ISO-8601 format: + * - Weekly: "P1W" (One week) + * - Every 4 weeks: "P4W" (Four weeks) + * - Monthly: "P1M" (One month) + * - Bimonthly: "P2M" (Two months) + * - Quarterly: "P3M" (Three months) + * - Every 4 months: "P4M" (Four months) + * - Semiannually: "P6M" (Six months) + * - Every 8 months: "P8M" (Eight months) + * - Yearly: "P1Y" (One year) */ data class ProductDetail( var productId: String, var planId: String, var productTitle: String, - var planTitle: String, var productType: ProductType, - var currencyCode: String, - var price: String, - var priceAmountMicros: Long = 0, - var freeTrialDays: Int = 0, - var billingPeriod: String, + var pricingDetails : List + ) { constructor() : this( productId = "", planId = "", productTitle = "", - planTitle = "", productType = ProductType.subs, - currencyCode = "", - price = "", - priceAmountMicros = 0, - freeTrialDays = 0, - billingPeriod = "", + pricingDetails = listOf(), ) } \ No newline at end of file diff --git a/billing/src/main/java/com/hypersoft/billing/enums/RecurringMode.kt b/billing/src/main/java/com/hypersoft/billing/enums/RecurringMode.kt new file mode 100644 index 0000000..c14c068 --- /dev/null +++ b/billing/src/main/java/com/hypersoft/billing/enums/RecurringMode.kt @@ -0,0 +1,21 @@ +package com.hypersoft.billing.enums + +/** + * Developer: Sohaib Ahmed + * Date: 10/3/2024 + * Profile: + * -> github.com/epegasus + * -> linkedin.com/in/epegasus + */ + +/** + * The recurring mode of the pricing phase, which can be: + * - FREE: Represents a free phase of the subscription. + * - DISCOUNTED: Represents a discounted phase of the subscription for specific time period. + * - ORIGINAL: Represents the original price phase of the subscription. + */ +enum class RecurringMode { + FREE, + DISCOUNTED, + ORIGINAL +} \ No newline at end of file diff --git a/billing/src/main/java/com/hypersoft/billing/repository/BillingRepository.kt b/billing/src/main/java/com/hypersoft/billing/repository/BillingRepository.kt index a34d3b7..51793c2 100644 --- a/billing/src/main/java/com/hypersoft/billing/repository/BillingRepository.kt +++ b/billing/src/main/java/com/hypersoft/billing/repository/BillingRepository.kt @@ -16,10 +16,12 @@ import com.android.billingclient.api.PurchasesUpdatedListener import com.android.billingclient.api.QueryPurchasesParams import com.hypersoft.billing.BillingManager.Companion.TAG import com.hypersoft.billing.dataClasses.CompletePurchase +import com.hypersoft.billing.dataClasses.PricingPhase import com.hypersoft.billing.dataClasses.ProductDetail import com.hypersoft.billing.dataClasses.ProductType import com.hypersoft.billing.dataClasses.PurchaseDetail import com.hypersoft.billing.dataClasses.QueryProductDetail +import com.hypersoft.billing.enums.RecurringMode import com.hypersoft.billing.enums.ResultState import com.hypersoft.billing.extensions.toFormattedDate import com.hypersoft.billing.interfaces.OnPurchaseListener @@ -396,16 +398,23 @@ open class BillingRepository(context: Context) { when (productDetails.productType) { BillingClient.ProductType.INAPP -> { + val pricingPhase = PricingPhase( + planTitle = "", + recurringMode = RecurringMode.ORIGINAL, + price = productDetails.oneTimePurchaseOfferDetails?.formattedPrice.toString().removeSuffix(".00"), + currencyCode = productDetails.oneTimePurchaseOfferDetails?.priceCurrencyCode.toString(), + billingCycleCount = 0, + billingPeriod = "", + priceAmountMicros = productDetails.oneTimePurchaseOfferDetails?.priceAmountMicros ?: 0L, + freeTrialPeriod = 0 + ) + val productDetail = ProductDetail( productId = productDetails.productId, planId = "", productTitle = productDetails.title, - planTitle = "", productType = ProductType.inapp, - currencyCode = productDetails.oneTimePurchaseOfferDetails?.priceCurrencyCode.toString(), - price = productDetails.oneTimePurchaseOfferDetails?.formattedPrice.toString().removeSuffix(".00"), - priceAmountMicros = productDetails.oneTimePurchaseOfferDetails?.priceAmountMicros ?: 0L, - billingPeriod = "" + pricingDetails = listOf(pricingPhase) ) _productDetailList.add(productDetail) queryProductDetail.add(QueryProductDetail(productDetail, productDetails, null)) @@ -420,25 +429,38 @@ open class BillingRepository(context: Context) { return@tag } + val pricingPhaseList = arrayListOf() + + offer.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem -> + val pricingPhase = PricingPhase() + if (pricingPhaseItem.formattedPrice.equals("Free", ignoreCase = true)) { + pricingPhase.recurringMode = RecurringMode.FREE + pricingPhase.freeTrialPeriod = queryUtils.getTrialDay(offer) + pricingPhase.billingCycleCount = 0 + } else if (pricingPhaseItem.recurrenceMode == 1) { + pricingPhase.recurringMode = RecurringMode.ORIGINAL + pricingPhase.freeTrialPeriod = 0 + pricingPhase.billingCycleCount = 0 + } else if (pricingPhaseItem.recurrenceMode == 2) { + pricingPhase.recurringMode = RecurringMode.DISCOUNTED + pricingPhase.freeTrialPeriod = 0 + pricingPhase.billingCycleCount = pricingPhaseItem.billingCycleCount + } + pricingPhase.planTitle = queryUtils.getPlanTitle(pricingPhaseItem.billingPeriod) + pricingPhase.currencyCode = pricingPhaseItem.priceCurrencyCode + pricingPhase.billingPeriod = pricingPhaseItem.billingPeriod + pricingPhase.price = pricingPhaseItem.formattedPrice.removeSuffix(".00") + pricingPhase.priceAmountMicros = pricingPhaseItem.priceAmountMicros + pricingPhaseList.add(pricingPhase) + } + val productDetail = ProductDetail().apply { productId = productDetails.productId planId = offer.basePlanId productTitle = productDetails.title productType = ProductType.subs + pricingDetails = pricingPhaseList } - - offer.pricingPhases.pricingPhaseList.forEach { pricingPhase -> - if (pricingPhase.formattedPrice.equals("Free", ignoreCase = true)) { - productDetail.freeTrialDays = queryUtils.getTrialDay(offer) - } else { - productDetail.planTitle = queryUtils.getPlanTitle(pricingPhase.billingPeriod) - productDetail.currencyCode = pricingPhase.priceCurrencyCode - productDetail.price = pricingPhase.formattedPrice.removeSuffix(".00") - productDetail.priceAmountMicros = pricingPhase.priceAmountMicros - productDetail.billingPeriod = pricingPhase.billingPeriod - } - } - _productDetailList.add(productDetail) queryProductDetail.add(QueryProductDetail(productDetail, productDetails, offer)) }