华为在应用程序中购买的版本与从android购买的普通应用程序中的版本相同吗

1 android huawei-mobile-services

我想知道华为在应用程序中购买的编码方式与从安卓购买的普通应用程序中的编码方式不同吗?

如果是的话,我们是否需要重新编码从android购买到华为版本的正常应用程序?

Osc*_*nez 8

是的,对于 appGallery(华为)来说,这是一种不同的方式。我已经在我的应用程序的应用程序购买中实现了 Playstore 和 appgallery。我只是使用风味和存储库模式分离了实现,如下所示。

productFlavors {
        googlePlay {
            versionCode 72
            versionName "1.6.2"

        }
        appGallery {
            versionCode 70
            versionName "1.5.9"
        }
 }
Run Code Online (Sandbox Code Playgroud)

然后在我的 gradle 文件中,我为 appGallery 和 Play 商店添加了自定义实现,如下所示。

对于应用程序库

appGalleryImplementation 'com.huawei.agconnect:agconnect-core:1.2.0.300'
appGalleryImplementation 'com.huawei.hms:iap:4.0.2.300'
Run Code Online (Sandbox Code Playgroud)

用于游戏商店

googlePlayImplementation 'com.android.billingclient:billing:3.0.0'
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个接口,这个接口需要两种风格都实现。

interface PurchaseRepository : LifecycleObserver {
    fun provideActivity(activity: Activity)
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onCreate()
    fun loadInventorySkus()
    fun purchaseItem(sku: String)
    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy()
    interface OwnedSkusPurchaseListener {
        fun onOwnedSkuLoaded(sku: Sku?)
    }
    interface PurchaseResultListener {
        fun onLoadingInventory()
        fun onCancelPurchase()
        fun onFailedPurchase(exception: Exception? = null)
        fun onOrderNotLogin()
        fun onSuccessPurchase()
        fun onInventoryLoaded(skus: List<Sku?>)
        fun onSkuAlreadyOwned()
    }
}
Run Code Online (Sandbox Code Playgroud)

应用程序库的实现如下所示

class PurchaseRepositoryImpl(userEndpoints: UserEndpoints,
                         private val userRepositoryConfig: UserRepositoryConfig,
                         private val skus: List<String>) : BasePurchaseRepository(userEndpoints, userRepositoryConfig) {

override fun onCreate() {
    updateOwnedProducts()
    if (BuildConfig.DEBUG)
        isSandBoxActivated()
}

override fun loadInventorySkus() {
    getSkuDetails(activity, skus, PRODUCT_TYPE_RENEWABLE_SUBSCRIPTION)
}

private fun updateOwnedProducts() {
    val task = Iap.getIapClient(activity).obtainOwnedPurchases(OwnedPurchasesReq().apply {
        priceType = PRODUCT_TYPE_RENEWABLE_SUBSCRIPTION
    })
    task.addOnSuccessListener { result ->
        if (result != null && result.inAppPurchaseDataList != null) {
            (0 until result.inAppPurchaseDataList.size).forEach { i ->
                val inAppPurchaseData = result.inAppPurchaseDataList[i]
                val inAppSignature = result.inAppSignature[i]

                val inAppPurchaseDataBean = InAppPurchaseData(inAppPurchaseData)
                val purchaseState = inAppPurchaseDataBean.purchaseState

                if (checkDeliverTransactionState(inAppPurchaseData, inAppSignature)
                        && purchaseState == InAppPurchaseData.PurchaseState.PURCHASED) {
                    listenerProductSku?.onOwnedSkuLoaded(inAppPurchaseDataBean.toSku())
                } else
                    listenerProductSku?.onOwnedSkuLoaded(null)

                sendUpdatePurchase(inAppPurchaseData)
            }
        }
    }.addOnFailureListener {
        listenerProductSku?.onOwnedSkuLoaded(null)
    }
}

private fun isSandBoxActivated() {
    val client = Iap.getIapClient(activity)
    val task = client.isSandboxActivated(IsSandboxActivatedReq())
    task.addOnSuccessListener {
        if (it != null)
            Timber.d("Sandbox is activated")
    }.addOnFailureListener {
        if (it != null)
            Timber.e(it)
    }
}

override fun purchaseItem(sku: String) {
    val client: IapClient = Iap.getIapClient(activity)
    val task = client.createPurchaseIntent(createGetBuyIntentReq(PRODUCT_TYPE_RENEWABLE_SUBSCRIPTION, sku))
    task.apply {
        addOnSuccessListener { it ->
            it.status?.let {
                // you should pull up the page to complete the payment process
                startResolutionForResult(activity, it, REQ_CODE_BUY)
            }
        }
        addOnFailureListener {
            when (it) {
                is IapApiException -> {
                    val apiException = it
                    val returnCode = apiException.statusCode
                    listenerPurchaseResult?.onFailedPurchase(exception)
                    Timber.d("getBuyIntent, returnCode: $returnCode")
                }
                else -> Timber.e(it)
            }
        }
    }
}

private fun startResolutionForResult(activity: Activity, status: Status?, reqCode: Int) {
    if (status == null) {
        Timber.e("status is null")
        return
    }
    if (status.hasResolution()) {
        try {
            status.startResolutionForResult(activity, reqCode)
        } catch (exp: IntentSender.SendIntentException) {
            listenerPurchaseResult?.onFailedPurchase(exp)
            Timber.e(exp)
        }
    } else {
        Timber.e("intent is null")
    }
}

private fun getSkuDetails(context: Context, skuList: List<String>, type: Int) {
    val client = Iap.getIapClient(context)
    val task = client.obtainProductInfo(createGetSkuDetailReq(type, ArrayList(skuList)))
    task.addOnSuccessListener { result ->
        if (result.productInfoList != null) {
            listenerPurchaseResult?.onInventoryLoaded(result.productInfoList.map { it.transform() })
        }
    }.addOnFailureListener { exception ->
        if (exception is IapApiException) {
            val returnCode = exception.statusCode
            if (returnCode == OrderStatusCode.ORDER_HWID_NOT_LOGIN)
                listenerPurchaseResult?.onOrderNotLogin()
            else
                listenerPurchaseResult?.onFailedPurchase(exception)
        } else
            listenerPurchaseResult?.onFailedPurchase(exception)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == REQ_CODE_BUY) {
        data?.let {
            val buyResultInfo = Iap.getIapClient(activity).parsePurchaseResultInfoFromIntent(data)
            when (buyResultInfo.returnCode) {
                OrderStatusCode.ORDER_STATE_CANCEL -> listenerPurchaseResult?.onCancelPurchase()
                OrderStatusCode.ORDER_PRODUCT_OWNED -> {
                    listenerPurchaseResult?.onSkuAlreadyOwned()
                    sendUpdatePurchase(buyResultInfo.inAppPurchaseData)
                }
                OrderStatusCode.ORDER_STATE_SUCCESS -> {
                    if (checkDeliverTransactionState(buyResultInfo.inAppPurchaseData, buyResultInfo.inAppDataSignature)) {
                        sendUpdatePurchase(buyResultInfo.inAppPurchaseData)
                        listenerPurchaseResult?.onSuccessPurchase()
                    } else
                        listenerPurchaseResult?.onFailedPurchase()
                }
                else -> listenerPurchaseResult?.onFailedPurchase()
            }
        }
    }
}

private fun checkDeliverTransactionState(appData: String, appSignature: String): Boolean {
    return CipherUtil.doCheck(appData, appSignature, API_KEY_HMS_IN_PURCHASE)
}

private fun sendUpdatePurchase(jsonAppPurchaseData: String?) {
    jsonAppPurchaseData?.let {
        val inAppPurchaseDataBean = InAppPurchaseData(jsonAppPurchaseData)
        userUpgradeRepository(inAppPurchaseDataBean.transform(userRepositoryConfig.uuid))
    }
}

private fun createGetSkuDetailReq(type: Int, skuList: ArrayList<String>): ProductInfoReq = ProductInfoReq().apply {
    productIds = skuList
    priceType = type
}

private fun createGetBuyIntentReq(type: Int, skuId: String?) = PurchaseIntentReq().apply {
    productId = skuId
    priceType = type
}
Run Code Online (Sandbox Code Playgroud)

}

以及 Play 商店的实现

 class PurchaseRepositoryImpl(userEndpoints: UserEndpoints,
                             private val userRepositoryConfig: UserRepositoryConfig,
                             private val skus: List<String>) : BasePurchaseRepository(userEndpoints, userRepositoryConfig) {

    private lateinit var checkout: ActivityCheckout
    private lateinit var inventory: Inventory
    private lateinit var selectedSku: String

    override fun onCreate() {
        checkout = Checkout.forActivity(activity, BaseApplication[activity].billing)
        checkout.start()
        checkout.createPurchaseFlow(PurchaseListener())
        loadInventorySkus()
    }

    override fun loadInventorySkus() {
        inventory = checkout.makeInventory()
        inventory.load(Inventory.Request.create()
                .loadAllPurchases()
                .loadSkus(ProductTypes.SUBSCRIPTION, skus), InventoryCallback())
    }

    override fun purchaseItem(sku: String) {
        checkout.whenReady(object : Checkout.EmptyListener() {
            override fun onReady(requests: BillingRequests) {
                selectedSku = sku
                requests.purchase(ProductTypes.SUBSCRIPTION, selectedSku, null, checkout.purchaseFlow)
            }
        })
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        checkout.onActivityResult(requestCode, resultCode, data)
    }

    private inner class PurchaseListener : EmptyRequestListener<Purchase>() {
        override fun onSuccess(purchase: Purchase) {
            if (purchase.state == Purchase.State.PURCHASED) {
                userUpgradeRepository(purchase.transform(userRepositoryConfig.getFirebaseId(), true))
                listenerPurchaseResult?.onSuccessPurchase()
            } else
                userUpgradeRepository(purchase.transform(userRepositoryConfig.getFirebaseId(), false))
        }

        override fun onError(response: Int, e: Exception) {
            if (e is BillingException) {
                when (e.response) {
                    ResponseCodes.ITEM_ALREADY_OWNED -> {
                        listenerPurchaseResult?.onSkuAlreadyOwned()
                        userUpgradeRepository(PurchaseInfo(
                                sku = selectedSku,
                                isPurchase = true
                        ))
                    }
                    ResponseCodes.USER_CANCELED -> listenerPurchaseResult?.onCancelPurchase()
                    else -> listenerPurchaseResult?.onFailedPurchase(e)
                }
            }
        }
    }

    private inner class InventoryCallback : Inventory.Callback {
        override fun onLoaded(products: Inventory.Products) {
            try {
                val skuProducts = products[ProductTypes.SUBSCRIPTION].skus.toList()
                        .map { it.transform() }
                        .sortedBy { it.detailedPrice?.amount }

                listenerPurchaseResult?.onInventoryLoaded(skuProducts)

                skuProducts.forEach { sku ->
                    val isPurchased = products[ProductTypes.SUBSCRIPTION].hasPurchaseInState(sku.id?.code
                            ?: "", Purchase.State.PURCHASED)
                    if (isPurchased) listenerProductSku?.onOwnedSkuLoaded(sku)
                    if (!isPurchased && userRepositoryConfig.getTypeSubscription() == sku.id?.code)
                        userUpgradeRepository(sku.toPurchaseInfo(userRepositoryConfig.getFirebaseId(), isPurchased))
                }

            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }}
Run Code Online (Sandbox Code Playgroud)

每个存储库实现必须在其各自的风味目录中,如下所示

在此处输入图片说明

这些购买存储库包含来自我的应用程序的一些业务逻辑,但我希望这个答案可以帮助您了解如何让您的应用程序在应用程序库和 Play 商店中运行。

在应用程序中购买华为代码实验室