Skip to content

Commit 338b4c8

Browse files
committed
feat(kado): check order status when provided to trigger sheet closure
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 87be29e commit 338b4c8

8 files changed

Lines changed: 230 additions & 24 deletions

File tree

app/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ dependencies {
194194
implementation(Libs.zxing)
195195
implementation(Libs.mixpanel)
196196

197+
implementation(Libs.retrofit)
198+
implementation(Libs.retrofit_converter)
199+
implementation(Libs.okhttp_logging_interceptor)
200+
197201
implementation(Libs.cloudy)
198202

199203
androidTestImplementation(Libs.androidx_test_runner)

app/proguard-rules.pro

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@
6161
-keep class org.json.** { *; }
6262
-keepclassmembers class org.json.** { *; }
6363

64+
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
65+
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
66+
-keep,allowobfuscation,allowshrinking class retrofit2.Response
67+
68+
# With R8 full mode generic signatures are stripped for classes that are not
69+
# kept. Suspend functions are wrapped in continuations where the type argument
70+
# is used.
71+
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
72+
6473
# libsodium
6574
-keep class com.ionspin.kotlin.crypto.** { *; }
6675
-keep class com.sun.jna.** { *; }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.getcode.api
2+
3+
import com.google.gson.JsonObject
4+
import okhttp3.ResponseBody
5+
import retrofit2.Response
6+
import retrofit2.http.GET
7+
import retrofit2.http.Path
8+
9+
interface KadoApi {
10+
@GET("v2/public/orders/{orderId}")
11+
suspend fun getOrderStatus(@Path("orderId") orderId: String): Response<ResponseBody>
12+
}

app/src/main/java/com/getcode/inject/ApiModule.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.getcode.BuildConfig
55
import com.getcode.R
66
import com.getcode.analytics.AnalyticsService
77
import com.getcode.annotations.DevManagedChannel
8+
import com.getcode.api.KadoApi
89
import com.getcode.manager.MnemonicManager
910
import com.getcode.model.CurrencyCode
1011
import com.getcode.model.PrefsString
@@ -44,8 +45,14 @@ import io.grpc.android.AndroidChannelBuilder
4445
import io.reactivex.rxjava3.core.Scheduler
4546
import io.reactivex.rxjava3.disposables.CompositeDisposable
4647
import io.reactivex.rxjava3.schedulers.Schedulers
48+
import okhttp3.OkHttpClient
49+
import okhttp3.logging.HttpLoggingInterceptor
4750
import org.kin.sdk.base.network.api.agora.OkHttpChannelBuilderForcedTls12
51+
import retrofit2.Retrofit
52+
import retrofit2.converter.gson.GsonConverterFactory
53+
import retrofit2.create
4854
import java.util.concurrent.TimeUnit
55+
import javax.inject.Named
4956
import javax.inject.Singleton
5057

5158
@Module
@@ -109,6 +116,37 @@ object ApiModule {
109116
return MixpanelAPI.getInstance(context, BuildConfig.MIXPANEL_API_KEY)
110117
}
111118

119+
@Singleton
120+
@Provides
121+
fun providesHttpLoggingInterceptor() = HttpLoggingInterceptor()
122+
.apply {
123+
level = HttpLoggingInterceptor.Level.BODY
124+
}
125+
126+
@Singleton
127+
@Provides
128+
fun providesOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient =
129+
OkHttpClient
130+
.Builder()
131+
.addInterceptor(httpLoggingInterceptor)
132+
.build()
133+
134+
@Singleton
135+
@Provides
136+
@Named("kado-retrofit")
137+
fun provideKadoRetrofit(okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
138+
.addConverterFactory(GsonConverterFactory.create())
139+
.baseUrl("https://api.kado.money/")
140+
.client(okHttpClient)
141+
.build()
142+
143+
@Singleton
144+
@Provides
145+
fun providesKadoApi(
146+
@Named("kado-retrofit")
147+
retrofit: Retrofit
148+
): KadoApi = retrofit.create(KadoApi::class.java)
149+
112150
@Singleton
113151
@Provides
114152
fun provideBalanceRepository(

app/src/main/java/com/getcode/navigation/screens/ModalScreens.kt

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import com.getcode.view.main.getKin.BuyAndSellKin
4040
import com.getcode.view.main.getKin.BuyKinScreen
4141
import com.getcode.view.main.getKin.GetKinSheet
4242
import com.getcode.view.main.getKin.GetKinSheetViewModel
43+
import com.getcode.view.main.getKin.KadoWebScreen
4344
import com.getcode.view.main.tip.ConnectAccountScreen
4445
import com.getcode.view.main.tip.EnterTipScreen
4546
import com.getcode.view.main.tip.IdentityConnectionReason
@@ -382,32 +383,10 @@ data class KadoWebScreen(val url: String) : MainGraph, ModalContent {
382383
backButton = { SheetTitleDefaults.CloseButton() },
383384
onBackClicked = { navigator.hide() },
384385
closeButtonEnabled = { true },
385-
closeButton = {
386-
SheetTitleDefaults.RefreshButton()
387-
},
386+
closeButton = { SheetTitleDefaults.RefreshButton() },
388387
onCloseClicked = { webNavigator.reload() }
389388
) {
390-
val loadingState = state.loadingState
391-
if (loadingState is LoadingState.Loading) {
392-
CodeCircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
393-
}
394-
WebView(
395-
modifier = Modifier
396-
.fillMaxSize()
397-
.imePadding(),
398-
captureBackPresses = false,
399-
navigator = webNavigator,
400-
state = state,
401-
onCreated = { nativeWebView ->
402-
nativeWebView.addJavascriptInterface(BuyKinWebInterface(), "Android")
403-
nativeWebView.clipToOutline = true
404-
nativeWebView.setBackgroundColor(Color.Transparent.toAGColor())
405-
nativeWebView.settings.apply {
406-
javaScriptEnabled = true
407-
domStorageEnabled = true
408-
}
409-
}
410-
)
389+
KadoWebScreen(viewModel = getViewModel(), state = state, webNavigator = webNavigator)
411390
}
412391
}
413392

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.getcode.view.main.getKin
2+
3+
import android.graphics.Bitmap
4+
import android.net.Uri
5+
import android.webkit.WebView
6+
import androidx.compose.foundation.layout.BoxScope
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.imePadding
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.runtime.LaunchedEffect
11+
import androidx.compose.runtime.getValue
12+
import androidx.compose.runtime.mutableStateOf
13+
import androidx.compose.runtime.remember
14+
import androidx.compose.runtime.setValue
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.graphics.Color
18+
import com.getcode.manager.TopBarManager
19+
import com.getcode.navigation.core.LocalCodeNavigator
20+
import com.getcode.navigation.screens.KadoWebScreen.BuyKinWebInterface
21+
import com.getcode.ui.components.CodeCircularProgressIndicator
22+
import com.getcode.ui.utils.toAGColor
23+
import com.kevinnzou.web.AccompanistWebViewClient
24+
import com.kevinnzou.web.LoadingState
25+
import com.kevinnzou.web.WebView
26+
import com.kevinnzou.web.WebViewNavigator
27+
import com.kevinnzou.web.WebViewState
28+
29+
@Composable
30+
fun BoxScope.KadoWebScreen(
31+
viewModel: KadoWebViewModel,
32+
state: WebViewState,
33+
webNavigator: WebViewNavigator,
34+
) {
35+
val navigator = LocalCodeNavigator.current
36+
37+
val loadingState = state.loadingState
38+
if (loadingState is LoadingState.Loading) {
39+
CodeCircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
40+
}
41+
42+
var orderId by remember { mutableStateOf("") }
43+
44+
val client = remember {
45+
object : AccompanistWebViewClient() {
46+
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
47+
super.onPageStarted(view, url, favicon)
48+
println("url=$url")
49+
if (url?.startsWith("https://app.kado.money/ramp/order/") == true) {
50+
// order created, extract order id
51+
orderId = Uri.parse(url).lastPathSegment.orEmpty()
52+
}
53+
}
54+
}
55+
}
56+
57+
LaunchedEffect(orderId) {
58+
if (orderId.isNotEmpty()) {
59+
// order created, start to check status
60+
viewModel.checkOrderStatus(orderId)
61+
.onSuccess {
62+
TopBarManager.showMessage(
63+
"Success! Funds Available Soon",
64+
"Your funds should be available in your Code Wallet in 5 to 10 minutes.",
65+
type = TopBarManager.TopBarMessageType.SUCCESS
66+
)
67+
navigator.hide()
68+
}.onFailure {
69+
TopBarManager.showMessage(
70+
"Something went wrong",
71+
"Your payment method was not charged. Please try again later."
72+
)
73+
navigator.hide()
74+
}
75+
}
76+
}
77+
WebView(
78+
modifier = Modifier
79+
.fillMaxSize()
80+
.imePadding(),
81+
captureBackPresses = false,
82+
navigator = webNavigator,
83+
state = state,
84+
client = client,
85+
onCreated = { nativeWebView ->
86+
nativeWebView.addJavascriptInterface(BuyKinWebInterface(), "Android")
87+
nativeWebView.clipToOutline = true
88+
nativeWebView.setBackgroundColor(Color.Transparent.toAGColor())
89+
nativeWebView.settings.apply {
90+
javaScriptEnabled = true
91+
domStorageEnabled = true
92+
}
93+
}
94+
)
95+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.getcode.view.main.getKin
2+
3+
import com.getcode.api.KadoApi
4+
import com.getcode.util.resources.ResourceHelper
5+
import com.getcode.view.BaseViewModel
6+
import dagger.hilt.android.lifecycle.HiltViewModel
7+
import kotlinx.coroutines.delay
8+
import kotlinx.serialization.json.Json
9+
import kotlinx.serialization.json.jsonObject
10+
import kotlinx.serialization.json.jsonPrimitive
11+
import retrofit2.HttpException
12+
import retrofit2.Response
13+
import java.io.IOException
14+
import javax.inject.Inject
15+
import kotlin.time.Duration.Companion.seconds
16+
17+
@HiltViewModel
18+
class KadoWebViewModel @Inject constructor(
19+
resources: ResourceHelper,
20+
private val kadoApi: KadoApi,
21+
): BaseViewModel(resources) {
22+
23+
suspend fun checkOrderStatus(orderId: String): Result<Unit> {
24+
while (true) {
25+
println("checking order status for $orderId")
26+
val result = kadoApi.getOrderStatus(orderId).toResult()
27+
28+
result.map {
29+
val ret = parsePaymentStatus(it.string())
30+
if (ret != null) {
31+
return ret
32+
}
33+
delay(2.seconds)
34+
}
35+
}
36+
}
37+
38+
private fun parsePaymentStatus(jsonString: String): Result<Unit>? {
39+
val json = Json.parseToJsonElement(jsonString).jsonObject["data"]?.jsonObject ?: return null
40+
val paymentStatus = json["paymentStatus"]?.jsonPrimitive?.content
41+
42+
return when (paymentStatus) {
43+
"success" -> Result.success(Unit)
44+
"failed" -> Result.failure(Throwable("Payment failed"))
45+
else -> null
46+
}
47+
}
48+
}
49+
50+
fun <T> Response<T>.toResult(): Result<T> {
51+
return try {
52+
if (isSuccessful) {
53+
val body = body()
54+
if (body != null) {
55+
Result.success(body)
56+
} else {
57+
Result.failure(NullPointerException("Response body is null"))
58+
}
59+
} else {
60+
Result.failure(HttpException(this))
61+
}
62+
} catch (e: IOException) {
63+
Result.failure(e)
64+
}
65+
}

buildSrc/src/main/java/Dependencies.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ object Versions {
4545
const val hilt = "2.50"
4646
const val hilt_jetpack = "1.1.0-beta01"
4747
const val okhttp = "4.9.3"
48+
const val retrofit = "2.6.0"
4849
const val rxjava: String = "3.1.3"
4950
const val rxandroid: String = "3.0.0"
5051

@@ -226,6 +227,9 @@ object Libs {
226227
const val qr_generator = "com.github.androidmads:QRGenerator:${Versions.qr_generator}"
227228
const val zxing = "com.google.zxing:core:${Versions.zxing}"
228229

230+
const val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}"
231+
const val retrofit_converter = "com.squareup.retrofit2:converter-gson:${Versions.retrofit}"
232+
229233
const val androidx_test_runner =
230234
"androidx.test:runner:${Versions.androidx_test_runner}"
231235
const val androidx_junit =

0 commit comments

Comments
 (0)