Skip to content

Commit

Permalink
Merge pull request #193 from lightsparkdev/feat/incoming_payments_for…
Browse files Browse the repository at this point in the history
…_payment_hash

Add incoming_payments_for_payment_hash query.
  • Loading branch information
jklein24 authored Aug 6, 2024
2 parents 84a0e78 + c4e17a6 commit dbd9826
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,22 @@ class LightsparkFuturesClient(config: ClientConfig) {
coroutinesClient.getOutgoingPaymentsForInvoice(encodedInvoice, transactionStatuses)
}

/**
* Fetch incoming payments for a given payment hash.
*
* @param paymentHash The payment hash of the invoice for which to fetch the incoming payments.
* @param transactionStatuses The transaction statuses to filter the payments by. If null, all payments will be
* returned.
* @return The list of incoming payments for the payment hash.
*/
@Throws(LightsparkException::class, LightsparkAuthenticationException::class)
fun getIncomingPaymentsForPaymentHash(
paymentHash: String,
transactionStatuses: List<TransactionStatus>? = null,
): CompletableFuture<List<IncomingPayment>> = coroutineScope.future {
coroutinesClient.getIncomingPaymentsForPaymentHash(paymentHash, transactionStatuses)
}

/**
* Creates an UMA invitation. If you are part of the incentive program you should use
* [createUmaInvitationWithIncentives].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,37 @@ class LightsparkCoroutinesClient private constructor(
)
}

/**
* Fetch incoming payments for a given payment hash.
*
* @param paymentHash The payment hash of the invoice for which to fetch the incoming payments.
* @param transactionStatuses The transaction statuses to filter the payments by. If null, all payments will be
* returned.
*/
suspend fun getIncomingPaymentsForPaymentHash(
paymentHash: String,
transactionStatuses: List<TransactionStatus>? = null,
): List<IncomingPayment> {
requireValidAuth()
return executeQuery(
Query(
IncomingPaymentsForPaymentHashQuery,
{
add("paymentHash", paymentHash)
transactionStatuses?.let {
add("transactionStatuses", serializerFormat.encodeToJsonElement(it))
}
},
) {
val outputJson =
requireNotNull(it["incoming_payments_for_payment_hash"]) { "No payment output found in response" }
val paymentsJson =
requireNotNull(outputJson.jsonObject["payments"]) { "No payments found in response" }
serializerFormat.decodeFromJsonElement(paymentsJson)
},
)
}

/**
* Creates an UMA invitation. If you are part of the incentive program you should use
* [createUmaInvitationWithIncentives].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,22 @@ class LightsparkSyncClient constructor(config: ClientConfig) {
asyncClient.getOutgoingPaymentsForInvoice(encodedInvoice, transactionStatuses)
}

/**
* Fetch incoming payments for a given payment hash.
*
* @param paymentHash The payment hash of the invoice for which to fetch the incoming payments.
* @param transactionStatuses The transaction statuses to filter the payments by. If null, all payments will be
* returned.
* @return The list of incoming payments for the payment hash.
*/
@Throws(LightsparkException::class, LightsparkAuthenticationException::class, CancellationException::class)
fun getIncomingPaymentsForPaymentHash(
paymentHash: String,
transactionStatuses: List<TransactionStatus>? = null,
): List<IncomingPayment> = runBlocking {
asyncClient.getIncomingPaymentsForPaymentHash(paymentHash, transactionStatuses)
}

/**
* Creates an UMA invitation. If you are part of the incentive program you should use
* [createUmaInvitationWithIncentives].
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.lightspark.sdk.graphql

import com.lightspark.sdk.model.IncomingPayment

const val IncomingPaymentsForPaymentHashQuery = """
query OutgoingPaymentsForInvoice(
${'$'}paymentHash: Hash32!,
${'$'}transactionStatuses: [TransactionStatus!] = null
) {
incoming_payments_for_payment_hash(input: {
payment_hash: ${'$'}paymentHash,
statuses: ${'$'}transactionStatuses
}) {
payments {
...IncomingPaymentFragment
}
}
}
${IncomingPayment.FRAGMENT}
"""
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,8 @@ query FetchAccountToPaymentRequestsConnection(${'$'}first: Int, ${'$'}after: Str
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
}
invoice_is_uma: is_uma
invoice_is_lnurl: is_lnurl
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
@file:Suppress("ktlint:standard:max-line-length")

package com.lightspark.sdk.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
*
* @param paymentHash The 32-byte hash of the payment preimage for which to fetch payments
* @param statuses An optional filter to only query incoming payments of given statuses.
*/
@Serializable
@SerialName("IncomingPaymentsForPaymentHashQueryInput")
data class IncomingPaymentsForPaymentHashQueryInput(
val paymentHash: String,
val statuses: List<TransactionStatus>? = null,
) {
companion object {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
@file:Suppress("ktlint:standard:max-line-length")

package com.lightspark.sdk.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
*
*/
@Serializable
@SerialName("IncomingPaymentsForPaymentHashQueryOutput")
data class IncomingPaymentsForPaymentHashQueryOutput(
@SerialName("incoming_payments_for_payment_hash_query_output_payments")
val payments: List<IncomingPayment>,
) {
companion object {
const val FRAGMENT = """
fragment IncomingPaymentsForPaymentHashQueryOutputFragment on IncomingPaymentsForPaymentHashQueryOutput {
type: __typename
incoming_payments_for_payment_hash_query_output_payments: payments {
id
}
}"""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import kotlinx.serialization.json.decodeFromJsonElement
* @param data The details of the invoice.
* @param status The status of the payment request.
* @param amountPaid The total amount that has been paid to this invoice.
* @param isUma Whether this invoice is an UMA invoice or not. NOTE: this field is only set if the invoice was created using the recommended `create_uma_invoice` function.
* @param isLnurl Whether this invoice is an LNURL invoice or not. NOTE: this field is only set if the invoice was created using the recommended `create_lnurl_invoice` function.
*/
@Serializable
@SerialName("Invoice")
Expand All @@ -35,6 +37,10 @@ data class Invoice(
override val status: PaymentRequestStatus,
@SerialName("invoice_amount_paid")
val amountPaid: CurrencyAmount? = null,
@SerialName("invoice_is_uma")
val isUma: Boolean? = null,
@SerialName("invoice_is_lnurl")
val isLnurl: Boolean? = null,
) : PaymentRequest,
Entity {
companion object {
Expand Down Expand Up @@ -363,6 +369,8 @@ fragment InvoiceFragment on Invoice {
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
}
invoice_is_uma: is_uma
invoice_is_lnurl: is_lnurl
}"""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ fragment PaymentRequestFragment on PaymentRequest {
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
}
invoice_is_uma: is_uma
invoice_is_lnurl: is_lnurl
}
}"""
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,9 @@ enum class RegionCode(
/** The code representing the country of Zimbabwe. **/
ZW("ZW"),

/** The code representing a fake region for testing. **/
NN("NN"),

/**
* This is an enum value that represents values that could be added in the future.
* Clients should support unknown values as more of them could be added without notice.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,8 @@ query FetchWalletToPaymentRequestsConnection(${'$'}first: Int, ${'$'}after: ID,
currency_amount_preferred_currency_value_rounded: preferred_currency_value_rounded
currency_amount_preferred_currency_value_approx: preferred_currency_value_approx
}
invoice_is_uma: is_uma
invoice_is_lnurl: is_lnurl
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,25 @@ class ClientIntegrationTests {
payment?.status.shouldBe(TransactionStatus.SUCCESS)
}

@Test
fun `test getIncomingPaymentsForPaymentHash`() = runTest {
val node = getFirstOskNode()
client.loadNodeSigningKey(node.id, PasswordRecoverySigningKeyLoader(node.id, NODE_PASSWORD))
val invoice = client.createInvoice(node.id, 100_000, "test invoice")
var payment: IncomingPayment? = client.createTestModePayment(node.id, invoice.data.encodedPaymentRequest)
payment.shouldNotBeNull()
while (payment?.status == TransactionStatus.PENDING) {
delay(500)
payment = IncomingPayment.getIncomingPaymentQuery(payment.id).execute(client)
println("Payment status: ${payment?.status}")
}

val payments = client.getIncomingPaymentsForPaymentHash(invoice.data.paymentHash)
payments.shouldNotBeNull()
payments.shouldHaveSize(1)
payments[0].id.shouldBe(payment?.id)
}

@Test
fun `test uma identifier hashing`() = runTest {
val privKeyBytes = "xyz".toByteArray()
Expand Down

0 comments on commit dbd9826

Please sign in to comment.