Skip to content
This repository was archived by the owner on Oct 17, 2025. It is now read-only.

Commit e2d4c31

Browse files
committed
refactor: use gql types in OpenIapStore
1 parent b5e78b0 commit e2d4c31

File tree

1 file changed

+58
-98
lines changed

1 file changed

+58
-98
lines changed

openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt

Lines changed: 58 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.hyo.openiap.store
22

33
import dev.hyo.openiap.ActiveSubscription
4+
import dev.hyo.openiap.AndroidSubscriptionOfferInput
45
import dev.hyo.openiap.DeepLinkOptions
56
import dev.hyo.openiap.FetchProductsResult
67
import dev.hyo.openiap.FetchProductsResultProducts
@@ -22,6 +23,13 @@ import dev.hyo.openiap.RequestSubscriptionAndroidProps
2223
import dev.hyo.openiap.RequestSubscriptionPropsByPlatforms
2324
import dev.hyo.openiap.RequestPurchaseResultPurchase
2425
import dev.hyo.openiap.RequestPurchaseResultPurchases
26+
import dev.hyo.openiap.RequestPurchaseResult
27+
import dev.hyo.openiap.MutationRequestPurchaseHandler
28+
import dev.hyo.openiap.QueryFetchProductsHandler
29+
import dev.hyo.openiap.QueryGetAvailablePurchasesHandler
30+
import dev.hyo.openiap.MutationFinishTransactionHandler
31+
import dev.hyo.openiap.MutationInitConnectionHandler
32+
import dev.hyo.openiap.MutationEndConnectionHandler
2533
import android.app.Activity
2634
import android.content.Context
2735
import com.android.billingclient.api.BillingClient
@@ -132,13 +140,20 @@ class OpenIapStore(private val module: OpenIapModule) {
132140
}
133141

134142
// -------------------------------------------------------------------------
135-
// Connection Management
143+
// Connection Management - Using GraphQL handler pattern
136144
// -------------------------------------------------------------------------
137-
suspend fun initConnection(): Boolean {
145+
val initConnection: MutationInitConnectionHandler = {
138146
setLoading { it.initConnection = true }
139-
return try {
147+
try {
140148
val ok = module.initConnection()
141149
_isConnected.value = ok
150+
151+
// Add listeners when connected
152+
if (ok) {
153+
addPurchaseUpdateListener(purchaseUpdateListener)
154+
addPurchaseErrorListener(purchaseErrorListener)
155+
}
156+
142157
ok
143158
} catch (e: Exception) {
144159
setError(e.message)
@@ -148,27 +163,28 @@ class OpenIapStore(private val module: OpenIapModule) {
148163
}
149164
}
150165

151-
suspend fun endConnection(): Boolean {
152-
return try {
166+
val endConnection: MutationEndConnectionHandler = {
167+
removePurchaseUpdateListener(purchaseUpdateListener)
168+
removePurchaseErrorListener(purchaseErrorListener)
169+
try {
153170
val ok = module.endConnection()
154171
_isConnected.value = false
172+
clear()
155173
ok
156174
} catch (e: Exception) {
157175
setError(e.message)
158176
throw e
159177
}
160178
}
161179

180+
162181
// -------------------------------------------------------------------------
163-
// Product Management
182+
// Product Management - Using GraphQL handler pattern
164183
// -------------------------------------------------------------------------
165-
suspend fun fetchProducts(
166-
skus: List<String>,
167-
type: ProductQueryType = ProductQueryType.All
168-
): FetchProductsResult {
184+
val fetchProducts: QueryFetchProductsHandler = { request ->
169185
setLoading { it.fetchProducts = true }
170-
return try {
171-
val result = module.fetchProducts(ProductRequest(skus = skus, type = type))
186+
try {
187+
val result = module.fetchProducts(request)
172188
when (result) {
173189
is FetchProductsResultProducts -> {
174190
// Merge new products with existing ones
@@ -183,7 +199,7 @@ class OpenIapStore(private val module: OpenIapModule) {
183199
val existingSubIds = _subscriptions.value.map { it.id }.toSet()
184200
val subsToAdd = subs.filter { it.id !in existingSubIds }
185201
_subscriptions.value = _subscriptions.value + subsToAdd
186-
202+
187203
// Also add subscription products to products list
188204
val subProducts = subs
189205
.filterIsInstance<ProductSubscriptionAndroid>()
@@ -202,12 +218,13 @@ class OpenIapStore(private val module: OpenIapModule) {
202218
}
203219
}
204220

221+
205222
// -------------------------------------------------------------------------
206-
// Purchases / Restore
223+
// Purchases / Restore - Using GraphQL handler pattern
207224
// -------------------------------------------------------------------------
208-
suspend fun getAvailablePurchases(options: PurchaseOptions? = null): List<Purchase> {
225+
val getAvailablePurchases: QueryGetAvailablePurchasesHandler = { options ->
209226
setLoading { it.restorePurchases = true }
210-
return try {
227+
try {
211228
val result = module.getAvailablePurchases(options)
212229
_availablePurchases.value = result
213230
result
@@ -219,52 +236,46 @@ class OpenIapStore(private val module: OpenIapModule) {
219236
}
220237
}
221238

222-
suspend fun restorePurchases(): List<Purchase> = getAvailablePurchases()
223-
224-
suspend fun loadPurchases(): List<Purchase> = getAvailablePurchases()
225239

226240
// -------------------------------------------------------------------------
227-
// Purchase Flow
241+
// Purchase Flow - Using GraphQL handler pattern
228242
// -------------------------------------------------------------------------
229-
suspend fun requestPurchase(
230-
skus: List<String>,
231-
type: ProductQueryType = ProductQueryType.InApp
232-
): List<Purchase> {
233-
val skuForStatus = skus.firstOrNull()
243+
val requestPurchase: MutationRequestPurchaseHandler = { props ->
244+
val skuForStatus = when (val request = props.request) {
245+
is RequestPurchaseProps.Request.Purchase -> request.value.android?.skus?.firstOrNull()
246+
is RequestPurchaseProps.Request.Subscription -> request.value.android?.skus?.firstOrNull()
247+
else -> null
248+
}
249+
234250
if (skuForStatus != null) {
235251
addPurchasing(skuForStatus)
236252
pendingRequestProductId = skuForStatus
237253
}
238-
return try {
239-
val request = buildRequestPurchaseProps(skus, type)
240-
when (val result = module.requestPurchase(request)) {
241-
is RequestPurchaseResultPurchases -> result.value.orEmpty()
242-
is RequestPurchaseResultPurchase -> result.value?.let(::listOf).orEmpty()
243-
else -> emptyList()
244-
}
254+
255+
try {
256+
module.requestPurchase(props)
245257
} finally {
246258
if (skuForStatus != null) removePurchasing(skuForStatus)
247259
}
248260
}
249261

250-
suspend fun finishTransaction(
251-
purchase: Purchase,
252-
isConsumable: Boolean = false
253-
): Boolean {
254-
val token = purchase.purchaseToken
255-
if ((purchase is PurchaseAndroid && purchase.isAcknowledgedAndroid == true) || (token != null && processedPurchaseTokens.contains(token))) {
256-
return true
257-
}
258-
return try {
259-
module.finishTransaction(purchase.toInput(), isConsumable)
260-
if (token != null) processedPurchaseTokens.add(token)
261-
true
262-
} catch (e: Exception) {
263-
setError(e.message)
264-
throw e
262+
263+
// Using GraphQL handler pattern
264+
val finishTransaction: MutationFinishTransactionHandler = { purchaseInput, isConsumable ->
265+
val token = purchaseInput.purchaseToken
266+
// Check if already processed - but we can't check isAcknowledgedAndroid on PurchaseInput
267+
if (token == null || !processedPurchaseTokens.contains(token)) {
268+
try {
269+
module.finishTransaction(purchaseInput, isConsumable)
270+
if (token != null) processedPurchaseTokens.add(token)
271+
} catch (e: Exception) {
272+
setError(e.message)
273+
throw e
274+
}
265275
}
266276
}
267277

278+
268279
// -------------------------------------------------------------------------
269280
// Subscriptions
270281
// -------------------------------------------------------------------------
@@ -352,57 +363,6 @@ class OpenIapStore(private val module: OpenIapModule) {
352363
_status.value = current.copy(loadings = current.loadings.copy(purchasing = set))
353364
}
354365

355-
private fun buildRequestPurchaseProps(skus: List<String>, type: ProductQueryType): RequestPurchaseProps {
356-
return when (type) {
357-
ProductQueryType.InApp -> {
358-
val android = RequestPurchaseAndroidProps(
359-
isOfferPersonalized = null,
360-
obfuscatedAccountIdAndroid = null,
361-
obfuscatedProfileIdAndroid = null,
362-
skus = skus
363-
)
364-
RequestPurchaseProps(
365-
request = RequestPurchaseProps.Request.Purchase(
366-
RequestPurchasePropsByPlatforms(android = android)
367-
),
368-
type = ProductQueryType.InApp
369-
)
370-
}
371-
ProductQueryType.Subs -> {
372-
val android = RequestSubscriptionAndroidProps(
373-
isOfferPersonalized = null,
374-
obfuscatedAccountIdAndroid = null,
375-
obfuscatedProfileIdAndroid = null,
376-
purchaseTokenAndroid = null,
377-
replacementModeAndroid = null,
378-
skus = skus,
379-
subscriptionOffers = null
380-
)
381-
RequestPurchaseProps(
382-
request = RequestPurchaseProps.Request.Subscription(
383-
RequestSubscriptionPropsByPlatforms(android = android)
384-
),
385-
type = ProductQueryType.Subs
386-
)
387-
}
388-
ProductQueryType.All -> throw IllegalArgumentException("type must be InApp or Subs when requesting a purchase")
389-
}
390-
}
391-
392-
private fun Purchase.toInput(): PurchaseInput = when (this) {
393-
is PurchaseAndroid -> PurchaseInput(
394-
id = id,
395-
ids = ids,
396-
isAutoRenewing = isAutoRenewing,
397-
platform = platform,
398-
productId = productId,
399-
purchaseState = purchaseState,
400-
purchaseToken = purchaseToken,
401-
quantity = quantity,
402-
transactionDate = transactionDate
403-
)
404-
else -> throw UnsupportedOperationException("Only Android purchases are supported on this platform")
405-
}
406366
}
407367

408368
// -----------------------------------------------------------------------------

0 commit comments

Comments
 (0)