mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 16:33:27 +00:00
feat(clink): route profile + DVM single-invoice pays through the default source
The two remaining 'pay this bolt11' sites now honor the selected default payment source instead of the binary NWC-or-intent check: - AccountViewModel gains payInvoiceViaClinkDebit, the single-invoice debit-rail counterpart of sendZapPaymentRequestFor. - DisplayLNAddress (profile LN-address pay) and DvmContentDiscoveryScreen (DVM invoice pay) dispatch on defaultPaymentSource(): CLINK debit -> 21002 round trip, NWC -> existing pay_invoice, none -> external wallet intent. NWC-only users are unaffected. :amethyst compiles; the debit payout path stays untested end-to-end.
This commit is contained in:
+17
@@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.model.privacyOptions.EmptyRoleBasedHttpClientBuilder
|
||||
import com.vitorpamplona.amethyst.model.privacyOptions.IRoleBasedHttpClientBuilder
|
||||
import com.vitorpamplona.amethyst.model.privacyOptions.RoleBasedHttpClientBuilder
|
||||
import com.vitorpamplona.amethyst.service.ClinkDebitPayer
|
||||
import com.vitorpamplona.amethyst.service.OnlineChecker
|
||||
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
|
||||
import com.vitorpamplona.amethyst.service.cashu.melt.MeltProcessor
|
||||
@@ -87,6 +88,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.eventsync.EventSync
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.ReloadMintRequest
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.quartz.experimental.clink.debits.DebitResponse
|
||||
import com.vitorpamplona.quartz.experimental.clink.pointers.NDebit
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent
|
||||
@@ -2027,6 +2030,20 @@ class AccountViewModel(
|
||||
onSent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Pays a single BOLT-11 through a CLINK debit pointer (kind 21002) — the debit-rail
|
||||
* counterpart of [sendZapPaymentRequestFor]. [onResult] receives the decrypted
|
||||
* response (`isOk()` with optional preimage, or a GFY failure), or null on timeout.
|
||||
* Untested end-to-end.
|
||||
*/
|
||||
fun payInvoiceViaClinkDebit(
|
||||
pointer: NDebit,
|
||||
bolt11: String,
|
||||
onResult: (DebitResponse?) -> Unit,
|
||||
) = launchSigner {
|
||||
onResult(ClinkDebitPayer.payInvoice(account, pointer, bolt11))
|
||||
}
|
||||
|
||||
fun getInteractiveStoryReadingState(dATag: String): AddressableNote = LocalCache.getOrCreateAddressableNote(InteractiveStoryReadingStateEvent.createAddress(account.signer.pubKey, dATag))
|
||||
|
||||
fun updateInteractiveStoryReadingState(
|
||||
|
||||
+48
-29
@@ -49,6 +49,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.model.payments.PaymentSource
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription
|
||||
@@ -379,39 +380,57 @@ fun DvmPaymentActions(
|
||||
if (invoice != null) {
|
||||
val context = LocalContext.current
|
||||
Button(onClick = {
|
||||
if (accountViewModel.account.nip47SignerState.hasWalletConnectSetup()) {
|
||||
accountViewModel.sendZapPaymentRequestFor(
|
||||
bolt11 = invoice,
|
||||
zappedNote = null,
|
||||
onSent = {
|
||||
onStatusUpdate(nwcPaymentRequest)
|
||||
},
|
||||
onResponse = { response ->
|
||||
when (val source = accountViewModel.account.settings.defaultPaymentSource()) {
|
||||
is PaymentSource.ClinkDebit -> {
|
||||
onStatusUpdate(nwcPaymentRequest)
|
||||
accountViewModel.payInvoiceViaClinkDebit(source.wallet.pointer, invoice) { response ->
|
||||
onStatusUpdate(
|
||||
if (response is PayInvoiceErrorResponse) {
|
||||
stringRes(
|
||||
context,
|
||||
R.string.wallet_connect_pay_invoice_error_error,
|
||||
response.error?.message
|
||||
?: response.error?.code?.toString() ?: "Error parsing error message",
|
||||
)
|
||||
} else {
|
||||
if (response?.isOk() == true) {
|
||||
thankYou
|
||||
} else {
|
||||
response?.error?.takeIf { it.isNotBlank() }
|
||||
?: stringRes(context, R.string.clink_debit_no_response)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
payViaIntent(
|
||||
invoice,
|
||||
context,
|
||||
onPaid = {
|
||||
onStatusUpdate(thankYou)
|
||||
},
|
||||
onError = {
|
||||
onStatusUpdate(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is PaymentSource.Nwc -> {
|
||||
accountViewModel.sendZapPaymentRequestFor(
|
||||
bolt11 = invoice,
|
||||
zappedNote = null,
|
||||
onSent = {
|
||||
onStatusUpdate(nwcPaymentRequest)
|
||||
},
|
||||
onResponse = { response ->
|
||||
onStatusUpdate(
|
||||
if (response is PayInvoiceErrorResponse) {
|
||||
stringRes(
|
||||
context,
|
||||
R.string.wallet_connect_pay_invoice_error_error,
|
||||
response.error?.message
|
||||
?: response.error?.code?.toString() ?: "Error parsing error message",
|
||||
)
|
||||
} else {
|
||||
thankYou
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
null -> {
|
||||
payViaIntent(
|
||||
invoice,
|
||||
context,
|
||||
onPaid = {
|
||||
onStatusUpdate(thankYou)
|
||||
},
|
||||
onError = {
|
||||
onStatusUpdate(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
val amountInInvoice =
|
||||
|
||||
+30
-13
@@ -34,6 +34,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.model.payments.PaymentSource
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.components.util.LongPressCopyText
|
||||
@@ -109,22 +110,38 @@ fun DisplayLNAddress(
|
||||
lud16,
|
||||
user,
|
||||
accountViewModel,
|
||||
onSuccess = {
|
||||
onSuccess = { invoice ->
|
||||
zapExpanded = false
|
||||
// pay directly
|
||||
if (accountViewModel.account.nip47SignerState.hasWalletConnectSetup()) {
|
||||
accountViewModel.sendZapPaymentRequestFor(it, null) { response ->
|
||||
if (response is PayInvoiceSuccessResponse) {
|
||||
showInfoMessageDialog = stringRes(context, R.string.payment_successful)
|
||||
} else if (response is PayInvoiceErrorResponse) {
|
||||
showErrorMessageDialog =
|
||||
response.error?.message
|
||||
?: response.error?.code?.toString()
|
||||
?: stringRes(context, R.string.error_parsing_error_message)
|
||||
// pay directly through the selected default payment source
|
||||
when (val source = accountViewModel.account.settings.defaultPaymentSource()) {
|
||||
is PaymentSource.ClinkDebit -> {
|
||||
accountViewModel.payInvoiceViaClinkDebit(source.wallet.pointer, invoice) { response ->
|
||||
if (response?.isOk() == true) {
|
||||
showInfoMessageDialog = stringRes(context, R.string.payment_successful)
|
||||
} else {
|
||||
showErrorMessageDialog =
|
||||
response?.error?.takeIf { it.isNotBlank() }
|
||||
?: stringRes(context, R.string.clink_debit_no_response)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
payViaIntent(it, context, { zapExpanded = false }, { showErrorMessageDialog = it })
|
||||
|
||||
is PaymentSource.Nwc -> {
|
||||
accountViewModel.sendZapPaymentRequestFor(invoice, null) { response ->
|
||||
if (response is PayInvoiceSuccessResponse) {
|
||||
showInfoMessageDialog = stringRes(context, R.string.payment_successful)
|
||||
} else if (response is PayInvoiceErrorResponse) {
|
||||
showErrorMessageDialog =
|
||||
response.error?.message
|
||||
?: response.error?.code?.toString()
|
||||
?: stringRes(context, R.string.error_parsing_error_message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
null -> {
|
||||
payViaIntent(invoice, context, { zapExpanded = false }, { showErrorMessageDialog = it })
|
||||
}
|
||||
}
|
||||
},
|
||||
onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
|
||||
|
||||
Reference in New Issue
Block a user