feat:初步实现H5唤起支付逻辑(不完善,用于打内测包验证)

This commit is contained in:
max
2024-07-08 17:57:55 +08:00
parent 83af08076a
commit 3b407c0bd6
9 changed files with 376 additions and 13 deletions

View File

@@ -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);
}
/**

View File

@@ -84,11 +84,12 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), 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<ActivitySettingBinding>(), 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)
}

View File

@@ -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<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 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<IProductDetails>
) {
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 -> {
}
}
}
}

View File

@@ -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)
}
}

View File

@@ -0,0 +1,4 @@
package com.chwl.app.ui.wallet.payment
class PaymentIntent(val productId: String, val listener: IPaymentClient.Listener) {
}

View File

@@ -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()
}

View File

@@ -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) {

View File

@@ -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<FragmentCommonWeb
private WebView webView;
protected String url;
private JSInterface jsInterface;
@Override
public void initiate() {
@@ -37,7 +35,7 @@ public class CommonWebViewFragment extends BaseBindingFragment<FragmentCommonWeb
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}
webView.getSettings().setTextZoom(100);
JSInterface jsInterface = new JSInterface(webView, getActivity());
jsInterface = new JSInterface(webView, getActivity());
webView.addJavascriptInterface(jsInterface, "androidJsObj");
webView.setWebViewClient(new WebViewClient() {
@@ -113,4 +111,12 @@ public class CommonWebViewFragment extends BaseBindingFragment<FragmentCommonWeb
ShowWebView(url);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (jsInterface != null) {
jsInterface.onCleared();
}
}
}

View File

@@ -14,13 +14,21 @@ import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chwl.app.earn.activity.EarnRecordActivity;
import com.chwl.app.ui.invite.InviteImageHelper;
import com.chwl.app.ui.invite.ShareInviteDialog;
import com.chwl.app.ui.invite.ShareInviteInfo;
import com.chwl.app.ui.wallet.payment.GPaymentClient;
import com.chwl.app.ui.wallet.payment.IPaymentClient;
import com.chwl.app.ui.wallet.payment.PaymentIntent;
import com.chwl.app.ui.wallet.payment.PaymentResult;
import com.chwl.library.language.LanguageHelper;
import com.chwl.library.utils.json.JsonUtils;
import com.example.lib_utils.ICleared;
import com.google.gson.Gson;
import com.netease.nim.uikit.common.util.log.LogUtil;
import com.orhanobut.logger.Logger;
@@ -66,7 +74,7 @@ import java.util.HashMap;
* <p> html js 与webview 交互接口</p>
* 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<String, Object> 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();
}
}
}