1.增加信用卡支付信息展示

This commit is contained in:
2026-06-24 16:00:39 +08:00
parent cf9d9e8224
commit 27b9a6c885
16 changed files with 302 additions and 104 deletions

View File

@@ -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"

View File

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

View File

@@ -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<String>
/**
* 保存支付信息
*/
@FormUrlEncoded
@POST("pay$STRIPE_VERSION_NUMBER/savePaymentMethodInfo")
suspend fun savePaymentMethodInfo(
@Field("userId") orderId: String,
@Field("paymentMethodID") paymentMethodID: String
): BaseResponse<String>
/**
* Stripe支付 Rsa算法解密测试
*/

View File

@@ -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>(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>(INetworkService.BASE_URL) {
service.cancelStripeSubscription(orderId, subscriptionId, iccid)
}
/**
* 保存支付信息
*/
suspend fun savePaymentMethodInfo(userId: String, paymentMethodID: String) = getResult {
service.savePaymentMethodInfo(userId, paymentMethodID)
}
/**
* Stripe支付 Rsa算法解密测试
*/

View File

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

View File

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

View File

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

View File

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

View File

@@ -786,7 +786,7 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
MMKVUtil.putBoolean(MMKVKey.isGpsToGCJ02, !isOutOfChina)
}
showNoSetWifiDialog(this)
// showNoSetWifiDialog(this)
mHomeMapCommon.refreshPetCurrentLocation(latitude, longitude, isMoveCamera)
isMoveCamera = false

View File

@@ -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<FragmentCreditCardPaymentBinding>
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<CreditCardBean>().apply {
add(stripeMethodInfo)
mCreditCardAdapter.setData(this, true)
}
}
ViewUtil.instance.viewShow(mViewBinding.rvCreditCardPaymentList)
}
fun goEditCard(cardBean: CreditCardBean) {
@@ -181,7 +206,7 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
}
mViewBinding.apply {
tvCreditCardPaymentChoose.visibility = View.VISIBLE
btnCreditCardPaymentAdd.visibility = View.VISIBLE
btnCreditCardPaymentAdd.visibility = View.INVISIBLE
}
}
@@ -212,7 +237,8 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
// }
// }
if (it.resultCode == ResultCode.ResultCode_1) {
getCardData()
// getCardData()
getHostActivity(PaymentMethodActivity::class.java)?.getUserPaymentMethodID()
}
}
@@ -228,38 +254,43 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
// }
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<PaymentMethod> {
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<PaymentMethod> {
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
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -28,11 +28,12 @@ class SubscriptionPayViewModel : ViewModel() {
val mCreateStripeOrderLiveData = MutableLiveData<Result<PayResultBean>>()
val mCreatePaypalOrderLiveData = MutableLiveData<Result<PayResultBean>>()
val mPaymentMethodIDLiveData = MutableLiveData<Result<PaymentIDBean>>()
val mSavePaymentMethodIDLiveData = MutableLiveData<Result<String>>()
/**
* 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
}
}
}

View File

@@ -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" />

View File

@@ -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"