mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-25 16:17:58 +00:00
Initial commit: Pezkuwi Wallet Android
Complete rebrand of Nova Wallet for Pezkuwichain ecosystem. ## Features - Full Pezkuwichain support (HEZ & PEZ tokens) - Polkadot ecosystem compatibility - Staking, Governance, DeFi, NFTs - XCM cross-chain transfers - Hardware wallet support (Ledger, Polkadot Vault) - WalletConnect v2 - Push notifications ## Languages - English, Turkish, Kurmanci (Kurdish), Spanish, French, German, Russian, Japanese, Chinese, Korean, Portuguese, Vietnamese Based on Nova Wallet by Novasama Technologies GmbH © Dijital Kurdistan Tech Institute 2026
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.di
|
||||
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_buy_api.di.BuyFeatureApi
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.BuyRouter
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
BuyFeatureDependencies::class
|
||||
],
|
||||
modules = [
|
||||
BuyFeatureModule::class
|
||||
]
|
||||
)
|
||||
@FeatureScope
|
||||
interface BuyFeatureComponent : BuyFeatureApi {
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
|
||||
fun create(
|
||||
@BindsInstance router: BuyRouter,
|
||||
deps: BuyFeatureDependencies
|
||||
): BuyFeatureComponent
|
||||
}
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
CommonApi::class,
|
||||
RuntimeApi::class,
|
||||
AccountFeatureApi::class,
|
||||
WalletFeatureApi::class,
|
||||
]
|
||||
)
|
||||
interface BuyFeatureDependenciesComponent : BuyFeatureDependencies
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.di
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.common.utils.ip.IpAddressReceiver
|
||||
import io.novafoundation.nova.common.utils.webView.InterceptingWebViewClientFactory
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
|
||||
import io.novafoundation.nova.feature_wallet_api.presentation.formatters.amount.AmountFormatter
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
interface BuyFeatureDependencies {
|
||||
|
||||
val context: Context
|
||||
|
||||
val amountFormatter: AmountFormatter
|
||||
|
||||
val chainRegistry: ChainRegistry
|
||||
|
||||
val accountUseCase: SelectedAccountUseCase
|
||||
|
||||
val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory
|
||||
|
||||
val interceptingWebViewClientFactory: InterceptingWebViewClientFactory
|
||||
|
||||
val gson: Gson
|
||||
|
||||
val okHttpClient: OkHttpClient
|
||||
|
||||
val resourceManager: ResourceManager
|
||||
|
||||
val ipAddressReceiver: IpAddressReceiver
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.di
|
||||
|
||||
import io.novafoundation.nova.common.di.FeatureApiHolder
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.BuyRouter
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import javax.inject.Inject
|
||||
|
||||
@ApplicationScope
|
||||
class BuyFeatureHolder @Inject constructor(
|
||||
featureContainer: FeatureContainer,
|
||||
private val router: BuyRouter
|
||||
) : FeatureApiHolder(featureContainer) {
|
||||
|
||||
override fun initializeDependencies(): Any {
|
||||
val dependencies = DaggerBuyFeatureComponent_BuyFeatureDependenciesComponent.builder()
|
||||
.commonApi(commonApi())
|
||||
.accountFeatureApi(getFeature(AccountFeatureApi::class.java))
|
||||
.walletFeatureApi(getFeature(WalletFeatureApi::class.java))
|
||||
.runtimeApi(getFeature(RuntimeApi::class.java))
|
||||
.build()
|
||||
|
||||
return DaggerBuyFeatureComponent.factory()
|
||||
.create(router, dependencies)
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.di
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.common.utils.ip.IpAddressReceiver
|
||||
import io.novafoundation.nova.common.utils.webView.InterceptingWebViewClientFactory
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.mixin.TradeMixin
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.common.MercuryoSignatureFactory
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoBuyRequestInterceptorFactory
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoSellRequestInterceptorFactory
|
||||
import io.novafoundation.nova.feature_buy_impl.BuildConfig
|
||||
import io.novafoundation.nova.feature_buy_impl.di.deeplinks.DeepLinkModule
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.common.RealMercuryoSignatureFactory
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.RealTradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.banxa.BanxaProvider
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.mercurio.MercuryoIntegratorFactory
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.mercurio.MercuryoProvider
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.transak.TransakProvider
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.mixin.TradeMixinFactory
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.interceptors.mercuryo.RealMercuryoBuyRequestInterceptorFactory
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.interceptors.mercuryo.RealMercuryoSellRequestInterceptorFactory
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@Module(includes = [DeepLinkModule::class])
|
||||
class BuyFeatureModule {
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideMercuryoSellRequestInterceptorFactory(
|
||||
gson: Gson,
|
||||
okHttpClient: OkHttpClient
|
||||
): MercuryoSellRequestInterceptorFactory = RealMercuryoSellRequestInterceptorFactory(
|
||||
gson = gson,
|
||||
okHttpClient = okHttpClient
|
||||
)
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideMercuryoBuyRequestInterceptorFactory(
|
||||
gson: Gson,
|
||||
okHttpClient: OkHttpClient
|
||||
): MercuryoBuyRequestInterceptorFactory = RealMercuryoBuyRequestInterceptorFactory(
|
||||
gson = gson,
|
||||
okHttpClient = okHttpClient
|
||||
)
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideMercuryoSignatureGenerator(
|
||||
ipAddressReceiver: IpAddressReceiver
|
||||
): MercuryoSignatureFactory = RealMercuryoSignatureFactory(ipAddressReceiver)
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideMercuryoIntegratorFactory(
|
||||
mercuryoBuyInterceptorFactory: MercuryoBuyRequestInterceptorFactory,
|
||||
mercuryoSellInterceptorFactory: MercuryoSellRequestInterceptorFactory,
|
||||
interceptingWebViewClientFactory: InterceptingWebViewClientFactory,
|
||||
mercuryoSignatureFactory: MercuryoSignatureFactory
|
||||
): MercuryoIntegratorFactory {
|
||||
return MercuryoIntegratorFactory(
|
||||
mercuryoBuyInterceptorFactory,
|
||||
mercuryoSellInterceptorFactory,
|
||||
interceptingWebViewClientFactory,
|
||||
mercuryoSignatureFactory
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideBanxaProvider(): BanxaProvider {
|
||||
return BanxaProvider(BuildConfig.BANXA_HOST)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideMercuryoProvider(integratorFactory: MercuryoIntegratorFactory): MercuryoProvider {
|
||||
return MercuryoProvider(
|
||||
host = BuildConfig.MERCURYO_HOST,
|
||||
widgetId = BuildConfig.MERCURYO_WIDGET_ID,
|
||||
secret = BuildConfig.MERCURYO_SECRET,
|
||||
integratorFactory = integratorFactory
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideTransakProvider(context: Context): TransakProvider {
|
||||
val environment = if (BuildConfig.DEBUG) "STAGING" else "PRODUCTION"
|
||||
|
||||
return TransakProvider(
|
||||
host = BuildConfig.TRANSAK_HOST,
|
||||
referrerDomain = context.packageName,
|
||||
environment = environment
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideBuyTokenIntegration(
|
||||
transakProvider: TransakProvider,
|
||||
mercuryoProvider: MercuryoProvider,
|
||||
banxaProvider: BanxaProvider
|
||||
): TradeTokenRegistry {
|
||||
return RealTradeTokenRegistry(
|
||||
providers = listOf(
|
||||
mercuryoProvider,
|
||||
transakProvider,
|
||||
banxaProvider,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideBuyMixinFactory(
|
||||
buyTokenRegistry: TradeTokenRegistry
|
||||
): TradeMixin.Factory = TradeMixinFactory(
|
||||
buyTokenRegistry = buyTokenRegistry
|
||||
)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.di.deeplinks
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.feature_buy_api.di.deeplinks.BuyDeepLinks
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.deeplink.BuyCallbackDeepLinkHandler
|
||||
|
||||
@Module
|
||||
class DeepLinkModule {
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideBuyCallbackDeepLinkHandler(
|
||||
resourceManager: ResourceManager
|
||||
) = BuyCallbackDeepLinkHandler(resourceManager)
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideDeepLinks(buyCallback: BuyCallbackDeepLinkHandler): BuyDeepLinks {
|
||||
return BuyDeepLinks(listOf(buyCallback))
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation
|
||||
|
||||
import io.novafoundation.nova.common.navigation.ReturnableRouter
|
||||
|
||||
interface BuyRouter : ReturnableRouter
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.common
|
||||
|
||||
interface MercuryoSignatureFactory {
|
||||
|
||||
suspend fun createSignature(address: String, secret: String, merchantTransactionId: String): String
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.common
|
||||
|
||||
import io.novafoundation.nova.common.utils.ip.IpAddressReceiver
|
||||
import io.novafoundation.nova.common.utils.sha512
|
||||
import io.novasama.substrate_sdk_android.extensions.toHexString
|
||||
|
||||
class RealMercuryoSignatureFactory(
|
||||
private val ipAddressReceiver: IpAddressReceiver
|
||||
) : MercuryoSignatureFactory {
|
||||
|
||||
override suspend fun createSignature(address: String, secret: String, merchantTransactionId: String): String {
|
||||
val ip = ipAddressReceiver.get()
|
||||
val signature = "$address$secret$ip$merchantTransactionId".encodeToByteArray()
|
||||
.sha512()
|
||||
.toHexString()
|
||||
|
||||
return "v2:$signature"
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.common
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
fun generateMerchantTransactionId(): String {
|
||||
return UUID.randomUUID().toString()
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.deeplink
|
||||
|
||||
import android.net.Uri
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.common.utils.singleReplaySharedFlow
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.providers.ProviderUtils
|
||||
import io.novafoundation.nova.feature_deep_linking.R
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.CallbackEvent
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.DeepLinkHandler
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
||||
class BuyCallbackDeepLinkHandler(
|
||||
private val resourceManager: ResourceManager
|
||||
) : DeepLinkHandler {
|
||||
|
||||
override val callbackFlow: MutableSharedFlow<CallbackEvent> = singleReplaySharedFlow()
|
||||
|
||||
override suspend fun matches(data: Uri): Boolean {
|
||||
val link = data.toString()
|
||||
return ProviderUtils.REDIRECT_URL_BASE in link
|
||||
}
|
||||
|
||||
override suspend fun handleDeepLink(data: Uri) = runCatching {
|
||||
val message = resourceManager.getString(R.string.buy_completed)
|
||||
callbackFlow.emit(CallbackEvent.Message(message))
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.mixin
|
||||
|
||||
import io.novafoundation.nova.common.utils.WithCoroutineScopeExtensions
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeProvider
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.mixin.TradeMixin
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
internal class TradeMixinFactory(
|
||||
private val buyTokenRegistry: TradeTokenRegistry,
|
||||
) : TradeMixin.Factory {
|
||||
|
||||
override fun create(scope: CoroutineScope): TradeMixin.Presentation {
|
||||
return TradeProviderMixin(
|
||||
buyTokenRegistry = buyTokenRegistry,
|
||||
coroutineScope = scope
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TradeProviderMixin(
|
||||
private val buyTokenRegistry: TradeTokenRegistry,
|
||||
coroutineScope: CoroutineScope,
|
||||
) : TradeMixin.Presentation,
|
||||
CoroutineScope by coroutineScope,
|
||||
WithCoroutineScopeExtensions by WithCoroutineScopeExtensions(coroutineScope) {
|
||||
|
||||
override fun providersFor(chainAsset: Chain.Asset, tradeType: TradeTokenRegistry.TradeType): List<TradeProvider> {
|
||||
return buyTokenRegistry.availableProvidersFor(chainAsset, tradeType)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T> providerFor(chainAsset: Chain.Asset, tradeFlow: TradeTokenRegistry.TradeType, providerId: String): T {
|
||||
return providersFor(chainAsset, tradeFlow)
|
||||
.first { it.id == providerId } as T
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade
|
||||
|
||||
import io.novafoundation.nova.common.utils.hasIntersectionWith
|
||||
import io.novafoundation.nova.common.utils.mapToSet
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
|
||||
class RealTradeTokenRegistry(private val providers: List<TradeTokenRegistry.Provider<*>>) : TradeTokenRegistry {
|
||||
|
||||
override fun hasProvider(chainAsset: Chain.Asset): Boolean {
|
||||
val supportedProviderIds = providers.mapToSet { it.id }
|
||||
return supportedProviderIds.hasIntersectionWith(chainAsset.buyProviders.keys) ||
|
||||
supportedProviderIds.hasIntersectionWith(chainAsset.sellProviders.keys)
|
||||
}
|
||||
|
||||
override fun hasProvider(chainAsset: Chain.Asset, tradeType: TradeTokenRegistry.TradeType): Boolean {
|
||||
return availableProvidersFor(chainAsset, tradeType).isNotEmpty()
|
||||
}
|
||||
|
||||
override fun availableProvidersFor(chainAsset: Chain.Asset, tradeType: TradeTokenRegistry.TradeType) = providers
|
||||
.filter { provider ->
|
||||
val providersByType = when (tradeType) {
|
||||
TradeTokenRegistry.TradeType.BUY -> chainAsset.buyProviders
|
||||
TradeTokenRegistry.TradeType.SELL -> chainAsset.sellProviders
|
||||
}
|
||||
|
||||
provider.id in providersByType
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.interceptors.mercuryo
|
||||
|
||||
import android.webkit.WebResourceRequest
|
||||
import com.google.gson.Gson
|
||||
import io.novafoundation.nova.common.utils.webView.makeRequestBlocking
|
||||
import io.novafoundation.nova.common.utils.webView.toOkHttpRequestBuilder
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoBuyRequestInterceptor
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoBuyRequestInterceptorFactory
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class RealMercuryoBuyRequestInterceptorFactory(
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val gson: Gson
|
||||
) : MercuryoBuyRequestInterceptorFactory {
|
||||
override fun create(onTradeOperationFinishedListener: OnTradeOperationFinishedListener): MercuryoBuyRequestInterceptor {
|
||||
return RealMercuryoBuyRequestInterceptor(okHttpClient, gson, onTradeOperationFinishedListener)
|
||||
}
|
||||
}
|
||||
|
||||
class RealMercuryoBuyRequestInterceptor(
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val gson: Gson,
|
||||
private val onTradeOperationFinishedListener: OnTradeOperationFinishedListener
|
||||
) : MercuryoBuyRequestInterceptor {
|
||||
|
||||
private val interceptionPattern = Regex("https://api\\.mercuryo\\.io/[a-zA-Z0-9.]+/widget/buy/([a-zA-Z0-9]+)/status.*")
|
||||
|
||||
override fun intercept(request: WebResourceRequest): Boolean {
|
||||
val url = request.url.toString()
|
||||
|
||||
val matches = interceptionPattern.find(url)
|
||||
|
||||
if (matches != null) {
|
||||
return performOkHttpRequest(request)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun performOkHttpRequest(request: WebResourceRequest): Boolean {
|
||||
val requestBuilder = request.toOkHttpRequestBuilder()
|
||||
|
||||
return try {
|
||||
val response = okHttpClient.makeRequestBlocking(requestBuilder)
|
||||
val buyStatusResponse = gson.fromJson(response.body!!.string(), BuyStatusResponse::class.java)
|
||||
|
||||
if (buyStatusResponse.isPaid()) {
|
||||
onTradeOperationFinishedListener.onTradeOperationFinished(success = true)
|
||||
}
|
||||
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {
|
||||
* "status": 200,
|
||||
* "data": {
|
||||
* "id": "0da637056a0c85319",
|
||||
* "status": "paid", // new, pending, paid
|
||||
* "payment_status": "charged",
|
||||
* "withdraw_transaction": {
|
||||
* "id": "0da63727a3ce33010",
|
||||
* "address": "12gkMmfdKq7aEnAXwb2NSxh9vLqKifoCaoafLrR6E6swZRmc",
|
||||
* "fee": "0",
|
||||
* "url": ""
|
||||
* },
|
||||
* "currency": "DOT",
|
||||
* "amount": "2.4742695641",
|
||||
* "fiat_currency": "USD",
|
||||
* "fiat_amount": "11.00",
|
||||
* "address": "12gkMmfdKq7aEnAXwb2NSxh9vLqKifoCaoafLrR6E6swZRmc",
|
||||
* "transaction": {
|
||||
* "id": "0da637056f0821757"
|
||||
* },
|
||||
* "local_fiat_currency_total": {
|
||||
* "local_fiat_currency": "EUR",
|
||||
* "local_fiat_amount": "10.25"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
private class BuyStatusResponse(val data: Data) {
|
||||
|
||||
class Data(val status: String)
|
||||
}
|
||||
|
||||
private fun BuyStatusResponse.isPaid() = data.status == "paid"
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.interceptors.mercuryo
|
||||
|
||||
import android.webkit.WebResourceRequest
|
||||
import com.google.gson.Gson
|
||||
import io.novafoundation.nova.common.utils.webView.makeRequestBlocking
|
||||
import io.novafoundation.nova.common.utils.webView.toOkHttpRequestBuilder
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnSellOrderCreatedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoSellRequestInterceptor
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoSellRequestInterceptorFactory
|
||||
import java.math.BigDecimal
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class RealMercuryoSellRequestInterceptorFactory(
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val gson: Gson
|
||||
) : MercuryoSellRequestInterceptorFactory {
|
||||
override fun create(
|
||||
tradeSellCallback: OnSellOrderCreatedListener,
|
||||
onTradeOperationFinishedListener: OnTradeOperationFinishedListener
|
||||
): MercuryoSellRequestInterceptor {
|
||||
return RealMercuryoSellRequestInterceptor(okHttpClient, gson, tradeSellCallback, onTradeOperationFinishedListener)
|
||||
}
|
||||
}
|
||||
|
||||
class RealMercuryoSellRequestInterceptor(
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val gson: Gson,
|
||||
private val tradeSellCallback: OnSellOrderCreatedListener,
|
||||
private val onTradeOperationFinishedListener: OnTradeOperationFinishedListener
|
||||
) : MercuryoSellRequestInterceptor {
|
||||
|
||||
private val openedOrderIds = mutableSetOf<String>()
|
||||
|
||||
private val interceptionPattern = Regex("https://api\\.mercuryo\\.io/[a-zA-Z0-9.]+/widget/sell-request/([a-zA-Z0-9]+)/status.*")
|
||||
|
||||
override fun intercept(request: WebResourceRequest): Boolean {
|
||||
val url = request.url.toString()
|
||||
|
||||
val matches = interceptionPattern.find(url)
|
||||
|
||||
if (matches != null) {
|
||||
val orderId = matches.groupValues[1]
|
||||
return performOkHttpRequest(orderId, request)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun performOkHttpRequest(orderId: String, request: WebResourceRequest): Boolean {
|
||||
val requestBuilder = request.toOkHttpRequestBuilder()
|
||||
|
||||
return try {
|
||||
val response = okHttpClient.makeRequestBlocking(requestBuilder)
|
||||
val sellStatusResponse = gson.fromJson(response.body!!.string(), SellStatusResponse::class.java)
|
||||
|
||||
// We should check that this data is exist in response before handling. Otherwise we will get an exception
|
||||
val address = sellStatusResponse.getAddress() ?: error("Address must be not null")
|
||||
val amount = sellStatusResponse.getAmount() ?: error("Amount must be not null")
|
||||
|
||||
when {
|
||||
sellStatusResponse.isNew() && orderId !in openedOrderIds -> {
|
||||
tradeSellCallback.onSellOrderCreated(orderId, address, amount)
|
||||
openedOrderIds.add(orderId)
|
||||
}
|
||||
|
||||
sellStatusResponse.isCompleted() -> onTradeOperationFinishedListener.onTradeOperationFinished(success = true)
|
||||
}
|
||||
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {
|
||||
* "status": 200,
|
||||
* "data": {
|
||||
* "status": "completed", // Status may be new, pending, completed
|
||||
* "is_partially_paid": 0,
|
||||
* "amounts": {
|
||||
* "request": {
|
||||
* "amount": "3.55",
|
||||
* "currency": "DOT",
|
||||
* "fiat_amount": "25.00",
|
||||
* "fiat_currency": "EUR"
|
||||
* },
|
||||
* "deposit": {
|
||||
* "amount": "3.5544722879",
|
||||
* "currency": "DOT",
|
||||
* "fiat_amount": "28.00",
|
||||
* "fiat_currency": "EUR"
|
||||
* },
|
||||
* "payout": {
|
||||
* "amount": "3.5544722879",
|
||||
* "currency": "DOT",
|
||||
* "fiat_amount": "24.98",
|
||||
* "fiat_currency": "EUR"
|
||||
* }
|
||||
* },
|
||||
* "next": null,
|
||||
* "deposit_transaction": {
|
||||
* "id": "1gb8dnc28jds8ch",
|
||||
* "address": "15AsDPtQ6rZdJgsLsEmQCahym5STRVBVaUYjWFiRRinMjYYaw",
|
||||
* "url": "https://polkadot.subscan.io/extrinsic/0x178f96e1f8837a3dd75ff8b5a5d4422c5c0f7848fbf5c00e343f03b9466e408b"
|
||||
* },
|
||||
* "address": "15AsDPtQ6rZdJgsLsEmQCahym5STRVBVaUYjWFiRRinMjYYaw",
|
||||
* "fiat_card_id": "1gb8dnc28jds8ch"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
private class SellStatusResponse(val data: Data?) {
|
||||
|
||||
class Data(
|
||||
val status: String?,
|
||||
val amounts: Amounts?,
|
||||
val address: String?
|
||||
)
|
||||
|
||||
class Amounts(val request: Request?) {
|
||||
|
||||
class Request(
|
||||
val amount: String?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SellStatusResponse.getAmount(): BigDecimal? {
|
||||
return data?.amounts?.request?.amount?.toBigDecimal()
|
||||
}
|
||||
|
||||
private fun SellStatusResponse.getAddress(): String? {
|
||||
return data?.address
|
||||
}
|
||||
|
||||
private fun SellStatusResponse.isNew() = data?.status == "new"
|
||||
|
||||
private fun SellStatusResponse.isCompleted() = data?.status == "completed"
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.banxa
|
||||
|
||||
import android.net.Uri
|
||||
import android.webkit.WebView
|
||||
import io.novafoundation.nova.common.utils.appendNullableQueryParameter
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnSellOrderCreatedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.providers.WebViewIntegrationProvider
|
||||
import io.novafoundation.nova.feature_buy_impl.R
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
|
||||
private const val COIN_KEY = "coinType"
|
||||
private const val BLOCKCHAIN_KEY = "blockchain"
|
||||
|
||||
class BanxaProvider(
|
||||
private val host: String
|
||||
) : WebViewIntegrationProvider {
|
||||
|
||||
override val id: String = "banxa"
|
||||
|
||||
override val name: String = "Banxa"
|
||||
override val officialUrl: String = "banxa.com"
|
||||
override val logoRes: Int = R.drawable.ic_banxa_provider_logo
|
||||
|
||||
override fun getDescriptionRes(tradeType: TradeTokenRegistry.TradeType): Int {
|
||||
return R.string.banxa_provider_description
|
||||
}
|
||||
|
||||
override fun getPaymentMethods(tradeType: TradeTokenRegistry.TradeType): List<TradeTokenRegistry.PaymentMethod> {
|
||||
return when (tradeType) {
|
||||
TradeTokenRegistry.TradeType.BUY -> listOf(
|
||||
TradeTokenRegistry.PaymentMethod.Visa,
|
||||
TradeTokenRegistry.PaymentMethod.MasterCard,
|
||||
TradeTokenRegistry.PaymentMethod.ApplePay,
|
||||
TradeTokenRegistry.PaymentMethod.GooglePay,
|
||||
TradeTokenRegistry.PaymentMethod.Sepa,
|
||||
TradeTokenRegistry.PaymentMethod.Other(5)
|
||||
)
|
||||
|
||||
TradeTokenRegistry.TradeType.SELL -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun createIntegrator(
|
||||
chainAsset: Chain.Asset,
|
||||
address: String,
|
||||
tradeFlow: TradeTokenRegistry.TradeType,
|
||||
onCloseListener: OnTradeOperationFinishedListener,
|
||||
onSellOrderCreatedListener: OnSellOrderCreatedListener
|
||||
): WebViewIntegrationProvider.Integrator {
|
||||
val providerDetails = chainAsset.buyProviders.getValue(id)
|
||||
val blockchain = providerDetails[BLOCKCHAIN_KEY] as? String
|
||||
val coinType = providerDetails[COIN_KEY] as? String
|
||||
return BanxaIntegrator(host, blockchain, coinType, address)
|
||||
}
|
||||
|
||||
private class BanxaIntegrator(
|
||||
private val host: String,
|
||||
private val blockchain: String?,
|
||||
private val coinType: String?,
|
||||
private val address: String
|
||||
) : WebViewIntegrationProvider.Integrator {
|
||||
|
||||
override suspend fun run(using: WebView) {
|
||||
using.loadUrl(createLink())
|
||||
}
|
||||
|
||||
private fun createLink(): String {
|
||||
return Uri.Builder()
|
||||
.scheme("https")
|
||||
.authority(host)
|
||||
.appendNullableQueryParameter(BLOCKCHAIN_KEY, blockchain)
|
||||
.appendNullableQueryParameter(COIN_KEY, coinType)
|
||||
.appendQueryParameter("walletAddress", address)
|
||||
.build()
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.mercurio
|
||||
|
||||
import android.net.Uri
|
||||
import android.webkit.WebView
|
||||
import io.novafoundation.nova.common.utils.TokenSymbol
|
||||
import io.novafoundation.nova.common.utils.appendNullableQueryParameter
|
||||
import io.novafoundation.nova.common.utils.urlEncoded
|
||||
import io.novafoundation.nova.common.utils.webView.InterceptingWebViewClient
|
||||
import io.novafoundation.nova.common.utils.webView.InterceptingWebViewClientFactory
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnSellOrderCreatedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.common.MercuryoSignatureFactory
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.common.generateMerchantTransactionId
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoBuyRequestInterceptorFactory
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.interceptors.mercuryo.MercuryoSellRequestInterceptorFactory
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.providers.WebViewIntegrationProvider
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.providers.ProviderUtils
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.mercurio.MercuryoIntegrator.Payload
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MercuryoIntegratorFactory(
|
||||
private val mercuryoBuyInterceptorFactory: MercuryoBuyRequestInterceptorFactory,
|
||||
private val mercuryoSellInterceptorFactory: MercuryoSellRequestInterceptorFactory,
|
||||
private val interceptingWebViewClientFactory: InterceptingWebViewClientFactory,
|
||||
private val signatureGenerator: MercuryoSignatureFactory
|
||||
) {
|
||||
|
||||
fun create(
|
||||
payload: Payload,
|
||||
onSellOrderCreatedListener: OnSellOrderCreatedListener,
|
||||
onCloseListener: OnTradeOperationFinishedListener
|
||||
): MercuryoIntegrator {
|
||||
val webViewClient = interceptingWebViewClientFactory.create(
|
||||
listOf(
|
||||
mercuryoBuyInterceptorFactory.create(onCloseListener),
|
||||
mercuryoSellInterceptorFactory.create(onSellOrderCreatedListener, onCloseListener)
|
||||
)
|
||||
)
|
||||
return MercuryoIntegrator(
|
||||
payload,
|
||||
webViewClient,
|
||||
signatureGenerator
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MercuryoIntegrator(
|
||||
private val payload: Payload,
|
||||
private val webViewClient: InterceptingWebViewClient,
|
||||
private val signatureGenerator: MercuryoSignatureFactory
|
||||
) : WebViewIntegrationProvider.Integrator {
|
||||
|
||||
class Payload(
|
||||
val host: String,
|
||||
val widgetId: String,
|
||||
val tokenSymbol: TokenSymbol,
|
||||
val network: String?,
|
||||
val address: String,
|
||||
val secret: String,
|
||||
val tradeFlow: TradeTokenRegistry.TradeType
|
||||
)
|
||||
|
||||
override suspend fun run(using: WebView) {
|
||||
withContext(Dispatchers.Main) {
|
||||
using.webViewClient = webViewClient
|
||||
|
||||
runCatching {
|
||||
val link = withContext(Dispatchers.IO) { createLink() }
|
||||
using.loadUrl(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createLink(): String {
|
||||
// Merchant transaction id is a custom id we can provide to mercuryo to track a transaction.
|
||||
// Seems useless for us now but required for signature
|
||||
val merchantTransactionId = generateMerchantTransactionId()
|
||||
val signature = signatureGenerator.createSignature(payload.address, payload.secret, merchantTransactionId)
|
||||
|
||||
val urlBuilder = Uri.Builder()
|
||||
.scheme("https")
|
||||
.authority(payload.host)
|
||||
.appendQueryParameter("widget_id", payload.widgetId)
|
||||
.appendQueryParameter("merchant_transaction_id", merchantTransactionId)
|
||||
.appendQueryParameter("type", payload.tradeFlow.getType())
|
||||
.appendNullableQueryParameter(MERCURYO_NETWORK_KEY, payload.network)
|
||||
.appendQueryParameter("currency", payload.tokenSymbol.value)
|
||||
.appendQueryParameter("return_url", ProviderUtils.REDIRECT_URL_BASE.urlEncoded())
|
||||
.appendQueryParameter("signature", signature)
|
||||
.appendQueryParameter("fix_currency", true.toString())
|
||||
|
||||
when (payload.tradeFlow) {
|
||||
TradeTokenRegistry.TradeType.BUY -> urlBuilder.appendQueryParameter("address", payload.address)
|
||||
TradeTokenRegistry.TradeType.SELL -> urlBuilder.appendQueryParameter("refund_address", payload.address)
|
||||
.appendQueryParameter("hide_refund_address", true.toString())
|
||||
}
|
||||
|
||||
return urlBuilder.build().toString()
|
||||
}
|
||||
|
||||
private fun TradeTokenRegistry.TradeType.getType(): String {
|
||||
return when (this) {
|
||||
TradeTokenRegistry.TradeType.BUY -> "buy"
|
||||
TradeTokenRegistry.TradeType.SELL -> "sell"
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.mercurio
|
||||
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnSellOrderCreatedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.providers.WebViewIntegrationProvider
|
||||
import io.novafoundation.nova.feature_buy_impl.R
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
|
||||
const val MERCURYO_NETWORK_KEY = "network"
|
||||
|
||||
class MercuryoProvider(
|
||||
private val host: String,
|
||||
private val widgetId: String,
|
||||
private val secret: String,
|
||||
private val integratorFactory: MercuryoIntegratorFactory
|
||||
) : WebViewIntegrationProvider {
|
||||
|
||||
override val id: String = "mercuryo"
|
||||
|
||||
override val name: String = "Mercuryo"
|
||||
override val officialUrl: String = "mercuryo.io"
|
||||
override val logoRes: Int = R.drawable.ic_mercurio_provider_logo
|
||||
|
||||
override fun getDescriptionRes(tradeType: TradeTokenRegistry.TradeType): Int {
|
||||
return R.string.mercurio_provider_description
|
||||
}
|
||||
|
||||
override fun getPaymentMethods(tradeFlow: TradeTokenRegistry.TradeType): List<TradeTokenRegistry.PaymentMethod> {
|
||||
return when (tradeFlow) {
|
||||
TradeTokenRegistry.TradeType.BUY -> listOf(
|
||||
TradeTokenRegistry.PaymentMethod.Visa,
|
||||
TradeTokenRegistry.PaymentMethod.MasterCard,
|
||||
TradeTokenRegistry.PaymentMethod.ApplePay,
|
||||
TradeTokenRegistry.PaymentMethod.GooglePay,
|
||||
TradeTokenRegistry.PaymentMethod.Sepa,
|
||||
TradeTokenRegistry.PaymentMethod.Other(5)
|
||||
)
|
||||
|
||||
TradeTokenRegistry.TradeType.SELL -> listOf(
|
||||
TradeTokenRegistry.PaymentMethod.Visa,
|
||||
TradeTokenRegistry.PaymentMethod.MasterCard,
|
||||
TradeTokenRegistry.PaymentMethod.Sepa,
|
||||
TradeTokenRegistry.PaymentMethod.BankTransfer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createIntegrator(
|
||||
chainAsset: Chain.Asset,
|
||||
address: String,
|
||||
tradeFlow: TradeTokenRegistry.TradeType,
|
||||
onCloseListener: OnTradeOperationFinishedListener,
|
||||
onSellOrderCreatedListener: OnSellOrderCreatedListener
|
||||
): WebViewIntegrationProvider.Integrator {
|
||||
val network = chainAsset.buyProviders.getValue(id)[MERCURYO_NETWORK_KEY] as? String
|
||||
val payload = MercuryoIntegrator.Payload(host, widgetId, chainAsset.symbol, network, address, secret, tradeFlow)
|
||||
return integratorFactory.create(payload, onSellOrderCreatedListener, onCloseListener)
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.transak
|
||||
|
||||
import android.net.Uri
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import io.novafoundation.nova.common.utils.TokenSymbol
|
||||
import io.novafoundation.nova.common.utils.appendNullableQueryParameter
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.providers.WebViewIntegrationProvider
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnSellOrderCreatedListener
|
||||
|
||||
// You can find a valid implementation in https://github.com/agtransak/TransakAndroidSample/blob/events/app/src/main/java/com/transak/sample/MainActivity.kt
|
||||
private const val JS_BRIDGE_NAME = "Android"
|
||||
|
||||
class TransakIntegrator(
|
||||
private val payload: Payload,
|
||||
private val closeListener: OnTradeOperationFinishedListener,
|
||||
private val sellOrderCreatedListener: OnSellOrderCreatedListener
|
||||
) : WebViewIntegrationProvider.Integrator {
|
||||
|
||||
class Payload(
|
||||
val host: String,
|
||||
val network: String?,
|
||||
val referrerDomain: String,
|
||||
val environment: String,
|
||||
val tokenSymbol: TokenSymbol,
|
||||
val address: String,
|
||||
val tradeFlow: TradeTokenRegistry.TradeType
|
||||
)
|
||||
|
||||
override suspend fun run(using: WebView) {
|
||||
using.webViewClient = TransakWebViewClient()
|
||||
using.addJavascriptInterface(TransakJsEventBridge(closeListener, sellOrderCreatedListener), JS_BRIDGE_NAME)
|
||||
|
||||
using.loadUrl(createLink())
|
||||
}
|
||||
|
||||
private fun createLink(): String {
|
||||
val urlBuilder = Uri.Builder()
|
||||
.scheme("https")
|
||||
.authority(payload.host)
|
||||
.appendQueryParameter("productsAvailed", payload.tradeFlow.getType())
|
||||
.appendQueryParameter("environment", payload.environment)
|
||||
.appendQueryParameter("cryptoCurrencyCode", payload.tokenSymbol.value)
|
||||
.appendQueryParameter("referrerDomain", payload.referrerDomain)
|
||||
.appendNullableQueryParameter(TRANSAK_NETWORK_KEY, payload.network)
|
||||
|
||||
if (payload.tradeFlow == TradeTokenRegistry.TradeType.BUY) {
|
||||
urlBuilder.appendQueryParameter("walletAddress", payload.address)
|
||||
.appendQueryParameter("disableWalletAddressForm", "true")
|
||||
}
|
||||
|
||||
return urlBuilder.build().toString()
|
||||
}
|
||||
|
||||
private fun TradeTokenRegistry.TradeType.getType(): String {
|
||||
return when (this) {
|
||||
TradeTokenRegistry.TradeType.BUY -> "BUY"
|
||||
TradeTokenRegistry.TradeType.SELL -> "SELL"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TransakWebViewClient : WebViewClient() {
|
||||
// We use it to override base transak loading otherwise transak navigates to android native browser
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
view.loadUrl(request.url.toString())
|
||||
return true
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.transak
|
||||
|
||||
import android.util.Log
|
||||
import android.webkit.JavascriptInterface
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnSellOrderCreatedListener
|
||||
import org.json.JSONObject
|
||||
|
||||
class TransakJsEventBridge(
|
||||
private val closeListener: OnTradeOperationFinishedListener,
|
||||
private val tradeSellCallback: OnSellOrderCreatedListener
|
||||
) {
|
||||
|
||||
@JavascriptInterface
|
||||
fun postMessage(eventData: String) {
|
||||
val json = JSONObject(eventData)
|
||||
val eventId = json.getString("event_id")
|
||||
Log.d("TransakEvent", "Event: $eventId, Data: $eventData")
|
||||
|
||||
val data = json.get("data")
|
||||
when (eventId) {
|
||||
"TRANSAK_WIDGET_CLOSE" -> {
|
||||
val isOrderSuccessful = data == true // For unsuccessful order data is JSONObject
|
||||
closeListener.onTradeOperationFinished(isOrderSuccessful)
|
||||
}
|
||||
|
||||
"TRANSAK_ORDER_CREATED" -> {
|
||||
require(data is JSONObject)
|
||||
if (data.getString("isBuyOrSell") == "SELL") {
|
||||
tradeSellCallback.onSellOrderCreated(
|
||||
data.getString("id"),
|
||||
data.getJSONObject("cryptoPaymentData").getString("paymentAddress"),
|
||||
data.getString("cryptoAmount").toBigDecimal()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package io.novafoundation.nova.feature_buy_impl.presentation.trade.providers.transak
|
||||
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.TradeTokenRegistry
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnTradeOperationFinishedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.common.OnSellOrderCreatedListener
|
||||
import io.novafoundation.nova.feature_buy_api.presentation.trade.providers.WebViewIntegrationProvider
|
||||
import io.novafoundation.nova.feature_buy_impl.R
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
|
||||
const val TRANSAK_NETWORK_KEY = "network"
|
||||
|
||||
class TransakProvider(
|
||||
private val host: String,
|
||||
private val referrerDomain: String,
|
||||
private val environment: String
|
||||
) : WebViewIntegrationProvider {
|
||||
|
||||
override val id = "transak"
|
||||
override val name = "Transak"
|
||||
override val officialUrl: String = "transak.com"
|
||||
override val logoRes: Int = R.drawable.ic_transak_provider_logo
|
||||
|
||||
override fun getDescriptionRes(tradeType: TradeTokenRegistry.TradeType): Int {
|
||||
return when (tradeType) {
|
||||
TradeTokenRegistry.TradeType.BUY -> R.string.transak_provider_buy_description
|
||||
TradeTokenRegistry.TradeType.SELL -> R.string.transak_provider_sell_description
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPaymentMethods(tradeType: TradeTokenRegistry.TradeType): List<TradeTokenRegistry.PaymentMethod> {
|
||||
return when (tradeType) {
|
||||
TradeTokenRegistry.TradeType.BUY -> listOf(
|
||||
TradeTokenRegistry.PaymentMethod.Visa,
|
||||
TradeTokenRegistry.PaymentMethod.MasterCard,
|
||||
TradeTokenRegistry.PaymentMethod.ApplePay,
|
||||
TradeTokenRegistry.PaymentMethod.GooglePay,
|
||||
TradeTokenRegistry.PaymentMethod.Sepa,
|
||||
TradeTokenRegistry.PaymentMethod.Other(12)
|
||||
)
|
||||
|
||||
TradeTokenRegistry.TradeType.SELL -> listOf(
|
||||
TradeTokenRegistry.PaymentMethod.Visa,
|
||||
TradeTokenRegistry.PaymentMethod.MasterCard,
|
||||
TradeTokenRegistry.PaymentMethod.Sepa,
|
||||
TradeTokenRegistry.PaymentMethod.BankTransfer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createIntegrator(
|
||||
chainAsset: Chain.Asset,
|
||||
address: String,
|
||||
tradeFlow: TradeTokenRegistry.TradeType,
|
||||
onCloseListener: OnTradeOperationFinishedListener,
|
||||
onSellOrderCreatedListener: OnSellOrderCreatedListener
|
||||
): WebViewIntegrationProvider.Integrator {
|
||||
val network = chainAsset.buyProviders.getValue(id)[TRANSAK_NETWORK_KEY] as? String
|
||||
|
||||
return TransakIntegrator(
|
||||
payload = TransakIntegrator.Payload(
|
||||
host = host,
|
||||
referrerDomain = referrerDomain,
|
||||
environment = environment,
|
||||
network = network,
|
||||
tokenSymbol = chainAsset.symbol,
|
||||
address = address,
|
||||
tradeFlow = tradeFlow
|
||||
),
|
||||
onCloseListener,
|
||||
onSellOrderCreatedListener
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
tools:background="@color/secondary_screen_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/bg_primary_list_item"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="19dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemSheetBuyProviderImage"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
tools:src="@drawable/ic_people_outline"
|
||||
app:tint="@color/icon_primary"
|
||||
tools:tint="@color/text_primary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemSheetBuyProviderText"
|
||||
style="@style/TextAppearance.NovaFoundation.Body1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_weight="1"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@color/text_primary"
|
||||
tools:text="Ramp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:tint="@color/icon_primary"
|
||||
android:src="@drawable/ic_chevron_right" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user