Purchases are returned to users after a few hours. How to confirm the purchase correctly?
"Your app should process a purchase in the following way:
Verify the purchase. Give content to the user, and acknowledge delivery of the content. Optionally, mark the item as consumed so that the user can buy the item again. To verify a purchase, first check that the purchase state PURCHASED"
class MainActivity : AppCompatActivity(), PurchasesUpdatedListener {
private lateinit var bind: ActivityMainBinding
private lateinit var billingClient: BillingClient
private lateinit var consentInformation: ConsentInformation
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bind = DataBindingUtil.setContentView(this, R.layout.activity_main)
setSupportActionBar(bind.toolbar)
val params = ConsentRequestParameters
.Builder()
.setTagForUnderAgeOfConsent(false)
.build()
consentInformation = UserMessagingPlatform.getConsentInformation(this)
consentInformation.requestConsentInfoUpdate(
this,
params,
{
UserMessagingPlatform.loadAndShowConsentFormIfRequired(
this@MainActivity
) { loadAndShowError ->
if (loadAndShowError != null) {
Log.w(
Constants.TAG, String.format(
"%s: %s",
loadAndShowError.errorCode,
loadAndShowError.message
)
)
}
if (consentInformation.canRequestAds()) {
initializeMobileAdsSdk()
}
}
},
{ requestConsentError ->
// Consent gathering failed.
Log.w(
Constants.TAG, String.format(
"%s: %s",
requestConsentError.errorCode,
requestConsentError.message
)
)
})
if (consentInformation.canRequestAds())
initializeMobileAdsSdk()
billingClient = BillingClient.newBuilder(App.instance)
.setListener(this)
.enablePendingPurchases()
.build()
}
// обновление информации о покупках
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
billingClient = BillingClient.newBuilder(App.instance)
.setListener(this)
.enablePendingPurchases()
.build()
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases)
handlePurchase(purchase)
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
showMessage(getString(R.string.snackbar_reset_app))
SharedPreferencesManager.isFullVersion = true
} else handleBillingError(billingResult.responseCode)
}
// установка соединения с google play для покупок
private fun establishConnection() {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
getSingleInAppDetail()
} else retryBillingServiceConnection()
}
override fun onBillingServiceDisconnected() {
retryBillingServiceConnection()
}
})
}
// повторное соединение с google play для покупок
private fun retryBillingServiceConnection() {
var tries = 1
val maxTries = 3
var isConnectionEstablished = false
do {
try {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingServiceDisconnected() {
retryBillingServiceConnection()
}
override fun onBillingSetupFinished(billingResult: BillingResult) {
tries++
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK)
isConnectionEstablished = true
else if (tries == maxTries)
handleBillingError(billingResult.responseCode)
}
})
} catch (e: Exception) {
tries++
}
} while (tries <= maxTries && !isConnectionEstablished)
if (!isConnectionEstablished)
handleBillingError(-1)
}
// список доступных покупок
private fun getSingleInAppDetail() {
val queryProductDetailsParams =
QueryProductDetailsParams.newBuilder()
.setProductList(
listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId(getString(R.string.billing_product_id))
.setProductType(BillingClient.ProductType.INAPP)
.build()
)
)
.build()
billingClient.queryProductDetailsAsync(queryProductDetailsParams) { _, productDetailsList ->
launchPurchaseFlow(
productDetailsList[0]
)
}
}
// запуск покупки
private fun launchPurchaseFlow(productDetails: ProductDetails?) {
val productList = ArrayList<ProductDetailsParams>()
productList.add(
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails!!)
.build()
)
val billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productList)
.build()
billingClient.launchBillingFlow(this, billingFlowParams)
}
// запуск покупки
private fun handlePurchase(purchase: Purchase) {
if (!purchase.isAcknowledged) {
billingClient.acknowledgePurchase(
AcknowledgePurchaseParams
.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
) {
for (pur in purchase.products) {
if (pur.equals(getString(R.string.billing_product_id), ignoreCase = true)) {
Log.d(Constants.TAG, "Purchase is successful")
Log.d(Constants.TAG, "Yay! Purchased")
showMessage(getString(R.string.snackbar_reset_app))
SharedPreferencesManager.isFullVersion = true
consumePurchase(purchase)
}
}
}
}
}
// запуск покупки
private fun consumePurchase(purchase: Purchase) {
val params = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.consumeAsync(params) { _, s ->
Log.d(Constants.TAG, "Consuming Successful: $s")
Log.d(Constants.TAG, "Product Consumed")
}
}
// обработка ошибок о покупках с google play
private fun handleBillingError(responseCode: Int) {
val errorMessage: String = when (responseCode) {
BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> "Billing service is currently unavailable. Please try again later."
BillingClient.BillingResponseCode.DEVELOPER_ERROR -> "An error occurred while processing the request. Please try again later."
BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> "This feature is not supported on your device."
BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> "You already own this item."
BillingClient.BillingResponseCode.ITEM_NOT_OWNED -> "You do not own this item."
BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> "This item is not available for purchase."
BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> "Billing service has been disconnected. Please try again later."
BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> "Billing service is currently unavailable. Please try again later."
BillingClient.BillingResponseCode.USER_CANCELED -> "The purchase has been canceled."
else -> "An unknown error occurred."
}
Log.d(Constants.TAG, errorMessage)
}
}