Skip to content

Commit cf33c74

Browse files
authored
Merge pull request #702 from code-payments/feat/notify-server-swap-before-gpay
feat(onramp): notify server of swap before Google Pay is presented
2 parents a6de615 + 2540b7b commit cf33c74

5 files changed

Lines changed: 20 additions & 72 deletions

File tree

apps/flipcash/features/onramp/src/main/kotlin/com/flipcash/app/onramp/internal/OnRampViewModel.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ internal class OnRampViewModel @Inject constructor(
176176
dispatchEvent(Event.UpdateOrderLookupState())
177177
}
178178
is CoinbaseOnRampState.Paying -> dispatchEvent(Event.UpdateOrderLookupState(loading = true))
179-
is CoinbaseOnRampState.Processing -> dispatchEvent(Event.UpdateOrderLookupState(loading = true))
180179
}
181180
}
182181
.launchIn(viewModelScope)

apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampController.kt

Lines changed: 11 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ import com.getcode.opencode.model.financial.Token
2525
import com.getcode.opencode.model.financial.usdf
2626
import com.getcode.opencode.model.transactions.SwapFundingSource
2727
import com.getcode.solana.keys.base58
28-
import com.getcode.utils.network.pollUntil
29-
import com.getcode.vendor.Base58
3028
import com.flipcash.app.core.AppRoute
3129
import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError
3230
import com.getcode.utils.CodeServerError
@@ -46,9 +44,7 @@ import kotlinx.serialization.Serializable
4644
import kotlinx.serialization.json.Json
4745
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
4846
import retrofit2.HttpException
49-
import java.security.SecureRandom
5047
import javax.inject.Inject
51-
import kotlin.time.Duration.Companion.seconds
5248

5349
typealias OrderWithPaymentLink = Pair<String, OnRampPurchaseResponse.PaymentLink>
5450

@@ -79,15 +75,15 @@ class CoinbaseOnRampController @Inject constructor(
7975
_pendingNavigation.tryEmit(route)
8076
}
8177

82-
fun startPayment(order: OnrampOrder, token: Token, amount: VerifiedFiat) {
83-
_state.value = CoinbaseOnRampState.Paying(order, token, amount)
78+
fun startPayment(order: OnrampOrder, token: Token, amount: VerifiedFiat, swapId: SwapId) {
79+
_state.value = CoinbaseOnRampState.Paying(order, token, amount, swapId)
8480
}
8581

8682
fun onPaymentSuccess(orderId: String) {
8783
val current = _state.value
8884
if (current is CoinbaseOnRampState.Paying) {
8985
_state.update {
90-
CoinbaseOnRampState.Processing(orderId, current.token, current.amount)
86+
CoinbaseOnRampState.Completed(current.swapId, current.token, current.amount)
9187
}
9288
}
9389
}
@@ -118,56 +114,20 @@ class CoinbaseOnRampController @Inject constructor(
118114
}
119115

120116
return placeOrderInclusiveOfFees(amount)
121-
.map { (orderId, paymentLink) ->
122-
val order = OnrampOrder(orderId, paymentLink.url)
123-
startPayment(order, token, verifiedFiat)
124-
}
125-
}
126-
127-
suspend fun processPayment(): Result<SwapId> {
128-
val current = _state.value
129-
if (current !is CoinbaseOnRampState.Processing) {
130-
return Result.failure(IllegalStateException("Not in Processing state"))
131-
}
132-
133-
return pollUntil(
134-
call = { lookupOrder(current.orderId).getOrThrow() },
135-
required = { order -> order.txHash != null },
136-
maxAttempts = 100,
137-
interval = 3.seconds,
138-
tag = "CoinbaseOrderPoller",
139-
).mapCatching { order ->
140-
order.txHash ?: throw IllegalStateException("No hash provided from provider")
141-
}.mapCatching { txHash ->
117+
.mapCatching { (orderId, paymentLink) ->
142118
val owner = userManager.accountCluster
143119
?: throw IllegalStateException("No account cluster")
144120

145-
transactionController.buy(
121+
val swapId = transactionController.buy(
146122
owner = owner,
147-
amount = current.amount,
148-
of = current.token,
149-
source = SwapFundingSource.ExternalWallet(
150-
transactionSignature = runCatching { Base58.decode(txHash) }
151-
.getOrElse { ByteArray(64).also { SecureRandom().nextBytes(it) } }
152-
.toList()
153-
),
123+
amount = verifiedFiat,
124+
of = token,
125+
source = SwapFundingSource.CoinbaseOnramp(orderId = orderId),
154126
fund = { Result.success(Unit) }
155127
).getOrThrow()
156-
}
157-
.onSuccess { swapId ->
158-
_state.update { CoinbaseOnRampState.Completed(swapId, current.token, current.amount) }
159-
}
160-
.onFailure { error ->
161-
trace(
162-
message = "Payment processing failed",
163-
tag = "OnRamp",
164-
metadata = {
165-
"orderId" to current.orderId
166-
"errorType" to error::class.simpleName.orEmpty()
167-
},
168-
error = error,
169-
)
170-
reset()
128+
129+
val order = OnrampOrder(orderId, paymentLink.url)
130+
startPayment(order, token, verifiedFiat, swapId)
171131
}
172132
}
173133

apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampHandler.kt

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,6 @@ fun CoinbaseOnRampHandler(
3737
)
3838
}
3939

40-
is CoinbaseOnRampState.Processing -> {
41-
LaunchedEffect(current) {
42-
delay(400) // let the system payment sheet finish its dismiss animation
43-
controller.processPayment()
44-
.onFailure {
45-
BottomBarManager.showError(
46-
title = "Something Went Wrong",
47-
message = "Failed to complete purchase. Please try again",
48-
)
49-
}
50-
}
51-
}
52-
5340
is CoinbaseOnRampState.Completed -> {
5441
LaunchedEffect(current) {
5542
controller.emitPendingNavigation(

apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampState.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ data class OnrampOrder(
1616

1717
sealed interface CoinbaseOnRampState {
1818
data object Idle : CoinbaseOnRampState
19-
data class Paying(val order: OnrampOrder, val token: Token, val amount: VerifiedFiat) : CoinbaseOnRampState
20-
data class Processing(val orderId: String, val token: Token, val amount: VerifiedFiat) : CoinbaseOnRampState
19+
data class Paying(val order: OnrampOrder, val token: Token, val amount: VerifiedFiat, val swapId: SwapId) : CoinbaseOnRampState
2120
data class Completed(val swapId: SwapId, val token: Token, val amount: VerifiedFiat) : CoinbaseOnRampState
2221
data class Failed(val error: CoinbaseOnRampWebError) : CoinbaseOnRampState
2322
}

services/opencode/src/main/kotlin/com/getcode/opencode/model/transactions/SwapRequest.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ data class SwapRequest(
1818
val swapId: SwapId,
1919
val verifiedState: VerifiedState,
2020
) {
21-
val fundingIntentId = when (kind) {
22-
is SwapStartKind.Reserve -> kind.fundingIntentId
23-
is SwapStartKind.Stablecoin -> kind.fundingIntentId
24-
}
21+
val fundingIntentId: List<Byte>
22+
get() = when (kind) {
23+
is SwapStartKind.Reserve -> kind.fundingIntentId
24+
is SwapStartKind.Stablecoin -> kind.fundingIntentId
25+
}
2526

2627
val totalTransferAmount: LocalFiat
2728
get() {
@@ -52,7 +53,9 @@ sealed interface SwapStartKind {
5253
get() = when (fundingSource) {
5354
is SwapFundingSource.ExternalWallet -> fundingSource.transactionSignature
5455
is SwapFundingSource.SubmitIntent -> fundingSource.id
55-
is SwapFundingSource.CoinbaseOnramp -> fundingSource.orderId
56+
is SwapFundingSource.CoinbaseOnramp -> throw IllegalArgumentException(
57+
"Coinbase onramp does not use a funding intent"
58+
)
5659
SwapFundingSource.Unknown -> throw IllegalArgumentException("Invalid funding source")
5760
}
5861
}

0 commit comments

Comments
 (0)