diff --git a/README.md b/README.md index 861a73ed..9cb5a585 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ Through a combination of Adapter and Facade design patterns, this library allows [![build_and_publish](https://github.com/rjsuzuki/billingz/actions/workflows/release-package.yml/badge.svg)](https://github.com/rjsuzuki/billingz/actions/workflows/release-package.yml) [![](https://jitpack.io/v/rjsuzuki/billingz.svg)](https://jitpack.io/#rjsuzuki/billingz) [![](https://jitci.com/gh/rjsuzuki/billingz/svg)](https://jitci.com/gh/rjsuzuki/billingz) Currently supports up to: - - `google billing: 5.0.0` - - `amazon in-app: 2.0.76` (v2.0.6) - - `amazon appstore sdk: 3.0.2` (v2.1.0+) + +- `google billing: 5.1.0` +- `amazon in-app: 2.0.76` (v2.0.6) +- `amazon appstore sdk: 3.0.4` (v2.1.0+) ## Version History [Release History and Notes](https://github.com/rjsuzuki/billingz/releases) diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index aa379847..49df705a 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -40,8 +40,8 @@ object Dependencies { /** * Billing libs */ - const val google_billing = "5.0.0" - const val amazon_iap = "3.0.2" + const val google_billing = "5.1.0" + const val amazon_iap = "3.0.4" /** * UI libs diff --git a/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/AmazonStore.kt b/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/AmazonStore.kt index 2a16f7f1..8d94f4c5 100644 --- a/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/AmazonStore.kt +++ b/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/AmazonStore.kt @@ -82,8 +82,9 @@ class AmazonStore internal constructor() : Storez { override fun resume() { Logger.v(TAG, "resuming...") - if (client.isReady()) + if (client.isReady()) { sales.refreshQueries() + } else if (!client.initialized()) { client.init(context, connectionListener) client.connect() diff --git a/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/sales/AmazonSales.kt b/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/sales/AmazonSales.kt index 50132916..c5001026 100644 --- a/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/sales/AmazonSales.kt +++ b/lib/amazon/src/main/java/com/zuko/billingz/amazon/store/sales/AmazonSales.kt @@ -148,6 +148,7 @@ class AmazonSales( PurchaseResponse.RequestStatus.ALREADY_PURCHASED -> Orderz.Result.PRODUCT_ALREADY_OWNED PurchaseResponse.RequestStatus.INVALID_SKU -> Orderz.Result.INVALID_PRODUCT PurchaseResponse.RequestStatus.NOT_SUPPORTED -> Orderz.Result.NOT_SUPPORTED + PurchaseResponse.RequestStatus.PENDING -> Orderz.Result.PENDING } } @@ -491,7 +492,8 @@ class AmazonSales( // again even if you receive a second receipt. return false } - else -> {} + + else -> Logger.w(TAG, "Unhandled ProductType: ${receipt.productType}") } return true } diff --git a/lib/core/src/main/java/com/zuko/billingz/core/store/client/Clientz.kt b/lib/core/src/main/java/com/zuko/billingz/core/store/client/Clientz.kt index 86857812..87d55ab1 100644 --- a/lib/core/src/main/java/com/zuko/billingz/core/store/client/Clientz.kt +++ b/lib/core/src/main/java/com/zuko/billingz/core/store/client/Clientz.kt @@ -25,8 +25,9 @@ import com.zuko.billingz.core.misc.CleanUpz * Blueprint of the core logic of the library. */ interface Clientz : CleanUpz { - - // fun getBillingClient(): BillingClient? + /** + * + */ var connectionState: MutableLiveData /** @@ -87,7 +88,7 @@ interface Clientz : CleanUpz { * Interface for reconnection logic to the billing service * INTERNAL USE ONLY */ - interface ReconnectListener { + interface ReconnectListener { //todo /** * diff --git a/lib/core/src/main/java/com/zuko/billingz/core/store/model/Offer.kt b/lib/core/src/main/java/com/zuko/billingz/core/store/model/Offer.kt new file mode 100644 index 00000000..d7ece794 --- /dev/null +++ b/lib/core/src/main/java/com/zuko/billingz/core/store/model/Offer.kt @@ -0,0 +1,33 @@ +/* + * + * * Copyright 2021 rjsuzuki + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * + * + */ + +package com.zuko.billingz.core.store.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class Offer( + override val billingPeriod: String, + override val formattedPrice: String, + override val priceCurrencyCode: String, + override val priceAmountMicros: Long, + override val recurrenceMode: Int, + override val billingCycleCount: Int +) : Productz.Offer, Parcelable diff --git a/lib/core/src/main/java/com/zuko/billingz/core/store/model/OfferDetails.kt b/lib/core/src/main/java/com/zuko/billingz/core/store/model/OfferDetails.kt new file mode 100644 index 00000000..bcfaf817 --- /dev/null +++ b/lib/core/src/main/java/com/zuko/billingz/core/store/model/OfferDetails.kt @@ -0,0 +1,30 @@ +/* + * + * * Copyright 2021 rjsuzuki + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * + * + */ + +package com.zuko.billingz.core.store.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class OfferDetails( + override val offerTags: List, + override val offerToken: String, + override val offers: List +) : Productz.OfferDetails, Parcelable diff --git a/lib/core/src/main/java/com/zuko/billingz/core/store/model/Orderz.kt b/lib/core/src/main/java/com/zuko/billingz/core/store/model/Orderz.kt index 31217dc2..718aa9eb 100644 --- a/lib/core/src/main/java/com/zuko/billingz/core/store/model/Orderz.kt +++ b/lib/core/src/main/java/com/zuko/billingz/core/store/model/Orderz.kt @@ -109,6 +109,7 @@ interface Orderz : ModuleIdentifier { ERROR(6), // FAILED PRODUCT_ALREADY_OWNED(7), PRODUCT_NOT_OWNED(8), - NO_RESULT(9) + NO_RESULT(9), + PENDING(10) } } diff --git a/lib/core/src/main/java/com/zuko/billingz/core/store/model/PricingInfo.kt b/lib/core/src/main/java/com/zuko/billingz/core/store/model/PricingInfo.kt index 7ac26dad..4ece2fa6 100644 --- a/lib/core/src/main/java/com/zuko/billingz/core/store/model/PricingInfo.kt +++ b/lib/core/src/main/java/com/zuko/billingz/core/store/model/PricingInfo.kt @@ -27,5 +27,6 @@ data class PricingInfo( override val introPrice: String?, override val introPricePeriod: String?, override val billingPeriod: String?, - override val trialPeriod: String? + override val trialPeriod: String?, + override val subscriptionOffers: List? ) : Productz.Pricing, Parcelable diff --git a/lib/core/src/main/java/com/zuko/billingz/core/store/model/Productz.kt b/lib/core/src/main/java/com/zuko/billingz/core/store/model/Productz.kt index 92517615..7ac3aca8 100644 --- a/lib/core/src/main/java/com/zuko/billingz/core/store/model/Productz.kt +++ b/lib/core/src/main/java/com/zuko/billingz/core/store/model/Productz.kt @@ -126,5 +126,31 @@ interface Productz : ModuleIdentifier { val introPricePeriod: String? val billingPeriod: String? val trialPeriod: String? + + /** + * Only available for Google products. Amazon products will return null. + */ + val subscriptionOffers: List? + } + + /** + * For Google Play SubscriptionOfferDetails support + */ + interface OfferDetails { + val offerTags: List + val offerToken: String + val offers: List + } + + /** + * For Google Play SubscriptionOfferDetails support + */ + interface Offer { + val billingPeriod: String + val formattedPrice: String + val priceCurrencyCode: String + val priceAmountMicros: Long + val recurrenceMode: Int + val billingCycleCount: Int } } diff --git a/lib/google/src/main/java/com/zuko/billingz/google/store/model/GoogleProduct.kt b/lib/google/src/main/java/com/zuko/billingz/google/store/model/GoogleProduct.kt index 60ceed26..4a32dd83 100644 --- a/lib/google/src/main/java/com/zuko/billingz/google/store/model/GoogleProduct.kt +++ b/lib/google/src/main/java/com/zuko/billingz/google/store/model/GoogleProduct.kt @@ -21,6 +21,8 @@ package com.zuko.billingz.google.store.model import com.android.billingclient.api.ProductDetails import com.android.billingclient.api.SkuDetails +import com.zuko.billingz.core.store.model.Offer +import com.zuko.billingz.core.store.model.OfferDetails import com.zuko.billingz.core.store.model.PricingInfo import com.zuko.billingz.core.store.model.Productz import java.util.Currency @@ -89,7 +91,8 @@ data class GoogleProduct( introPrice = skuDetails.introductoryPrice, introPricePeriod = skuDetails.introductoryPricePeriod, billingPeriod = skuDetails.subscriptionPeriod, - trialPeriod = skuDetails.freeTrialPeriod + trialPeriod = skuDetails.freeTrialPeriod, + subscriptionOffers = null ) } @@ -117,24 +120,14 @@ data class GoogleProduct( currency = Currency.getInstance(productDetails.subscriptionOfferDetails?.firstOrNull()?.pricingPhases?.pricingPhaseList?.firstOrNull()?.priceCurrencyCode) - productDetails.subscriptionOfferDetails?.forEach { offerDetails -> - offerDetails.offerToken - offerDetails.offerTags - offerDetails.pricingPhases.pricingPhaseList.forEach { pricingPhase -> - pricingPhase.billingPeriod - pricingPhase.formattedPrice - pricingPhase.priceCurrencyCode - pricingPhase.recurrenceMode - pricingPhase.billingCycleCount - pricingPhase.priceAmountMicros - } - } pricingInfo = PricingInfo( introPrice = null, introPricePeriod = null, - billingPeriod = productDetails.subscriptionOfferDetails?.firstOrNull()?.pricingPhases?.pricingPhaseList?.firstOrNull()?.billingPeriod, - trialPeriod = null + billingPeriod = null, + trialPeriod = null, + subscriptionOffers = convertSubscriptionOfferDetailsTo(productDetails.subscriptionOfferDetails) ) + } else { price = productDetails.oneTimePurchaseOfferDetails?.formattedPrice currency = @@ -142,6 +135,42 @@ data class GoogleProduct( } } + private fun convertSubscriptionOfferDetailsTo(offers: List?): List? { + if (offers.isNullOrEmpty()) { + return null + } + val offerDetailsList = mutableListOf() + offers.forEach { + val details = convertSubscriptionOfferTo(it) + offerDetailsList.add(details) + } + return offerDetailsList + } + + private fun convertSubscriptionOfferTo(offer: ProductDetails.SubscriptionOfferDetails): OfferDetails { + val offers = mutableListOf() + offer.pricingPhases.pricingPhaseList.forEach { pricingPhase -> + val o = convertPricingPhaseTo(pricingPhase) + offers.add(o) + } + return OfferDetails( + offerTags = offer.offerTags, + offerToken = offer.offerToken, + offers = offers + ) + } + + private fun convertPricingPhaseTo(p: ProductDetails.PricingPhase): Offer { + return Offer( + billingPeriod = p.billingPeriod, + formattedPrice = p.formattedPrice, + priceCurrencyCode = p.priceCurrencyCode, + priceAmountMicros = p.priceAmountMicros, + recurrenceMode = p.recurrenceMode, + billingCycleCount = p.billingCycleCount + ) + } + override fun getProductId(): String? { return productId } diff --git a/lib/google/src/main/java/com/zuko/billingz/google/store/sales/GoogleSales.kt b/lib/google/src/main/java/com/zuko/billingz/google/store/sales/GoogleSales.kt index 51c4ae07..caf45b58 100644 --- a/lib/google/src/main/java/com/zuko/billingz/google/store/sales/GoogleSales.kt +++ b/lib/google/src/main/java/com/zuko/billingz/google/store/sales/GoogleSales.kt @@ -818,7 +818,11 @@ class GoogleSales( private fun queryOrderHistory(type: Productz.Type?) { Logger.v(TAG, "queryOrderHistory: $type") val skuType = - if (type == Productz.Type.SUBSCRIPTION) BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP + if (type == Productz.Type.SUBSCRIPTION) { + BillingClient.ProductType.SUBS + } else { + BillingClient.ProductType.INAPP + } mainScope.launch(dispatcher.io()) { if (isNewVersion) { @@ -862,9 +866,9 @@ class GoogleSales( receipt.entitlement = record.purchaseToken receipt.orderDate = Date(record.purchaseTime) if (isNewVersion) { - receipt.skus = record.products + receipt.skus = record.products.toList() } else { - receipt.skus = record.skus + receipt.skus = record.skus.toList() } receipt.originalJson = record.originalJson receipt.quantity = record.quantity diff --git a/version.properties b/version.properties index 68e179b7..f792e88e 100644 --- a/version.properties +++ b/version.properties @@ -19,4 +19,4 @@ v.major=3 v.minor=0 -v.patch=10 +v.patch=11