Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ interface SendPaymentService {
val totalAmount: String
)

suspend fun payWithDashUrl(dashUri: String): Transaction
suspend fun payWithDashUrl(dashUri: String, serviceName: String?): Transaction
}
6 changes: 3 additions & 3 deletions common/src/main/java/org/dash/wallet/common/util/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ object Constants {
val HTTP_CLIENT: OkHttpClient = OkHttpClient.Builder()
.followRedirects(false)
.followSslRedirects(true)
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.addInterceptor(
HttpLoggingInterceptor { log.debug(it) }.setLevel(HttpLoggingInterceptor.Level.BASIC)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ data class GiftCardResponse(
@SerializedName("barcodeUrl") val barcodeUrl: String? = "",
@SerializedName("number") val cardNumber: String? = "",
@SerializedName("pin") val cardPin: String? = "",

val cardFiatAmount: String? = "",
val cardFiatCurrency: String? = "",
val merchantName: String? = "",
@SerializedName("paymentCryptoAmount") val cryptoAmount: String? = "",
@SerializedName("paymentCryptoCurrency") val cryptoCurrency: String? = "",
val paymentCryptoNetwork: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class RemoteDataSource @Inject constructor(private val config: CTXSpendConfig) {
authenticator?.let { client.authenticator(it) }
// if (BuildConfig.DEBUG) { TODO
val logging = HttpLoggingInterceptor { message -> log.info(message) }
logging.redactHeader("Authorization")
logging.level = HttpLoggingInterceptor.Level.BODY
client.addInterceptor(logging)
// }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class FakeDashSpendService @Inject constructor(
return realService.estimateNetworkFee(address, amount, emptyWallet)
}

override suspend fun payWithDashUrl(dashUri: String): Transaction {
override suspend fun payWithDashUrl(dashUri: String, serviceName: String?): Transaction {
return if (dashUri.startsWith(DASH_SPEND_SCHEMA)) {
val uri = Uri.parse(dashUri)
val amount = Coin.valueOf(uri.getQueryParameter("amount")?.toLong() ?: 0)
Expand All @@ -74,7 +74,7 @@ class FakeDashSpendService @Inject constructor(
amount
)
} else {
realService.payWithDashUrl(dashUri)
realService.payWithDashUrl(dashUri, serviceName)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.dash.wallet.features.exploredash.repository

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.flow.Flow
import org.dash.wallet.common.data.ResponseResource
import org.dash.wallet.common.data.safeApiCall
Expand All @@ -40,20 +39,22 @@ class CTXSpendException(
message: String,
val errorCode: Int? = null,
val errorBody: String? = null,
cause: Exception? = null
cause: Throwable? = null
) : Exception(message, cause) {
var resourceString: ResourceString? = null
var giftCardResponse: GiftCardResponse? = null
private val errorMap: Map<String, Any>

constructor(message: ResourceString) : this("") {
constructor(message: ResourceString, giftCardResponse: GiftCardResponse? = null) : this("") {
this.resourceString = message
this.giftCardResponse = giftCardResponse
}

init {
val type = object : TypeToken<Map<String, Any>>() {}.type
errorMap = try {
if (errorBody != null) {
Gson().fromJson(errorBody, type) ?: emptyMap()
@Suppress("UNCHECKED_CAST")
Gson().fromJson(errorBody, Map::class.java) as? Map<String, Any> ?: emptyMap()
} else {
emptyMap()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ import org.bitcoinj.utils.MonetaryFormat
import org.dash.wallet.common.Configuration
import org.dash.wallet.common.WalletDataProvider
import org.dash.wallet.common.data.ResponseResource
import org.dash.wallet.common.data.ServiceName
import org.dash.wallet.common.data.entity.ExchangeRate
import org.dash.wallet.common.data.entity.GiftCard
import org.dash.wallet.common.services.*
import org.dash.wallet.common.services.analytics.AnalyticsService
import org.dash.wallet.common.util.Constants
import org.dash.wallet.common.util.toBigDecimal
import org.dash.wallet.features.exploredash.data.ctxspend.model.DenominationType
import org.dash.wallet.features.exploredash.data.ctxspend.model.GetMerchantResponse
import org.dash.wallet.features.exploredash.data.ctxspend.model.GiftCardResponse
Expand Down Expand Up @@ -148,11 +148,11 @@ class CTXSpendViewModel @Inject constructor(
suspend fun purchaseGiftCard(): GiftCardResponse {
giftCardMerchant?.merchantId?.let {
val amountValue = giftCardPaymentValue.value

val fiatAmount = MonetaryFormat.FIAT.noCode().format(amountValue).toString()
val response = try {
repository.purchaseGiftCard(
merchantId = it,
fiatAmount = MonetaryFormat.FIAT.noCode().format(amountValue).toString(),
fiatAmount = fiatAmount,
fiatCurrency = "USD",
cryptoCurrency = Constants.DASH_CURRENCY
)
Expand All @@ -173,19 +173,23 @@ class CTXSpendViewModel @Inject constructor(
is ResponseResource.Failure -> {
log.error("purchaseGiftCard error ${response.errorCode}: ${response.errorBody}")
throw CTXSpendException(
"purchaseGiftCard error ${response.errorCode}: ${response.errorBody}",
"/gift-cards POST { fiatAmount=$fiatAmount, merchantId = $it}",
response.errorCode,
response.errorBody
response.errorBody,
response.throwable
)
}
// else -> {}
}
}
throw CTXSpendException("purchaseGiftCard error")
throw CTXSpendException("purchaseGiftCard error: no merchant")
}

suspend fun createSendingRequestFromDashUri(paymentUri: String): Sha256Hash {
val transaction = sendPaymentService.payWithDashUrl(paymentUri)
val transaction = sendPaymentService.payWithDashUrl(
paymentUri,
giftCardMerchant?.source?.lowercase() ?: ServiceName.CTXSpend
)
log.info("ctx spend transaction: ${transaction.txId}")
transactionMetadata.markGiftCardTransaction(transaction.txId, giftCardMerchant?.logoLocation)

Expand Down Expand Up @@ -273,13 +277,13 @@ class CTXSpendViewModel @Inject constructor(

suspend fun logout() = repository.logout()

fun saveGiftCardDummy(txId: Sha256Hash, giftCardId: String) {
fun saveGiftCardDummy(txId: Sha256Hash, giftCardResponse: GiftCardResponse) {
val giftCard = GiftCard(
txId = txId,
merchantName = giftCardMerchant?.name ?: "",
price = giftCardPaymentValue.value.toBigDecimal().toDouble(),
price = giftCardResponse.cardFiatAmount?.toDouble() ?: 0.0,
merchantUrl = giftCardMerchant?.website,
note = giftCardId
note = giftCardResponse.id
)
viewModelScope.launch {
giftCardDao.insertGiftCard(giftCard)
Expand Down Expand Up @@ -329,10 +333,11 @@ class CTXSpendViewModel @Inject constructor(

fun createEmailIntent(
subject: String,
ex: CTXSpendException
sentToCTX: Boolean,
ex: CTXSpendException?
) = Intent(Intent.ACTION_SEND).apply {
setType("message/rfc822")
putExtra(Intent.EXTRA_EMAIL, arrayOf(CTXSpendConstants.REPORT_EMAIL))
putExtra(Intent.EXTRA_EMAIL, arrayOf(if (sentToCTX) CTXSpendConstants.REPORT_EMAIL else "support@dash.org"))
putExtra(Intent.EXTRA_SUBJECT, subject)
putExtra(Intent.EXTRA_TEXT, createReportEmail(ex))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
Expand All @@ -342,7 +347,7 @@ class CTXSpendViewModel @Inject constructor(
return savedStateHandle.get<String>(MERCHANT_ID_KEY)
}

private fun createReportEmail(ex: CTXSpendException): String {
private fun createReportEmail(ex: CTXSpendException?): String {
val report = StringBuilder()
report.append("CTX Issue Report").append("\n")
giftCardMerchant?.let { merchant ->
Expand All @@ -358,15 +363,42 @@ class CTXSpendViewModel @Inject constructor(
} ?: run {
report.append("No merchant selected").append("\n")
}

report.append("\n")
report.append("Purchase Details").append("\n")
report.append("amount: ").append(giftCardPaymentValue.value.toFriendlyString()).append("\n")
report.append("amount entered: ").append(giftCardPaymentValue.value.toFriendlyString()).append("\n")
report.append("\n")
ex.errorCode?.let {
report.append("code: ").append(it).append("\n")
}
ex.errorBody?.let {
report.append("body:\n").append(it).append("\n")
ex?.let { exception ->
exception.message?.let {
report.append(it).append("\n")
}
exception.errorCode?.let {
report.append("code: ").append(it).append("\n")
}
exception.errorBody?.let {
report.append("body:\n").append(it).append("\n")
}
exception.giftCardResponse?.let { giftCard ->
report.append("Gift Card Information: ").append("\n")
.append("id: ").append(giftCard.id).append("\n")
.append("status: ").append(giftCard.status).append("\n")
.append("barcodeUrl: ").append(giftCard.barcodeUrl ?: "N/A").append("\n")
.append("cardNumber: ").append(giftCard.cardNumber ?: "N/A").append("\n")
.append("cardPin: ").append(giftCard.cardPin ?: "N/A").append("\n")
.append("cryptoAmount: ").append(giftCard.cryptoAmount ?: "N/A").append("\n")
.append("cryptoCurrency: ").append(giftCard.cryptoCurrency ?: "N/A").append("\n")
.append("paymentCryptoNetwork: ").append(giftCard.paymentCryptoNetwork).append("\n")
.append("paymentId: ").append(giftCard.paymentId).append("\n")
.append("percentDiscount: ").append(giftCard.percentDiscount).append("\n")
.append("rate: ").append(giftCard.rate).append("\n")
.append("redeemUrl: ").append(giftCard.redeemUrl).append("\n")
.append("fiatAmount: ").append(giftCard.fiatAmount ?: "N/A").append("\n")
.append("fiatCurrency: ").append(giftCard.fiatCurrency ?: "N/A").append("\n")
.append("paymentUrls: ").append(giftCard.paymentUrls?.toString() ?: "N/A").append("\n")
}
exception.cause?.let {
report.append("Stack trace\n").append(exception.stackTraceToString())
}
}
return report.toString()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,20 +353,20 @@ class PurchaseGiftCardFragment : Fragment(R.layout.fragment_purchase_ctxspend_gi
}

private fun exceedsBalance(): Boolean {
val rate = viewModel.usdExchangeRate.value
val rate = viewModel.usdExchangeRate.value ?: return false

if (rate == null) {
return false
}

val balanceWithDiscount = viewModel.balanceWithDiscount

if (balanceWithDiscount == null) {
return false
}
val balanceWithDiscount = viewModel.balanceWithDiscount ?: return false

var paymentValue = viewModel.giftCardPaymentValue.value
val myRate = ExchangeRate(rate.fiat)
val amountDash = myRate.fiatToCoin(viewModel.giftCardPaymentValue.value)
// this is called when the after a purchase with the user's selected currency, not USD
if (paymentValue.currencyCode != Constants.USD_CURRENCY) {
paymentValue = Fiat.valueOf(
Constants.USD_CURRENCY,
paymentValue.value
)
}
val amountDash = myRate.fiatToCoin(paymentValue)

return amountDash.isGreaterThan(balanceWithDiscount)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.os.Bundle
import android.util.Size
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StyleRes
import androidx.core.net.toUri
import androidx.core.os.bundleOf
Expand Down Expand Up @@ -55,6 +56,7 @@ import org.dash.wallet.features.exploredash.R
import org.dash.wallet.features.exploredash.data.ctxspend.model.Barcode
import org.dash.wallet.features.exploredash.databinding.DialogGiftCardDetailsBinding
import org.dash.wallet.features.exploredash.repository.CTXSpendException
import org.dash.wallet.features.exploredash.ui.ctxspend.CTXSpendViewModel
import org.slf4j.LoggerFactory
import java.text.DecimalFormat
import java.text.NumberFormat
Expand All @@ -79,6 +81,7 @@ class GiftCardDetailsDialog : OffsetDialogFragment(R.layout.dialog_gift_card_det
override val forceExpand = true
private val binding by viewBinding(DialogGiftCardDetailsBinding::bind)
private val viewModel by viewModels<GiftCardDetailsViewModel>()
private val ctxSpendViewModel by viewModels<CTXSpendViewModel>()
private var originalBrightness: Float = -1f

private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
Expand All @@ -92,6 +95,10 @@ class GiftCardDetailsDialog : OffsetDialogFragment(R.layout.dialog_gift_card_det
}
}

private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
// Optionally handle result here
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

Expand Down Expand Up @@ -162,8 +169,10 @@ class GiftCardDetailsDialog : OffsetDialogFragment(R.layout.dialog_gift_card_det

binding.cardError.isVisible = true
binding.cardError.text = message ?: getString(R.string.gift_card_details_error)
binding.contactSupport.isVisible = true
} else {
binding.cardError.isVisible = false
binding.contactSupport.isVisible = false
}
}

Expand All @@ -174,6 +183,19 @@ class GiftCardDetailsDialog : OffsetDialogFragment(R.layout.dialog_gift_card_det
binding.viewTransactionDetailsCard.setOnClickListener {
deepLinkNavigate(DeepLinkDestination.Transaction(viewModel.transactionId.toString()))
}
binding.contactSupport.setOnClickListener {
val intent = ctxSpendViewModel.createEmailIntent(
"CTX Issue with tx: ${viewModel.transactionId.toStringBase58()}",
sentToCTX = true,
viewModel.uiState.value.error as? CTXSpendException
)

val chooser = Intent.createChooser(
intent,
getString(R.string.report_issue_dialog_mail_intent_chooser)
)
launcher.launch(chooser)
}

subscribeToBottomSheetCallback()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ class GiftCardDetailsViewModel @Inject constructor(
ResourceString(
R.string.gift_card_redeem_url_not_supported,
listOf(giftCard.id, giftCard.paymentId, txid)
)
),
giftCard
)
)
}
Expand All @@ -183,7 +184,8 @@ class GiftCardDetailsViewModel @Inject constructor(
ResourceString(
R.string.gift_card_rejected,
listOf(giftCard.id, giftCard.paymentId, txid)
)
),
giftCard
)
)
}
Expand Down
Loading