rename example

This commit is contained in:
2026-06-01 03:09:19 +02:00
parent 7f3d14a764
commit b5473bfafc
59 changed files with 261 additions and 182 deletions
@@ -0,0 +1,272 @@
package com.bitcointxoko.gudariwallet.api
import com.google.gson.TypeAdapter
import com.google.gson.annotations.SerializedName
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
// ── Wallet ────────────────────────────────────────────────────────────────────
data class WalletResponse(
val id: String,
val name: String,
val balance: Long // millisatoshis
)
// ── Create invoice ────────────────────────────────────────────────────────────
data class CreateInvoiceRequest(
val out: Boolean,
val amount: Long, // satoshis
val memo: String
)
data class CreateInvoiceResponse(
@SerializedName("payment_hash") val paymentHash: String,
@SerializedName("payment_request") val paymentRequest: String,
@SerializedName("expiry") val expiry : String?
)
// ── Pay invoice ───────────────────────────────────────────────────────────────
data class PayInvoiceRequest(
val out: Boolean,
val bolt11: String
)
data class PayInvoiceResponse(
@SerializedName("payment_hash") val paymentHash: String,
@SerializedName("checking_id") val checkingId: String
)
// ── Check payment ─────────────────────────────────────────────────────────────
data class PaymentStatusResponse(
val paid: Boolean,
val details: PaymentDetails? = null
)
data class PaymentDetails(
val amount: Long?,
val fee: Long?,
val memo: String?
)
// ── Decode BOLT-11 ────────────────────────────────────────────────────────────
data class DecodeInvoiceRequest(
val data: String
)
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
)
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
)
// ── LNURL scan ────────────────────────────────────────────────────────────────
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"
)
// ── LNURL-pay callback response (LUD-06) ─────────────────────────────────────
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
)
// ── LNURL pay ─────────────────────────────────────────────────────────────────
data class LnurlPayRes(
val tag: String?,
val callback: String?,
@SerializedName("minSendable") val minSendable: Long?,
@SerializedName("maxSendable") val maxSendable: Long?,
val metadata: String?,
@SerializedName("commentAllowed") val commentAllowed: Int?,
@SerializedName("allowsNostr") val allowsNostr: Boolean? = null,
@SerializedName("nostrPubkey") val nostrPubkey: String? = null
)
data class PayLnurlRequest(
val res: LnurlPayRes,
val lnurl: String,
val amount: Long,
val comment: String? = null
)
data class PayLnurlResponse(
@SerializedName("payment_hash") val paymentHash: String,
@SerializedName("checking_id") val checkingId: String
)
data class WithdrawCallbackResponse(
val status : String,
val reason : String? = null
)
// ── LNURLp pay link (lightning address) ──────────────────────────────────────
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
)
// ── Payment history ───────────────────────────────────────────────────────────
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?
) {
val amountSat: Long get() = kotlin.math.abs(amountMsat) / 1000L
val feeSat: Long get() = kotlin.math.abs(feeMsat) / 1000L
val isOutgoing: Boolean get() = amountMsat < 0
}
data class PaymentExtra(
@SerializedName("tag") val tag: String?,
@SerializedName("comment") 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?
)
data class SuccessAction(
@SerializedName("tag") val tag: String?,
@SerializedName("message") val message: String?,
@SerializedName("url") val url: String?,
@SerializedName("description") val description: String?
)
/**
* LNbits returns `success_action` as either:
* - a JSON object {"tag":"message","message":"Thanks!"}
* - 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.
*
* Registered centrally in [GsonProvider] — applies to all deserialization
* paths automatically.
*/
class SuccessActionAdapter : TypeAdapter<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 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()
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)
}
else -> { reader.skipValue(); null }
}
}
}
data class PaymentDetailResponse(
val paid: Boolean,
val details: PaymentRecord?
)
// ── WebSocket payment message ─────────────────────────────────────────────────
data class WsPaymentMessage(
val payment: WsPayment?
)
data class WsPayment(
@SerializedName("payment_hash") val paymentHash: String,
val amount: Long,
val status: String,
val memo: String?
)
// ── Fiat rate ────────────────────────────────────────────────────────────────
data class FiatRateResponse(
val rate: Double, // sat to fiat rate
val price : Double // BTC price in the requested fiat currency
)