콘텐츠 상세, 콘텐츠 구매

- pg 테스트 계정의 경우 캔이 아닌 원으로 표시되도록 하고 콘텐츠 구매시 바로 결제 후 구매 되도록 수정
This commit is contained in:
2024-05-20 15:37:46 +09:00
parent ba4d707d45
commit b220b8bce4
13 changed files with 523 additions and 35 deletions

View File

@@ -0,0 +1,237 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.google.gson.Gson
import com.orhanobut.logger.Logger
import kr.co.bootpay.android.Bootpay
import kr.co.bootpay.android.events.BootpayEventListener
import kr.co.bootpay.android.models.BootUser
import kr.co.bootpay.android.models.Payload
import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityCanPaymentBinding
import kr.co.vividnext.sodalive.extensions.fontSpan
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import org.koin.android.ext.android.inject
class CanPaymentTempActivity : BaseActivity<ActivityCanPaymentBinding>(
ActivityCanPaymentBinding::inflate
) {
enum class PaymentMethod(val method: String) {
CARD("카드"), BANK("계좌이체"), PHONE("휴대폰")
}
private val viewModel: CanPaymentTempViewModel by inject()
private val handler = Handler(Looper.getMainLooper())
private lateinit var loadingDialog: LoadingDialog
private lateinit var title: String
private var can: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
}
@SuppressLint("SetTextI18n")
override fun setupView() {
title = intent.getStringExtra("title") ?: ""
can = intent.getIntExtra("can", 0)
loadingDialog = LoadingDialog(this, layoutInflater)
if (title.isBlank() || can <= 0) {
Toast.makeText(
applicationContext,
"다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.",
Toast.LENGTH_LONG
).show()
finish()
}
binding.toolbar.tvBack.text = "결제하기"
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.ivCan.visibility = View.GONE
binding.tvAlert.visibility = View.GONE
binding.tvChargeCanTitle.text = title
binding.tvPrice.text = (can * 110).moneyFormat()
binding.tvPaymentPrice.text = "${(can * 110).moneyFormat()}".fontSpan(
ResourcesCompat.getFont(applicationContext, R.font.gmarket_sans_light),
""
)
binding.tvAgree.setOnClickListener {
binding.tvAgree.isSelected = !binding.tvAgree.isSelected
}
binding.tvPayment.setOnClickListener {
if (viewModel.paymentMethodLiveData.value == null) {
Toast.makeText(
applicationContext,
"결제수단을 선택해 주세요.",
Toast.LENGTH_LONG
).show()
return@setOnClickListener
}
if (!binding.tvAgree.isSelected) {
Toast.makeText(
applicationContext,
"결제 진행에 동의하셔야 결제가 가능합니다.",
Toast.LENGTH_LONG
).show()
return@setOnClickListener
}
requestCharge()
}
binding.tvMethodCard.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.CARD) }
binding.tvMethodBank.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.BANK) }
binding.tvMethodPhone.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.PHONE) }
}
private fun allPaymentMethodSelectFalse() {
paymentMethodSelectFalse(binding.tvMethodBank)
paymentMethodSelectFalse(binding.tvMethodCard)
paymentMethodSelectFalse(binding.tvMethodPhone)
}
private fun paymentMethodSelectFalse(view: TextView) {
view.typeface = ResourcesCompat.getFont(
applicationContext,
R.font.gmarket_sans_medium
)
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_eeeeee))
view.setBackgroundResource(R.drawable.bg_round_corner_10_232323_777777)
}
private fun paymentMethodSelect(view: TextView) {
view.typeface = ResourcesCompat.getFont(
applicationContext,
R.font.gmarket_sans_bold
)
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_3bb9f1))
view.setBackgroundResource(R.drawable.bg_round_corner_10_13181b_3bb9f1)
}
private fun requestCharge() {
viewModel.chargeCan(
can = can,
paymentGateway = PaymentGateway.PG,
onSuccess = {
requestPayment(chargeId = it)
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
private fun requestPayment(chargeId: Long) {
val user = BootUser()
.setId("${SharedPreferenceManager.userId}")
.setUsername(SharedPreferenceManager.nickname)
val payload = Payload()
.setApplicationId(BuildConfig.BOOTPAY_APP_ID)
.setOrderId("$chargeId")
.setOrderName(title)
.setPrice((can * 110).toDouble())
.setTaxFree(0.toDouble())
.setPg("세틀뱅크")
.setMethod(viewModel.paymentMethodLiveData.value!!.method)
.setUser(user)
Bootpay.init(this, this)
.setPayload(payload)
.setEventListener(object : BootpayEventListener {
override fun onCancel(data: String) {
Logger.e("onCancel: $data")
}
override fun onError(data: String) {
Logger.e("onError: $data")
Toast.makeText(applicationContext, data, Toast.LENGTH_LONG).show()
}
override fun onClose() {
Logger.e("onClose")
Bootpay.removePaymentWindow()
}
override fun onIssued(data: String) {
Logger.e("onIssued: $data")
}
override fun onConfirm(data: String): Boolean {
Logger.e("onConfirm: $data")
return true
}
override fun onDone(data: String) {
Logger.e("onDone: $data")
handler.post {
verifyPayment(data)
Bootpay.removePaymentWindow()
}
}
}).requestPayment()
}
private fun verifyPayment(data: String) {
val bootpayResponse = Gson().fromJson(data, BootpayResponse::class.java)
val request = VerifyRequest(bootpayResponse.data.receiptId, bootpayResponse.data.orderId)
viewModel.verify(
request,
onSuccess = {
SharedPreferenceManager.can += can
setResult(RESULT_OK)
finish()
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
private fun bindData() {
viewModel.paymentMethodLiveData.observe(this) {
allPaymentMethodSelectFalse()
if (it != null) {
when (it) {
PaymentMethod.CARD -> paymentMethodSelect(binding.tvMethodCard)
PaymentMethod.BANK -> paymentMethodSelect(binding.tvMethodBank)
PaymentMethod.PHONE -> paymentMethodSelect(binding.tvMethodPhone)
}
}
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
}
}

View File

@@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
class CanPaymentTempRepository(private val api: CanTempApi) {
fun chargeCan(
request: ChargeTempRequest,
token: String
) = api.chargeCan(request, authHeader = token)
fun verify(
request: VerifyRequest,
token: String
) = api.verifyCharge(request, authHeader = token)
}

View File

@@ -0,0 +1,88 @@
package kr.co.vividnext.sodalive.mypage.can.payment
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.charge.pg.VerifyRequest
class CanPaymentTempViewModel(private val repository: CanPaymentTempRepository) : BaseViewModel() {
private val _paymentMethodLiveData = MutableLiveData<CanPaymentTempActivity.PaymentMethod?>()
val paymentMethodLiveData: LiveData<CanPaymentTempActivity.PaymentMethod?>
get() = _paymentMethodLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
fun chargeCan(
can: Int,
paymentGateway: PaymentGateway,
onSuccess: (Long) -> Unit,
onFailure: (String) -> Unit
) {
_isLoading.value = true
val request = ChargeTempRequest(can, can * 110, paymentGateway)
compositeDisposable.add(
repository.chargeCan(request, "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) {
onFailure(it.message)
} else {
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun verify(request: VerifyRequest, onSuccess: () -> Unit, onFailure: (String) -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.verify(
request = request,
"Bearer ${SharedPreferenceManager.token}"
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
onSuccess()
} else {
if (it.message != null) {
onFailure(it.message)
} else {
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun setPaymentMethod(paymentMethod: CanPaymentTempActivity.PaymentMethod) {
_paymentMethodLiveData.value = paymentMethod
}
}

View File

@@ -0,0 +1,30 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import com.google.gson.annotations.SerializedName
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST
interface CanTempApi {
@POST("/charge/temp")
fun chargeCan(
@Body chargeRequest: ChargeTempRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<ChargeResponse>>
@POST("/charge/temp/verify")
fun verifyCharge(
@Body request: VerifyRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
}
data class ChargeTempRequest(
@SerializedName("can") val can: Int,
@SerializedName("price") val price: Int,
@SerializedName("paymentGateway") val paymentGateway: PaymentGateway
)