mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
feat: move NIP-A3 payment targets out of experimental, follow the merged spec
NIP-A3 is merged upstream, so quartz's experimental/nipA3 package becomes nipA3PaymentTargets (imports updated in every module). - PaymentTargetTag lowercases the type on parse and assemble (the spec says the type is always lowercase). - The spec's Cash App type is `cashme`; `cashapp` stays as an alias of it, and a bare cashtag gets its $ in the cash.app link. - URI hand-off for the spec's listed types: nano: for nano, bitcoin:?sp= for bip352 silent payments (BIP-321), revolut.me for revolut, on top of the existing schemes; bip353 and unknown types fall back to payto://type/addr. - The payment targets editor stores the spec spelling of known aliases. - Profile chips get styles for cashme, nano, revolut, bip352 and bip353. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MGR1u8SyzcUuekub39SBsc
This commit is contained in:
@@ -88,6 +88,7 @@ ent for NIP-04) |
|
||||
| 96 | `nip96FileStorage/` | HTTP file storage | HTTP-based file storage |
|
||||
| 99 | `nip99Classifieds/` | ClassifiedsEvent.kt | Classifieds/marketplace (kind 30402) |
|
||||
| A0 | `nipA0VoiceMessages/` | Voice messages | Voice message events |
|
||||
| A3 | `nipA3PaymentTargets/` | PaymentTargetsEvent.kt | Payment targets (`payto` tags, kind 10133) |
|
||||
| B7 | `nipB7Blossom/` | Blossom server URLs | Blossom file storage |
|
||||
|
||||
### Web/Storage/Other
|
||||
@@ -127,7 +128,6 @@ Located at `/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/
|
||||
| `limits/` | Limit enforcement |
|
||||
| `medical/` | Medical data |
|
||||
| `nip95/` | File storage support |
|
||||
| `nipA3/` | A3 protocol extension |
|
||||
| `nns/` | Nostr Name System |
|
||||
| `profileGallery/` | Profile gallery lists |
|
||||
| `publicMessages/` | Public message lists |
|
||||
|
||||
@@ -67,7 +67,6 @@ import com.vitorpamplona.amethyst.model.preferences.UiSharedPreferences
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
@@ -98,6 +97,7 @@ import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListEvent
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -199,7 +199,6 @@ import com.vitorpamplona.quartz.experimental.nip95.header.dimension
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.fileSize
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.hash
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.mimeType
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.blurhash
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.dimension
|
||||
@@ -337,6 +336,7 @@ import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipB0WebBookmarks.WebBookmarkEvent
|
||||
import com.vitorpamplona.quartz.nipC7Chats.ChatEvent
|
||||
import com.vitorpamplona.quartz.utils.DualCase
|
||||
|
||||
@@ -50,7 +50,6 @@ import com.vitorpamplona.amethyst.commons.relayauth.RelayAuthPolicy
|
||||
import com.vitorpamplona.amethyst.commons.service.pow.PoWCategory
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
@@ -84,6 +83,7 @@ import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListEvent
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ package com.vitorpamplona.amethyst.model.nipA3PaymentTargets
|
||||
import com.vitorpamplona.amethyst.commons.model.NoteState
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
@@ -28,12 +28,12 @@ import com.vitorpamplona.amethyst.commons.model.payments.PaymentTargetTypes
|
||||
import com.vitorpamplona.amethyst.model.MIN_ONCHAIN_ZAP_SATS
|
||||
import com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletState
|
||||
import com.vitorpamplona.amethyst.service.payments.PayToAppAvailability
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.BaseZapSplitSetup
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupLnAddress
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
|
||||
/**
|
||||
* What payment rails would actually reach the recipient(s) of a zap.
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.net.toUri
|
||||
import com.vitorpamplona.amethyst.commons.model.payments.PaymentTargetTypes
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
+1
-1
@@ -25,7 +25,6 @@ import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFil
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.model.nip78AppSpecific.AppSpecificState.Companion.APP_SPECIFIC_DATA_D_TAG
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
@@ -53,6 +52,7 @@ import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListEvent
|
||||
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
||||
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
|
||||
@Composable
|
||||
fun PaymentTargetsScreen(
|
||||
|
||||
+4
-2
@@ -23,9 +23,10 @@ package com.vitorpamplona.amethyst.ui.actions.paymentTargets
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.model.payments.PaymentTargetTypes
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
@@ -62,7 +63,8 @@ class PaymentTargetsViewModel : ViewModel() {
|
||||
type: String,
|
||||
authority: String,
|
||||
) {
|
||||
val trimmedType = type.trim().lowercase()
|
||||
// Stores the NIP-A3 spelling of known aliases (btc -> bitcoin, cashapp -> cashme).
|
||||
val trimmedType = PaymentTargetTypes.canonical(type)
|
||||
val trimmedAuthority = authority.trim()
|
||||
if (trimmedType.isEmpty() || trimmedAuthority.isEmpty()) return
|
||||
val target = PaymentTarget(trimmedType, trimmedAuthority)
|
||||
|
||||
@@ -238,8 +238,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.PaymentTarge
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.paymentTargetStyleFor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.OnchainZapSendDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.navigateToReloadMint
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
@@ -248,6 +246,8 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
|
||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
+1
-1
@@ -100,7 +100,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListDiff
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListDiff
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsDiff
|
||||
import com.vitorpamplona.quartz.nip01Core.diff.ContentChange
|
||||
import com.vitorpamplona.quartz.nip01Core.diff.EventDiff
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataDiff
|
||||
@@ -118,6 +117,7 @@ import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListDiff
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListDiff
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataDiff
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListDiff
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsDiff
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListDiff
|
||||
import org.jetbrains.compose.resources.PluralStringResource
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
|
||||
+3
-3
@@ -127,9 +127,6 @@ import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListDiff
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListDiff
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsDiff
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
@@ -161,6 +158,9 @@ import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListEvent
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListDiff
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsDiff
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListDiff
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import org.jetbrains.compose.resources.PluralStringResource
|
||||
|
||||
+1
-1
@@ -118,7 +118,6 @@ import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListDiff
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListDiff
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsDiff
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.diff.ContentChange
|
||||
@@ -149,6 +148,7 @@ import com.vitorpamplona.quartz.nip65RelayList.tags.AdvertisedRelayType
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListDiff
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataDiff
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListDiff
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsDiff
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListDiff
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
|
||||
|
||||
+14
-4
@@ -39,7 +39,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size16Modifier
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment.ProfilePaymentMethod
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.SegwitAddress
|
||||
|
||||
fun isLightningPaymentTarget(rawType: String): Boolean = rawType.trim().lowercase() in PaymentTargetTypes.LIGHTNING_TYPES
|
||||
@@ -72,7 +72,7 @@ fun inAppPaymentRouteFor(
|
||||
|
||||
/**
|
||||
* The URI an external wallet app should receive for [target]: the type's own
|
||||
* scheme (`bitcoin:`, `lightning:`, `https://cash.app/…`) and RFC 8905
|
||||
* scheme (`bitcoin:`, `lightning:`, `https://cash.app/$…`) and RFC 8905
|
||||
* `payto://` for types Amethyst has no dedicated scheme for. Shared with the
|
||||
* payment-target dialog so the same pill hands off to the same app wherever
|
||||
* it is tapped.
|
||||
@@ -159,6 +159,10 @@ fun paymentTargetStyleFor(rawType: String): PaymentTargetStyle {
|
||||
return when (type) {
|
||||
"bitcoin", "btc", "onchain" ->
|
||||
PaymentTargetStyle(MaterialSymbols.CurrencyBitcoin, BitcoinOrange, "BITCOIN")
|
||||
"bip352" ->
|
||||
PaymentTargetStyle(MaterialSymbols.CurrencyBitcoin, BitcoinOrange, "SILENT PAYMENT")
|
||||
"bip353" ->
|
||||
PaymentTargetStyle(MaterialSymbols.CurrencyBitcoin, BitcoinOrange, "BIP-353")
|
||||
"lightning", "ln" ->
|
||||
PaymentTargetStyle(MaterialSymbols.Bolt, BitcoinOrange, "LIGHTNING")
|
||||
"lnurl" ->
|
||||
@@ -183,8 +187,12 @@ fun paymentTargetStyleFor(rawType: String): PaymentTargetStyle {
|
||||
PaymentTargetStyle(walletIcon, SOLANA_PURPLE, "SOLANA")
|
||||
"tron", "trx" ->
|
||||
PaymentTargetStyle(walletIcon, TRON_RED, "TRON")
|
||||
"cashapp" ->
|
||||
PaymentTargetStyle(walletIcon, CASHAPP_LIME, "CASHAPP")
|
||||
"cashme", "cashapp" ->
|
||||
PaymentTargetStyle(walletIcon, CASHAPP_LIME, "CASH APP")
|
||||
"nano", "xno" ->
|
||||
PaymentTargetStyle(walletIcon, NANO_BLUE, "NANO")
|
||||
"revolut" ->
|
||||
PaymentTargetStyle(walletIcon, REVOLUT_BLUE, "REVOLUT")
|
||||
"venmo" ->
|
||||
PaymentTargetStyle(walletIcon, VENMO_BLUE, "VENMO")
|
||||
"paypal" ->
|
||||
@@ -213,4 +221,6 @@ private val TRON_RED = Color(0xFFEF0027)
|
||||
private val CASHAPP_LIME = Color(0xFF00E64D)
|
||||
private val VENMO_BLUE = Color(0xFF008CFF)
|
||||
private val PAYPAL_DEEP_BLUE = Color(0xFF003087)
|
||||
private val NANO_BLUE = Color(0xFF209CE9)
|
||||
private val REVOLUT_BLUE = Color(0xFF0666EB)
|
||||
private val GENERIC_TARGET_COLOR = Color(0xFF7C8DA0)
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.QrCodeDrawer
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
|
||||
+2
-2
@@ -78,8 +78,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment.ProfilePaymentMethod
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.payment.rememberProfileClinkOffer
|
||||
import com.vitorpamplona.quartz.experimental.clink.pointers.NOffer
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.TaprootAddress
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
+1
-1
@@ -195,7 +195,6 @@ import com.vitorpamplona.quartz.experimental.music.playlist.MusicPlaylistEvent
|
||||
import com.vitorpamplona.quartz.experimental.music.track.MusicTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent
|
||||
@@ -324,6 +323,7 @@ import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.nipB0WebBookmarks.WebBookmarkEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.zap.Bolt12ZapEvent
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.WalletViewModel
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
|
||||
@Composable
|
||||
fun NIP47SetupScreen(
|
||||
|
||||
@@ -30,8 +30,6 @@ import com.vitorpamplona.amethyst.commons.model.nip85TrustedAssertions.UserCards
|
||||
import com.vitorpamplona.amethyst.commons.util.KmpLock
|
||||
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
|
||||
import com.vitorpamplona.amethyst.commons.util.withLock
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
|
||||
@@ -43,6 +41,8 @@ import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.info.tags.NutzapMintTag
|
||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import kotlin.concurrent.Volatile
|
||||
|
||||
+1
-1
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.commons.model.backups
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.diff.DiffableEvent
|
||||
@@ -53,6 +52,7 @@ import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListEvent
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.ClientTag
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
|
||||
/** Which of the account's backed-up events a conflict is about. Drives the dialog's wording. */
|
||||
|
||||
+1
-1
@@ -177,7 +177,6 @@ import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.Softw
|
||||
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.asset.SoftwareAssetEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent
|
||||
@@ -392,6 +391,7 @@ import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent
|
||||
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallHangupEvent
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.model.payments
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
|
||||
/**
|
||||
* Picks the NIP-A3 payment targets a sender can hand off to when paying a note's
|
||||
|
||||
+31
-3
@@ -37,6 +37,9 @@ package com.vitorpamplona.amethyst.commons.model.payments
|
||||
* and the installed-app probe (which needs the URI before it has an authority).
|
||||
*/
|
||||
object PaymentTargetTypes {
|
||||
private const val CASHME = "cashme"
|
||||
private const val DOLLAR = "\$"
|
||||
|
||||
/** Lightning-family types Amethyst can pay in-app through the Send Payment screen. */
|
||||
val LIGHTNING_TYPES = setOf("lightning", "ln", "lnurl")
|
||||
|
||||
@@ -62,10 +65,14 @@ object PaymentTargetTypes {
|
||||
"doge" to "dogecoin",
|
||||
"sol" to "solana",
|
||||
"trx" to "tron",
|
||||
"xno" to "nano",
|
||||
// NIP-A3 names the Cash App cashtag type `cashme`; `cashapp` is what
|
||||
// Amethyst (and others) wrote before the spec settled on it.
|
||||
"cashapp" to "cashme",
|
||||
)
|
||||
|
||||
/** Types whose hand-off is a web page rather than a registered URI scheme. */
|
||||
private val WEB_TYPES = setOf("cashapp", "venmo", "paypal")
|
||||
private val WEB_TYPES = setOf("cashme", "venmo", "paypal", "revolut")
|
||||
|
||||
/** Types with a dedicated URI scheme, keyed by canonical name. */
|
||||
private val SCHEMES =
|
||||
@@ -82,16 +89,31 @@ object PaymentTargetTypes {
|
||||
"dogecoin" to "dogecoin",
|
||||
"solana" to "solana",
|
||||
"tron" to "tron",
|
||||
"nano" to "nano",
|
||||
)
|
||||
|
||||
/**
|
||||
* Types whose scheme carries the address in a query parameter instead of the path:
|
||||
* a BIP-352 silent payment address goes in a BIP-321 `bitcoin:` URI's `sp` field.
|
||||
*/
|
||||
private val QUERY_SCHEMES =
|
||||
mapOf(
|
||||
"bip352" to "bitcoin:?sp=",
|
||||
)
|
||||
|
||||
private val WEB_HOSTS =
|
||||
mapOf(
|
||||
"cashapp" to "https://cash.app/",
|
||||
"cashme" to "https://cash.app/",
|
||||
"venmo" to "https://venmo.com/",
|
||||
"paypal" to "https://paypal.me/",
|
||||
"revolut" to "https://revolut.me/",
|
||||
)
|
||||
|
||||
/** Trims, lowercases and collapses known aliases onto one family name. */
|
||||
/**
|
||||
* Trims, lowercases and collapses known aliases onto one family name. The result is the
|
||||
* NIP-A3 spelling where the spec lists one (`cashapp` -> `cashme`), so it is also what a
|
||||
* new `payto` tag should carry.
|
||||
*/
|
||||
fun canonical(rawType: String): String {
|
||||
val trimmed = rawType.trim().lowercase()
|
||||
return ALIASES[trimmed] ?: trimmed
|
||||
@@ -130,6 +152,12 @@ object PaymentTargetTypes {
|
||||
val type = canonical(rawType)
|
||||
val value = authority.trim()
|
||||
SCHEMES[type]?.let { return "$it:$value" }
|
||||
QUERY_SCHEMES[type]?.let { return "$it$value" }
|
||||
if (type == CASHME) {
|
||||
// cash.app/$cashtag: NIP-A3 describes the `$`-prefixed tag, but accept a bare one.
|
||||
val cashtag = if (value.isEmpty() || value.startsWith(DOLLAR)) value else DOLLAR + value
|
||||
return WEB_HOSTS.getValue(CASHME) + cashtag
|
||||
}
|
||||
WEB_HOSTS[type]?.let { return "$it$value" }
|
||||
return "payto://$type/$value"
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.commons.relayClient.profile
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
||||
@@ -33,6 +32,7 @@ import com.vitorpamplona.quartz.nip51Lists.followSet.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
|
||||
val UserProfileListKinds =
|
||||
listOf(
|
||||
|
||||
+1
-1
@@ -25,7 +25,6 @@ import com.vitorpamplona.amethyst.commons.relayClient.event.loaders.forEachChunk
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.commons.relays.EOSEAccountFast
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.hints.HintIndexer
|
||||
@@ -37,6 +36,7 @@ import com.vitorpamplona.quartz.nip38UserStatus.UserStatusEvent
|
||||
import com.vitorpamplona.quartz.nip39ExtIdentities.ExternalIdentitiesEvent
|
||||
import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent
|
||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.offer.Bolt12OfferListEvent
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
|
||||
|
||||
+37
-1
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.model.payments
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTarget
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
@@ -76,6 +76,42 @@ class PaymentTargetTypesTest {
|
||||
assertFalse(PaymentTargetTypes.isWebTarget("iban"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun specCashmeIsCashAppAndCashappStaysAnAlias() {
|
||||
assertEquals("cashme", PaymentTargetTypes.canonical("cashapp"))
|
||||
assertEquals("cashme", PaymentTargetTypes.canonical("CashMe"))
|
||||
assertEquals("https://cash.app/\$vitor", PaymentTargetTypes.uriFor("cashme", "\$vitor"))
|
||||
// NIP-A3 describes a $-prefixed cashtag; accept the bare name too.
|
||||
assertEquals("https://cash.app/\$vitor", PaymentTargetTypes.uriFor("cashme", "vitor"))
|
||||
assertTrue(PaymentTargetTypes.isWebTarget("cashme"))
|
||||
assertEquals(PaymentTargetTypes.probeKeyFor("cashapp"), PaymentTargetTypes.probeKeyFor("cashme"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun specListedTypesUseTheirNativeScheme() {
|
||||
assertEquals("bitcoin:bc1q", PaymentTargetTypes.uriFor("bitcoin", "bc1q"))
|
||||
assertEquals("bitcoincash:qq1", PaymentTargetTypes.uriFor("bitcoincash", "qq1"))
|
||||
assertEquals("ethereum:0xabc", PaymentTargetTypes.uriFor("ethereum", "0xabc"))
|
||||
assertEquals("lightning:me@ln.tips", PaymentTargetTypes.uriFor("lightning", "me@ln.tips"))
|
||||
assertEquals("litecoin:ltc1", PaymentTargetTypes.uriFor("litecoin", "ltc1"))
|
||||
assertEquals("monero:4A", PaymentTargetTypes.uriFor("monero", "4A"))
|
||||
assertEquals("nano:nano_1dctq", PaymentTargetTypes.uriFor("nano", "nano_1dctq"))
|
||||
assertEquals("solana:So1", PaymentTargetTypes.uriFor("solana", "So1"))
|
||||
assertEquals("tron:T9", PaymentTargetTypes.uriFor("tron", "T9"))
|
||||
assertEquals("zcash:zs1", PaymentTargetTypes.uriFor("zcash", "zs1"))
|
||||
// BIP-352 silent payment addresses travel in a BIP-321 bitcoin: URI's sp parameter.
|
||||
assertEquals("bitcoin:?sp=sp1qq", PaymentTargetTypes.uriFor("bip352", "sp1qq"))
|
||||
assertEquals("https://paypal.me/vitor", PaymentTargetTypes.uriFor("paypal", "vitor"))
|
||||
assertEquals("https://venmo.com/vitor", PaymentTargetTypes.uriFor("venmo", "vitor"))
|
||||
assertEquals("https://revolut.me/vitor", PaymentTargetTypes.uriFor("revolut", "vitor"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun specListedTypesWithoutASchemeUsePayto() {
|
||||
// A BIP-353 name has to be resolved over DNS first; there is no URI scheme for it.
|
||||
assertEquals("payto://bip353/vitor@example.com", PaymentTargetTypes.uriFor("bip353", "vitor@example.com"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unknownTypesFallBackToPayto() {
|
||||
assertEquals("payto://iban/DE75512108001245126199", PaymentTargetTypes.uriFor("IBAN", "DE75512108001245126199"))
|
||||
|
||||
@@ -59,7 +59,6 @@ import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.Softw
|
||||
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.asset.SoftwareAssetEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent
|
||||
@@ -309,6 +308,7 @@ import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent
|
||||
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallHangupEvent
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nipA3
|
||||
package com.vitorpamplona.quartz.nipA3PaymentTargets
|
||||
|
||||
data class PaymentTarget(
|
||||
val type: String,
|
||||
+5
-3
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nipA3
|
||||
package com.vitorpamplona.quartz.nipA3PaymentTargets
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
@@ -38,9 +38,11 @@ class PaymentTargetTag {
|
||||
ensure(tag.has(2)) { return null }
|
||||
ensure(tag[2].isNotEmpty()) { return null }
|
||||
|
||||
return PaymentTarget(tag[1], tag[2])
|
||||
// NIP-A3: the type is always lowercase. Normalize what other clients wrote so
|
||||
// `Bitcoin` and `bitcoin` are one rail.
|
||||
return PaymentTarget(tag[1].lowercase(), tag[2])
|
||||
}
|
||||
|
||||
fun assemble(paymentTarget: PaymentTarget) = arrayOf(TAG_NAME, paymentTarget.type, paymentTarget.authority)
|
||||
fun assemble(paymentTarget: PaymentTarget) = arrayOf(TAG_NAME, paymentTarget.type.lowercase(), paymentTarget.authority)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nipA3
|
||||
package com.vitorpamplona.quartz.nipA3PaymentTargets
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.diff.EventDiff
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.experimental.nipA3
|
||||
package com.vitorpamplona.quartz.nipA3PaymentTargets
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
|
||||
@@ -149,7 +149,6 @@ import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.Softw
|
||||
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.asset.SoftwareAssetEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent
|
||||
@@ -411,6 +410,7 @@ import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||
import com.vitorpamplona.quartz.nipA3PaymentTargets.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent
|
||||
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallHangupEvent
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nipA3PaymentTargets
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertContentEquals
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class PaymentTargetTagTest {
|
||||
@Test
|
||||
fun parsesTheSpecExample() {
|
||||
val tags =
|
||||
arrayOf(
|
||||
arrayOf("payto", "bitcoin", "bc1qxq66e0t8d7ugdecwnmv58e90tpry23nc84pg9k"),
|
||||
arrayOf("payto", "nano", "nano_1dctqbmqxfppo9pswbm6kg9d4s4mbraqn8i4m7ob9gnzz91aurmuho48jx3c"),
|
||||
arrayOf("payto", "unknowntype", "l7tbta5b9xze6ckkfc99uohzxd009b0r"),
|
||||
)
|
||||
val event = PaymentTargetsEvent("0".repeat(64), "1".repeat(64), 1L, tags, "", "")
|
||||
assertEquals(
|
||||
listOf(
|
||||
PaymentTarget("bitcoin", "bc1qxq66e0t8d7ugdecwnmv58e90tpry23nc84pg9k"),
|
||||
PaymentTarget("nano", "nano_1dctqbmqxfppo9pswbm6kg9d4s4mbraqn8i4m7ob9gnzz91aurmuho48jx3c"),
|
||||
PaymentTarget("unknowntype", "l7tbta5b9xze6ckkfc99uohzxd009b0r"),
|
||||
),
|
||||
event.paymentTargets(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun typeIsAlwaysLowercase() {
|
||||
assertEquals(PaymentTarget("bitcoin", "bc1q"), PaymentTargetTag.parse(arrayOf("payto", "Bitcoin", "bc1q")))
|
||||
assertContentEquals(arrayOf("payto", "cashme", "\$Vitor"), PaymentTargetTag.assemble(PaymentTarget("CashMe", "\$Vitor")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectsIncompleteTags() {
|
||||
assertNull(PaymentTargetTag.parse(arrayOf("payto", "bitcoin")))
|
||||
assertNull(PaymentTargetTag.parse(arrayOf("payto", "", "bc1q")))
|
||||
assertNull(PaymentTargetTag.parse(arrayOf("payto", "bitcoin", "")))
|
||||
assertNull(PaymentTargetTag.parse(arrayOf("r", "bitcoin", "bc1q")))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user