feat(can-charge): 이롬넷(Payverse) 통합결제 추가

This commit is contained in:
2025-10-01 01:47:42 +09:00
parent 2635b7d3c3
commit 662f18bceb
12 changed files with 366 additions and 64 deletions

View File

@@ -54,6 +54,7 @@ android {
buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"'
buildConfigField 'String', 'KAKAO_APP_KEY', '"231cf78acfa8252fca38b9eedf87c5cb"'
buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com"'
buildConfigField 'String', 'APPSCHEME', '"voiceon"'
manifestPlaceholders = [
URISCHEME : "voiceon",
APPLINK_HOST : "voiceon.onelink.me",
@@ -79,6 +80,7 @@ android {
buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"'
buildConfigField 'String', 'KAKAO_APP_KEY', '"20cf19413d63bfdfd30e8e6dff933d33"'
buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com"'
buildConfigField 'String', 'APPSCHEME', '"voiceon-test"'
manifestPlaceholders = [
URISCHEME : "voiceon-test",
APPLINK_HOST : "voiceon-test.onelink.me",

View File

@@ -85,6 +85,15 @@
<data android:scheme="${URISCHEME}" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- PayVerse 리다이렉트에 등록할 커스텀 스킴/호스트 -->
<data android:scheme="${URISCHEME}"
android:host="payverse"
android:path="/result"/>
</intent-filter>
</activity>
<activity
android:name=".splash.SplashActivity"
@@ -107,7 +116,9 @@
<activity
android:name=".mypage.can.charge.CanChargeActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity
android:name=".mypage.can.payment.CanPaymentActivity"
android:launchMode="singleTop" />
<activity android:name=".mypage.can.payment.CanPaymentTempActivity" />
<activity android:name=".mypage.can.coupon.CanCouponActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" />

View File

@@ -0,0 +1,25 @@
<!-- app/src/main/assets/payverse_starter_debug.html -->
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<!-- PayVerse SDK -->
<script src="https://ui.payverseglobal.com/js/payments.js"></script>
</head>
<body>
<script>
// 안드로이드에서 JSON 문자열을 넘기면 이 함수를 호출
function startPay(payloadJson) {
try {
const p = JSON.parse(payloadJson);
// 즉시 실행: 페이지가 열리자마자 결제창 시작
window.payVerse.requestUI(p);
} catch (e) {
console.error('startPay error', e);
alert('결제 초기화에 실패했습니다.');
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,25 @@
<!-- app/src/main/assets/payverse_starter_debug.html -->
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<!-- PayVerse SDK -->
<script src="https://ui-snd.payverseglobal.com/js/payments.js"></script>
</head>
<body>
<script>
// 안드로이드에서 JSON 문자열을 넘기면 이 함수를 호출
function startPay(payloadJson) {
try {
const p = JSON.parse(payloadJson);
// 즉시 실행: 페이지가 열리자마자 결제창 시작
window.payVerse.requestUI(p);
} catch (e) {
console.error('startPay error', e);
alert('결제 초기화에 실패했습니다.');
}
}
</script>
</body>
</html>

View File

@@ -1,19 +1,39 @@
package kr.co.vividnext.sodalive.main
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity
import kr.co.vividnext.sodalive.splash.SplashActivity
class DeepLinkActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val data: Uri? = intent?.data
if (data != null && data.scheme != null) {
val host = data.host
val path = data.path
// Payverse 결제 완료 딥링크라면 결제 화면으로 직접 전달
if (host == "payverse" && path == "/result") {
val paymentIntent = Intent(this, CanPaymentActivity::class.java).apply {
action = Intent.ACTION_VIEW
this.data = data
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
startActivity(paymentIntent)
finish()
return
}
}
// 그 외 일반 딥링크는 기존처럼 Splash로 위임
startActivity(
Intent(applicationContext, SplashActivity::class.java).apply {
data = intent.data
setData(intent.data)
}
)

View File

@@ -8,6 +8,9 @@ import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.coupon.UseCanCouponRequest
import kr.co.vividnext.sodalive.mypage.can.payment.payverse.PayverseChargeRequest
import kr.co.vividnext.sodalive.mypage.can.payment.payverse.PayverseChargeResponse
import kr.co.vividnext.sodalive.mypage.can.payment.payverse.PayverseVerifyRequest
import kr.co.vividnext.sodalive.mypage.can.status.GetCanStatusResponse
import kr.co.vividnext.sodalive.mypage.can.status.charge.GetCanChargeStatusResponseItem
import kr.co.vividnext.sodalive.mypage.can.status.use.GetCanUseStatusResponseItem
@@ -42,6 +45,18 @@ interface CanApi {
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@POST("/charge/payverse")
fun payverseChargeCan(
@Body request: PayverseChargeRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<PayverseChargeResponse>>
@POST("/charge/payverse/verify")
fun payverseVerifyCharge(
@Body request: PayverseVerifyRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/can")
fun getCans(
@Header("Authorization") authHeader: String

View File

@@ -4,6 +4,8 @@ import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest
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 kr.co.vividnext.sodalive.mypage.can.payment.payverse.PayverseChargeRequest
import kr.co.vividnext.sodalive.mypage.can.payment.payverse.PayverseVerifyRequest
import java.util.TimeZone
class CanRepository(private val api: CanApi) {
@@ -57,4 +59,14 @@ class CanRepository(private val api: CanApi) {
request = UseCanCouponRequest(couponNumber),
authHeader = token
)
fun payverseChargeCan(canId: Long, token: String) = api.payverseChargeCan(
request = PayverseChargeRequest(canId),
authHeader = token
)
fun payverseVerify(transactionId: String, orderId: String, token: String) = api.payverseVerifyCharge(
request = PayverseVerifyRequest(transactionId = transactionId, orderId = orderId),
authHeader = token
)
}

View File

@@ -2,19 +2,26 @@ package kr.co.vividnext.sodalive.mypage.can.payment
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.view.View
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.IntentCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.net.toUri
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.bootpay.android.webview.BootpayUrlHelper
import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
@@ -27,14 +34,14 @@ import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
import org.json.JSONObject
import org.koin.android.ext.android.inject
class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
ActivityCanPaymentBinding::inflate
) {
enum class PaymentMethod(val method: String) {
CARD("카드"),
BANK("계좌이체"),
UNIFIED("통합 결제"),
PHONE("휴대폰"),
KAKAOPAY("카카오페이")
}
@@ -106,8 +113,7 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
requestCharge()
}
binding.tvMethodCard.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.CARD) }
binding.tvMethodBank.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.BANK) }
binding.tvMethodUnified.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.UNIFIED) }
binding.tvMethodPhone.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.PHONE) }
binding.flMethodKakaopay.setOnClickListener {
viewModel.setPaymentMethod(PaymentMethod.KAKAOPAY)
@@ -118,8 +124,7 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
if (it != null) {
when (it) {
PaymentMethod.CARD -> paymentMethodSelect(binding.tvMethodCard)
PaymentMethod.BANK -> paymentMethodSelect(binding.tvMethodBank)
PaymentMethod.UNIFIED -> paymentMethodSelect(binding.tvMethodUnified)
PaymentMethod.PHONE -> paymentMethodSelect(binding.tvMethodPhone)
PaymentMethod.KAKAOPAY -> {
isKakao = true
@@ -133,8 +138,7 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
private fun allPaymentMethodSelectFalse() {
isKakao = false
paymentMethodSelectFalse(binding.tvMethodBank)
paymentMethodSelectFalse(binding.tvMethodCard)
paymentMethodSelectFalse(binding.tvMethodUnified)
paymentMethodSelectFalse(binding.tvMethodPhone)
binding.flMethodKakaopay
.setBackgroundResource(R.drawable.bg_round_corner_10_232323_777777)
@@ -161,16 +165,37 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
}
private fun requestCharge() {
viewModel.chargeCan(
canId = canResponse!!.id,
paymentGateway = PaymentGateway.PG,
onSuccess = {
requestPayment(chargeId = it)
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
when (viewModel.paymentMethodLiveData.value) {
PaymentMethod.UNIFIED -> {
viewModel.payverseChargeCan(
canId = canResponse!!.id,
onSuccess = { response ->
// Payverse payloadJson을 이용하여 WebView 결제 시작
startPayverse(response.payloadJson)
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
)
PaymentMethod.KAKAOPAY, PaymentMethod.PHONE -> {
viewModel.chargeCan(
canId = canResponse!!.id,
paymentGateway = PaymentGateway.PG,
onSuccess = {
requestPayment(chargeId = it)
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
else -> {
Toast.makeText(applicationContext, "결제수단을 다시 선택해 주세요.", Toast.LENGTH_LONG).show()
}
}
}
private fun requestPayment(chargeId: Long) {
@@ -259,4 +284,99 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
}
)
}
@SuppressLint("SetJavaScriptEnabled")
private fun startPayverse(payloadJson: String) {
try {
val appScheme = BuildConfig.APPSCHEME
val payload = JSONObject(payloadJson)
payload.put("returnUrl", "$appScheme://payverse/result")
payload.put("webhookUrl", "${BuildConfig.BASE_URL}/charge/payverse/webhook")
payload.put("appScheme", appScheme)
val jsonForJs = payload.toString()
val webView: WebView = binding.webviewPayverse
webView.visibility = View.VISIBLE
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
val escaped = JSONObject.quote(jsonForJs)
view.evaluateJavascript("startPay($escaped)", null)
}
override fun shouldOverrideUrlLoading(
view: WebView,
request: WebResourceRequest
): Boolean {
val url = request.url.toString()
if (url.startsWith("$appScheme://")) {
startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
return true
}
return BootpayUrlHelper.shouldOverrideUrlLoading(view, url)
}
}
if (BuildConfig.DEBUG && appScheme.contains("test", ignoreCase = true)) {
webView.loadUrl("file:///android_asset/payverse_starter_debug.html")
} else {
webView.loadUrl("file:///android_asset/payverse_starter.html")
}
} catch (e: Exception) {
Logger.e(e.message ?: "payverse start error")
Toast.makeText(applicationContext, "결제 초기화에 실패했습니다.", Toast.LENGTH_LONG).show()
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.data?.let { handlePayverseDeeplink(it) }
}
private fun handlePayverseDeeplink(uri: Uri) {
val resultStatus = uri.getQueryParameter("resultStatus")
val transactionId = uri.getQueryParameter("tid")
val orderId = uri.getQueryParameter("orderId")
if (
resultStatus == "FAILED" ||
resultStatus == "DECLINE" ||
transactionId.isNullOrEmpty() ||
orderId.isNullOrEmpty()
) {
Toast.makeText(
applicationContext,
"결제를 하지 못했습니다.\n다시 시도해 주세요",
Toast.LENGTH_LONG
).show()
binding.webviewPayverse.visibility = View.GONE
finish()
return
}
viewModel.payverseVerify(
transactionId = transactionId,
orderId = orderId,
onSuccess = {
completePaymentSuccess()
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
private fun completePaymentSuccess() {
Toast.makeText(applicationContext, "캔이 충전되었습니다", Toast.LENGTH_LONG).show()
SharedPreferenceManager.can += (canResponse!!.rewardCan + canResponse!!.can)
if (gotoPrevPage) {
setResult(RESULT_OK)
} else {
val intent = Intent(applicationContext, CanStatusActivity::class.java)
startActivity(intent)
}
finish()
}
}

View File

@@ -31,7 +31,7 @@ class CanPaymentTempActivity : BaseActivity<ActivityCanPaymentBinding>(
ActivityCanPaymentBinding::inflate
) {
enum class PaymentMethod(val method: String) {
CARD("카드"), BANK("계좌이체"), PHONE("휴대폰")
UNIFIED("통합 결제"), PHONE("휴대폰")
}
private val viewModel: CanPaymentTempViewModel by inject()
@@ -101,14 +101,12 @@ class CanPaymentTempActivity : BaseActivity<ActivityCanPaymentBinding>(
requestCharge()
}
binding.tvMethodCard.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.CARD) }
binding.tvMethodBank.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.BANK) }
binding.tvMethodUnified.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.UNIFIED) }
binding.tvMethodPhone.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.PHONE) }
}
private fun allPaymentMethodSelectFalse() {
paymentMethodSelectFalse(binding.tvMethodBank)
paymentMethodSelectFalse(binding.tvMethodCard)
paymentMethodSelectFalse(binding.tvMethodUnified)
paymentMethodSelectFalse(binding.tvMethodPhone)
}
@@ -219,8 +217,7 @@ class CanPaymentTempActivity : BaseActivity<ActivityCanPaymentBinding>(
if (it != null) {
when (it) {
PaymentMethod.CARD -> paymentMethodSelect(binding.tvMethodCard)
PaymentMethod.BANK -> paymentMethodSelect(binding.tvMethodBank)
PaymentMethod.UNIFIED -> paymentMethodSelect(binding.tvMethodUnified)
PaymentMethod.PHONE -> paymentMethodSelect(binding.tvMethodPhone)
}
}

View File

@@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.mypage.can.CanRepository
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.payment.payverse.PayverseChargeResponse
class CanPaymentViewModel(private val repository: CanRepository) : BaseViewModel() {
@@ -120,4 +121,66 @@ class CanPaymentViewModel(private val repository: CanRepository) : BaseViewModel
fun setPaymentMethod(paymentMethod: CanPaymentActivity.PaymentMethod) {
_paymentMethodLiveData.value = paymentMethod
}
fun payverseChargeCan(
canId: Long,
onSuccess: (PayverseChargeResponse) -> Unit,
onFailure: (String) -> Unit
) {
_isLoading.value = true
compositeDisposable.add(
repository.payverseChargeCan(
canId = canId,
token = "Bearer ${SharedPreferenceManager.token}"
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
onSuccess(it.data)
} else {
onFailure(it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
},
{
_isLoading.value = false
it.message?.let { m -> Logger.e(m) }
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun payverseVerify(
transactionId: String,
orderId: String,
onSuccess: () -> Unit,
onFailure: (String) -> Unit
) {
_isLoading.value = true
compositeDisposable.add(
repository.payverseVerify(
transactionId = transactionId,
orderId = orderId,
token = "Bearer ${SharedPreferenceManager.token}"
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
onSuccess()
} else {
onFailure(it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
},
{
_isLoading.value = false
it.message?.let { m -> Logger.e(m) }
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@@ -0,0 +1,20 @@
package kr.co.vividnext.sodalive.mypage.can.payment.payverse
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
@Keep
data class PayverseChargeRequest(
@SerializedName("canId") val canId: Long
)
@Keep
data class PayverseChargeResponse(
@SerializedName("chargeId") val chargeId: Long,
@SerializedName("payloadJson") val payloadJson: String
)
data class PayverseVerifyRequest(
@SerializedName("transactionId") val transactionId: String,
@SerializedName("orderId") val orderId: String
)

View File

@@ -103,10 +103,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="16.7dp">
android:layout_marginTop="16.7dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_method_card"
android:id="@+id/tv_method_unified"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -114,30 +115,26 @@
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="카드"
android:text="통합 결제"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_method_bank"
<FrameLayout
android:id="@+id/fl_method_kakaopay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_marginHorizontal="16.7dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_232323_777777"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="계좌이체"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
</LinearLayout>
android:paddingVertical="8.3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="16.7dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:contentDescription="@null"
android:src="@drawable/ic_kakaopay" />
</FrameLayout>
<TextView
android:id="@+id/tv_method_phone"
@@ -151,31 +148,14 @@
android:text="휴대폰 결제"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<FrameLayout
android:id="@+id/fl_method_kakaopay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_232323_777777"
android:paddingVertical="8.3dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:contentDescription="@null"
android:src="@drawable/ic_kakaopay" />
</FrameLayout>
</LinearLayout>
<TextView
android:id="@+id/tv_agree"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="13.3dp"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="16.7dp"
android:button="@null"
android:drawablePadding="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
@@ -189,7 +169,7 @@
android:id="@+id/tv_alert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="- 충전된 캔의 유효기간은 충전 후 5년 입니다.\n- 결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n 단, 캔의 일부를 사용하면 결제 취소할 수 없습니다.\n- 광고성 이벤트 등 회사가 무료로 지급한 \n포인트는 환불되지 않습니다.\n- 자세한 내용은 보이스온 이용약관에서 확인할 수 있습니다."
@@ -198,6 +178,18 @@
</LinearLayout>
</ScrollView>
<WebView
android:id="@+id/webview_payverse"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/black"
android:elevation="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/ll_payment"
android:layout_width="match_parent"