feat:适配BillingService2,完善支付回调

This commit is contained in:
max
2024-07-09 14:36:12 +08:00
parent 3b407c0bd6
commit 6df95cf11f
17 changed files with 224 additions and 89 deletions

View File

@@ -84,7 +84,6 @@ import com.chwl.library.common.file.FileHelper;
import com.chwl.library.net.rxnet.RxNet;
import com.chwl.library.net.rxnet.converter.GsonConverterPlugins;
import com.chwl.library.utils.AppMetaDataUtil;
import com.chwl.library.utils.AppUtils;
import com.chwl.library.utils.DeviceUuidFactory;
import com.chwl.library.utils.ResUtil;
import com.chwl.library.utils.SingleToastUtil;
@@ -419,7 +418,7 @@ public class App extends BaseApp {
httpParams.put("netType", String.valueOf(SystemUtils.getNetworkType(context)));
httpParams.put("model", SystemUtils.getPhoneModel());
httpParams.put("appVersion", VersionUtil.getLocalName(context));
httpParams.put("appVersionCode", String.valueOf(AppUtils.getVersionCode(context)));
httpParams.put("appVersionCode", String.valueOf(VersionUtil.getVersionCode(context)));
httpParams.put("deviceId", DeviceUuidFactory.getDeviceId(context));
httpParams.put("androidId", MD5Utils.getMD5String(Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID)));
httpParams.put("channel", AppMetaDataUtil.getChannelID());

View File

@@ -11,18 +11,22 @@ import com.chwl.core.pay.bean.PayRecordId
import com.chwl.library.utils.SingleToastUtil
import com.example.lib_utils.log.ILog
import com.example.module_base.support.billing.IBillingResult
import com.example.module_base.support.billing.IBillingService
import com.example.module_base.support.billing.IBillingService2
import com.example.module_base.support.billing.IProductDetails
import com.example.module_base.support.billing.IPurchase
import com.example.module_base.support.billing.OnBillingClientStateListener
import com.example.module_base.support.billing.OnConsumeResponseListener
import com.example.module_base.support.billing.OnProductDetailsResponseListener
import com.example.module_base.support.billing.OnPurchasesResponseListener
import com.example.module_base.support.google.IGoogleService
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.Listener,
IBillingService.ProductDetailsResponseListener, LifecycleEventObserver, ILog {
class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService2.Listener,
OnProductDetailsResponseListener, LifecycleEventObserver, ILog, OnBillingClientStateListener {
private var compositeDisposable: CompositeDisposable? = null
private var _billingService: IBillingService? = null
private var _billingService: IBillingService2? = null
private val dialogManager = DialogManager(activity)
private var paymentIntent: PaymentIntent? = null
@@ -35,9 +39,11 @@ class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.L
(activity as? LifecycleOwner)?.lifecycle?.addObserver(this)
}
private fun getBillingService(): IBillingService? {
private fun getBillingService(): IBillingService2? {
if (_billingService == null) {
_billingService = IGoogleService.newBillingService(activity, this)
_billingService = IGoogleService.newBillingService2(activity, this)
_billingService?.setLogEnabled(true)
_billingService?.startConnection(this)
}
return _billingService
}
@@ -54,6 +60,14 @@ class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.L
}
private fun tryLaunch() {
val billingService = getBillingService()
if (billingService == null) {
callFailed(
IPaymentClient.CODE_NONSUPPORT,
PaymentException(activity.getString(R.string.bean_response_serviceresult_015))
)
return
}
val productId = paymentIntent?.productId
val productItem = productItem
logD("tryLaunch() productId:$productId productItem:$productItem")
@@ -78,7 +92,7 @@ class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.L
{ recordId: PayRecordId ->
if (paymentIntent?.productId == productItem.getProductId()) {
orderId = recordId.recordId
getBillingService()?.initiatePurchaseFlow(
getBillingService()?.launchBillingFlow(
productItem,
recordId.recordId
)
@@ -91,47 +105,94 @@ class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.L
})
}
override fun onBillingClientSetupFinished() {
logD("onBillingClientSetupFinished()")
tryLaunch()
override fun onBillingSetupFinished(billingResult: IBillingResult) {
if (billingResult.isResponseOk()) {
logD("onBillingClientSetupFinished()")
getBillingService()?.queryPurchases(object : OnPurchasesResponseListener {
override fun onQueryPurchasesResponse(
billingResult: IBillingResult,
purchases: List<IPurchase>
) {
if (billingResult.isResponseOk() && purchases.isNotEmpty()) {
purchases.forEach {
handlePurchases(false, it)
}
}
}
})
tryLaunch()
} else {
callFailed(
IPaymentClient.CODE_UNAVAILABLE
)
}
}
override fun onPurchasesUpdated(purchases: List<IPurchase>) {
logD("onPurchasesUpdated() purchases:${purchases.size}")
purchases.forEach {
logD("onPurchasesUpdated() item.state:${it.getPurchaseState()}")
logD("onPurchasesUpdated() item.orderId:${it.getOrderId()}")
logD("onPurchasesUpdated() item.token:${it.getPurchaseToken()}")
logD("onPurchasesUpdated() item.data:${it.getData()}")
val identifiers = it.getAccountIdentifiers()
if (it.isPurchasedState() && identifiers != null) {
addDisposable(
PayModel.get().verifyOrder(
identifiers.getObfuscatedAccountId(),
it.getProducts().firstOrNull(),
it.getPackageName(),
it.getPurchaseToken()
).subscribe({
getBillingService()?.consumeAsync(it)
}, {
callFailed(IPaymentClient.CODE_VERIFY_ORDER_FAILED, it)
})
)
override fun onPurchasesUpdated(billingResult: IBillingResult, purchases: List<IPurchase>?) {
logD("onPurchasesUpdated() billingResult:${billingResult.getResponseCode()} purchases:${purchases?.size}")
if (billingResult.isResponseOk() && !purchases.isNullOrEmpty()) {
purchases.forEach {
handlePurchases(true, it)
}
} else {
callFailed(IPaymentClient.CODE_PURCHASE_FAILED)
}
}
override fun onConsumeFinished(token: String?, result: Int) {
logD("onConsumeFinished() token:${token} result:$result")
val orderId = orderId
if (orderId != null) {
callSuccess(orderId)
private fun handlePurchases(isCurrentPaying: Boolean, purchases: IPurchase) {
logD("handlePurchases state:${purchases.getPurchaseState()}")
logD("handlePurchases orderId:${purchases.getOrderId()}")
logD("handlePurchases token:${purchases.getPurchaseToken()}")
logD("handlePurchases data:${purchases.getData()}")
logD(
"handlePurchases obfuscatedAccountId:${
purchases.getAccountIdentifiers()?.getObfuscatedAccountId()
}"
)
logD(
"handlePurchases obfuscatedProfileId:${
purchases.getAccountIdentifiers()?.getObfuscatedProfileId()
}"
)
val identifiers = purchases.getAccountIdentifiers()
if (purchases.isPurchasedState() && identifiers != null) {
logD("handlePurchases() verifyOrder")
addDisposable(
PayModel.get().verifyOrder(
identifiers.getObfuscatedAccountId(),
purchases.getProducts().firstOrNull(),
purchases.getPackageName(),
purchases.getPurchaseToken()
).subscribe({
logD("handlePurchases() verifyOrder consumeAsync")
consumeAsync(isCurrentPaying, it)
}, {
logD("handlePurchases() error:${it.message}")
if (isCurrentPaying) {
callFailed(IPaymentClient.CODE_VERIFY_ORDER_FAILED, it)
}
})
)
}
}
override fun onFailedHandle(result: Int) {
logD("onFailedHandle() result:${result}")
callFailed(IPaymentClient.CODE_OTHER)
private fun consumeAsync(isCurrentPaying: Boolean, token: String) {
logD("consumeAsync() isCurrentPaying:${isCurrentPaying} token:$token")
getBillingService()?.consumeAsync(token, object : OnConsumeResponseListener {
override fun onConsumeResponse(billingResult: IBillingResult, purchaseToken: String) {
logD("consumeAsync() onConsumeResponse billingResult:${billingResult} purchaseToken:$purchaseToken")
if (isCurrentPaying) {
if (billingResult.isResponseOk()) {
val orderId = orderId
if (orderId != null) {
callSuccess(orderId)
}
} else {
callFailed(IPaymentClient.CODE_CONSUME_ORDER_FAILED)
}
}
}
})
}
override fun onProductDetailsResponse(
@@ -147,10 +208,10 @@ class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.L
if (item != null) {
tryLaunch()
} else {
callFailed(IPaymentClient.CODE_NOT_FOUND)
callFailed(IPaymentClient.CODE_PRODUCT_NOT_FOUND)
}
} else {
callFailed(IPaymentClient.CODE_NOT_FOUND)
callFailed(IPaymentClient.CODE_PRODUCT_NOT_FOUND)
}
}
@@ -163,10 +224,20 @@ class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.L
dialogManager.dismissDialog()
}
private fun callFailed(code: Int, throwable: Throwable? = null) {
throwable?.printStackTrace()
logD("callFailed() code:${code} throwable:${throwable?.message}")
throwable?.let {
private fun callFailed(code: Int) {
callFailed(
code,
PaymentException(
code,
message = activity.getString(R.string.common_operation_prompt_format).format(code)
)
)
}
private fun callFailed(code: Int, throwable: Throwable) {
throwable.printStackTrace()
logD("callFailed() code:${code} throwable:${throwable.message}")
throwable.let {
SingleToastUtil.showToast(
it.message
)
@@ -202,12 +273,6 @@ class GPaymentClient(val activity: Activity) : IPaymentClient, IBillingService.L
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
logD("onStateChanged() event:$event")
when (event) {
Lifecycle.Event.ON_RESUME -> {
if (_billingService?.isServiceConnected() == true) {
_billingService?.onQueryPurchases()
}
}
Lifecycle.Event.ON_DESTROY -> {
(activity as? LifecycleOwner)?.lifecycle?.removeObserver(this)
onCleared()

View File

@@ -10,17 +10,26 @@ interface IPaymentClient : ICleared {
// 未知异常原因
const val CODE_OTHER = 0
// 服务不可用
const val CODE_UNAVAILABLE = 1
// 不支持该功能
const val CODE_NONSUPPORT = 1
// 查询订单信息失败
const val CODE_NOT_FOUND = 2
// 服务暂不可用
const val CODE_UNAVAILABLE = 2
// 未查询到购买信息
const val CODE_PRODUCT_NOT_FOUND = 3
// 预下单失败
const val CODE_PLACE_ORDER_FAILED = 3
const val CODE_PLACE_ORDER_FAILED = 4
// 购买失败
const val CODE_PURCHASE_FAILED = 5
// 验证订单失败
const val CODE_VERIFY_ORDER_FAILED = 4
const val CODE_VERIFY_ORDER_FAILED = 6
// 核消订单失败
const val CODE_CONSUME_ORDER_FAILED = 7
}
fun launchPayment(intent: PaymentIntent)

View File

@@ -0,0 +1,13 @@
package com.chwl.app.ui.wallet.payment
import androidx.annotation.Keep
@Keep
class PaymentException : Exception {
var code = 0
constructor(message: String) : super(message)
constructor(code: Int, message: String) : super(message) {
this.code = code
}
}

View File

@@ -59,7 +59,6 @@ import com.chwl.library.record.AuditRecorderConfiguration;
import com.chwl.library.record.ExtAudioRecorder;
import com.chwl.library.rxbus.RxBus;
import com.chwl.library.utils.AppMetaDataUtil;
import com.chwl.library.utils.AppUtils;
import com.chwl.library.utils.DeviceUuidFactory;
import com.chwl.library.utils.SystemUtils;
import com.chwl.library.utils.VersionUtil;
@@ -545,7 +544,7 @@ public class JSInterface implements ICleared {
jsonObject.put("netType", String.valueOf(SystemUtils.getNetworkType(context)));
jsonObject.put("model", SystemUtils.getPhoneModel());
jsonObject.put("appVersion", VersionUtil.getLocalName(context));
jsonObject.put("appVersionCode", String.valueOf(AppUtils.getVersionCode(context)));
jsonObject.put("appVersionCode", String.valueOf(VersionUtil.getVersionCode(context)));
jsonObject.put("deviceId", DeviceUtil.getDeviceId(context));
jsonObject.put("channel", AppMetaDataUtil.getChannelID());
jsonObject.put("Accept-Language", LanguageHelper.INSTANCE.getCurrentLanguageType());
@@ -630,7 +629,6 @@ public class JSInterface implements ICleared {
@Override
public void onCleared() {
ICleared.super.onCleared();
if (paymentClient != null) {
paymentClient.onCleared();
}

View File

@@ -12,7 +12,6 @@ import com.tencent.vasdolly.helper.ChannelReaderUtil;
import com.chwl.app.application.App;
import com.chwl.core.XConstants;
import com.chwl.core.Constants;
import com.chwl.library.utils.AppUtils;
import com.chwl.library.utils.VersionUtil;
/**
@@ -108,7 +107,7 @@ public class SimpleJSInterface {
jsonObject.put("os", "android");
jsonObject.put("app", XConstants.APP_MARK);
jsonObject.put("appVersion", VersionUtil.getLocalName(context));
jsonObject.put("appVersionCode", String.valueOf(AppUtils.getVersionCode(context)));
jsonObject.put("appVersionCode", String.valueOf(VersionUtil.getVersionCode(context)));
jsonObject.put("channel", getChannel());
Log.e(TAG, "getDeviceInfo: " + jsonObject);
return jsonObject.toJSONString();

View File

@@ -683,4 +683,5 @@
<string name="parameter_error">خطأ في المعلمة</string>
<string name="pk_refuse">رفض الطرف الآخر طلب الـ PK</string>
<string name="common_operation_prompt_format">操作失敗 (%s)</string>
</resources>

View File

@@ -670,4 +670,6 @@
<string name="parameter_error">參數錯誤</string>
<string name="pk_refuse">對方拒絕你的PK請求</string>
<string name="common_operation_prompt_format">操作失敗 (%s)</string>
</resources>

View File

@@ -665,5 +665,6 @@
<string name="pk_refuse">The opponent rejected your PK request</string>
<string name="common_operation_prompt_format">操作失敗 (%s)</string>
</resources>

View File

@@ -1,22 +0,0 @@
package com.chwl.library.utils;
import android.content.Context;
import android.content.pm.PackageManager;
/**
* <p> </p>
*
* @author jiahui
* date 2018/2/28
*/
public class AppUtils {
public static int getVersionCode(Context context) {
try {
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 0;
}
}

View File

@@ -4,7 +4,9 @@ package com.example.module_base.support.billing
* Created by Max on 2023/11/22 17:44
* Desc:支付
**/
@Deprecated("逐步迁移到IBillingService2")
interface IBillingService {
fun setLogEnabled(enabled: Boolean)
fun isServiceConnected(): Boolean
@@ -28,7 +30,5 @@ interface IBillingService {
fun onFailedHandle(result: Int)
}
interface ProductDetailsResponseListener {
fun onProductDetailsResponse(billingResult: IBillingResult, productDetails: List<IProductDetails>)
}
interface ProductDetailsResponseListener : OnProductDetailsResponseListener
}

View File

@@ -0,0 +1,31 @@
package com.example.module_base.support.billing
/**
* Created by Max on 2023/11/22 17:44
* Desc:支付
**/
interface IBillingService2 {
fun setLogEnabled(enabled: Boolean)
fun startConnection(listener: OnBillingClientStateListener)
fun isServiceConnected(): Boolean
fun queryPurchases(listener: OnPurchasesResponseListener)
fun querySkuDetailsAsync(
productIdList: List<String>,
listener: OnProductDetailsResponseListener
)
fun consumeAsync(purchaseToken: String, listener: OnConsumeResponseListener)
fun launchBillingFlow(productDetails: IProductDetails, recordId: String)
fun destroy()
interface Listener {
fun onPurchasesUpdated(billingResult: IBillingResult, purchases: List<IPurchase>?)
}
}

View File

@@ -0,0 +1,5 @@
package com.example.module_base.support.billing
interface OnBillingClientStateListener {
fun onBillingSetupFinished(billingResult: IBillingResult)
}

View File

@@ -0,0 +1,8 @@
package com.example.module_base.support.billing
interface OnConsumeResponseListener {
fun onConsumeResponse(
billingResult: IBillingResult,
purchaseToken: String
)
}

View File

@@ -0,0 +1,8 @@
package com.example.module_base.support.billing
interface OnProductDetailsResponseListener {
fun onProductDetailsResponse(
billingResult: IBillingResult,
productDetails: List<IProductDetails>
)
}

View File

@@ -0,0 +1,5 @@
package com.example.module_base.support.billing
interface OnPurchasesResponseListener {
fun onQueryPurchasesResponse(billingResult: IBillingResult, purchases: List<IPurchase>)
}

View File

@@ -4,6 +4,7 @@ import android.app.Activity
import com.alibaba.android.arouter.facade.template.IProvider
import com.alibaba.android.arouter.launcher.ARouter
import com.example.module_base.support.billing.IBillingService
import com.example.module_base.support.billing.IBillingService2
import com.example.module_base.support.login.ILoginService
/**
@@ -26,6 +27,13 @@ interface IGoogleService : IProvider {
): IBillingService? {
return instance?.newBillingService(activity, listener)
}
fun newBillingService2(
activity: Activity,
listener: IBillingService2.Listener
): IBillingService2? {
return instance?.newBillingService2(activity, listener)
}
}
fun newLoginService(): ILoginService
@@ -34,4 +42,9 @@ interface IGoogleService : IProvider {
activity: Activity,
listener: IBillingService.Listener
): IBillingService
fun newBillingService2(
activity: Activity,
listener: IBillingService2.Listener
): IBillingService2
}