인 앱 결제 추가

This commit is contained in:
klaus 2024-03-25 17:40:17 +09:00
parent f0a8ca5823
commit 2101cdeb86
5 changed files with 139 additions and 125 deletions

View File

@ -40,8 +40,8 @@ android {
applicationId "kr.co.vividnext.sodalive"
minSdk 23
targetSdk 33
versionCode 41
versionName "1.8.16"
versionCode 46
versionName "1.8.21"
}
buildTypes {

View File

@ -85,7 +85,9 @@
<activity android:name=".settings.terms.TermsActivity" />
<activity android:name=".user.find_password.FindPasswordActivity" />
<activity android:name=".mypage.can.status.CanStatusActivity" />
<activity android:name=".mypage.can.charge.CanChargeActivity" />
<activity
android:name=".mypage.can.charge.CanChargeActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity android:name=".mypage.can.coupon.CanCouponActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" />

View File

@ -41,7 +41,8 @@ class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
false
)
changeFragment(IAP_TAG_KEY)
supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, CanChargeIapFragment()).commit()
}
override fun setupView() {
@ -59,13 +60,18 @@ class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
val tabs = binding.tabs
tabs.visibility = View.VISIBLE
tabs.addTab(tabs.newTab().setText("인 앱 결제").setTag(IAP_TAG_KEY))
tabs.addTab(tabs.newTab().setText("PG").setTag(PG_TAG_KEY))
tabs.addTab(tabs.newTab().setText("인 앱 결제"))
tabs.addTab(tabs.newTab().setText("PG"))
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
val tag = tab.tag as String
changeFragment(tag)
when (tab.position) {
0 -> supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, CanChargeIapFragment()).commit()
1 -> supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, CanChargePgFragment()).commit()
}
}
override fun onTabUnselected(tab: TabLayout.Tab) {
@ -79,33 +85,6 @@ class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
}
}
private fun changeFragment(tag: String) {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val currentFragment = fragmentManager.primaryNavigationFragment
if (currentFragment != null) {
fragmentTransaction.hide(currentFragment)
}
var fragment = fragmentManager.findFragmentByTag(tag)
if (fragment == null) {
fragment = if (tag == PG_TAG_KEY) {
CanChargePgFragment()
} else {
CanChargeIapFragment()
}
fragmentTransaction.add(R.id.fl_container, fragment, tag)
} else {
fragmentTransaction.show(fragment)
}
fragmentTransaction.setPrimaryNavigationFragment(fragment)
fragmentTransaction.setReorderingAllowed(true)
fragmentTransaction.commitNow()
}
fun selectCan(model: CanResponse) {
val intent = Intent(applicationContext, CanPaymentActivity::class.java)
intent.putExtra(Constants.EXTRA_CAN, model)
@ -123,9 +102,4 @@ class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
finish()
}
companion object {
const val IAP_TAG_KEY = "iap"
const val PG_TAG_KEY = "pg"
}
}

View File

@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.mypage.can.charge.iap
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.os.Handler
@ -13,10 +14,12 @@ 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.ConsumeParams
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.QueryProductDetailsParams
import com.android.billingclient.api.QueryPurchasesParams
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
@ -37,57 +40,10 @@ class CanChargeIapFragment : BaseFragment<FragmentCanChargeIapBinding>(
private val handler = Handler(Looper.getMainLooper())
private var selectedProductDetails: ProductDetails? = null
private val purchaseUpdateListener = PurchasesUpdatedListener { billingResult, purchases ->
if (
billingResult.responseCode == BillingClient.BillingResponseCode.OK &&
purchases != null &&
selectedProductDetails != null
) {
for (purchase in purchases) {
handlePurchase(purchase)
}
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
selectedProductDetails = null
handler.post {
Toast.makeText(requireActivity(), "구매를 취소했습니다.", Toast.LENGTH_LONG).show()
}
} else {
selectedProductDetails = null
Toast.makeText(
requireActivity(),
"구매를 하지 못했습니다.\n다시 시도해 주세요.",
Toast.LENGTH_LONG
).show()
}
}
private lateinit var purchaseUpdateListener: PurchasesUpdatedListener
private fun handlePurchase(purchase: Purchase) {
if (
purchase.purchaseState == Purchase.PurchaseState.PURCHASED &&
!purchase.isAcknowledged
) {
viewModel.chargeCan(
title = selectedProductDetails!!.name,
selectedProductDetails = selectedProductDetails!!,
purchase = purchase
) { chargeCan ->
handler.post {
Toast.makeText(
requireActivity(),
"캔이 충전되었습니다",
Toast.LENGTH_LONG
).show()
SharedPreferenceManager.can += chargeCan
val activity = requireActivity() as? CanChargeActivity
if (activity != null) {
activity.successIapCharge()
} else {
requireActivity().finish()
}
}
}
}
fun safeContext(): Context? {
return if (isAdded) context else null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -100,6 +56,31 @@ class CanChargeIapFragment : BaseFragment<FragmentCanChargeIapBinding>(
setupBillingClient()
}
override fun onStart() {
super.onStart()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingServiceDisconnected() {
viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
viewModel.setLoading(false)
}
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
queryAndConsumeUnconsumedPurchases()
} else {
viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
viewModel.setLoading(false)
}
}
})
}
override fun onStop() {
super.onStop()
billingClient.endConnection()
}
private fun bindData() {
viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) {
@ -162,39 +143,34 @@ class CanChargeIapFragment : BaseFragment<FragmentCanChargeIapBinding>(
}
private fun setupBillingClient() {
purchaseUpdateListener = PurchasesUpdatedListener { billingResult, purchases ->
handler.post {
if (
billingResult.responseCode == BillingClient.BillingResponseCode.OK &&
purchases != null &&
selectedProductDetails != null
) {
for (purchase in purchases) {
handlePurchase(purchase)
}
} else if (
billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED
) {
selectedProductDetails = null
viewModel.showToast("구매를 취소했습니다.")
} else {
selectedProductDetails = null
viewModel.showToast("구매를 하지 못했습니다.\n다시 시도해 주세요.")
}
}
}
billingClient = BillingClient.newBuilder(requireActivity())
.enablePendingPurchases()
.setListener(purchaseUpdateListener)
.build()
loadingDialog.show(screenWidth)
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingServiceDisconnected() {
handler.post {
Toast.makeText(
requireActivity(),
"인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.",
Toast.LENGTH_LONG
).show()
}
loadingDialog.dismiss()
}
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
queryAvailableCans()
} else {
handler.post {
Toast.makeText(
requireActivity(),
"인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.",
Toast.LENGTH_LONG
).show()
}
loadingDialog.dismiss()
}
}
})
}
@SuppressLint("NotifyDataSetChanged")
@ -224,16 +200,70 @@ class CanChargeIapFragment : BaseFragment<FragmentCanChargeIapBinding>(
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
handler.post { adapter.addItems(productDetailsList) }
} else {
handler.post {
Toast.makeText(
requireActivity(),
"인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.",
Toast.LENGTH_LONG
).show()
}
viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
}
handler.post { loadingDialog.dismiss() }
viewModel.setLoading(false)
}
}
private fun queryAndConsumeUnconsumedPurchases() {
val queryPurchaseParams = QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.INAPP)
.build()
billingClient.queryPurchasesAsync(
queryPurchaseParams
) { result, purchaseList ->
if (result.responseCode == BillingClient.BillingResponseCode.OK) {
if (purchaseList.isNotEmpty()) {
for (purchase in purchaseList) {
if (!purchase.isAcknowledged) {
consumePurchase(purchase)
}
}
} else {
queryAvailableCans()
}
}
}
}
private fun consumePurchase(purchase: Purchase) {
val params = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.consumeAsync(params) { billingResult, _ ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
queryAvailableCans()
}
}
}
private fun handlePurchase(purchase: Purchase) {
if (
purchase.purchaseState == Purchase.PurchaseState.PURCHASED &&
!purchase.isAcknowledged
) {
viewModel.chargeCan(
title = selectedProductDetails!!.name,
selectedProductDetails = selectedProductDetails!!,
purchase = purchase
) { chargeCan ->
handler.post {
viewModel.showToast("캔이 충전되었습니다")
SharedPreferenceManager.can += chargeCan
if (activity != null) {
if (activity as? CanChargeActivity != null) {
(activity as CanChargeActivity).successIapCharge()
} else {
requireActivity().finish()
}
}
}
}
}
}

View File

@ -71,4 +71,12 @@ class CanChargeIapViewModel(private val repository: CanRepository) : BaseViewMod
_toastLiveData.value = "구매를 하지 못했습니다.\n고객센터로 문의해 주시기 바랍니다."
}
}
fun showToast(message: String) {
_toastLiveData.value = message
}
fun setLoading(isLoading: Boolean) {
_isLoading.value = isLoading
}
}