Add API endpoint to list payments by external id
Creating an invoice now accepts an optional external id. Also added new payment endpoints to the CLI.
This commit is contained in:
parent
65b1db5716
commit
9d984591bc
@ -98,16 +98,32 @@ class Api(private val nodeParams: NodeParams, private val peer: Peer, private va
|
||||
val amount = formParameters.getLong("amountSat").sat
|
||||
val description = formParameters.getString("description")
|
||||
val invoice = peer.createInvoice(randomBytes32(), amount.toMilliSatoshi(), Either.Left(description))
|
||||
formParameters["externalId"]?.takeUnless { it.isBlank() }?.let { externalId ->
|
||||
paymentDb.metadataQueries.insertExternalId(WalletPaymentId.IncomingPaymentId(invoice.paymentHash), externalId)
|
||||
}
|
||||
call.respond(GeneratedInvoice(invoice.amount?.truncateToSatoshi(), invoice.paymentHash, serialized = invoice.write()))
|
||||
}
|
||||
get("payments/incoming/{paymentHash}") {
|
||||
val paymentHash = call.parameters.getByteVector32("paymentHash")
|
||||
println("fetching details for payment=$paymentHash")
|
||||
paymentDb.getIncomingPayment(paymentHash)?.let {
|
||||
val metadata = paymentDb.metadataQueries.get(WalletPaymentId.IncomingPaymentId(paymentHash))
|
||||
call.respond(IncomingPayment(it, metadata))
|
||||
} ?: call.respond(HttpStatusCode.NotFound)
|
||||
}
|
||||
get("payments/incoming") {
|
||||
val externalId = call.parameters.getString("externalId")
|
||||
val metadataList = paymentDb.metadataQueries.getByExternalId(externalId)
|
||||
metadataList.mapNotNull { (paymentId, metadata) ->
|
||||
when (paymentId) {
|
||||
is WalletPaymentId.IncomingPaymentId -> paymentDb.getIncomingPayment(paymentId.paymentHash)?.let {
|
||||
IncomingPayment(it, metadata)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}.let { payments ->
|
||||
call.respond(payments)
|
||||
}
|
||||
}
|
||||
get("payments/outgoing/{uuid}") {
|
||||
val uuid = call.parameters.getUUID("uuid")
|
||||
paymentDb.getLightningOutgoingPayment(uuid)?.let {
|
||||
@ -188,6 +204,7 @@ class Api(private val nodeParams: NodeParams, private val peer: Peer, private va
|
||||
private fun Parameters.getString(argName: String): String = (this[argName] ?: missing(argName))
|
||||
|
||||
private fun Parameters.getByteVector32(argName: String): ByteVector32 = getString(argName).let { hex -> kotlin.runCatching { ByteVector32.fromValidHex(hex) }.getOrNull() ?: invalidType(argName, "hex32") }
|
||||
|
||||
private fun Parameters.getUUID(argName: String): UUID = getString(argName).let { uuid -> kotlin.runCatching { UUID.fromString(uuid) }.getOrNull() ?: invalidType(argName, "uuid") }
|
||||
|
||||
private fun Parameters.getAddressAndConvertToScript(argName: String): ByteVector = Script.write(Bitcoin.addressToPublicKeyScript(nodeParams.chainHash, getString(argName)).right ?: error("invalid address")).toByteVector()
|
||||
|
@ -2,6 +2,7 @@ package fr.acinq.lightning.bin.db.payments
|
||||
|
||||
import fr.acinq.lightning.bin.db.PaymentMetadata
|
||||
import fr.acinq.lightning.bin.db.WalletPaymentId
|
||||
import fr.acinq.lightning.utils.currentTimestampMillis
|
||||
import fr.acinq.phoenix.db.PaymentsDatabase
|
||||
|
||||
class PaymentsMetadataQueries(private val database: PaymentsDatabase) {
|
||||
@ -13,10 +14,20 @@ class PaymentsMetadataQueries(private val database: PaymentsDatabase) {
|
||||
.executeAsOneOrNull()
|
||||
}
|
||||
|
||||
fun getByExternalId(id: String): List<Pair<WalletPaymentId, PaymentMetadata>> {
|
||||
return queries.getByExternalId(id).executeAsList().mapNotNull { res ->
|
||||
WalletPaymentId.create(type = res.type, id = res.id)?.let { it to mapper(res.external_id, res.created_at) }
|
||||
}
|
||||
}
|
||||
|
||||
fun insertExternalId(walletPaymentId: WalletPaymentId, id: String) {
|
||||
database.transaction {
|
||||
queries.insert(type = walletPaymentId.dbType.value, id = walletPaymentId.dbId, external_id = id, created_at = currentTimestampMillis())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun mapper(
|
||||
type: Long,
|
||||
id: String,
|
||||
external_id: String?,
|
||||
created_at: Long,
|
||||
): PaymentMetadata {
|
||||
|
@ -93,15 +93,13 @@ sealed class ApiType {
|
||||
|
||||
@Serializable
|
||||
@SerialName("incoming_payment")
|
||||
data class IncomingPayment(val paymentHash: ByteVector32, val preimage: ByteVector32, val externalId: String?, val description: String?, val isPaid: Boolean, val receivedSat: Satoshi, val fees: MilliSatoshi, val completedAt: Long?, val createdAt: Long) {
|
||||
data class IncomingPayment(val paymentHash: ByteVector32, val preimage: ByteVector32, val externalId: String?, val description: String?, val invoice: String?, val isPaid: Boolean, val receivedSat: Satoshi, val fees: MilliSatoshi, val completedAt: Long?, val createdAt: Long) {
|
||||
constructor(payment: fr.acinq.lightning.db.IncomingPayment, metadata: PaymentMetadata?) : this (
|
||||
paymentHash = payment.paymentHash,
|
||||
preimage = payment.preimage,
|
||||
externalId = metadata?.externalId,
|
||||
description = when (val or = payment.origin) {
|
||||
is fr.acinq.lightning.db.IncomingPayment.Origin.Invoice -> or.paymentRequest.description
|
||||
else -> null
|
||||
},
|
||||
description = (payment.origin as? fr.acinq.lightning.db.IncomingPayment.Origin.Invoice)?.paymentRequest?.description,
|
||||
invoice = (payment.origin as? fr.acinq.lightning.db.IncomingPayment.Origin.Invoice)?.paymentRequest?.write(),
|
||||
isPaid = payment.completedAt != null,
|
||||
receivedSat = payment.amount.truncateToSatoshi(),
|
||||
fees = payment.fees,
|
||||
|
@ -15,6 +15,7 @@ import fr.acinq.bitcoin.ByteVector32
|
||||
import fr.acinq.lightning.bin.conf.readConfFile
|
||||
import fr.acinq.lightning.bin.homeDirectory
|
||||
import fr.acinq.lightning.payment.Bolt11Invoice
|
||||
import fr.acinq.lightning.utils.UUID
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.plugins.auth.*
|
||||
import io.ktor.client.plugins.auth.providers.*
|
||||
@ -30,7 +31,7 @@ import kotlinx.serialization.json.Json
|
||||
|
||||
fun main(args: Array<String>) =
|
||||
PhoenixCli()
|
||||
.subcommands(GetInfo(), GetBalance(), ListChannels(), CreateInvoice(), PayInvoice(), SendToAddress(), CloseChannel())
|
||||
.subcommands(GetInfo(), GetBalance(), ListChannels(), GetOutgoingPayment(), GetIncomingPayment(), ListIncomingPayments(), CreateInvoice(), PayInvoice(), SendToAddress(), CloseChannel())
|
||||
.main(args)
|
||||
|
||||
data class HttpConf(val baseUrl: Url, val httpClient: HttpClient)
|
||||
@ -114,16 +115,61 @@ class ListChannels : CliktCommand(name = "listchannels", help = "List all channe
|
||||
}
|
||||
}
|
||||
|
||||
class GetOutgoingPayment : CliktCommand(name = "getoutgoingpayment", help = "Get outgoing payment") {
|
||||
private val commonOptions by requireObject<HttpConf>()
|
||||
private val uuid by option("--uuid").convert { UUID.fromString(it) }.required()
|
||||
override fun run() {
|
||||
runBlocking {
|
||||
val res = commonOptions.httpClient.get(
|
||||
url = commonOptions.baseUrl / "payments/outgoing/$uuid"
|
||||
)
|
||||
echo(res.bodyAsText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetIncomingPayment : CliktCommand(name = "getincomingpayment", help = "Get incoming payment") {
|
||||
private val commonOptions by requireObject<HttpConf>()
|
||||
private val paymentHash by option("--paymentHash", "--h").convert { ByteVector32.fromValidHex(it) }.required()
|
||||
override fun run() {
|
||||
runBlocking {
|
||||
val res = commonOptions.httpClient.get(
|
||||
url = commonOptions.baseUrl / "payments/incoming/$paymentHash"
|
||||
)
|
||||
echo(res.bodyAsText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ListIncomingPayments : CliktCommand(name = "listincomingpayments", help = "List incoming payments matching the given externalId") {
|
||||
private val commonOptions by requireObject<HttpConf>()
|
||||
private val externalId by option("--externalId", "--eid").required()
|
||||
override fun run() {
|
||||
runBlocking {
|
||||
val res = commonOptions.httpClient.get(
|
||||
url = commonOptions.baseUrl / "payments/incoming",
|
||||
) {
|
||||
url {
|
||||
parameters.append("externalId", externalId)
|
||||
}
|
||||
}
|
||||
echo(res.bodyAsText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CreateInvoice : CliktCommand(name = "createinvoice", help = "Create a Lightning invoice", printHelpOnEmptyArgs = true) {
|
||||
private val commonOptions by requireObject<HttpConf>()
|
||||
private val amountSat by option("--amountSat").long()
|
||||
private val description by option("--description", "--desc").required()
|
||||
private val externalId by option("--externalId")
|
||||
override fun run() {
|
||||
runBlocking {
|
||||
val res = commonOptions.httpClient.submitForm(
|
||||
url = (commonOptions.baseUrl / "createinvoice").toString(),
|
||||
formParameters = parameters {
|
||||
amountSat?.let { append("amountSat", amountSat.toString()) }
|
||||
amountSat?.let { append("amountSat", it.toString()) }
|
||||
externalId?.let { append("externalId", it) }
|
||||
append("description", description)
|
||||
}
|
||||
)
|
||||
@ -170,7 +216,7 @@ class SendToAddress : CliktCommand(name = "sendtoaddress", help = "Send to a Bit
|
||||
}
|
||||
}
|
||||
|
||||
class CloseChannel : CliktCommand(name = "closechannel", help = "Close all channels", printHelpOnEmptyArgs = true) {
|
||||
class CloseChannel : CliktCommand(name = "closechannel", help = "Close channel", printHelpOnEmptyArgs = true) {
|
||||
private val commonOptions by requireObject<HttpConf>()
|
||||
private val channelId by option("--channelId").convert { ByteVector32.fromValidHex(it) }.required()
|
||||
private val address by option("--address").required().check { runCatching { Base58Check.decode(it) }.isSuccess || runCatching { Bech32.decodeWitnessAddress(it) }.isSuccess }
|
||||
|
@ -10,11 +10,9 @@ CREATE TABLE IF NOT EXISTS payments_metadata (
|
||||
PRIMARY KEY (type, id)
|
||||
);
|
||||
|
||||
-- queries for payments_metadata tablexxxx`
|
||||
CREATE INDEX IF NOT EXISTS payments_metadata_external_id ON payments_metadata(external_id);
|
||||
|
||||
count:
|
||||
SELECT COUNT(*) FROM payments_metadata
|
||||
WHERE type = ? AND id = ?;
|
||||
-- queries for payments_metadata table
|
||||
|
||||
insert:
|
||||
INSERT INTO payments_metadata (
|
||||
@ -25,5 +23,9 @@ INSERT INTO payments_metadata (
|
||||
) VALUES (?, ?, ?, ?);
|
||||
|
||||
get:
|
||||
SELECT * FROM payments_metadata
|
||||
SELECT external_id, created_at FROM payments_metadata
|
||||
WHERE type = ? AND id = ?;
|
||||
|
||||
getByExternalId:
|
||||
SELECT type, id, external_id, created_at FROM payments_metadata
|
||||
WHERE external_id = ?;
|
||||
|
Loading…
x
Reference in New Issue
Block a user