refactor: migrate to kotlinx serialization, eliminate gson dependency
This commit is contained in:
@@ -1,50 +1,66 @@
|
||||
package com.bitcointxoko.gudariwallet.api
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.JsonDecoder
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonEncoder
|
||||
import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
||||
// ── Wallet ────────────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class WalletResponse(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val balance: Long // millisatoshis
|
||||
val id: String = "",
|
||||
val name: String = "",
|
||||
val balance: Long = 0L // millisatoshis
|
||||
)
|
||||
|
||||
// ── Create invoice ────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class CreateInvoiceRequest(
|
||||
val out: Boolean,
|
||||
val amount: Long, // satoshis
|
||||
val memo: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class CreateInvoiceResponse(
|
||||
@SerializedName("payment_hash") val paymentHash: String,
|
||||
@SerializedName("payment_request") val paymentRequest: String,
|
||||
@SerializedName("expiry") val expiry : String?
|
||||
@SerialName("payment_hash") val paymentHash: String,
|
||||
@SerialName("payment_request") val paymentRequest: String,
|
||||
@SerialName("expiry") val expiry: String?
|
||||
)
|
||||
|
||||
// ── Pay invoice ───────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class PayInvoiceRequest(
|
||||
val out: Boolean,
|
||||
val bolt11: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PayInvoiceResponse(
|
||||
@SerializedName("payment_hash") val paymentHash: String,
|
||||
@SerializedName("checking_id") val checkingId: String
|
||||
@SerialName("payment_hash") val paymentHash: String,
|
||||
@SerialName("checking_id") val checkingId: String
|
||||
)
|
||||
|
||||
// ── Check payment ─────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class PaymentStatusResponse(
|
||||
val paid: Boolean,
|
||||
val details: PaymentDetails? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PaymentDetails(
|
||||
val amount: Long?,
|
||||
val fee: Long?,
|
||||
@@ -52,82 +68,80 @@ data class PaymentDetails(
|
||||
)
|
||||
|
||||
// ── Decode BOLT-11 ────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class DecodeInvoiceRequest(
|
||||
val data: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DecodeInvoiceResponse(
|
||||
@SerializedName("payment_hash") val paymentHash : String? = null,
|
||||
@SerializedName("amount_msat") val amountMsat : Long? = null,
|
||||
@SerializedName("description") val description : String? = null,
|
||||
@SerializedName("payee") val payee : String? = null,
|
||||
@SerializedName("route_hints") val routeHints : List<List<RouteHintHop>>? = null
|
||||
@SerialName("payment_hash") val paymentHash: String? = null,
|
||||
@SerialName("amount_msat") val amountMsat: Long? = null,
|
||||
@SerialName("description") val description: String? = null,
|
||||
@SerialName("payee") val payee: String? = null,
|
||||
@SerialName("route_hints") val routeHints: List<List<RouteHintHop>>? = null
|
||||
)
|
||||
|
||||
|
||||
@Serializable
|
||||
data class RouteHintHop(
|
||||
@SerializedName("public_key") val publicKey : String,
|
||||
@SerializedName("short_channel_id") val shortChannelId : String? = null,
|
||||
@SerializedName("base_fee") val baseFeeMsat : Long = 0,
|
||||
@SerializedName("ppm_fee") val ppmFee : Long = 0,
|
||||
@SerializedName("cltv_expiry_delta") val cltvExpiryDelta : Int = 0
|
||||
@SerialName("public_key") val publicKey: String,
|
||||
@SerialName("short_channel_id") val shortChannelId: String? = null,
|
||||
@SerialName("base_fee") val baseFeeMsat: Long = 0,
|
||||
@SerialName("ppm_fee") val ppmFee: Long = 0,
|
||||
@SerialName("cltv_expiry_delta") val cltvExpiryDelta: Int = 0
|
||||
)
|
||||
|
||||
// ── LNURL scan ────────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class LnurlScanResponse(
|
||||
val tag: String?,
|
||||
// ── payRequest fields ────────────────────────────────────────────────────
|
||||
val callback: String? = null,
|
||||
@SerializedName("minSendable") val minSendable: Long? = null,
|
||||
@SerializedName("maxSendable") val maxSendable: Long? = null,
|
||||
val metadata: String? = null,
|
||||
@SerializedName("commentAllowed") val commentAllowed: Int? = null,
|
||||
@SerializedName("allowsNostr") val allowsNostr: Boolean? = null,
|
||||
@SerializedName("nostrPubkey") val nostrPubkey: String? = null,
|
||||
val bolt11: String? = null,
|
||||
@SerializedName("amount_msat") val amountMsat: Long? = null,
|
||||
val disposable: Boolean? = null, // LUD-06: null means treat as true (do not cache)
|
||||
// ── withdrawRequest fields ───────────────────────────────────────────────
|
||||
val k1 : String? = null,
|
||||
@SerializedName("defaultDescription")
|
||||
val defaultDescription: String? = null,
|
||||
@SerializedName("minWithdrawable")
|
||||
val minWithdrawable : Long? = null,
|
||||
@SerializedName("maxWithdrawable")
|
||||
val maxWithdrawable : Long? = null,
|
||||
@SerializedName("balanceCheck")
|
||||
val balanceCheck : String? = null,
|
||||
@SerializedName("currentBalance")
|
||||
val currentBalance : Long? = null,
|
||||
// ── Error fields ─────────────────────────────────────────────────────────
|
||||
val status : String? = null, // "OK" or "ERROR"
|
||||
val reason : String? = null // error message when status == "ERROR"
|
||||
// ── payRequest fields ─────────────────────────────────────────────────────
|
||||
val callback: String? = null,
|
||||
@SerialName("minSendable") val minSendable: Long? = null,
|
||||
@SerialName("maxSendable") val maxSendable: Long? = null,
|
||||
val metadata: String? = null,
|
||||
@SerialName("commentAllowed") val commentAllowed: Int? = null,
|
||||
@SerialName("allowsNostr") val allowsNostr: Boolean? = null,
|
||||
@SerialName("nostrPubkey") val nostrPubkey: String? = null,
|
||||
val bolt11: String? = null,
|
||||
@SerialName("amount_msat") val amountMsat: Long? = null,
|
||||
val disposable: Boolean? = null, // LUD-06: null means treat as true (do not cache)
|
||||
// ── withdrawRequest fields ────────────────────────────────────────────────
|
||||
val k1: String? = null,
|
||||
@SerialName("defaultDescription") val defaultDescription: String? = null,
|
||||
@SerialName("minWithdrawable") val minWithdrawable: Long? = null,
|
||||
@SerialName("maxWithdrawable") val maxWithdrawable: Long? = null,
|
||||
@SerialName("balanceCheck") val balanceCheck: String? = null,
|
||||
@SerialName("currentBalance") val currentBalance: Long? = null,
|
||||
// ── Error fields ──────────────────────────────────────────────────────────
|
||||
val status: String? = null, // "OK" or "ERROR"
|
||||
val reason: String? = null // error message when status == "ERROR"
|
||||
)
|
||||
|
||||
// ── LNURL-pay callback response (LUD-06) ─────────────────────────────────────
|
||||
@Serializable
|
||||
data class LnurlCallbackResponse(
|
||||
val pr : String, // BOLT-11 invoice to pay
|
||||
val routes : List<Any>? = null,
|
||||
@SerializedName("successAction")
|
||||
val successAction : SuccessAction? = null,
|
||||
// Error fields (server may return status=ERROR instead of pr)
|
||||
val status : String? = null,
|
||||
val reason : String? = null
|
||||
val pr: String,
|
||||
val routes: List<JsonElement>? = null,
|
||||
@SerialName("successAction") val successAction: SuccessAction? = null,
|
||||
val status: String? = null,
|
||||
val reason: String? = null
|
||||
)
|
||||
|
||||
|
||||
// ── LNURL pay ─────────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class LnurlPayRes(
|
||||
val tag: String?,
|
||||
val callback: String?,
|
||||
@SerializedName("minSendable") val minSendable: Long?,
|
||||
@SerializedName("maxSendable") val maxSendable: Long?,
|
||||
@SerialName("minSendable") val minSendable: Long?,
|
||||
@SerialName("maxSendable") val maxSendable: Long?,
|
||||
val metadata: String?,
|
||||
@SerializedName("commentAllowed") val commentAllowed: Int?,
|
||||
@SerializedName("allowsNostr") val allowsNostr: Boolean? = null,
|
||||
@SerializedName("nostrPubkey") val nostrPubkey: String? = null
|
||||
@SerialName("commentAllowed") val commentAllowed: Int?,
|
||||
@SerialName("allowsNostr") val allowsNostr: Boolean? = null,
|
||||
@SerialName("nostrPubkey") val nostrPubkey: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PayLnurlRequest(
|
||||
val res: LnurlPayRes,
|
||||
val lnurl: String,
|
||||
@@ -135,45 +149,48 @@ data class PayLnurlRequest(
|
||||
val comment: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PayLnurlResponse(
|
||||
@SerializedName("payment_hash") val paymentHash: String,
|
||||
@SerializedName("checking_id") val checkingId: String
|
||||
@SerialName("payment_hash") val paymentHash: String,
|
||||
@SerialName("checking_id") val checkingId: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class WithdrawCallbackResponse(
|
||||
val status : String,
|
||||
val reason : String? = null
|
||||
val status: String,
|
||||
val reason: String? = null
|
||||
)
|
||||
|
||||
// ── LNURLp pay link (lightning address) ──────────────────────────────────────
|
||||
@Serializable
|
||||
data class LnurlpLink(
|
||||
val id : String,
|
||||
val wallet : String,
|
||||
val username : String?, // the part before the @ in the lightning address
|
||||
val description : String?,
|
||||
val min : Long,
|
||||
val max : Long
|
||||
val id: String,
|
||||
val wallet: String,
|
||||
val username: String?, // the part before the @ in the lightning address
|
||||
val description: String?,
|
||||
@Serializable(with = LongAsDoubleSerializer::class) val min: Long,
|
||||
@Serializable(with = LongAsDoubleSerializer::class) val max: Long
|
||||
)
|
||||
|
||||
|
||||
// ── Payment history ───────────────────────────────────────────────────────────
|
||||
@Immutable
|
||||
@Serializable
|
||||
data class PaymentRecord(
|
||||
@SerializedName("payment_hash") val paymentHash: String,
|
||||
@SerializedName("checking_id") val checkingId: String,
|
||||
@SerializedName("amount") val amountMsat: Long,
|
||||
@SerializedName("fee") val feeMsat: Long,
|
||||
@SerializedName("memo") val memo: String?,
|
||||
@SerializedName("time") val time: String,
|
||||
@SerializedName("status") val status: String,
|
||||
@SerializedName("bolt11") val bolt11: String?,
|
||||
@SerializedName("pending") val pending: Boolean,
|
||||
@SerializedName("preimage") val preimage: String?,
|
||||
@SerializedName("extra") val extra: PaymentExtra?,
|
||||
@SerialName("payment_hash") val paymentHash: String = "",
|
||||
@SerialName("checking_id") val checkingId: String = "",
|
||||
@SerialName("amount") val amountMsat: Long = 0L,
|
||||
@SerialName("fee") val feeMsat: Long = 0L,
|
||||
@SerialName("memo") val memo: String? = null,
|
||||
@SerialName("time") val time: String = "",
|
||||
@SerialName("status") val status: String = "",
|
||||
@SerialName("bolt11") val bolt11: String? = null,
|
||||
@SerialName("pending") val pending: Boolean = false,
|
||||
@SerialName("preimage") val preimage: String? = null,
|
||||
@SerialName("extra") val extra: PaymentExtra? = null,
|
||||
// Not from the API — populated only when loaded from Room via toDomain()
|
||||
val createdAt: Long? = null,
|
||||
val pubkey: String? = null,
|
||||
val alias: String? = null
|
||||
val pubkey: String? = null,
|
||||
val alias: String? = null
|
||||
) {
|
||||
val amountSat: Long get() = kotlin.math.abs(amountMsat) / 1000L
|
||||
val feeSat: Long get() = kotlin.math.abs(feeMsat) / 1000L
|
||||
@@ -181,13 +198,13 @@ data class PaymentRecord(
|
||||
|
||||
companion object {
|
||||
fun fromPayment(
|
||||
paymentHash : String,
|
||||
amountSats : Long,
|
||||
feeSats : Long?,
|
||||
bolt11 : String?,
|
||||
memo : String?,
|
||||
pubkey : String?,
|
||||
alias : String?,
|
||||
paymentHash: String,
|
||||
amountSats: Long,
|
||||
feeSats: Long?,
|
||||
bolt11: String?,
|
||||
memo: String?,
|
||||
pubkey: String?,
|
||||
alias: String?,
|
||||
): PaymentRecord = PaymentRecord(
|
||||
paymentHash = paymentHash,
|
||||
checkingId = paymentHash,
|
||||
@@ -207,23 +224,25 @@ data class PaymentRecord(
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
||||
@Serializable
|
||||
data class PaymentExtra(
|
||||
@SerializedName("tag") val tag: String?,
|
||||
@JsonAdapter(FlexibleStringAdapter::class)
|
||||
val comment: String?,
|
||||
@SerializedName("success_action") val successAction: SuccessAction?,
|
||||
@SerializedName("wallet_id") val walletId: String?,
|
||||
@SerializedName("destination") val destination: String?,
|
||||
@SerializedName("expires_at") val expiresAt: String?
|
||||
@SerialName("tag") val tag: String? = null,
|
||||
@Serializable(with = FlexibleStringSerializer::class)
|
||||
val comment: String? = null,
|
||||
@SerialName("success_action") @Serializable(with = SuccessActionSerializer::class)
|
||||
val successAction: SuccessAction? = null,
|
||||
@SerialName("wallet_id") val walletId: String? = null,
|
||||
@SerialName("destination") val destination: String? = null,
|
||||
@SerialName("expires_at") val expiresAt: String? = null
|
||||
)
|
||||
|
||||
@Immutable
|
||||
@Serializable(with = SuccessActionSerializer::class)
|
||||
data class SuccessAction(
|
||||
@SerializedName("tag") val tag: String?,
|
||||
@SerializedName("message") val message: String?,
|
||||
@SerializedName("url") val url: String?,
|
||||
@SerializedName("description") val description: String?
|
||||
@SerialName("tag") val tag: String?,
|
||||
@SerialName("message") val message: String?,
|
||||
@SerialName("url") val url: String?,
|
||||
@SerialName("description") val description: String?
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -232,99 +251,101 @@ data class SuccessAction(
|
||||
* - a plain string "Thanks!"
|
||||
* - null
|
||||
*
|
||||
* Gson's default deserialiser throws IllegalStateException when it sees a
|
||||
* string where it expects {. This adapter handles all three cases.
|
||||
* kotlinx.serialization's default deserialiser throws when it sees a
|
||||
* string where it expects {. This serializer handles all three cases.
|
||||
*
|
||||
* Registered centrally in [GsonProvider] — applies to all deserialization
|
||||
* paths automatically.
|
||||
* Applied via @Serializable(with = SuccessActionSerializer::class) on
|
||||
* the SuccessAction class and on PaymentExtra.successAction.
|
||||
*/
|
||||
class SuccessActionAdapter : TypeAdapter<SuccessAction?>() {
|
||||
object SuccessActionSerializer : KSerializer<SuccessAction?> {
|
||||
|
||||
override fun write(out: JsonWriter, value: SuccessAction?) {
|
||||
if (value == null) { out.nullValue(); return }
|
||||
out.beginObject()
|
||||
out.name("tag"); out.value(value.tag)
|
||||
out.name("message"); out.value(value.message)
|
||||
out.name("url"); out.value(value.url)
|
||||
out.name("description"); out.value(value.description)
|
||||
out.endObject()
|
||||
override val descriptor: SerialDescriptor =
|
||||
buildClassSerialDescriptor("SuccessAction")
|
||||
|
||||
override fun serialize(encoder: Encoder, value: SuccessAction?) {
|
||||
val jsonEncoder = encoder as JsonEncoder
|
||||
if (value == null) {
|
||||
jsonEncoder.encodeJsonElement(JsonNull)
|
||||
} else {
|
||||
jsonEncoder.encodeJsonElement(
|
||||
JsonObject(
|
||||
mapOf(
|
||||
"tag" to JsonPrimitive(value.tag),
|
||||
"message" to JsonPrimitive(value.message),
|
||||
"url" to JsonPrimitive(value.url),
|
||||
"description" to JsonPrimitive(value.description)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(reader: JsonReader): SuccessAction? {
|
||||
return when (reader.peek()) {
|
||||
JsonToken.NULL -> { reader.nextNull(); null }
|
||||
|
||||
// Plain string — treat as a message-type success action
|
||||
JsonToken.STRING -> {
|
||||
val text = reader.nextString()
|
||||
override fun deserialize(decoder: Decoder): SuccessAction? {
|
||||
val jsonDecoder = decoder as JsonDecoder
|
||||
return when (val element = jsonDecoder.decodeJsonElement()) {
|
||||
is JsonNull -> null
|
||||
is JsonPrimitive -> {
|
||||
// Plain string — treat as a message-type success action
|
||||
val text = element.jsonPrimitive.content
|
||||
if (text.isBlank()) null
|
||||
else SuccessAction(tag = "message", message = text, url = null, description = null)
|
||||
}
|
||||
|
||||
// Normal object
|
||||
JsonToken.BEGIN_OBJECT -> {
|
||||
reader.beginObject()
|
||||
var tag: String? = null
|
||||
var message: String? = null
|
||||
var url: String? = null
|
||||
var description: String? = null
|
||||
while (reader.hasNext()) {
|
||||
when (reader.nextName()) {
|
||||
"tag" -> tag = reader.nextString()
|
||||
"message" -> message = reader.nextString()
|
||||
"url" -> url = reader.nextString()
|
||||
"description" -> description = reader.nextString()
|
||||
else -> reader.skipValue()
|
||||
}
|
||||
}
|
||||
reader.endObject()
|
||||
SuccessAction(tag = tag, message = message, url = url, description = description)
|
||||
is JsonObject -> {
|
||||
SuccessAction(
|
||||
tag = element.jsonObject["tag"]?.jsonPrimitive?.content,
|
||||
message = element.jsonObject["message"]?.jsonPrimitive?.content,
|
||||
url = element.jsonObject["url"]?.jsonPrimitive?.content,
|
||||
description = element.jsonObject["description"]?.jsonPrimitive?.content
|
||||
)
|
||||
}
|
||||
|
||||
else -> { reader.skipValue(); null }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class PaymentDetailResponse(
|
||||
val paid: Boolean,
|
||||
val details: PaymentRecord?
|
||||
)
|
||||
|
||||
// ── WebSocket payment message ─────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class WsPaymentMessage(
|
||||
@SerializedName("wallet_balance") val walletBalance: Long?,
|
||||
@SerialName("wallet_balance") val walletBalance: Long?,
|
||||
val payment: WsPayment?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class WsPayment(
|
||||
@SerializedName("payment_hash") val paymentHash: String,
|
||||
@SerializedName("checking_id") val checkingId: String,
|
||||
@SerializedName("amount") val amount: Long,
|
||||
@SerializedName("fee") val fee: Long,
|
||||
@SerializedName("memo") val memo: String?,
|
||||
@SerializedName("time") val time: String,
|
||||
@SerializedName("status") val status: String,
|
||||
@SerializedName("bolt11") val bolt11: String?,
|
||||
@SerializedName("pending") val pending: Boolean,
|
||||
@SerializedName("preimage") val preimage: String?,
|
||||
@SerializedName("extra") val extra: PaymentExtra?
|
||||
@SerialName("payment_hash") val paymentHash: String,
|
||||
@SerialName("checking_id") val checkingId: String,
|
||||
@SerialName("amount") val amount: Long,
|
||||
@SerialName("fee") val fee: Long,
|
||||
@SerialName("memo") val memo: String?,
|
||||
@SerialName("time") val time: String,
|
||||
@SerialName("status") val status: String,
|
||||
@SerialName("bolt11") val bolt11: String?,
|
||||
@SerialName("pending") val pending: Boolean,
|
||||
@SerialName("preimage") val preimage: String?,
|
||||
@SerialName("extra") val extra: PaymentExtra?
|
||||
)
|
||||
|
||||
|
||||
// ── Fiat rate ────────────────────────────────────────────────────────────────
|
||||
// ── Fiat rate ─────────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class FiatRateResponse(
|
||||
val rate: Double, // sat to fiat rate
|
||||
val price : Double // BTC price in the requested fiat currency
|
||||
val price: Double // BTC price in the requested fiat currency
|
||||
)
|
||||
|
||||
// ── History sync ─────────────────────────────────────────────────────────────
|
||||
// ── History sync ──────────────────────────────────────────────────────────────
|
||||
@Serializable
|
||||
data class PaginatedPaymentsResponse(
|
||||
@SerializedName("data") val data: List<PaymentRecord>,
|
||||
@SerializedName("total") val total: Int
|
||||
@SerialName("data") val data: List<PaymentRecord>,
|
||||
@SerialName("total") val total: Int
|
||||
)
|
||||
|
||||
// ── NWC Provider models ───────────────────────────────────────────────────────
|
||||
|
||||
@Serializable
|
||||
data class NwcKey(
|
||||
val pubkey: String,
|
||||
val wallet: String,
|
||||
@@ -335,6 +356,7 @@ data class NwcKey(
|
||||
val last_used: Long
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class NwcBudget(
|
||||
val id: Int,
|
||||
val pubkey: String,
|
||||
@@ -344,22 +366,41 @@ data class NwcBudget(
|
||||
val used_budget_msats: Long = 0
|
||||
)
|
||||
|
||||
/** Response for GET /nwc and GET /nwc/{pubkey} */
|
||||
@Serializable
|
||||
data class NwcGetResponse(
|
||||
val data: NwcKey,
|
||||
val budgets: List<NwcBudget>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class NwcNewBudget(
|
||||
val budget_msats: Long,
|
||||
val refresh_window: Int, // seconds
|
||||
val created_at : Long = System.currentTimeMillis() / 1000L
|
||||
val created_at: Long = System.currentTimeMillis() / 1000L
|
||||
)
|
||||
|
||||
/** Request body for PUT /nwc/{pubkey} */
|
||||
@Serializable
|
||||
data class NwcRegistrationRequest(
|
||||
val permissions: List<String>,
|
||||
val description: String,
|
||||
val expires_at: Long, // unix timestamp; 0 = no expiry
|
||||
val expires_at: Long, // unix timestamp; 0 = no expiry
|
||||
val budgets: List<NwcNewBudget> = emptyList()
|
||||
)
|
||||
|
||||
/**
|
||||
* Deserializes a JSON number that may arrive as either an integer (1)
|
||||
* or a float (1.0) into a Long, truncating any fractional part.
|
||||
*/
|
||||
object LongAsDoubleSerializer : KSerializer<Long> {
|
||||
override val descriptor = buildClassSerialDescriptor("LongAsDouble")
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Long) = encoder.encodeLong(value)
|
||||
|
||||
override fun deserialize(decoder: Decoder): Long {
|
||||
val jsonDecoder = decoder as JsonDecoder
|
||||
return when (val element = jsonDecoder.decodeJsonElement()) {
|
||||
is JsonPrimitive -> element.content.toDouble().toLong()
|
||||
else -> 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user