Commit 5df2c27c authored by Seatel's avatar Seatel

implement acleda service

parent 8e495f6c
Pipeline #97 failed with stages
package com.seatel.mobilehall.ui.home.activity
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import com.seatel.mobilehall.R
import com.seatel.mobilehall.ui.base.activity.BaseActivity
import com.seatel.mobilehall.util.Constant
import com.seatel.mobilehall.util.SeatelAlertDialog
import com.seatel.mobilehall.util.customview.ErrorHandleView
import androidx.core.net.toUri
import com.seatel.mobilehall.databinding.ActivityAcledaBinding
class AcledaActivity : BaseActivity<ActivityAcledaBinding>() {
private lateinit var mUrlPay: String
private var mAmount: Double = 0.0
private var mPhoneNumber: String? = null
private var isAbleBack: Boolean = false
private var isShowValentineDayPromotion = false
private val mWebViewClient = object : WebViewClient() {
// override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
// println("===>>> WebView onPageStarted: $url")
// super.onPageStarted(view, url, favicon)
// }
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
showHideLoading(false)
}
override fun onReceivedError(
view: WebView, request: WebResourceRequest, error: WebResourceError
) {
super.onReceivedError(view, request, error)
//closeWingPaymentWithError()
closeAcledaPaymentWithError()
}
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url.toString()
val uri = request?.url.toString()
val host = request?.url?.host ?: ""
Log.d("TAG::>>>", "shouldOverrideUrlLoading: url=$url host=$host")
println("===> URI: $uri")
// SUCCESS
if (url.contains("_paymentresult=SUCCESS")) {
TopUpSuccessActivity.lunch(
this@AcledaActivity,
Constant.TOPUP_SUCCESS,
mAmount.toString(),
mPhoneNumber ?: ""
)
finish()
return true
}
// CANCEL / SESSION EXPIRED
if (url.contains("cancel")) {
val uri = url.toUri()
val resultMsg = uri.getQueryParameter("_resultmsg") ?: "Payment failed"
exitAcledaPayment(false)
return true
}
val allowedHosts = listOf(
"yes.com.kh",
"dev.yes.com.kh",
"api.yes.com.kh",
"acleda.com.kh",
"api-dev.yes.com.kh"
)
println("===> allowedHosts: $allowedHosts")
if (allowedHosts.any { host.contains(it) }) return false
println("===> host return : $host")
startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
return true
}
}
private fun closeAcledaPaymentWithError() {
showHideLoading(false)/* SeatelAlertDialog.with(this, getString(R.string.message_wing_error)).setCancelable(true)
.setPositiveButton(getString(R.string.yes)) { _, _ ->
exitWingPayment(false)
}.show()*/
}
private fun closePaymentProcress() {
if (binding.webViewAcleda.canGoBack()) {
binding.webViewAcleda.goBack()
} else {
showHideLoading(false)
SeatelAlertDialog.with(this, getString(R.string.cancel_process_payment))
.setCancelable(true).setPositiveButton(getString(R.string.yes)) { _, _ ->
exitAcledaPayment(false)
}.setNegativeButton(getString(R.string.cancel)) { dailog, _ ->
dailog.dismiss()
}.show()
}
}
override fun isDisplayBackgroundBlur(): Boolean {
return false
}
override fun isDisplayToolbar(): Boolean {
return false
}
override fun isStatusBarColorDark(): Boolean {
return true
}
override fun getViewBinding(): ActivityAcledaBinding {
return ActivityAcledaBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
println("===> OnCreateAcledaCalled")
activityEnterFadeInAnimation()
showHideLoading(true)
init()
}
private fun showHideLoading(isShow: Boolean) {
if (isShow) {
binding.errorView.visibility = View.VISIBLE
binding.errorView.setViewMode(ErrorHandleView.Mode.LOADING)
} else {
binding.errorView.visibility = View.GONE
}
}
@SuppressLint("SetJavaScriptEnabled")
private fun init() {
mUrlPay = intent.getStringExtra("payment_url") ?: ""
println("===> Payment URL: $mUrlPay")
// Log.d("TAG::>>>", "acledaUrl: $mUrlPay")
// if (mUrlPay.isEmpty()) {
// closeAcledaPaymentWithError()
// return
// }
isShowValentineDayPromotion = intent.getBooleanExtra("show_valentine_promotion", false)
isAbleBack = intent.getBooleanExtra("is_able_back", false)
mAmount = intent.getDoubleExtra("top_amount", 0.0)
mPhoneNumber = intent.getStringExtra(Constant.PHONE_NUMBER)
binding.webViewAcleda.settings.javaScriptEnabled = true
binding.webViewAcleda.webViewClient = mWebViewClient
binding.webViewAcleda.loadUrl(mUrlPay)
}
private fun exitAcledaPayment(isSuccess: Boolean) {
showHideLoading(false)
if (isSuccess) {
val intent = Intent(this, PaymentActivity::class.java)
intent.putExtra("show_valentine_promotion", isShowValentineDayPromotion)
setResult(Activity.RESULT_OK, intent)
}
activityExitLeftAnimation()
finish()
}
override fun onBackPressed() {
super.onBackPressed()
closePaymentProcress()
}
override fun onPause() {
super.onPause()
binding.webViewAcleda.onPause()
}
override fun onResume() {
super.onResume()
binding.webViewAcleda.onResume()
}
companion object {
fun lunch(
context: Context,
url: String,
amount: Double,
isAbleBackPress: Boolean = false,
showValentineDayPromotion: Boolean = false,
phoneNumber: String,
) {
val i = Intent(context, AcledaActivity::class.java)
i.putExtra("payment_url", url)
i.putExtra("top_amount", amount)
i.putExtra("is_able_back", isAbleBackPress)
// i.putExtra("show_valentine_promotion", showValentineDayPromotion)
i.putExtra(Constant.PHONE_NUMBER, phoneNumber)
(context as Activity).startActivityForResult(i, Constant.REQUEST_ACLEDA)
}
}
}
\ No newline at end of file
package com.seatel.mobilehall.ui.home.activity package com.seatel.mobilehall.ui.home.activity
import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
...@@ -10,6 +11,9 @@ import android.text.TextUtils ...@@ -10,6 +11,9 @@ import android.text.TextUtils
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.android.volley.NoConnectionError import com.android.volley.NoConnectionError
import com.android.volley.Request import com.android.volley.Request
...@@ -59,6 +63,11 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -59,6 +63,11 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
private var isWingInstalled = 0 private var isWingInstalled = 0
private val WING_SCHEME_DEV = "com.wingmoney.wingpay.uat" private val WING_SCHEME_DEV = "com.wingmoney.wingpay.uat"
private val WING_SCHEME_PRO = "com.wingmoney.wingpay" private val WING_SCHEME_PRO = "com.wingmoney.wingpay"
//private var acledaRemark: Int = 0
private var acledaRemark: String? = null
private var selectedPaymentIndex: Int = -1
private var paymentTokenId: String = ""
//Topup //Topup
private lateinit var mTopupTransactionPresenter: TopupTransactionPresenter private lateinit var mTopupTransactionPresenter: TopupTransactionPresenter
...@@ -207,7 +216,67 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -207,7 +216,67 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
mAmountTopup, mAmountTopup,
paymentType paymentType
) )
} else { }
else if (paymentType.equals("acleda", ignoreCase = true)) {
val myRemark = remark
acledaRemark = myRemark
openAcledaDeeplink(myRemark)
}
else if (paymentType.equals("acleda-khqr", ignoreCase = true)) {
val khqrRemark = acledaRemark ?: remark
object : SeatelJSONObjectRequest(this) {
override fun getFunctionName() = "v2/payment-methods/acleda/open-session"
override fun getMethod() = Request.Method.POST
override fun onGetBodyRequest(): String {
val body = JSONObject().apply {
put("remark", khqrRemark)
put("paymentType", "khqr")
}
println("===> Body Request : $body")
return body.toString()
}
}.execute { response ->
hideProgress()
val jsonResponse = response as JSONObject
val payment_url = jsonResponse.optString("link")
println("===> Acleda KHQR Link: $payment_url")
if (payment_url.isNotEmpty()) {
try {
AcledaActivity.lunch(
context = this,
url = payment_url,
amount = mAmountTopup,
isAbleBackPress = true,
showValentineDayPromotion = false,
phoneNumber = mPhoneNumber,
)
} catch (
e: Exception
) {
println(
"===> Error: ${e.message}"
)
}
// if (!acledaLink.isNullOrEmpty()) {
// AcledaActivity.lunch(
// context = this@PaymentActivity,
// url = acledaLink,
// amount = mAmountTopup,
// phoneNumber = mPhoneNumber,
// isAbleBackPress = true,
// showValentineDayPromotion = false
// )
// } else {
// println("===> Error: acledaLink is empty")
// }
}
}
}else {
if (TextUtils.equals(mTopupStatus.lowercase(Locale.getDefault()), "success")) { if (TextUtils.equals(mTopupStatus.lowercase(Locale.getDefault()), "success")) {
checkCallBack = false checkCallBack = false
TopUpSuccessActivity.lunch( TopUpSuccessActivity.lunch(
...@@ -267,6 +336,12 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -267,6 +336,12 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
this, Constant.TOPUP_SUCCESS, originalAmount.toString(), mPhoneNumber.toString() this, Constant.TOPUP_SUCCESS, originalAmount.toString(), mPhoneNumber.toString()
) )
} }
else if (resultCode == Constant.REQUEST_ACLEDA) {
println("===> Acleda Payment Success")
TopUpSuccessActivity.lunch(
this, Constant.TOPUP_SUCCESS, originalAmount.toString(), mPhoneNumber.toString()
)
}
} }
private fun startUnionPayProcess() { private fun startUnionPayProcess() {
...@@ -276,6 +351,8 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -276,6 +351,8 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
private fun checkTopupPaymentTypeProcess(index: Int) { private fun checkTopupPaymentTypeProcess(index: Int) {
selectedPaymentIndex = index // ✅ store for later use
println("===> checkTopupPaymentTypeProcess called with index: $index, mPaymentType: $mPaymentType")
if (handlerRefresh == null) handlerRefresh = Handler() if (handlerRefresh == null) handlerRefresh = Handler()
if (handlerRefresh != null) handlerRefresh?.postDelayed({ if (handlerRefresh != null) handlerRefresh?.postDelayed({
mAdapterPayActivity?.notifyItemChanged(index) mAdapterPayActivity?.notifyItemChanged(index)
...@@ -283,7 +360,8 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -283,7 +360,8 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
handlerRefresh = null handlerRefresh = null
}, 100) }, 100)
when (mPaymentType) {/* "unionpay" -> { when (mPaymentType) {
/* "unionpay" -> {
startUnionPayProcess() startUnionPayProcess()
FirebaseAnalyticsUtil.getInstance(this).logCustomEvent(Constant.SeaTelAnalytics.Value.UNION_PAY) FirebaseAnalyticsUtil.getInstance(this).logCustomEvent(Constant.SeaTelAnalytics.Value.UNION_PAY)
}*/ }*/
...@@ -310,6 +388,12 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -310,6 +388,12 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
Constant.ABA_CARD -> { Constant.ABA_CARD -> {
startPaymentProcess("aba-card") startPaymentProcess("aba-card")
} }
Constant.ACLEDA -> {
startPaymentProcess("acleda")
}
Constant.ACLEDA_KHQR -> {
startPaymentProcess("acleda-khqr")
}
else -> { else -> {
SeatelAlertDialog.with(this, getString(R.string.choose_payment_option)).show() SeatelAlertDialog.with(this, getString(R.string.choose_payment_option)).show()
...@@ -394,7 +478,7 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -394,7 +478,7 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
super.activityEnterRightAnimation() super.activityEnterRightAnimation()
// setContentView(R.layout.activity_payment) // setContentView(R.layout.activity_payment)
checkCallBack = false checkCallBack = false
showHideLoading(true) showHideLoading(true)
...@@ -472,21 +556,36 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -472,21 +556,36 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
mAdapterPayActivity?.notifyDataSetChanged() mAdapterPayActivity?.notifyDataSetChanged()
showHideLoading(false) showHideLoading(false)
if (checkCallBack) getAbaCallBack() if (checkCallBack) getAbaCallBack()
println("===> Check Transaction By PaymentTokenId : $paymentTokenId")
if (checkCallBack) {
showProgress()
getAcledaCallBack()
}
} }
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
println("===> ONNEWINTENT")
val appLinkAction: String? = intent.action val appLinkAction: String? = intent.action
val appLinkData: Uri? = intent.data val appLinkData: Uri? = intent.data
Log.d(TAG, "onNewIntent: ${appLinkData.toString()} $appLinkAction") Log.d(TAG, "onNewIntent: ${appLinkData.toString()} $appLinkAction")
println("===> appDeepData Acldeda========================: $appLinkData")
if (appLinkData.toString().contains("transaction_status=true")) { if (appLinkData.toString().contains("transaction_status=true")) {
TopUpSuccessActivity.lunch( TopUpSuccessActivity.lunch(
this, Constant.TOPUP_SUCCESS, originalAmount.toString(), mPhoneNumber this, Constant.TOPUP_SUCCESS, originalAmount.toString(), mPhoneNumber
) )
finish() finish()
} }
val acledaAction: String? = intent.action
val acledaLinkData: Uri? = intent.data
val link = intent.data?.toString() ?: ""
println("===> ACLEDAACTION: $acledaAction")
println("===> ACLEDALINKDATA: $acledaLinkData")
println("===> LINK CALLBACK: $link")
if (acledaLinkData != null) {
showProgress()
checkVerify()
}
} }
...@@ -501,4 +600,110 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction ...@@ -501,4 +600,110 @@ class PaymentActivity : BaseActivity<ActivityPaymentBinding>(), TopupTransaction
context.startActivity(intent) context.startActivity(intent)
} }
} }
private fun openAcledaDeeplink(remark: String) {
object : SeatelJSONObjectRequest(this) {
override fun getFunctionName() = "v2/payment-methods/acleda/open-session"
override fun getMethod() = Request.Method.POST
override fun onGetBodyRequest(): String {
val body = JSONObject().apply {
put("remark", remark)
put("paymentType", "deeplink")
}
println("===> Body Deeplink : $body")
return body.toString()
}
}.execute { response ->
hideProgress()
checkCallBack = true
val link = (response as JSONObject).optString("link")
val paymentTokenId = response.optString("paymentTokenId")
this.paymentTokenId = paymentTokenId
try {
val intent = Intent(Intent.ACTION_VIEW, link.toUri())
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
} catch (e: ActivityNotFoundException) {
SeatelAlertDialog.with(this, "ACLEDA app not installed").show()
installAcledaApp(this)
}
}
}
private fun installAcledaApp(context: Context) {
val packageName = "com.domain.acledabankqr"
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName"))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
// Play Store not installed, open in browser instead
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}
}
private fun checkVerify(){
object : SeatelJSONObjectRequest(this) {
override fun getFunctionName() = "acleda/transaction"
override fun getMethod() = Request.Method.POST
override fun onGetBodyRequest(): String {
val body = JSONObject().apply {
put("paymentTokenId", paymentTokenId)
}
println("===> Body Request : $body")
return body.toString()
}
}.execute { response ->
hideProgress()
val json = JSONObject(response.toString())
val result = json.optJSONObject("result")
if (result != null) {
if (result.optString("errorDetails") == "SUCCESS") {
TopUpSuccessActivity.lunch(
this, Constant.TOPUP_SUCCESS, originalAmount.toString(), mPhoneNumber
)
}
finish()
}
}
}
private fun getAcledaCallBack(){
object : SeatelJSONObjectRequest(this) {
override fun getFunctionName(): String {
return "acleda/transaction"
}
override fun onGetBodyRequest(): String {
val obj = JSONObject()
obj.put("paymentTokenId" , paymentTokenId)
return obj.toString()
}
override fun getMethod(): Int {
return Request.Method.POST
}
}.setOnErrorListener {
SeatelAlertDialog.with(this, SeatelSuperRequest.getErrorMessageFrom(it)).show()
}.execute {
hideProgress()
val json = JSONObject(it.toString())
val result = json.optJSONObject("result")
if (result != null) {
if (result.optString("errorDetails") == "SUCCESS") {
TopUpSuccessActivity.lunch(
this, Constant.TOPUP_SUCCESS, originalAmount.toString(), mPhoneNumber
)
}else{
SeatelAlertDialog.with(this, "Acleda Payment Failed").show()
}
}
//finish()
}
}
} }
...@@ -85,6 +85,7 @@ object Constant { ...@@ -85,6 +85,7 @@ object Constant {
const val SMS_PERMISSION_CODE = 108 const val SMS_PERMISSION_CODE = 108
const val PHONE_PERMISSION = 109 const val PHONE_PERMISSION = 109
const val REQUEST_WING = 99 const val REQUEST_WING = 99
const val REQUEST_ACLEDA = 200
const val PICK_UP_YES_HUB_LOCATION = 98 const val PICK_UP_YES_HUB_LOCATION = 98
const val DEVICE_OPTION = 96 const val DEVICE_OPTION = 96
const val CART_DEVICE_DETAIL = 95 const val CART_DEVICE_DETAIL = 95
...@@ -115,6 +116,8 @@ object Constant { ...@@ -115,6 +116,8 @@ object Constant {
var ALI_PAY = "alipay" var ALI_PAY = "alipay"
var ABA = "aba" var ABA = "aba"
var ABA_CARD = "aba-card" var ABA_CARD = "aba-card"
var ACLEDA = "acleda"
var ACLEDA_KHQR: String = "acleda-khqr"
/** /**
* Request * Request
......
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBgWhiteGray"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:scrollbars="vertical">
<View
android:id="@+id/viewSpace"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="@android:color/transparent" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/viewSpace"
android:background="@color/colorBgWhiteGray"
android:fillViewport="true"
android:scrollbars="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBgWhiteGray">
<WebView
android:id="@+id/web_view_acleda"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="insideOverlay"
android:scrollbars="vertical" />
<com.seatel.mobilehall.util.customview.ErrorHandleView
android:id="@+id/error_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</RelativeLayout>
</ScrollView>
</RelativeLayout>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment