From 3b407c0bd6fa8f1adcd1742dead9f13d5c11256d Mon Sep 17 00:00:00 2001 From: max Date: Mon, 8 Jul 2024 17:57:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0H5?= =?UTF-8?q?=E5=94=A4=E8=B5=B7=E6=94=AF=E4=BB=98=E9=80=BB=E8=BE=91=EF=BC=88?= =?UTF-8?q?=E4=B8=8D=E5=AE=8C=E5=96=84=EF=BC=8C=E7=94=A8=E4=BA=8E=E6=89=93?= =?UTF-8?q?=E5=86=85=E6=B5=8B=E5=8C=85=E9=AA=8C=E8=AF=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/chwl/app/application/App.java | 4 +- .../chwl/app/ui/setting/SettingActivity.kt | 9 +- .../app/ui/wallet/payment/GPaymentClient.kt | 221 ++++++++++++++++++ .../app/ui/wallet/payment/IPaymentClient.kt | 33 +++ .../app/ui/wallet/payment/PaymentIntent.kt | 4 + .../app/ui/wallet/payment/PaymentResult.kt | 12 + .../app/ui/webview/CommonWebViewActivity.java | 7 +- .../app/ui/webview/CommonWebViewFragment.java | 14 +- .../com/chwl/app/ui/webview/JSInterface.java | 85 ++++++- 9 files changed, 376 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/chwl/app/ui/wallet/payment/GPaymentClient.kt create mode 100644 app/src/main/java/com/chwl/app/ui/wallet/payment/IPaymentClient.kt create mode 100644 app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentIntent.kt create mode 100644 app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentResult.kt diff --git a/app/src/main/java/com/chwl/app/application/App.java b/app/src/main/java/com/chwl/app/application/App.java index 87ab47c2b..9282a9aed 100644 --- a/app/src/main/java/com/chwl/app/application/App.java +++ b/app/src/main/java/com/chwl/app/application/App.java @@ -325,7 +325,9 @@ public class App extends BaseApp { BasicConfig.INSTANCE.setVoiceDir(Constants.VOICE_DIR); BasicConfig.INSTANCE.setCacheDir(Constants.CACHE_DIR); BasicConfig.INSTANCE.setImageDir(Constants.IMAGE_CACHE_DIR); - com.example.lib_utils.log.LogUtil.INSTANCE.setConsolePrinterEnabled(BuildConfig.DEBUG); + // TODO 临时调整 + com.example.lib_utils.log.LogUtil.INSTANCE.setConsolePrinterEnabled(true); +// com.example.lib_utils.log.LogUtil.INSTANCE.setConsolePrinterEnabled(BuildConfig.DEBUG); } /** diff --git a/app/src/main/java/com/chwl/app/ui/setting/SettingActivity.kt b/app/src/main/java/com/chwl/app/ui/setting/SettingActivity.kt index 181ba5e63..92530abad 100644 --- a/app/src/main/java/com/chwl/app/ui/setting/SettingActivity.kt +++ b/app/src/main/java/com/chwl/app/ui/setting/SettingActivity.kt @@ -84,11 +84,12 @@ class SettingActivity : BaseViewBindingActivity(), View. binding.tvLanugage.setOnClickListener(this) binding.rlyPayPwd.setOnClickListener(this) - if (BuildConfig.DEBUG) { + // TODO 临时调整 +// if (BuildConfig.DEBUG) { binding.titleBar.setOnTitleClickListener { debug() } - } +// } } @SuppressLint("CheckResult") @@ -222,8 +223,8 @@ class SettingActivity : BaseViewBindingActivity(), View. } private fun debug() { - startActivity(Intent(this, DebugActivity::class.java)) -// CommonWebViewActivity.start(this,"https://api.molistar.xyz/molistar/activity/2024-invitationFission/index.html") +// startActivity(Intent(this, DebugActivity::class.java)) + CommonWebViewActivity.start(this,"http://beta.api.molistar.xyz/molistar/modules/order/index.html") // PublicChatRoomMessageActivity.start(this) // MyDecorationActivity.start(this,0) } diff --git a/app/src/main/java/com/chwl/app/ui/wallet/payment/GPaymentClient.kt b/app/src/main/java/com/chwl/app/ui/wallet/payment/GPaymentClient.kt new file mode 100644 index 000000000..ccc3896ac --- /dev/null +++ b/app/src/main/java/com/chwl/app/ui/wallet/payment/GPaymentClient.kt @@ -0,0 +1,221 @@ +package com.chwl.app.ui.wallet.payment + +import android.app.Activity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import com.chwl.app.R +import com.chwl.app.common.widget.dialog.DialogManager +import com.chwl.core.pay.PayModel +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.IProductDetails +import com.example.module_base.support.billing.IPurchase +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 { + + private var compositeDisposable: CompositeDisposable? = null + private var _billingService: IBillingService? = null + private val dialogManager = DialogManager(activity) + + private var paymentIntent: PaymentIntent? = null + private var productItem: IProductDetails? = null + private var orderId: String? = null + + init { + dialogManager.setCanceledOnClickOutside(false) + dialogManager.setCanceledOnClickBackKey(false) + (activity as? LifecycleOwner)?.lifecycle?.addObserver(this) + } + + private fun getBillingService(): IBillingService? { + if (_billingService == null) { + _billingService = IGoogleService.newBillingService(activity, this) + } + return _billingService + } + + override fun launchPayment(intent: PaymentIntent) { + logD("launchPayment() intent:${intent.productId}") + if (paymentIntent != null) { + SingleToastUtil.showToast(R.string.avroom_activity_roomblacklistactivity_015) + return + } + dialogManager.showProgressDialog(activity) + this.paymentIntent = intent + tryLaunch() + } + + private fun tryLaunch() { + val productId = paymentIntent?.productId + val productItem = productItem + logD("tryLaunch() productId:$productId productItem:$productItem") + if (productId == null) { + return + } + if (getBillingService()?.isServiceConnected() != true) { + logD("tryLaunch() isServiceConnected = false") + return + } + if (productItem == null) { + getBillingService()?.querySkuDetailsAsync(listOf(productId), this) + } else { + placeOrder(productItem) + } + } + + private fun placeOrder(productItem: IProductDetails) { + logD("placeOrder() productItem:${productItem.getProductId()}") + addDisposable(PayModel.get().placeOrder(productItem.getProductId()) + .subscribe( + { recordId: PayRecordId -> + if (paymentIntent?.productId == productItem.getProductId()) { + orderId = recordId.recordId + getBillingService()?.initiatePurchaseFlow( + productItem, + recordId.recordId + ) + } else { + logE("placeOrder() 意图发生改变") + } + } + ) { throwable: Throwable -> + callFailed(IPaymentClient.CODE_PLACE_ORDER_FAILED, throwable) + }) + } + + override fun onBillingClientSetupFinished() { + logD("onBillingClientSetupFinished()") + tryLaunch() + } + + override fun onPurchasesUpdated(purchases: List) { + 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 onConsumeFinished(token: String?, result: Int) { + logD("onConsumeFinished() token:${token} result:$result") + val orderId = orderId + if (orderId != null) { + callSuccess(orderId) + } + } + + override fun onFailedHandle(result: Int) { + logD("onFailedHandle() result:${result}") + callFailed(IPaymentClient.CODE_OTHER) + } + + override fun onProductDetailsResponse( + billingResult: IBillingResult, + productDetails: List + ) { + logD("onProductDetailsResponse() billingResult:${billingResult.getResponseCode()} productDetails:${productDetails.size}") + if (billingResult.isResponseOk()) { + val item = productDetails.firstOrNull { + it.getProductId() == paymentIntent?.productId + } + productItem = item + if (item != null) { + tryLaunch() + } else { + callFailed(IPaymentClient.CODE_NOT_FOUND) + } + } else { + callFailed(IPaymentClient.CODE_NOT_FOUND) + } + } + + private fun callSuccess(orderRecordId: String) { + logD("callSuccess() orderRecordId:${orderRecordId} productId:${paymentIntent?.productId}") + paymentIntent?.let { + it.listener.onResponse(PaymentResult.PaymentSuccess(it.productId, orderRecordId)) + } + paymentIntent = null + dialogManager.dismissDialog() + } + + private fun callFailed(code: Int, throwable: Throwable? = null) { + throwable?.printStackTrace() + logD("callFailed() code:${code} throwable:${throwable?.message}") + throwable?.let { + SingleToastUtil.showToast( + it.message + ) + } + paymentIntent?.let { + it.listener.onResponse(PaymentResult.PaymentFailed(it.productId, code, throwable)) + } + paymentIntent = null + dialogManager.dismissDialog() + } + + private fun getCompositeDisposable(): CompositeDisposable { + var disposable = compositeDisposable + if (disposable == null) { + disposable = CompositeDisposable() + compositeDisposable = disposable + } + return disposable + } + + private fun addDisposable(disposable: Disposable) { + getCompositeDisposable().add(disposable) + } + + override fun onCleared() { + logD("onCleared()") + paymentIntent = null + compositeDisposable?.dispose() + compositeDisposable = null + dialogManager.dismissDialog() + } + + 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() + } + + else -> { + + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/ui/wallet/payment/IPaymentClient.kt b/app/src/main/java/com/chwl/app/ui/wallet/payment/IPaymentClient.kt new file mode 100644 index 000000000..6d0e8b10e --- /dev/null +++ b/app/src/main/java/com/chwl/app/ui/wallet/payment/IPaymentClient.kt @@ -0,0 +1,33 @@ +package com.chwl.app.ui.wallet.payment + +import com.example.lib_utils.ICleared + +interface IPaymentClient : ICleared { + companion object { + // 支付成功 + const val CODE_SUCCESS = 200 + + // 未知异常原因 + const val CODE_OTHER = 0 + + // 服务不可用 + const val CODE_UNAVAILABLE = 1 + + // 查询订单信息失败 + const val CODE_NOT_FOUND = 2 + + // 预下单失败 + const val CODE_PLACE_ORDER_FAILED = 3 + + // 验证订单失败 + const val CODE_VERIFY_ORDER_FAILED = 4 + } + + fun launchPayment(intent: PaymentIntent) + + override fun onCleared() + + interface Listener { + fun onResponse(result: PaymentResult) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentIntent.kt b/app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentIntent.kt new file mode 100644 index 000000000..38911660e --- /dev/null +++ b/app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentIntent.kt @@ -0,0 +1,4 @@ +package com.chwl.app.ui.wallet.payment + +class PaymentIntent(val productId: String, val listener: IPaymentClient.Listener) { +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentResult.kt b/app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentResult.kt new file mode 100644 index 000000000..839869072 --- /dev/null +++ b/app/src/main/java/com/chwl/app/ui/wallet/payment/PaymentResult.kt @@ -0,0 +1,12 @@ +package com.chwl.app.ui.wallet.payment + +sealed class PaymentResult { + + data class PaymentSuccess(val productId: String, val orderId: String) : PaymentResult() + + data class PaymentFailed( + val productId: String, + val code: Int, + val exception: Throwable? = null + ) : PaymentResult() +} diff --git a/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewActivity.java b/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewActivity.java index be902be58..f268015f3 100644 --- a/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewActivity.java +++ b/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewActivity.java @@ -91,6 +91,8 @@ public class CommonWebViewActivity extends BaseActivity { private static final String POSITION = "position"; private int mPosition; + private JSInterface jsInterface; + public static void start(Context context, String url) { Intent intent = new Intent(context, CommonWebViewActivity.class); intent.putExtra("url", url); @@ -194,7 +196,7 @@ public class CommonWebViewActivity extends BaseActivity { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); } webView.getSettings().setTextZoom(100); - JSInterface jsInterface = new JSInterface(webView, this); + jsInterface = new JSInterface(webView, this); jsInterface.setPosition(mPosition); webView.addJavascriptInterface(jsInterface, "androidJsObj"); webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); @@ -441,6 +443,9 @@ public class CommonWebViewActivity extends BaseActivity { @Override protected void onDestroy() { + if (jsInterface != null) { + jsInterface.onCleared(); + } EventBus.getDefault().unregister(this); if (webViewCallBack != null) { diff --git a/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewFragment.java b/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewFragment.java index 193145e14..a327d7270 100644 --- a/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewFragment.java +++ b/app/src/main/java/com/chwl/app/ui/webview/CommonWebViewFragment.java @@ -1,8 +1,6 @@ package com.chwl.app.ui.webview; import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; import android.net.http.SslError; import android.os.Build; import android.text.TextUtils; @@ -16,7 +14,6 @@ import androidx.appcompat.app.AlertDialog; import com.orhanobut.logger.Logger; import com.chwl.app.R; import com.chwl.app.base.BaseBindingFragment; -import com.chwl.app.common.widget.dialog.BaseAlertDialogBuilder; import com.chwl.app.databinding.FragmentCommonWebViewBinding; import com.chwl.library.annatation.ActLayoutRes; import com.chwl.library.utils.ResUtil; @@ -26,6 +23,7 @@ public class CommonWebViewFragment extends BaseBindingFragment html js 与webview 交互接口

* Created by ${user} on 2017/11/6. */ -public class JSInterface { +public class JSInterface implements ICleared { private static final String TAG = JSInterface.class.getSimpleName(); private WebView mWebView; private CommonWebViewActivity mActivity; @@ -75,6 +83,7 @@ public class JSInterface { private MediaRecorder recorder; private File myRecAudioFile; private ExtAudioRecorder extAudioRecorder; + private GPaymentClient paymentClient; public JSInterface(WebView webView, CommonWebViewActivity activity) { mWebView = webView; @@ -87,6 +96,68 @@ public class JSInterface { this.context = context; } + private void loadUrl(String url) { + com.example.lib_utils.log.LogUtil.d("JSInterface", "loadUrl url:" + url, false); + if (Looper.myLooper() == Looper.getMainLooper()) { + if (mWebView != null) { + mWebView.loadUrl(url); + } + } else { + if (mActivity != null) { + mActivity.runOnUiThread(() -> { + if (mWebView != null) { + mWebView.loadUrl(url); + } + }); + } + } + } + + private void callJsWithJson(String name, Object params) { + if (name == null) { + return; + } + if (params != null) { + String jsonStr = JsonUtils.toJson(params); + loadUrl("javascript:" + name + "(" + jsonStr + ")"); + } else { + loadUrl("javascript:" + name + "()"); + } + } + + /** + * 拉起本地支付 + */ + @JavascriptInterface + public void openPayment(String productId) { + com.example.lib_utils.log.LogUtil.d("JSInterface", "openPayment productId:" + productId, false); + if (mActivity == null || TextUtils.isEmpty(productId)) { + return; + } + mActivity.runOnUiThread(() -> { + if (paymentClient == null) { + paymentClient = new GPaymentClient(mActivity); + } + PaymentIntent paymentIntent = new PaymentIntent(productId, new IPaymentClient.Listener() { + @Override + public void onResponse(@NonNull PaymentResult result) { + HashMap map = new HashMap<>(); + if (result instanceof PaymentResult.PaymentSuccess) { + map.put("orderId", ((PaymentResult.PaymentSuccess) result).getOrderId()); + map.put("productId", ((PaymentResult.PaymentSuccess) result).getProductId()); + map.put("code", 200); + } else if (result instanceof PaymentResult.PaymentFailed) { + map.put("code", ((PaymentResult.PaymentFailed) result).getCode()); + map.put("productId", ((PaymentResult.PaymentFailed) result).getProductId()); + } + callJsWithJson("openPaymentCallback", map); + } + }); + paymentClient.launchPayment(paymentIntent); + }); + } + + /** * 调转个人主页 * @@ -122,7 +193,7 @@ public class JSInterface { map.put(IReportConstants.MODULE, IReportConstants.MOLISTAR_PAY); ReportManager.get().reportEvent(IReportConstants.PAYPAGE_SHOW, map); // if (AppMetaDataUtil.getChannelID().equals(Constants.GOOGLE)) { - ChargeActivity.start(context); + ChargeActivity.start(context); // } else { // CommonWebViewActivity.start( // context, UriProvider.getOfficialPay( @@ -175,7 +246,7 @@ public class JSInterface { if (activity == null) { return; } - ShareInviteInfo info = new Gson().fromJson(json,ShareInviteInfo.class); + ShareInviteInfo info = new Gson().fromJson(json, ShareInviteInfo.class); if (info.getType() != null) { if (info.getType() == 1) { activity.runOnUiThread(() -> { @@ -556,4 +627,12 @@ public class JSInterface { PraiseModel.get().praise(Long.parseLong(uid), true).subscribe(); NimP2PMessageActivity.start(context, uid); } + + @Override + public void onCleared() { + ICleared.super.onCleared(); + if (paymentClient != null) { + paymentClient.onCleared(); + } + } }