diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a3ecd18..f03caba 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -89,6 +89,7 @@
android:name=".mypage.can.charge.CanChargeActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
+
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/detail/AudioContentDetailActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/detail/AudioContentDetailActivity.kt
index f042cc2..d308f48 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/detail/AudioContentDetailActivity.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/detail/AudioContentDetailActivity.kt
@@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.audio_content.detail
import android.annotation.SuppressLint
+import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -13,6 +14,8 @@ import android.view.View
import android.widget.RelativeLayout
import android.widget.SeekBar
import android.widget.Toast
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -37,13 +40,16 @@ import kr.co.vividnext.sodalive.common.Utils
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentDetailBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.extensions.dpToPx
+import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
import kr.co.vividnext.sodalive.mypage.auth.Auth
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
+import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempActivity
import kr.co.vividnext.sodalive.report.ReportType
import org.koin.android.ext.android.inject
+import kotlin.math.ceil
class AudioContentDetailActivity : BaseActivity(
ActivityAudioContentDetailBinding::inflate
@@ -63,6 +69,10 @@ class AudioContentDetailActivity : BaseActivity
+ private lateinit var audioContent: GetAudioContentDetailResponse
+ private lateinit var orderType: OrderType
+
@SuppressLint("SetTextI18n")
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
@@ -87,6 +97,14 @@ class AudioContentDetailActivity : BaseActivity(
+ 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()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentTempRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentTempRepository.kt
new file mode 100644
index 0000000..7ae714d
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentTempRepository.kt
@@ -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)
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentTempViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentTempViewModel.kt
new file mode 100644
index 0000000..a03a00d
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentTempViewModel.kt
@@ -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()
+ val paymentMethodLiveData: LiveData
+ get() = _paymentMethodLiveData
+
+ private var _isLoading = MutableLiveData(false)
+ val isLoading: LiveData
+ 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
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanTempApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanTempApi.kt
new file mode 100644
index 0000000..48ec651
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanTempApi.kt
@@ -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>
+
+ @POST("/charge/temp/verify")
+ fun verifyCharge(
+ @Body request: VerifyRequest,
+ @Header("Authorization") authHeader: String
+ ): Single>
+}
+
+data class ChargeTempRequest(
+ @SerializedName("can") val can: Int,
+ @SerializedName("price") val price: Int,
+ @SerializedName("paymentGateway") val paymentGateway: PaymentGateway
+)
diff --git a/app/src/main/res/layout/activity_audio_content_detail.xml b/app/src/main/res/layout/activity_audio_content_detail.xml
index 496d2e2..2852815 100644
--- a/app/src/main/res/layout/activity_audio_content_detail.xml
+++ b/app/src/main/res/layout/activity_audio_content_detail.xml
@@ -546,6 +546,7 @@
android:visibility="gone">
-
+ app:layout_constraintTop_toTopOf="parent">
-
+
+
+
+