From 27b9a6c885c04e6a61e0b5d1d9e8af6be2ac6013 Mon Sep 17 00:00:00 2001 From: yezhiqiu <123456@qq.com> Date: Wed, 24 Jun 2026 16:00:39 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E4=BF=A1=E7=94=A8=E5=8D=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E4=BF=A1=E6=81=AF=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 6 +- .../tracker/adapter/CreditCardAdapter.kt | 22 +++- .../tracker/retrofit2/INetworkService.kt | 17 ++- .../abbidot/tracker/retrofit2/NetworkApi.kt | 12 +- .../tracker/ui/activity/SplashActivity.kt | 7 +- .../ui/activity/pet/PetProfileActivity.kt | 6 +- .../subscribe/AddCreditCardPaymentActivity.kt | 116 ++++++++++++++++- .../subscribe/PaymentMethodActivity.kt | 23 +--- .../tracker/ui/fragment/map/MapV3Fragment.kt | 2 +- .../subscribe/CreditCardPaymentFragment.kt | 117 +++++++++++------- .../subscribe/PaypalPaymentFragment.kt | 5 +- .../tracker/util/SocketUtilManageV2.kt | 26 ++-- .../java/com/abbidot/tracker/util/Util.kt | 31 ++++- .../tracker/vm/SubscriptionPayViewModel.kt | 13 +- .../layout/fragment_credit_card_payment.xml | 1 + .../abbidot/baselibrary/constant/MMKVKey.kt | 2 - 16 files changed, 302 insertions(+), 104 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c0b8b4e..6d34233 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,9 +28,9 @@ android { applicationId "com.abbidot.tracker" minSdkVersion 23 targetSdkVersion 35 - versionCode 2211 - versionName "2.2.11" -// versionName "2.2.11-Beta1" + versionCode 2212 +// versionName "2.2.12" + versionName "2.2.12-Beta1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/abbidot/tracker/adapter/CreditCardAdapter.kt b/app/src/main/java/com/abbidot/tracker/adapter/CreditCardAdapter.kt index 88da45d..fe47719 100644 --- a/app/src/main/java/com/abbidot/tracker/adapter/CreditCardAdapter.kt +++ b/app/src/main/java/com/abbidot/tracker/adapter/CreditCardAdapter.kt @@ -34,7 +34,7 @@ class CreditCardAdapter( } primary.visibility = if (item.isPrimary) View.VISIBLE else View.GONE holder.getTextView(R.id.tv_credit_card_number_item).apply { - val formatCardNumber = Util.formatCardNumber(item.cardNumber) + val formatCardNumber = maskString(Util.formatCardNumber(item.cardNumber)) val cardLogoImageId = Util.getCreditCardType(item.cardNumber) text = formatCardNumber if (cardLogoImageId == 0) setCompoundDrawablesWithIntrinsicBounds( @@ -50,4 +50,24 @@ class CreditCardAdapter( ) } } + + private fun maskString(input: String): String { + if (input.isBlank()) return input + // 先去除原有所有空格,统一重新4位分隔 + val noSpace = input.replace(" ", "") + // 每4位分组加空格 + val splitStr = buildString { + for (i in noSpace.indices step 4) { + val end = minOf(i + 4, noSpace.length) + append(noSpace.substring(i, end)) + if (end != noSpace.length) append(" ") + } + } + // 再对分隔后的字符串脱敏(仅最后4位明文,其余非空格变*) + if (splitStr.length <= 4) return splitStr + val hidePart = splitStr.dropLast(4) + val showPart = splitStr.takeLast(4) + val maskHide = hidePart.map { if (it == ' ') ' ' else '*' }.joinToString("") + return maskHide + showPart + } } \ No newline at end of file diff --git a/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt b/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt index 8558b1d..19c24c7 100644 --- a/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt +++ b/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt @@ -56,8 +56,11 @@ interface INetworkService { //亚马逊服务器(国外ip) const val BASE_URL_ABROAD = "https://aws.abbidot.com/abbidot/" -// const val BASE_URL_ABROAD = "http://192.168.0.220:8080/abbidot/" -// const val BASE_URL_ABROAD = "$IP_SERVER:8443/abbidotServer/" + + //测试服务器 + const val BASE_URL_TEST = "https://aws.abbidot.com:8443/abbidot/" +// const val BASE_URL_TEST = "http://aws.abbidot.com:8089/abbidot/" + // const val BASE_URL_ABROAD = "http://192.168.0.220:8080/abbidot/" //亚马逊服务器(国内ip) // const val BASE_URL_CN = "$IP_SERVER:8443/abbidotServer/" @@ -1133,6 +1136,16 @@ interface INetworkService { @Field("iccid") iccid: String ): BaseResponse + /** + * 保存支付信息 + */ + @FormUrlEncoded + @POST("pay$STRIPE_VERSION_NUMBER/savePaymentMethodInfo") + suspend fun savePaymentMethodInfo( + @Field("userId") orderId: String, + @Field("paymentMethodID") paymentMethodID: String + ): BaseResponse + /** * Stripe支付 Rsa算法解密测试 */ diff --git a/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt b/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt index afaf1f6..5637eb6 100644 --- a/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt +++ b/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt @@ -1,8 +1,6 @@ package com.abbidot.tracker.retrofit2 -import com.abbidot.baselibrary.constant.MMKVKey import com.abbidot.baselibrary.network.base.BaseNetworkApi -import com.abbidot.baselibrary.util.MMKVUtil import com.abbidot.tracker.bean.FencesBean import com.abbidot.tracker.bean.PayResultBean import com.abbidot.tracker.bean.SetMealBean @@ -1008,9 +1006,8 @@ object NetworkApi : BaseNetworkApi(INetworkService.BASE_URL) { * Stripe支付创建消费用户 */ suspend fun createCustomer( - param: String, userId: String, userName: String, email: String + param: String, userId: String, userName: String, email: String, paymentMethodID: String ) = getResult { - val paymentMethodID = MMKVUtil.getString(MMKVKey.PaymentMethodID) service.createCustomer(param, userId, userName, email, paymentMethodID) } @@ -1062,6 +1059,13 @@ object NetworkApi : BaseNetworkApi(INetworkService.BASE_URL) { service.cancelStripeSubscription(orderId, subscriptionId, iccid) } + /** + * 保存支付信息 + */ + suspend fun savePaymentMethodInfo(userId: String, paymentMethodID: String) = getResult { + service.savePaymentMethodInfo(userId, paymentMethodID) + } + /** * Stripe支付 Rsa算法解密测试 */ diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/SplashActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/SplashActivity.kt index cb71df1..9bde85d 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/SplashActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/SplashActivity.kt @@ -81,11 +81,12 @@ class SplashActivity : QMUIActivity() { Utils.SECRET_KEY = Util.getMetadata(applicationContext, KeyNames.HTTP_REQUEST_KEY) INetworkService.apply { //国内外服务器切换域名 - BASE_URL = if (AppUtils.isChina()) { + BASE_URL = if (AppUtils.isDebug()) BASE_URL_TEST + else if (AppUtils.isChina()) BASE_URL_CN - } else { + else BASE_URL_ABROAD - } + } SRBleUtil.instance.init(application) diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/pet/PetProfileActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/pet/PetProfileActivity.kt index b2a253a..fd72f42 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/pet/PetProfileActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/pet/PetProfileActivity.kt @@ -267,7 +267,8 @@ class PetProfileActivity : private fun setPetData() { mViewBinding.apply { mPetBean?.let { - ViewUtil.instance.setPetTypeHead(mContext, + ViewUtil.instance.setPetTypeHead( + mContext, ilPetProfileHeadLayout.ilPetProfileHead.appHeadImage, it.imgurl, it.petType ) @@ -332,7 +333,8 @@ class PetProfileActivity : val dataBean = typeList[pos] mPetBean?.apply { petType = dataBean.menuType - if (TextUtils.isEmpty(mSelectPetHeadPath) && TextUtils.isEmpty(imgurl)) ViewUtil.instance.setPetTypeHead(mContext, + if (TextUtils.isEmpty(mSelectPetHeadPath) && TextUtils.isEmpty(imgurl)) ViewUtil.instance.setPetTypeHead( + mContext, mViewBinding.ilPetProfileHeadLayout.ilPetProfileHead.appHeadImage, imgurl, petType diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/AddCreditCardPaymentActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/AddCreditCardPaymentActivity.kt index 1c15f97..6703a53 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/AddCreditCardPaymentActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/AddCreditCardPaymentActivity.kt @@ -6,20 +6,30 @@ import android.text.InputType import android.text.TextUtils import android.view.View import android.view.View.OnFocusChangeListener +import androidx.activity.viewModels import androidx.appcompat.widget.AppCompatEditText import androidx.core.widget.addTextChangedListener import androidx.lifecycle.lifecycleScope import com.abbidot.baselibrary.constant.ResultCode import com.abbidot.baselibrary.util.AppUtils +import com.abbidot.baselibrary.util.LogUtil import com.abbidot.tracker.R import com.abbidot.tracker.base.BaseActivity import com.abbidot.tracker.base.BaseDialog import com.abbidot.tracker.bean.CreditCardBean +import com.abbidot.tracker.constant.ConstantInt import com.abbidot.tracker.constant.ConstantString +import com.abbidot.tracker.constant.GetResultCallback import com.abbidot.tracker.database.MyDatabase import com.abbidot.tracker.databinding.ActivityAddCreditCardPaymentBinding +import com.abbidot.tracker.util.ThirdPartyUtil import com.abbidot.tracker.util.Util import com.abbidot.tracker.util.ViewUtil +import com.abbidot.tracker.vm.SubscriptionPayViewModel +import com.stripe.android.ApiResultCallback +import com.stripe.android.Stripe +import com.stripe.android.model.PaymentMethod +import com.stripe.android.model.PaymentMethodCreateParams import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -28,6 +38,8 @@ class AddCreditCardPaymentActivity : BaseActivity(ActivityAddCreditCardPaymentBinding::inflate), OnFocusChangeListener { + private val mSubscriptionPayViewModel: SubscriptionPayViewModel by viewModels() + private var mCreditCard: CreditCardBean? = null private var mOldCard: CreditCardBean? = null private var isEdit = false @@ -47,7 +59,7 @@ class AddCreditCardPaymentActivity : if (isEdit) { mOldCard = mCreditCard setTopBarTitle(R.string.txt_edit_pay_method) - mRightImageButton = addRightImageButton(R.drawable.icon_delete_svg) +// mRightImageButton = addRightImageButton(R.drawable.icon_delete_svg) } else { setTopBarTitle(R.string.txt_add_payment) } @@ -116,6 +128,21 @@ class AddCreditCardPaymentActivity : } } + override fun liveDataObserve() { + mSubscriptionPayViewModel.mSavePaymentMethodIDLiveData.observe(this) { + dealRequestResult(it, object : GetResultCallback { + override fun onResult(any: Any) { + setResult(ResultCode.ResultCode_1, null) + showToast(R.string.txt_save_successful, isFinish = true) + } + + override fun onRequestError(exceptionCode: String?) { + setButtonEnabled(mViewBinding.btnSaveNewCreditCard, ConstantInt.Type1) + } + }) + } + } + /** * 编辑时显示还原内容 */ @@ -215,7 +242,6 @@ class AddCreditCardPaymentActivity : } lifecycleScope.launch(Dispatchers.IO) { - val list = MyDatabase.creditCardDao().findAll(MyDatabase.CREDIT_CARD_TABLE) var existCard: CreditCardBean? = null list?.apply { @@ -318,6 +344,90 @@ class AddCreditCardPaymentActivity : } } + private fun confirmSaveCardV2() { + mViewBinding.apply { + val cardNumber = ilAddCardNumber.etInputView2Content.text.toString().replace(" ", "") + val cardName = ilAddCardNameOnCard.etInputView2Content.text.toString() + val creditCardValidity = etAddCardMmDdContent.text.toString() + val cvv = etAddCardCvcContent.text.toString() + + if (TextUtils.isEmpty(cardNumber) || TextUtils.isEmpty(cardName) || TextUtils.isEmpty( + creditCardValidity + ) || TextUtils.isEmpty(cvv) + ) { + showToast(R.string.txt_not_null) + return + } + + if (!creditCardValidity.contains("/")) { + showToast(R.string.txt_card_validity_format) + return + } else { + val dates = creditCardValidity.split("/") + try { + if (dates[0].toInt() > 12) { + showToast(R.string.txt_card_validity_format) + return + } + } catch (e: Exception) { + showToast(R.string.txt_card_validity_format) + return + } + } + if (Util.getCreditCardType(cardNumber) == 0 || cardNumber.length < 9) { + showToast(R.string.txt_card_number_format) + return + } + + setButtonEnabled(btnSaveNewCreditCard, ConstantInt.Type0) + lifecycleScope.launch(Dispatchers.IO) { + mCreditCard?.let { + MyDatabase.creditCardDao().deleteAll(MyDatabase.CREDIT_CARD_TABLE) + it.cardNumber = cardNumber + it.cardName = cardName + it.expirationDate = creditCardValidity + it.cvv = cvv + val insertResult = MyDatabase.creditCardDao().insert(it) + if (insertResult > 0) { + if (isEdit) { + val dates = creditCardValidity.split("/") + // 用 PaymentMethodCreateParams 封装银行卡,不再直接new Card + val cardParams = + PaymentMethodCreateParams.Card.Builder().setNumber(cardNumber) + .setExpiryMonth(dates[0].toInt()) + .setExpiryYear(dates[1].toInt()).setCvc(cvv).build() + val paymentMethodParams = PaymentMethodCreateParams.create(cardParams) + val stripeKey = ThirdPartyUtil.instance.getStripeKey(mContext) + LogUtil.e("支付stripeKey=$stripeKey") + Stripe(mContext, stripeKey).createPaymentMethod( + paymentMethodParams, + callback = object : ApiResultCallback { + override fun onError(e: Exception) { + showToast(R.string.txt_missing_param) + setButtonEnabled(btnSaveNewCreditCard, ConstantInt.Type1) + LogUtil.e("PaymentMethodCreateParams,${e.message}") + } + + override fun onSuccess(result: PaymentMethod) { + LogUtil.e(result.toString()) + val paymentMethodID = "${result.id}" + mSubscriptionPayViewModel.savePaymentMethodInfo( + paymentMethodID + ) + } + }) + } else { + runOnUiThread { + setResult(ResultCode.ResultCode_1, null) + showToast(R.string.txt_save_successful, isFinish = true) + } + } + } + } + } + } + } + private fun deleteCard() { ViewUtil.instance.showDialog( mContext, R.string.txt_sure_delete_card, object : BaseDialog.OnDialogOkListener { @@ -338,7 +448,7 @@ class AddCreditCardPaymentActivity : override fun onClick(v: View?) { mViewBinding.apply { when (v!!) { - btnSaveNewCreditCard -> confirmSaveCard() + btnSaveNewCreditCard -> confirmSaveCardV2() llAddCardMmDdLayout -> setClickEditViewVisible(etAddCardMmDdContent) llAddCardCvcLayout -> setClickEditViewVisible(etAddCardCvcContent) ilAddCardNameOnCard.root -> setClickEditViewVisible(ilAddCardNameOnCard.etInputView2Content) diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/PaymentMethodActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/PaymentMethodActivity.kt index dbfb844..aa27aa5 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/PaymentMethodActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/PaymentMethodActivity.kt @@ -46,26 +46,6 @@ class PaymentMethodActivity : setTopBarTitle(R.string.txt_payment_method) setLeftBackImage(R.drawable.icon_white_back_svg) - // 用 PaymentMethodCreateParams 封装银行卡,不再直接new Card -// val cardParams = PaymentMethodCreateParams.Card.Builder() -// .setNumber("4242424242424242") -// .setExpiryMonth(12) -// .setExpiryYear(2028) -// .setCvc("123") -// .build() -// val paymentMethodParams = PaymentMethodCreateParams.create(cardParams) -// val stripeKey = ThirdPartyUtil.instance.getStripeKey(mContext) -// Stripe(mContext, stripeKey).createPaymentMethod( -// paymentMethodParams, callback = object : ApiResultCallback { -// override fun onError(e: Exception) { -// LogUtil.e("ApiResultCallback,${e.message}") -// } -// -// override fun onSuccess(result: PaymentMethod) { -// LogUtil.e(result.toString()) -// } -// }) - intent.extras?.apply { mOrderBean = Util.getParcelableAdaptive( intent, ConstantString.LkSetMeal, SubscriptionsOrderBean::class.java @@ -103,7 +83,7 @@ class PaymentMethodActivity : getUserPaymentMethodID() } - fun getUserPaymentMethodID(){ + fun getUserPaymentMethodID() { mSubscriptionPayViewModel.getUserPaymentMethodID() } @@ -117,6 +97,7 @@ class PaymentMethodActivity : override fun onResult(any: Any) { it.getOrNull()?.let { p -> mPaymentIDBean = p + (mFragments[0] as CreditCardPaymentFragment).getCardData(p.stripeMethodInfo) if (mFragments.size > 1) { (mFragments[1] as PaypalPaymentFragment).showPaypalInfo() } diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt index 0db8e36..9f1746e 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt @@ -786,7 +786,7 @@ class MapV3Fragment : BaseFragment(FragmentMapV3Binding::i MMKVUtil.putBoolean(MMKVKey.isGpsToGCJ02, !isOutOfChina) } - showNoSetWifiDialog(this) +// showNoSetWifiDialog(this) mHomeMapCommon.refreshPetCurrentLocation(latitude, longitude, isMoveCamera) isMoveCamera = false diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/CreditCardPaymentFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/CreditCardPaymentFragment.kt index 9203091..828452c 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/CreditCardPaymentFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/CreditCardPaymentFragment.kt @@ -2,16 +2,15 @@ package com.abbidot.tracker.ui.fragment.subscribe import android.content.Context import android.content.Intent +import android.text.TextUtils import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope -import com.abbidot.baselibrary.constant.MMKVKey import com.abbidot.baselibrary.constant.ResultCode import com.abbidot.baselibrary.list.BaseRecyclerAdapter import com.abbidot.baselibrary.util.AppUtils import com.abbidot.baselibrary.util.LogUtil -import com.abbidot.baselibrary.util.MMKVUtil import com.abbidot.tracker.R import com.abbidot.tracker.adapter.CreditCardAdapter import com.abbidot.tracker.base.BaseFragment @@ -90,20 +89,46 @@ class CreditCardPaymentFragment : BaseFragment setOnClickListenerViews(btnCreditCardPaymentAdd, btnMakePaymentCreditCard) } - getCardData() +// getCardData() } - private fun getCardData() { - viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { - val list = MyDatabase.creditCardDao().findAll(MyDatabase.CREDIT_CARD_TABLE) - list?.let { - mActivity?.runOnUiThread { - val sortedByList = list.sortedByDescending { l -> l.isPrimary }.toMutableList() - setCheckAndPrimaryState(sortedByList) - mCreditCardAdapter.setData(sortedByList, true) + fun getCardData(stripeMethodInfo: CreditCardBean) { + if (TextUtils.isEmpty(stripeMethodInfo.lastFourNumber)) { + viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + val list = MyDatabase.creditCardDao().findAll(MyDatabase.CREDIT_CARD_TABLE) + list?.let { + mActivity?.runOnUiThread { + val sortedByList = + list.sortedByDescending { l -> l.isPrimary }.toMutableList() + setCheckAndPrimaryState(sortedByList) + mCreditCardAdapter.setData(sortedByList, true) + } } } + } else { + stripeMethodInfo.apply { + expirationDate = "$expMonth/$expYear" + cardNumber = lastFourNumber + cardName = "****" + cvv = "****" + } + mViewBinding.apply { + tvCreditCardPaymentChoose.visibility = View.VISIBLE + btnCreditCardPaymentAdd.visibility = View.INVISIBLE + //卡管理 不需要点击效果 + if (null == getHostActivity(PaymentMethodActivity::class.java)?.mOrderBean) { + btnMakePaymentCreditCard.visibility = View.GONE + } else { + stripeMethodInfo.checked = true + btnMakePaymentCreditCard.visibility = View.VISIBLE + } + } + mutableListOf().apply { + add(stripeMethodInfo) + mCreditCardAdapter.setData(this, true) + } } + ViewUtil.instance.viewShow(mViewBinding.rvCreditCardPaymentList) } fun goEditCard(cardBean: CreditCardBean) { @@ -181,7 +206,7 @@ class CreditCardPaymentFragment : BaseFragment } mViewBinding.apply { tvCreditCardPaymentChoose.visibility = View.VISIBLE - btnCreditCardPaymentAdd.visibility = View.VISIBLE + btnCreditCardPaymentAdd.visibility = View.INVISIBLE } } @@ -212,7 +237,8 @@ class CreditCardPaymentFragment : BaseFragment // } // } if (it.resultCode == ResultCode.ResultCode_1) { - getCardData() +// getCardData() + getHostActivity(PaymentMethodActivity::class.java)?.getUserPaymentMethodID() } } @@ -228,38 +254,43 @@ class CreditCardPaymentFragment : BaseFragment // } private fun startCreditCardPayment() { - val list = mCreditCardAdapter.getData() - for (card in list) { - if (card.checked) { - showNoCancelableLoading(true) + getHostActivity(PaymentMethodActivity::class.java)?.let { + val list = mCreditCardAdapter.getData() + for (card in list) { + if (card.checked) { + showNoCancelableLoading(true) + if (null == it.mPaymentIDBean || TextUtils.isEmpty(it.mPaymentIDBean?.stripeMethodInfo?.lastFourNumber)) { + val dates = card.expirationDate.split("/") + // 用 PaymentMethodCreateParams 封装银行卡,不再直接new Card + val cardParams = + PaymentMethodCreateParams.Card.Builder().setNumber(card.cardNumber) + .setExpiryMonth(dates[0].toInt()).setExpiryYear(dates[1].toInt()) + .setCvc(card.cvv).build() + val paymentMethodParams = PaymentMethodCreateParams.create(cardParams) + val stripeKey = ThirdPartyUtil.instance.getStripeKey(mContext!!) + LogUtil.e("支付stripeKey=$stripeKey") + Stripe(mContext!!, stripeKey).createPaymentMethod( + paymentMethodParams, + callback = object : ApiResultCallback { + override fun onError(e: Exception) { + showToast(R.string.txt_missing_param) + showNoCancelableLoading(false) + LogUtil.e("ApiResultCallback,${e.message}") + } - val dates = card.expirationDate.split("/") - // 用 PaymentMethodCreateParams 封装银行卡,不再直接new Card - val cardParams = PaymentMethodCreateParams.Card.Builder() - .setNumber(card.cardNumber) - .setExpiryMonth(dates[0].toInt()) - .setExpiryYear(dates[1].toInt()) - .setCvc(card.cvv) - .build() - val paymentMethodParams = PaymentMethodCreateParams.create(cardParams) - val stripeKey = ThirdPartyUtil.instance.getStripeKey(mContext!!) - Stripe(mContext!!, stripeKey).createPaymentMethod( - paymentMethodParams, callback = object : ApiResultCallback { - override fun onError(e: Exception) { - showToast(R.string.txt_missing_param) - showNoCancelableLoading(false) - LogUtil.e("ApiResultCallback,${e.message}") - } - - override fun onSuccess(result: PaymentMethod) { - LogUtil.e(result.toString()) - val paymentMethodID = "${result.id}" - MMKVUtil.putString(MMKVKey.PaymentMethodID, paymentMethodID) - mSubscriptionPayViewModel.createCustomer(card) - } - }) - break + override fun onSuccess(result: PaymentMethod) { + LogUtil.e(result.toString()) + val paymentMethodID = "${result.id}" + mSubscriptionPayViewModel.createCustomer(card, paymentMethodID) + } + }) + } else { + mSubscriptionPayViewModel.createCustomer(card, "") + } + break + } } + } } diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/PaypalPaymentFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/PaypalPaymentFragment.kt index f6a6ed7..47f7a9f 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/PaypalPaymentFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/subscribe/PaypalPaymentFragment.kt @@ -39,14 +39,17 @@ class PaypalPaymentFragment : tvPaypalPaymentGoTip.visibility = View.GONE btnSureRedirectPaypal.visibility = View.GONE ivSureRedirectPaypalBg.visibility = View.GONE + showPaypalInfo() } else { tvNoPaypalPaymentEmail.visibility = View.GONE } - showPaypalInfo() } } fun showPaypalInfo() { + if (null == getHostActivity(PaymentMethodActivity::class.java)?.mOrderBean) { + return + } getHostActivity(PaymentMethodActivity::class.java)?.let { it.mPaymentIDBean?.let { pId -> mViewBinding.tvPaypalPaymentEmail.text = pId.paypalMethodInfo.email diff --git a/app/src/main/java/com/abbidot/tracker/util/SocketUtilManageV2.kt b/app/src/main/java/com/abbidot/tracker/util/SocketUtilManageV2.kt index 488348f..35b398f 100644 --- a/app/src/main/java/com/abbidot/tracker/util/SocketUtilManageV2.kt +++ b/app/src/main/java/com/abbidot/tracker/util/SocketUtilManageV2.kt @@ -28,17 +28,21 @@ class SocketUtilManageV2 { @Synchronized fun initEasySocket(context: Context, mac: String, data: ByteArray) { - val data1 = SRBleCmdUtil.instance.byteToInt(data[1]) - val data2 = SRBleCmdUtil.instance.byteToInt(data[2]) - val data3 = SRBleCmdUtil.instance.byteToInt(data[3]) - val data4 = SRBleCmdUtil.instance.byteToInt(data[4]) - val ip = "$data1.$data2.$data3.$data4" - val port = SRBleCmdUtil.instance.byte2ArrayToInt(data.sliceArray(5..6)) - val socketAddress = "$ip:$port" - LogUtil.e(socketAddress) - mMac = mac - mNeedSendMessage = data.sliceArray(8..data.size - 2) - initEasySocket(context, ip, port) + try { + val data1 = SRBleCmdUtil.instance.byteToInt(data[1]) + val data2 = SRBleCmdUtil.instance.byteToInt(data[2]) + val data3 = SRBleCmdUtil.instance.byteToInt(data[3]) + val data4 = SRBleCmdUtil.instance.byteToInt(data[4]) + val ip = "$data1.$data2.$data3.$data4" + val port = SRBleCmdUtil.instance.byte2ArrayToInt(data.sliceArray(5..6)) + val socketAddress = "$ip:$port" + LogUtil.e(socketAddress) + mMac = mac + mNeedSendMessage = data.sliceArray(8..data.size - 2) + initEasySocket(context, ip, port) + } catch (e: Exception) { + LogUtil.e("initEasySocket 异常:${e.message}") + } } /** diff --git a/app/src/main/java/com/abbidot/tracker/util/Util.kt b/app/src/main/java/com/abbidot/tracker/util/Util.kt index 80812b9..1e41dca 100644 --- a/app/src/main/java/com/abbidot/tracker/util/Util.kt +++ b/app/src/main/java/com/abbidot/tracker/util/Util.kt @@ -873,17 +873,38 @@ class Util { /** - * 限制limitLetterLength个字母或limitLetterLength/2个中文,限制空格输入 - * 截断到最大limitLetterLength单位 - * @param limitLetterLength 限制字母的个数 + * 字符串截断 + 空格规则: + * 1. 首位不能是空格 + * 2. 首位之后,只允许连续 1 个空格 + * 3. 按[字符计算长度]截断,不超过限制长度 + * @param limitLetterLength 最大字符单位长度 + * @return 处理后的字符串 */ fun String.limitLetterChinese(limitLetterLength: Int): String { val sb = StringBuilder() + // 标记:上一个字符是否是空格 + var lastIsSpace = false + for (c in this) { - if (c == ' ') break + // 规则1:首位直接拦截空格 + if (sb.isEmpty() && c == ' ') { + continue + } + + // 规则2:已存在一个连续空格,再遇到空格直接跳过(只保留单个空格) + if (c == ' ' && lastIsSpace) { + continue + } + + // 拼接后判断是否超出长度限制,超出则终止 val temp = sb.toString() + c - if (temp.calcLengthUnit() > limitLetterLength) break + if (temp.calcLengthUnit() > limitLetterLength) { + break + } + + // 追加字符,并更新空格标记 sb.append(c) + lastIsSpace = (c == ' ') } return sb.toString() } diff --git a/app/src/main/java/com/abbidot/tracker/vm/SubscriptionPayViewModel.kt b/app/src/main/java/com/abbidot/tracker/vm/SubscriptionPayViewModel.kt index 2d9519b..f9a2d4e 100644 --- a/app/src/main/java/com/abbidot/tracker/vm/SubscriptionPayViewModel.kt +++ b/app/src/main/java/com/abbidot/tracker/vm/SubscriptionPayViewModel.kt @@ -28,11 +28,12 @@ class SubscriptionPayViewModel : ViewModel() { val mCreateStripeOrderLiveData = MutableLiveData>() val mCreatePaypalOrderLiveData = MutableLiveData>() val mPaymentMethodIDLiveData = MutableLiveData>() + val mSavePaymentMethodIDLiveData = MutableLiveData>() /** * Stripe支付创建消费用户 */ - fun createCustomer(card: CreditCardBean) { + fun createCustomer(card: CreditCardBean,paymentMethodID: String) { // activity.showLoading(true) viewModelScope.launch(Dispatchers.IO) { val userId = MMKVUtil.getString(MMKVKey.UserId) @@ -52,7 +53,7 @@ class SubscriptionPayViewModel : ViewModel() { val rsaKey = RSA.encrypt(cardJso, b) LogUtil.e(cardJso) - val result = NetworkApi.createCustomer(rsaKey, userId, userName, email) + val result = NetworkApi.createCustomer(rsaKey, userId, userName, email,paymentMethodID) mSubscriptionCustomerLiveData.postValue(result) } } @@ -80,4 +81,12 @@ class SubscriptionPayViewModel : ViewModel() { mPaymentMethodIDLiveData.value = result } } + + fun savePaymentMethodInfo(paymentMethodID: String) { + viewModelScope.launch { + val userId = MMKVUtil.getString(MMKVKey.UserId) + val result = NetworkApi.savePaymentMethodInfo(userId, paymentMethodID) + mSavePaymentMethodIDLiveData.value = result + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_credit_card_payment.xml b/app/src/main/res/layout/fragment_credit_card_payment.xml index 7444946..cbd09d4 100644 --- a/app/src/main/res/layout/fragment_credit_card_payment.xml +++ b/app/src/main/res/layout/fragment_credit_card_payment.xml @@ -41,6 +41,7 @@ android:id="@+id/rv_credit_card_payment_list" android:layout_width="match_parent" android:layout_height="match_parent" + android:visibility="gone" android:layout_above="@id/btn_make_payment_credit_card" android:layout_below="@id/btn_credit_card_payment_add" android:layout_marginTop="@dimen/dp_12" /> diff --git a/baselibrary/src/main/java/com/abbidot/baselibrary/constant/MMKVKey.kt b/baselibrary/src/main/java/com/abbidot/baselibrary/constant/MMKVKey.kt index e8ff141..3b080ac 100644 --- a/baselibrary/src/main/java/com/abbidot/baselibrary/constant/MMKVKey.kt +++ b/baselibrary/src/main/java/com/abbidot/baselibrary/constant/MMKVKey.kt @@ -38,7 +38,6 @@ import androidx.annotation.StringDef MMKVKey.MapType, MMKVKey.ShowFence, MMKVKey.ShowDashedLine, - MMKVKey.PaymentMethodID, MMKVKey.ShowAllPet, MMKVKey.isCrash, MMKVKey.AvailableOrder, @@ -60,7 +59,6 @@ annotation class MMKVKey { const val Phone = "phone" const val ActivityGoal = "activityGoal" const val Location = "location" - const val PaymentMethodID = "paymentMethodID" const val Gender = "gender" const val BirthdayDate = "birthdayDate" const val CountryCode = "countryCode"