From 6e3a4e112569b8b7d05ad040ba3a05358009cf13 Mon Sep 17 00:00:00 2001 From: klaus Date: Thu, 21 Mar 2024 04:14:52 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BA=94=20=EC=B6=A9=EC=A0=84=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20-=20=EC=9D=B8=20=EC=95=B1=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 7 +- app/src/main/AndroidManifest.xml | 1 + .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 2 + .../vividnext/sodalive/mypage/can/CanApi.kt | 14 ++ .../sodalive/mypage/can/CanRepository.kt | 12 + .../mypage/can/charge/CanChargeActivity.kt | 18 +- .../can/charge/iap/CanChargeIapAdapter.kt | 57 +++++ .../can/charge/iap/CanChargeIapFragment.kt | 221 ++++++++++++++++++ .../can/charge/iap/CanChargeIapViewModel.kt | 104 +++++++++ .../can/charge/iap/GoogleChargeRequest.kt | 18 ++ .../can/charge/pg/CanChargePgFragment.kt | 6 +- .../mypage/can/payment/CanPaymentActivity.kt | 4 +- .../bg_round_corner_10_4d9970ff_9970ff.xml | 8 - .../bg_round_corner_6_7_4d9970ff_9970ff.xml | 8 - .../res/layout/fragment_can_charge_iap.xml | 16 ++ app/src/main/res/layout/item_can_charge.xml | 30 +-- 16 files changed, 477 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/GoogleChargeRequest.kt delete mode 100644 app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml delete mode 100644 app/src/main/res/drawable/bg_round_corner_6_7_4d9970ff_9970ff.xml create mode 100644 app/src/main/res/layout/fragment_can_charge_iap.xml diff --git a/app/build.gradle b/app/build.gradle index 4511b31..3d2a1cf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,8 +40,8 @@ android { applicationId "kr.co.vividnext.sodalive" minSdk 23 targetSdk 33 - versionCode 27 - versionName "1.8.2" + versionCode 28 + versionName "1.8.3" } buildTypes { @@ -149,4 +149,7 @@ dependencies { annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' implementation "com.michalsvec:single-row-calednar:1.0.0" + + // google in-app-purchase + implementation "com.android.billingclient:billing-ktx:6.2.0" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7354621..3812e77 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 22885e8..1d8150e 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -74,6 +74,7 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthApi import kr.co.vividnext.sodalive.mypage.auth.AuthRepository import kr.co.vividnext.sodalive.mypage.can.CanApi import kr.co.vividnext.sodalive.mypage.can.CanRepository +import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapViewModel import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanChargePgViewModel import kr.co.vividnext.sodalive.mypage.can.coupon.CanCouponViewModel import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel @@ -231,6 +232,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { CreatorCommunityWriteViewModel(get()) } viewModel { CreatorCommunityModifyViewModel(get()) } viewModel { CanCouponViewModel(get()) } + viewModel { CanChargeIapViewModel(get()) } } private val repositoryModule = module { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt index 2cfb113..17d28cd 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt @@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.mypage.can import io.reactivex.rxjava3.core.Single import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest +import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleVerifyRequest import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeResponse @@ -17,6 +19,18 @@ import retrofit2.http.POST import retrofit2.http.Query interface CanApi { + @POST("/charge/google") + fun googleChargeCan( + @Body request: GoogleChargeRequest, + @Header("Authorization") authHeader: String + ): Single> + + @POST("/charge/google/verify") + fun googleChargeVerify( + @Body request: GoogleVerifyRequest, + @Header("Authorization") authHeader: String + ): Single> + @POST("/charge") fun chargeCan( @Body chargeRequest: ChargeRequest, diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt index 4c0b3ed..8ef5bdf 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt @@ -1,11 +1,23 @@ package kr.co.vividnext.sodalive.mypage.can +import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest +import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleVerifyRequest import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest import kr.co.vividnext.sodalive.mypage.can.coupon.UseCanCouponRequest import java.util.TimeZone class CanRepository(private val api: CanApi) { + fun googleChargeCan( + request: GoogleChargeRequest, + token: String + ) = api.googleChargeCan(request, authHeader = token) + + fun googleChargeVerify( + request: GoogleVerifyRequest, + token: String + ) = api.googleChargeVerify(request, authHeader = token) + fun chargeCan( chargeRequest: ChargeRequest, token: String diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt index 714f39e..0e4aefa 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt @@ -12,9 +12,11 @@ import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.databinding.ActivityCanChargeBinding +import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapFragment import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanChargePgFragment import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity +import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity class CanChargeActivity : BaseActivity( ActivityCanChargeBinding::inflate @@ -39,7 +41,7 @@ class CanChargeActivity : BaseActivity( false ) - changeFragment(PG_TAG_KEY) + changeFragment(IAP_TAG_KEY) } override fun setupView() { @@ -56,7 +58,7 @@ class CanChargeActivity : BaseActivity( if (SharedPreferenceManager.isAuth) { val tabs = binding.tabs - tabs.visibility = View.GONE + tabs.visibility = View.VISIBLE tabs.addTab(tabs.newTab().setText("인 앱 결제").setTag(IAP_TAG_KEY)) tabs.addTab(tabs.newTab().setText("PG").setTag(PG_TAG_KEY)) @@ -91,7 +93,7 @@ class CanChargeActivity : BaseActivity( fragment = if (tag == PG_TAG_KEY) { CanChargePgFragment() } else { - CanChargePgFragment() + CanChargeIapFragment() } fragmentTransaction.add(R.id.fl_container, fragment, tag) @@ -111,6 +113,16 @@ class CanChargeActivity : BaseActivity( activityResultLauncher.launch(intent) } + fun successIapCharge() { + if (gotoPrevPage) { + setResult(RESULT_OK) + finish() + } else { + val intent = Intent(applicationContext, CanStatusActivity::class.java) + startActivity(intent) + } + } + companion object { const val IAP_TAG_KEY = "iap" const val PG_TAG_KEY = "pg" diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapAdapter.kt new file mode 100644 index 0000000..cd0205e --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapAdapter.kt @@ -0,0 +1,57 @@ +package kr.co.vividnext.sodalive.mypage.can.charge.iap + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.recyclerview.widget.RecyclerView +import com.android.billingclient.api.ProductDetails +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.databinding.ItemCanChargeBinding +import kr.co.vividnext.sodalive.extensions.fontSpan + +class CanChargeIapAdapter( + private val onClick: (ProductDetails) -> Unit +) : RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val context: Context, + private val binding: ItemCanChargeBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: ProductDetails) { + binding.tvPrice.text = item.oneTimePurchaseOfferDetails?.formattedPrice + + val typeface = ResourcesCompat.getFont(context, R.font.gmarket_sans_medium) + binding.tvTitle.text = item.name.fontSpan( + typeface, + "캔" + ) + + binding.root.setOnClickListener { onClick(item) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemCanChargeBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.count() + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.addAll(items.sortedBy { it.description.toInt() }) + notifyDataSetChanged() + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt new file mode 100644 index 0000000..678b79f --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt @@ -0,0 +1,221 @@ +package kr.co.vividnext.sodalive.mypage.can.charge.iap + +import android.annotation.SuppressLint +import android.graphics.Rect +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.View +import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.android.billingclient.api.BillingClient +import com.android.billingclient.api.BillingClientStateListener +import com.android.billingclient.api.BillingFlowParams +import com.android.billingclient.api.BillingResult +import com.android.billingclient.api.ProductDetails +import com.android.billingclient.api.PurchasesUpdatedListener +import com.android.billingclient.api.QueryProductDetailsParams +import kr.co.vividnext.sodalive.base.BaseFragment +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.databinding.FragmentCanChargeIapBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity +import org.koin.android.ext.android.inject + +class CanChargeIapFragment : BaseFragment( + FragmentCanChargeIapBinding::inflate +) { + private val viewModel: CanChargeIapViewModel by inject() + + private lateinit var adapter: CanChargeIapAdapter + private lateinit var loadingDialog: LoadingDialog + private lateinit var billingClient: BillingClient + + private val handler = Handler(Looper.getMainLooper()) + + private var chargeId: Long = 0 + private var chargeCan: Int = 0 + + private val purchaseUpdateListener = PurchasesUpdatedListener { billingResult, purchases -> + if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) { + for (purchase in purchases) { + viewModel.verifyPayment( + productId = purchase.products[0], + purchaseToken = purchase.purchaseToken, + chargeId = chargeId + ) { + SharedPreferenceManager.can += chargeCan + (requireActivity() as CanChargeActivity).successIapCharge() + } + } + } else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) { + chargeId = 0 + chargeCan = 0 + showToast("구매를 취소했습니다.") + } else { + chargeId = 0 + chargeCan = 0 + showToast("구매를 하지 못했습니다.\n다시 시도해 주세요.") + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + loadingDialog = LoadingDialog(requireActivity(), layoutInflater) + + bindData() + setupRecyclerView() + setupBillingClient() + } + + private fun bindData() { + viewModel.isLoading.observe(viewLifecycleOwner) { + if (it) { + loadingDialog.show(screenWidth) + } else { + loadingDialog.dismiss() + } + } + } + + private fun setupRecyclerView() { + val recyclerView = binding.rvChargeCan + adapter = CanChargeIapAdapter { productDetails -> + chargeCan = productDetails.description.toInt() + viewModel.chargeCan( + title = productDetails.name, + chargeCan = chargeCan, + price = ( + productDetails.oneTimePurchaseOfferDetails?.priceAmountMicros ?: 0L + ).toDouble() / 1000000, + currencyCode = productDetails.oneTimePurchaseOfferDetails?.priceCurrencyCode + ?: "KRW" + ) { chargeId -> + this.chargeId = chargeId + launchPurchaseFlow(productDetails) + } + } + + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.VERTICAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.top = 13.3f.dpToPx().toInt() + outRect.bottom = 6.7f.dpToPx().toInt() + } + + adapter.itemCount - 1 -> { + outRect.top = 6.7f.dpToPx().toInt() + outRect.bottom = 26.7f.dpToPx().toInt() + } + + else -> { + outRect.top = 6.7f.dpToPx().toInt() + outRect.bottom = 6.7f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = adapter + } + + private fun setupBillingClient() { + billingClient = BillingClient.newBuilder(requireContext()) + .enablePendingPurchases() + .setListener(purchaseUpdateListener) + .build() + + loadingDialog.show(screenWidth) + billingClient.startConnection(object : BillingClientStateListener { + override fun onBillingServiceDisconnected() { + loadingDialog.dismiss() + } + + override fun onBillingSetupFinished(billingResult: BillingResult) { + if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { + queryAvailableCans() + } else { + showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.") + loadingDialog.dismiss() + } + } + }) + } + + @SuppressLint("NotifyDataSetChanged") + private fun queryAvailableCans() { + val skuList = listOf( + "${requireContext().packageName}.can_35", + "${requireContext().packageName}.can_55", + "${requireContext().packageName}.can_105", + "${requireContext().packageName}.can_350", + "${requireContext().packageName}.can_550", + "${requireContext().packageName}.can_1170" + ) + + val params = QueryProductDetailsParams.newBuilder() + .setProductList( + skuList.map { + QueryProductDetailsParams.Product.newBuilder() + .setProductId(it) + .setProductType(BillingClient.ProductType.INAPP) // Use SUBS for subscriptions + .build() + } + ) + .build() + + billingClient.queryProductDetailsAsync(params) { billingResult, productDetailsList -> + if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { + handler.post { + adapter.addItems(productDetailsList) + productDetailsList.forEach { + Toast.makeText( + requireContext(), + "${it.name}, ${it.description}, ${it.productId}, ${it.productType}, ${ + it.oneTimePurchaseOfferDetails?.priceAmountMicros + }, ${it.oneTimePurchaseOfferDetails?.priceCurrencyCode}", + Toast.LENGTH_LONG + ).show() + } + } + } else { + handler.post { showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.") } + } + + handler.post { loadingDialog.dismiss() } + } + } + + private fun launchPurchaseFlow(productDetails: ProductDetails) { + val billingFlowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList( + listOf( + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productDetails) + .build() + ) + ).build() + + billingClient.launchBillingFlow(requireActivity(), billingFlowParams) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt new file mode 100644 index 0000000..527d13b --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt @@ -0,0 +1,104 @@ +package kr.co.vividnext.sodalive.mypage.can.charge.iap + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.mypage.can.CanRepository + +class CanChargeIapViewModel(private val repository: CanRepository) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + fun chargeCan( + title: String, + chargeCan: Int, + price: Double, + currencyCode: String, + onSuccess: (Long) -> Unit + ) { + _isLoading.value = true + + compositeDisposable.add( + repository.googleChargeCan( + request = GoogleChargeRequest( + title = title, + chargeCan = chargeCan, + price = price, + currencyCode = currencyCode + ), + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success && it.data != null) { + onSuccess(it.data.chargeId) + } else { + if (it.message != null) { + _toastLiveData.value = it.message + } else { + _toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + } + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + } + ) + ) + } + + fun verifyPayment( + productId: String, + purchaseToken: String, + chargeId: Long, + onSuccess: () -> Unit + ) { + _isLoading.value = true + + compositeDisposable.add( + repository.googleChargeVerify( + request = GoogleVerifyRequest( + productId = productId, + purchaseToken = purchaseToken, + chargeId = chargeId + ), + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success) { + onSuccess() + } else { + if (it.message != null) { + _toastLiveData.value = it.message + } else { + _toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + } + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + } + ) + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/GoogleChargeRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/GoogleChargeRequest.kt new file mode 100644 index 0000000..80caf16 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/GoogleChargeRequest.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.mypage.can.charge.iap + +import com.google.gson.annotations.SerializedName +import kr.co.vividnext.sodalive.mypage.can.payment.PaymentGateway + +data class GoogleChargeRequest( + @SerializedName("title") val title: String, + @SerializedName("chargeCan") val chargeCan: Int, + @SerializedName("price") val price: Double, + @SerializedName("currencyCode") val currencyCode: String, + @SerializedName("paymentGateway") val paymentGateway: PaymentGateway = PaymentGateway.GOOGLE_IAP +) + +data class GoogleVerifyRequest( + @SerializedName("productId") val productId: String, + @SerializedName("purchaseToken") val purchaseToken: String, + @SerializedName("chargeId") val chargeId: Long +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/pg/CanChargePgFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/pg/CanChargePgFragment.kt index 1293b45..5354dcb 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/pg/CanChargePgFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/pg/CanChargePgFragment.kt @@ -81,16 +81,16 @@ class CanChargePgFragment : BaseFragment( @SuppressLint("NotifyDataSetChanged") private fun bindData() { - viewModel.canChargeLiveData.observe(this) { + viewModel.canChargeLiveData.observe(viewLifecycleOwner) { adapter.items.addAll(it) adapter.notifyDataSetChanged() } - viewModel.toastLiveData.observe(this) { + viewModel.toastLiveData.observe(viewLifecycleOwner) { it?.let { Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show() } } - viewModel.isLoading.observe(this) { + viewModel.isLoading.observe(viewLifecycleOwner) { if (it) { loadingDialog.show(screenWidth) } else { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt index 156dda0..fe8c9f8 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt @@ -140,8 +140,8 @@ class CanPaymentActivity : BaseActivity( R.font.gmarket_sans_bold ) - view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_9970ff)) - view.setBackgroundResource(R.drawable.bg_round_corner_10_4d9970ff_9970ff) + view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_3bb9f1)) + view.setBackgroundResource(R.drawable.bg_round_corner_10_13181b_3bb9f1) } private fun requestCharge() { diff --git a/app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml b/app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml deleted file mode 100644 index ffe4bab..0000000 --- a/app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/bg_round_corner_6_7_4d9970ff_9970ff.xml b/app/src/main/res/drawable/bg_round_corner_6_7_4d9970ff_9970ff.xml deleted file mode 100644 index 65d78c1..0000000 --- a/app/src/main/res/drawable/bg_round_corner_6_7_4d9970ff_9970ff.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/fragment_can_charge_iap.xml b/app/src/main/res/layout/fragment_can_charge_iap.xml new file mode 100644 index 0000000..7a37a39 --- /dev/null +++ b/app/src/main/res/layout/fragment_can_charge_iap.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/app/src/main/res/layout/item_can_charge.xml b/app/src/main/res/layout/item_can_charge.xml index e476b32..73c94e8 100644 --- a/app/src/main/res/layout/item_can_charge.xml +++ b/app/src/main/res/layout/item_can_charge.xml @@ -21,31 +21,15 @@ app:layout_constraintTop_toTopOf="parent" tools:text="5000 캔 + 1000 캔" /> - - - - - - - + app:layout_constraintTop_toTopOf="parent" + tools:text="₩3,300" />