Skip to content

Commit

Permalink
Merge pull request #190 from kf99916/main
Browse files Browse the repository at this point in the history
Support multiple offers
  • Loading branch information
akshaaatt authored Jan 18, 2025
2 parents 61ae0b2 + dbf5a2a commit a273cba
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ public void onPurchaseFailed(@Nullable DataWrappers.PurchaseInfo purchaseInfo, @
);

binding.btnMonthly.setOnClickListener(it ->
iapConnector.subscribe(this, "subscription", null, null)
iapConnector.subscribe(this, "subscription", null, null, null)
);

binding.btnYearly.setOnClickListener(it ->
iapConnector.subscribe(this, "yearly", null, null)
iapConnector.subscribe(this, "yearly", null, null, null)
);

binding.btnQuite.setOnClickListener(it ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class KotlinSampleActivity : AppCompatActivity() {
})

iapConnector.addPurchaseListener(object : PurchaseServiceListener {
override fun onPricesUpdated(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>) {
override fun onPricesUpdated(iapKeyPrices: Map<String, DataWrappers.ProductDetails>) {
// list of available products will be received here, so you can update UI with prices if needed
}

Expand Down Expand Up @@ -102,7 +102,7 @@ class KotlinSampleActivity : AppCompatActivity() {
}
}

override fun onPricesUpdated(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>) {
override fun onPricesUpdated(iapKeyPrices: Map<String, DataWrappers.ProductDetails>) {
// list of available products will be received here, so you can update UI with prices if needed
}

Expand Down
77 changes: 51 additions & 26 deletions iap/src/main/java/com/limurse/iap/BillingService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,28 +82,34 @@ class BillingService(
return
}

launchBillingFlow(activity, sku, BillingClient.ProductType.INAPP, obfuscatedAccountId, obfuscatedProfileId)
launchBillingFlow(activity, sku, BillingClient.ProductType.INAPP, null, obfuscatedAccountId, obfuscatedProfileId)
}

override fun subscribe(activity: Activity, sku: String, obfuscatedAccountId: String?, obfuscatedProfileId: String?) {
override fun subscribe(activity: Activity, sku: String, offerId: String?, obfuscatedAccountId: String?, obfuscatedProfileId: String?) {
if (!sku.isProductReady()) {
log("buy. Google billing service is not ready yet. (SKU is not ready yet -2)")
return
}

launchBillingFlow(activity, sku, BillingClient.ProductType.SUBS, obfuscatedAccountId, obfuscatedProfileId)
launchBillingFlow(activity, sku, BillingClient.ProductType.SUBS, offerId, obfuscatedAccountId, obfuscatedProfileId)
}

private fun launchBillingFlow(activity: Activity, sku: String, type: String, obfuscatedAccountId: String?, obfuscatedProfileId: String?) {
private fun launchBillingFlow(activity: Activity, sku: String, type: String, offerId: String?, obfuscatedAccountId: String?, obfuscatedProfileId: String?) {
sku.toProductDetails(type) { productDetails ->
if (productDetails != null) {

val productDetailsParamsList = mutableListOf<BillingFlowParams.ProductDetailsParams>()
val builder = BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)

if(type == BillingClient.ProductType.SUBS){
productDetails.subscriptionOfferDetails?.getOrNull(0)?.let {
if(type == BillingClient.ProductType.SUBS) {
var index = 0
productDetails.subscriptionOfferDetails?.indexOfFirst { it.offerId == offerId }?.also {
if (it >= 0) {
index = it
}
}
productDetails.subscriptionOfferDetails?.getOrNull(index)?.also {
builder.setOfferToken(it.offerToken)
}
}
Expand Down Expand Up @@ -306,30 +312,49 @@ class BillingService(
entry.value?.let {
when(it.productType){
BillingClient.ProductType.SUBS->{
entry.key to (it.subscriptionOfferDetails?.getOrNull(0)?.pricingPhases?.pricingPhaseList?.map { pricingPhase ->
DataWrappers.ProductDetails(
title = it.title,
description = it.description,
priceCurrencyCode = pricingPhase.priceCurrencyCode,
price = pricingPhase.formattedPrice,
priceAmount = pricingPhase.priceAmountMicros.div(1000000.0),
billingCycleCount = pricingPhase.billingCycleCount,
billingPeriod = pricingPhase.billingPeriod,
recurrenceMode = pricingPhase.recurrenceMode
)
} ?: listOf())
entry.key to DataWrappers.ProductDetails(
title = it.title,
description = it.description,
offers = it.subscriptionOfferDetails?.map { offerDetails ->
DataWrappers.Offer(
id = offerDetails.offerId,
token = offerDetails.offerToken,
tags = offerDetails.offerTags,
pricingPhases = offerDetails.pricingPhases.pricingPhaseList.map { pricingPhase ->
DataWrappers.PricingPhase(
priceCurrencyCode = pricingPhase.priceCurrencyCode,
price = pricingPhase.formattedPrice,
priceAmount = pricingPhase.priceAmountMicros.div(1000000.0),
billingCycleCount = pricingPhase.billingCycleCount,
billingPeriod = pricingPhase.billingPeriod,
recurrenceMode = pricingPhase.recurrenceMode
)
}
)
}
)
}
else->{
entry.key to listOf(DataWrappers.ProductDetails(
entry.key to DataWrappers.ProductDetails(
title = it.title,
description = it.description,
priceCurrencyCode = it.oneTimePurchaseOfferDetails?.priceCurrencyCode,
price = it.oneTimePurchaseOfferDetails?.formattedPrice,
priceAmount = it.oneTimePurchaseOfferDetails?.priceAmountMicros?.div(1000000.0),
billingCycleCount = null,
billingPeriod = null,
recurrenceMode = ProductDetails.RecurrenceMode.NON_RECURRING
))
offers = it.oneTimePurchaseOfferDetails?.let { offerDetails ->
listOf(DataWrappers.Offer(
id = null,
token = null,
tags = null,
pricingPhases = listOf(
DataWrappers.PricingPhase(
priceCurrencyCode = offerDetails.priceCurrencyCode,
price = offerDetails.formattedPrice,
priceAmount = offerDetails.priceAmountMicros.div(1000000.0),
billingCycleCount = null,
billingPeriod = null,
recurrenceMode = ProductDetails.RecurrenceMode.NON_RECURRING
))
))
} ?: listOf()
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface BillingServiceListener {
*
* @param iapKeyPrices - a map with available products
*/
fun onPricesUpdated(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>)
fun onPricesUpdated(iapKeyPrices: Map<String, DataWrappers.ProductDetails>)

/**
* Callback will be triggered when a purchase was failed.
Expand Down
23 changes: 17 additions & 6 deletions iap/src/main/java/com/limurse/iap/DataWrappers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ class DataWrappers {
data class ProductDetails(
val title: String?,
val description: String?,
val price: String?,
val priceAmount: Double?,
val priceCurrencyCode: String?,
val billingCycleCount: Int?,
val billingPeriod: String?,
val recurrenceMode: Int?
val offers: List<Offer>?
)

data class PurchaseInfo(
Expand All @@ -29,4 +24,20 @@ class DataWrappers {
val sku: String,
val accountIdentifiers: AccountIdentifiers?
)

data class Offer(
val id: String?,
val token: String?,
val tags: List<String>?,
val pricingPhases: List<PricingPhase>
)

data class PricingPhase(
val price: String?,
val priceAmount: Double?,
val priceCurrencyCode: String?,
val billingCycleCount: Int?,
val billingPeriod: String?,
val recurrenceMode: Int?
)
}
6 changes: 3 additions & 3 deletions iap/src/main/java/com/limurse/iap/IBillingService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ abstract class IBillingService {
}
}

fun updatePrices(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>) {
fun updatePrices(iapKeyPrices: Map<String, DataWrappers.ProductDetails>) {
findUiHandler().post {
updatePricesInternal(iapKeyPrices)
}
}

private fun updatePricesInternal(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>) {
private fun updatePricesInternal(iapKeyPrices: Map<String, DataWrappers.ProductDetails>) {
for (billingServiceListener in purchaseServiceListeners) {
billingServiceListener.onPricesUpdated(iapKeyPrices)
}
Expand Down Expand Up @@ -121,7 +121,7 @@ abstract class IBillingService {

abstract fun init(key: String?)
abstract fun buy(activity: Activity, sku: String, obfuscatedAccountId: String?, obfuscatedProfileId: String?)
abstract fun subscribe(activity: Activity, sku: String, obfuscatedAccountId: String?, obfuscatedProfileId: String?)
abstract fun subscribe(activity: Activity, sku: String, offerId: String?, obfuscatedAccountId: String?, obfuscatedProfileId: String?)
abstract fun unsubscribe(activity: Activity, sku: String)
abstract fun enableDebugLogging(enable: Boolean)

Expand Down
5 changes: 3 additions & 2 deletions iap/src/main/java/com/limurse/iap/IapConnector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ class IapConnector @JvmOverloads constructor(
getBillingService().buy(activity, sku, obfuscatedAccountId, obfuscatedProfileId)
}

fun subscribe(activity: Activity, sku: String, obfuscatedAccountId: String? = null, obfuscatedProfileId: String? = null) {
getBillingService().subscribe(activity, sku, obfuscatedAccountId, obfuscatedProfileId)
fun subscribe(activity: Activity, sku: String, offerId: String? = null,
obfuscatedAccountId: String? = null, obfuscatedProfileId: String? = null) {
getBillingService().subscribe(activity, sku, offerId, obfuscatedAccountId, obfuscatedProfileId)
}

fun unsubscribe(activity: Activity, sku: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface PurchaseServiceListener : BillingServiceListener {
*
* @param iapKeyPrices - a map with available products
*/
override fun onPricesUpdated(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>)
override fun onPricesUpdated(iapKeyPrices: Map<String, DataWrappers.ProductDetails>)

/**
* Callback will be triggered when a product purchased successfully
Expand Down

0 comments on commit a273cba

Please sign in to comment.