Skip to content

Commit

Permalink
Merge pull request #48 from epegasus/master
Browse files Browse the repository at this point in the history
In App Billing
  • Loading branch information
hypersoftdev authored Oct 3, 2024
2 parents dba4a42 + dbb3efb commit ba21203
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 56 deletions.
9 changes: 2 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<PricingPhase>

) {
constructor() : this(
productId = "",
planId = "",
productTitle = "",
planTitle = "",
productType = ProductType.subs,
currencyCode = "",
price = "",
priceAmountMicros = 0,
freeTrialDays = 0,
billingPeriod = "",
pricingDetails = listOf(),
)
}
21 changes: 21 additions & 0 deletions billing/src/main/java/com/hypersoft/billing/enums/RecurringMode.kt
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand All @@ -420,25 +429,38 @@ open class BillingRepository(context: Context) {
return@tag
}

val pricingPhaseList = arrayListOf<PricingPhase>()

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))
}
Expand Down

0 comments on commit ba21203

Please sign in to comment.