Skip to content

Commit 2bd4359

Browse files
authored
Fetch native purchases return optional error (#75)
* Fetch native purchases return optional error * Improve billing connection
1 parent 24a5380 commit 2bd4359

File tree

5 files changed

+36
-11
lines changed

5 files changed

+36
-11
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ android.enableJetifier=true
2020
# Kotlin code style for this project: "official" or "obsolete":
2121
kotlin.code.style=official
2222

23-
sdkVersion=2.3.7
23+
sdkVersion=2.3.8

sdk/src/main/java/com/apphud/sdk/Apphud.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,8 @@ object Apphud {
535535
* Compared to `Apphud.restorePurchases`, this method offers a quicker way
536536
* to determine the presence of owned purchases as it bypasses validation by Apphud.
537537
*
538+
* Returns `BillingClient.BillingResponseCode` as second parameter of Pair.
539+
*
538540
* Usage of this function for granting premium access is not advised,
539541
* as these purchases may not yet be validated.
540542
*
@@ -545,7 +547,7 @@ object Apphud {
545547
* Apphud will automatically track and validate them in the background,
546548
* so developer doesn't need to call `Apphud.restorePurchases` afterwards.
547549
*/
548-
suspend fun nativePurchases(): List<Purchase> = ApphudInternal.fetchNativePurchases()
550+
suspend fun nativePurchases(): Pair<List<Purchase>, Int> = ApphudInternal.fetchNativePurchases()
549551

550552
//endregion
551553
//region === Attribution ===

sdk/src/main/java/com/apphud/sdk/ApphudInternal+RestorePurchases.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@ private val mutexSync = Mutex()
2222

2323
private var unvalidatedPurchases = listOf<Purchase>()
2424

25-
internal suspend fun ApphudInternal.fetchNativePurchases(): List<Purchase> {
25+
internal suspend fun ApphudInternal.fetchNativePurchases(): Pair<List<Purchase>, Int> {
26+
var responseCode = BillingClient.BillingResponseCode.OK
2627
if (unvalidatedPurchases.isEmpty()) {
27-
val purchases = billing.queryPurchasesSync()
28+
val result = billing.queryPurchasesSync()
29+
val purchases = result.first
30+
responseCode = result.second
2831
if (!purchases.isNullOrEmpty()) {
2932
unvalidatedPurchases = purchases
3033
syncPurchases(unvalidatedPurchs = unvalidatedPurchases)
3134
}
3235
}
33-
return unvalidatedPurchases
36+
return Pair(unvalidatedPurchases, responseCode)
3437
}
3538

3639
internal fun ApphudInternal.syncPurchases(

sdk/src/main/java/com/apphud/sdk/internal/BillingWrapper.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,24 @@ internal class BillingWrapper(context: Context) : Closeable {
3232

3333
private val mutex = Mutex()
3434

35+
private var connectionResponse: Int = BillingClient.BillingResponseCode.OK
36+
3537
private suspend fun connectIfNeeded(): Boolean {
3638
var result: Boolean
3739
mutex.withLock {
3840
if (billing.isReady) {
3941
result = true
4042
} else {
43+
var retries = 0
4144
try {
42-
while (!billing.connect()) {
45+
val MAX_RETRIES = 5
46+
var connected = false
47+
while (!connected && retries < MAX_RETRIES) {
4348
Thread.sleep(300)
49+
retries += 1
50+
connected = billing.connect()
4451
}
45-
result = true
52+
result = connected
4653
} catch (ex: java.lang.Exception) {
4754
ApphudLog.log("Connect to Billing failed: ${ex.message ?: "error"}")
4855
result = false
@@ -58,6 +65,7 @@ internal class BillingWrapper(context: Context) : Closeable {
5865
startConnection(
5966
object : BillingClientStateListener {
6067
override fun onBillingSetupFinished(billingResult: BillingResult) {
68+
connectionResponse = billingResult.responseCode
6169
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
6270
if (continuation.isActive && !resumed) {
6371
resumed = true
@@ -72,6 +80,7 @@ internal class BillingWrapper(context: Context) : Closeable {
7280
}
7381

7482
override fun onBillingServiceDisconnected() {
83+
connectionResponse = BillingClient.BillingResponseCode.SERVICE_DISCONNECTED
7584
}
7685
},
7786
)
@@ -96,9 +105,9 @@ internal class BillingWrapper(context: Context) : Closeable {
96105
consume.callBack = value
97106
}
98107

99-
suspend fun queryPurchasesSync(): List<Purchase>? {
108+
suspend fun queryPurchasesSync(): Pair<List<Purchase>?, Int> {
100109
val connectIfNeeded = connectIfNeeded()
101-
if (!connectIfNeeded) return null
110+
if (!connectIfNeeded) return Pair(null, connectionResponse)
102111
return history.queryPurchasesSync()
103112
}
104113

sdk/src/main/java/com/apphud/sdk/internal/HistoryWrapper.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.apphud.sdk.internal
22

33
import com.android.billingclient.api.BillingClient
4+
import com.android.billingclient.api.BillingResult
45
import com.android.billingclient.api.Purchase
56
import com.android.billingclient.api.QueryPurchaseHistoryParams
67
import com.android.billingclient.api.QueryPurchasesParams
@@ -39,17 +40,22 @@ internal class HistoryWrapper(
3940
}
4041
}
4142

42-
suspend fun queryPurchasesSync(): List<Purchase> = coroutineScope {
43+
suspend fun queryPurchasesSync(): Pair<List<Purchase>, Int> = coroutineScope {
4344
val paramsSubs = QueryPurchasesParams.newBuilder()
4445
.setProductType(BillingClient.ProductType.SUBS)
4546
.build()
4647

4748
val subsDeferred = CompletableDeferred<List<Purchase>>()
4849

50+
var responseResult = BillingClient.BillingResponseCode.OK
51+
4952
billing.queryPurchasesAsync(paramsSubs) { result, purchases ->
5053
if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
5154
subsDeferred.complete(purchases)
5255
} else {
56+
if (responseResult == BillingClient.BillingResponseCode.OK) {
57+
responseResult = result.responseCode
58+
}
5359
subsDeferred.complete(emptyList())
5460
}
5561
}
@@ -64,14 +70,19 @@ internal class HistoryWrapper(
6470
if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
6571
inAppsDeferred.complete(purchases)
6672
} else {
73+
if (responseResult == BillingClient.BillingResponseCode.OK) {
74+
responseResult = result.responseCode
75+
}
6776
inAppsDeferred.complete(emptyList())
6877
}
6978
}
7079

7180
val subsPurchases = async { subsDeferred.await() }
7281
val inAppsPurchases = async { inAppsDeferred.await() }
7382

74-
subsPurchases.await() + inAppsPurchases.await()
83+
val finalPurchases = subsPurchases.await() + inAppsPurchases.await()
84+
85+
return@coroutineScope Pair(finalPurchases, responseResult)
7586
}
7687

7788
suspend fun queryPurchaseHistorySync(

0 commit comments

Comments
 (0)