chore: invoice and payment search
Some checks failed
Test, lint and build / test_lint_build (push) Has been cancelled
Release Please / release-please (push) Has been cancelled

This commit is contained in:
Anthony Potdevin 2026-06-24 15:51:01 -06:00
parent 28d1aa376b
commit 5e116d7359
No known key found for this signature in database
GPG key ID: 4403F1DFBE779457
14 changed files with 715 additions and 48 deletions

View file

@ -867,6 +867,7 @@ type Query {
getConfigState: ConfigState!
getForwards(days: Float!): GetForwards!
getHello: String!
getInvoice(id: String!): InvoiceType!
getInvoiceStatusChange(id: String!): String!
getInvoices(token: String): GetInvoicesType!
getLatestVersion: String!
@ -877,6 +878,7 @@ type Query {
getNodeBalances: Balances!
getNodeInfo: NodeInfo!
getNodeSocialInfo(pubkey: String!): LightningNodeSocialInfo!
getPayment(id: String!): PaymentType!
getPayments(token: String): GetPaymentsType!
getPeers: [Peer!]!
getPendingChannels: [PendingChannel!]!

View file

@ -0,0 +1,180 @@
import * as Types from '../../types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type GetInvoiceQueryVariables = Types.Exact<{
id: Types.Scalars['String']['input'];
}>;
export type GetInvoiceQuery = {
__typename?: 'Query';
getInvoice: {
__typename?: 'InvoiceType';
chain_address?: string | null;
confirmed_at?: string | null;
created_at: string;
description: string;
description_hash?: string | null;
expires_at: string;
id: string;
is_canceled?: boolean | null;
is_confirmed: boolean;
is_held?: boolean | null;
is_private: boolean;
is_push?: boolean | null;
received: number;
received_mtokens: string;
request?: string | null;
secret: string;
tokens: string;
type: string;
date: string;
payments: Array<{
__typename?: 'InvoicePayment';
canceled_at?: string | null;
confirmed_at?: string | null;
created_at: string;
created_height: number;
is_canceled: boolean;
is_confirmed: boolean;
is_held: boolean;
mtokens: string;
pending_index?: number | null;
timeout: number;
tokens: number;
total_mtokens?: string | null;
in_channel: string;
messages?: { __typename?: 'MessageType'; message?: string | null } | null;
}>;
};
};
export const GetInvoiceDocument = gql`
query GetInvoice($id: String!) {
getInvoice(id: $id) {
chain_address
confirmed_at
created_at
description
description_hash
expires_at
id
is_canceled
is_confirmed
is_held
is_private
is_push
received
received_mtokens
request
secret
tokens
type
date
payments {
canceled_at
confirmed_at
created_at
created_height
is_canceled
is_confirmed
is_held
mtokens
pending_index
timeout
tokens
total_mtokens
in_channel
messages {
message
}
}
}
}
`;
/**
* __useGetInvoiceQuery__
*
* To run a query within a React component, call `useGetInvoiceQuery` and pass it any options that fit your needs.
* When your component renders, `useGetInvoiceQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetInvoiceQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useGetInvoiceQuery(
baseOptions: Apollo.QueryHookOptions<
GetInvoiceQuery,
GetInvoiceQueryVariables
> &
(
| { variables: GetInvoiceQueryVariables; skip?: boolean }
| { skip: boolean }
)
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<GetInvoiceQuery, GetInvoiceQueryVariables>(
GetInvoiceDocument,
options
);
}
export function useGetInvoiceLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetInvoiceQuery,
GetInvoiceQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetInvoiceQuery, GetInvoiceQueryVariables>(
GetInvoiceDocument,
options
);
}
// @ts-ignore
export function useGetInvoiceSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetInvoiceQuery,
GetInvoiceQueryVariables
>
): Apollo.UseSuspenseQueryResult<GetInvoiceQuery, GetInvoiceQueryVariables>;
export function useGetInvoiceSuspenseQuery(
baseOptions?:
| Apollo.SkipToken
| Apollo.SuspenseQueryHookOptions<GetInvoiceQuery, GetInvoiceQueryVariables>
): Apollo.UseSuspenseQueryResult<
GetInvoiceQuery | undefined,
GetInvoiceQueryVariables
>;
export function useGetInvoiceSuspenseQuery(
baseOptions?:
| Apollo.SkipToken
| Apollo.SuspenseQueryHookOptions<GetInvoiceQuery, GetInvoiceQueryVariables>
) {
const options =
baseOptions === Apollo.skipToken
? baseOptions
: { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<GetInvoiceQuery, GetInvoiceQueryVariables>(
GetInvoiceDocument,
options
);
}
export type GetInvoiceQueryHookResult = ReturnType<typeof useGetInvoiceQuery>;
export type GetInvoiceLazyQueryHookResult = ReturnType<
typeof useGetInvoiceLazyQuery
>;
export type GetInvoiceSuspenseQueryHookResult = ReturnType<
typeof useGetInvoiceSuspenseQuery
>;
export type GetInvoiceQueryResult = Apollo.QueryResult<
GetInvoiceQuery,
GetInvoiceQueryVariables
>;

View file

@ -17,6 +17,7 @@ export type GetOfferReadinessQuery = {
is_peer_connected: boolean;
has_pending_order: boolean;
pending_order_id?: string | null;
pending_order_status?: string | null;
onchain_balance_sats: string;
onchain_asset_balance: string;
btc_channels: {
@ -47,6 +48,7 @@ export const GetOfferReadinessDocument = gql`
is_peer_connected
has_pending_order
pending_order_id
pending_order_status
onchain_balance_sats
onchain_asset_balance
btc_channels {

View file

@ -0,0 +1,165 @@
import * as Types from '../../types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type GetPaymentQueryVariables = Types.Exact<{
id: Types.Scalars['String']['input'];
}>;
export type GetPaymentQuery = {
__typename?: 'Query';
getPayment: {
__typename?: 'PaymentType';
created_at: string;
destination: string;
fee: number;
fee_mtokens: string;
id: string;
is_confirmed: boolean;
is_outgoing: boolean;
mtokens: string;
request?: string | null;
safe_fee: number;
safe_tokens?: number | null;
secret: string;
tokens: string;
type: string;
date: string;
destination_node: {
__typename?: 'Node';
node?: {
__typename?: 'NodeType';
alias: string;
public_key: string;
} | null;
};
hops: Array<{
__typename?: 'Node';
node?: {
__typename?: 'NodeType';
alias: string;
public_key: string;
} | null;
}>;
};
};
export const GetPaymentDocument = gql`
query GetPayment($id: String!) {
getPayment(id: $id) {
created_at
destination
destination_node {
node {
alias
public_key
}
}
fee
fee_mtokens
hops {
node {
alias
public_key
}
}
id
is_confirmed
is_outgoing
mtokens
request
safe_fee
safe_tokens
secret
tokens
type
date
}
}
`;
/**
* __useGetPaymentQuery__
*
* To run a query within a React component, call `useGetPaymentQuery` and pass it any options that fit your needs.
* When your component renders, `useGetPaymentQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetPaymentQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useGetPaymentQuery(
baseOptions: Apollo.QueryHookOptions<
GetPaymentQuery,
GetPaymentQueryVariables
> &
(
| { variables: GetPaymentQueryVariables; skip?: boolean }
| { skip: boolean }
)
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<GetPaymentQuery, GetPaymentQueryVariables>(
GetPaymentDocument,
options
);
}
export function useGetPaymentLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetPaymentQuery,
GetPaymentQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetPaymentQuery, GetPaymentQueryVariables>(
GetPaymentDocument,
options
);
}
// @ts-ignore
export function useGetPaymentSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetPaymentQuery,
GetPaymentQueryVariables
>
): Apollo.UseSuspenseQueryResult<GetPaymentQuery, GetPaymentQueryVariables>;
export function useGetPaymentSuspenseQuery(
baseOptions?:
| Apollo.SkipToken
| Apollo.SuspenseQueryHookOptions<GetPaymentQuery, GetPaymentQueryVariables>
): Apollo.UseSuspenseQueryResult<
GetPaymentQuery | undefined,
GetPaymentQueryVariables
>;
export function useGetPaymentSuspenseQuery(
baseOptions?:
| Apollo.SkipToken
| Apollo.SuspenseQueryHookOptions<GetPaymentQuery, GetPaymentQueryVariables>
) {
const options =
baseOptions === Apollo.skipToken
? baseOptions
: { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<GetPaymentQuery, GetPaymentQueryVariables>(
GetPaymentDocument,
options
);
}
export type GetPaymentQueryHookResult = ReturnType<typeof useGetPaymentQuery>;
export type GetPaymentLazyQueryHookResult = ReturnType<
typeof useGetPaymentLazyQuery
>;
export type GetPaymentSuspenseQueryHookResult = ReturnType<
typeof useGetPaymentSuspenseQuery
>;
export type GetPaymentQueryResult = Apollo.QueryResult<
GetPaymentQuery,
GetPaymentQueryVariables
>;

View file

@ -0,0 +1,45 @@
import { gql } from '@apollo/client';
export const GET_INVOICE = gql`
query GetInvoice($id: String!) {
getInvoice(id: $id) {
chain_address
confirmed_at
created_at
description
description_hash
expires_at
id
is_canceled
is_confirmed
is_held
is_private
is_push
received
received_mtokens
request
secret
tokens
type
date
payments {
canceled_at
confirmed_at
created_at
created_height
is_canceled
is_confirmed
is_held
mtokens
pending_index
timeout
tokens
total_mtokens
in_channel
messages {
message
}
}
}
}
`;

View file

@ -0,0 +1,35 @@
import { gql } from '@apollo/client';
export const GET_PAYMENT = gql`
query GetPayment($id: String!) {
getPayment(id: $id) {
created_at
destination
destination_node {
node {
alias
public_key
}
}
fee
fee_mtokens
hops {
node {
alias
public_key
}
}
id
is_confirmed
is_outgoing
mtokens
request
safe_fee
safe_tokens
secret
tokens
type
date
}
}
`;

View file

@ -948,6 +948,7 @@ export type OfferReadinessResult = {
onchain_asset_balance: Scalars['String']['output'];
onchain_balance_sats: Scalars['String']['output'];
pending_order_id?: Maybe<Scalars['String']['output']>;
pending_order_status?: Maybe<Scalars['String']['output']>;
};
export type OnChainBalance = {
@ -1157,6 +1158,7 @@ export type Query = {
getConfigState: ConfigState;
getForwards: GetForwards;
getHello: Scalars['String']['output'];
getInvoice: InvoiceType;
getInvoiceStatusChange: Scalars['String']['output'];
getInvoices: GetInvoicesType;
getLatestVersion: Scalars['String']['output'];
@ -1167,6 +1169,7 @@ export type Query = {
getNodeBalances: Balances;
getNodeInfo: NodeInfo;
getNodeSocialInfo: LightningNodeSocialInfo;
getPayment: PaymentType;
getPayments: GetPaymentsType;
getPeers: Array<Peer>;
getPendingChannels: Array<PendingChannel>;
@ -1206,6 +1209,10 @@ export type QueryGetForwardsArgs = {
days: Scalars['Float']['input'];
};
export type QueryGetInvoiceArgs = {
id: Scalars['String']['input'];
};
export type QueryGetInvoiceStatusChangeArgs = {
id: Scalars['String']['input'];
};
@ -1227,6 +1234,10 @@ export type QueryGetNodeSocialInfoArgs = {
pubkey: Scalars['String']['input'];
};
export type QueryGetPaymentArgs = {
id: Scalars['String']['input'];
};
export type QueryGetPaymentsArgs = {
token?: InputMaybe<Scalars['String']['input']>;
};

View file

@ -1,8 +1,8 @@
import { useState, useMemo, useCallback } from 'react';
import { useState, useMemo, useCallback, FormEvent } from 'react';
import toast from 'react-hot-toast';
import { InvoiceCard } from '../views/transactions/InvoiceCard';
import { GridWrapper } from '../components/gridWrapper/GridWrapper';
import { Settings, Loader2 } from 'lucide-react';
import { Settings, Loader2, Search, X } from 'lucide-react';
import { useLocalStorage } from '../hooks/UseLocalStorage';
import { useNodeInfo } from '../hooks/UseNodeInfo';
import {
@ -13,6 +13,7 @@ import { format } from 'date-fns';
import { getErrorContent } from '../utils/error';
import { PaymentsCard } from '../views/transactions/PaymentsCards';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { FlowBox } from '../views/home/reports/flow';
import {
GetInvoicesQuery,
@ -22,6 +23,8 @@ import {
GetPaymentsQuery,
useGetPaymentsQuery,
} from '../graphql/queries/__generated__/getPayments.generated';
import { useGetInvoiceLazyQuery } from '../graphql/queries/__generated__/getInvoice.generated';
import { useGetPaymentLazyQuery } from '../graphql/queries/__generated__/getPayment.generated';
import { Card, CardContent, CardFooter } from '@/components/ui/card';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { TradeDisplayMode } from '../views/transactions/tradeMemo';
@ -32,6 +35,41 @@ const TransactionsView = () => {
const [settingsOpen, setSettingsOpen] = useState(false);
const [graphShow, setGraphShow] = useState('invoices');
const [graphType, setGraphType] = useState('count');
const [lookupValue, setLookupValue] = useState('');
const [lookupOpen, setLookupOpen] = useState(1);
const [lookupInvoice, invoiceLookup] = useGetInvoiceLazyQuery({
onError: error => toast.error(getErrorContent(error)),
});
const [lookupPayment, paymentLookup] = useGetPaymentLazyQuery({
onError: error => toast.error(getErrorContent(error)),
});
const activeLookup = activeTab === 'invoices' ? invoiceLookup : paymentLookup;
const handleLookupSubmit = (e: FormEvent) => {
e.preventDefault();
const id = lookupValue.trim();
if (!id) return;
setLookupOpen(1);
if (activeTab === 'invoices') {
lookupInvoice({ variables: { id } });
} else {
lookupPayment({ variables: { id } });
}
};
const handleLookupClear = () => {
setLookupValue('');
invoiceLookup.reset();
paymentLookup.reset();
};
const handleTabChange = (v: string) => {
if (!v) return;
setActiveTab(v);
handleLookupClear();
};
const { publicKey } = useNodeInfo();
@ -250,7 +288,7 @@ const TransactionsView = () => {
variant="outline"
size="sm"
value={activeTab}
onValueChange={v => v && setActiveTab(v)}
onValueChange={handleTabChange}
>
<ToggleGroupItem value="invoices">Invoices</ToggleGroupItem>
<ToggleGroupItem value="payments">Payments</ToggleGroupItem>
@ -264,6 +302,81 @@ const TransactionsView = () => {
</Button>
</div>
<Card>
<CardContent>
<form
onSubmit={handleLookupSubmit}
className="flex flex-col gap-2 sm:flex-row sm:items-center"
>
<div className="relative flex-1">
<Search
size={14}
className="absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground"
/>
<Input
value={lookupValue}
onChange={e => setLookupValue(e.target.value)}
placeholder={
activeTab === 'invoices'
? 'Lookup invoice by r-hash (payment hash)'
: 'Lookup payment by payment hash'
}
className="pl-8 pr-8 font-mono text-xs"
spellCheck={false}
/>
{lookupValue && (
<button
type="button"
onClick={handleLookupClear}
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
aria-label="Clear"
>
<X size={14} />
</button>
)}
</div>
<Button
type="submit"
size="sm"
disabled={!lookupValue.trim() || activeLookup.loading}
>
{activeLookup.loading ? (
<Loader2 className="animate-spin" size={14} />
) : (
'Lookup'
)}
</Button>
</form>
{activeLookup.called && !activeLookup.loading && (
<div className="mt-3">
{activeTab === 'invoices' && invoiceLookup.data?.getInvoice ? (
<InvoiceCard
invoice={invoiceLookup.data.getInvoice as any}
tradeDisplayMode={tradeDisplayMode}
index={1}
setIndexOpen={setLookupOpen}
indexOpen={lookupOpen}
/>
) : activeTab === 'payments' && paymentLookup.data?.getPayment ? (
<PaymentsCard
payment={paymentLookup.data.getPayment as any}
tradeDisplayMode={tradeDisplayMode}
index={1}
setIndexOpen={setLookupOpen}
indexOpen={lookupOpen}
/>
) : activeLookup.error ? (
<div className="text-center text-sm text-muted-foreground py-3">
{activeTab === 'invoices'
? 'No invoice found for that hash.'
: 'No payment found for that hash.'}
</div>
) : null}
</div>
)}
</CardContent>
</Card>
{settingsOpen && (
<Card>
<CardContent>

View file

@ -27,7 +27,7 @@ interface InvoiceCardProps {
indexOpen: number;
}
const ChannelAlias: FC<{ id: string }> = ({ id }) => {
const ChannelPeer: FC<{ id: string }> = ({ id }) => {
const { data, loading, error } = useGetChannelQuery({
variables: { id },
});
@ -35,10 +35,20 @@ const ChannelAlias: FC<{ id: string }> = ({ id }) => {
if (loading) return <LoadingCard noCard={true} />;
if (error) return <span className="text-muted-foreground">Unknown</span>;
const alias =
data?.getChannel.partner_node_policies?.node?.node?.alias || 'Unknown';
const peer = data?.getChannel.partner_node_policies?.node?.node;
const alias = peer?.alias || 'Unknown';
const pubkey = peer?.public_key;
return <span>{alias}</span>;
return (
<div className="flex flex-col items-end gap-0.5">
<span>{alias}</span>
{pubkey && (
<span className="font-mono text-[10px] text-muted-foreground break-all">
{pubkey}
</span>
)}
</div>
);
};
const StatusBadge = ({
@ -166,46 +176,6 @@ export const InvoiceCard = ({
{isOpen && (
<div className="px-3 pb-3">
<Separator className="mb-3" />
{payments.length > 0 && (
<div className="mb-3 space-y-2">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
Payments
</div>
{payments.map((p, idx) => {
const message = getTradeMemoText(
p.messages?.message,
tradeDisplayMode
);
const messageDisplay = getTradeMemoDisplay(p.messages?.message);
return (
<div key={idx} className="rounded bg-muted/50 p-2">
<DetailTable>
<DetailRow label="Amount">
<Price amount={p.tokens} />
</DetailRow>
<DetailRow label="Channel">{p.in_channel}</DetailRow>
<DetailRow label="Peer">
<ChannelAlias id={p.in_channel} />
</DetailRow>
{messageDisplay?.isTradeMemo && (
<DetailRow label="Parsed Message">
<div className="inline-flex items-start gap-1">
<span>{messageDisplay.computed}</span>
<ComputedMarker />
</div>
</DetailRow>
)}
{message && (
<DetailRow label="Message">
{messageDisplay?.raw ?? message}
</DetailRow>
)}
</DetailTable>
</div>
);
})}
</div>
)}
<DetailTable>
{descriptionDisplay?.isTradeMemo && (
<DetailRow label="Parsed Description">
@ -244,6 +214,76 @@ export const InvoiceCard = ({
{secret && <DetailRow label="Secret">{secret}</DetailRow>}
{request && <DetailRow label="Request">{request}</DetailRow>}
</DetailTable>
{payments.length > 0 && (
<div className="mt-3 space-y-2">
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
HTLCs
</div>
{payments.map((p, idx) => {
const message = getTradeMemoText(
p.messages?.message,
tradeDisplayMode
);
const messageDisplay = getTradeMemoDisplay(p.messages?.message);
return (
<div key={idx} className="rounded bg-muted/50 p-2">
<DetailTable>
<DetailRow label="State">
<StatusBadge
confirmed={p.is_confirmed}
canceled={p.is_canceled}
/>
</DetailRow>
{p.pending_index != null && (
<DetailRow label="HTLC Index">
{p.pending_index}
</DetailRow>
)}
<DetailRow label="Amount">
<Price amount={p.tokens} />
</DetailRow>
<DetailRow label="Amount (msat)">{p.mtokens}</DetailRow>
{p.total_mtokens && (
<DetailRow label="MPP Total (msat)">
{p.total_mtokens}
</DetailRow>
)}
<DetailRow label="Channel">{p.in_channel}</DetailRow>
<DetailRow label="Peer">
<ChannelPeer id={p.in_channel} />
</DetailRow>
<DetailRow label="Accepted">
{`${getDateDif(p.created_at)} ago (${getFormatDate(p.created_at)})`}
</DetailRow>
<DetailRow label="Accept Height">
{p.created_height}
</DetailRow>
{p.is_confirmed && p.confirmed_at && (
<DetailRow label="Resolved">
{`${getDateDif(p.confirmed_at)} ago (${getFormatDate(p.confirmed_at)})`}
</DetailRow>
)}
<DetailRow label="Expiry Height">{p.timeout}</DetailRow>
{p.is_held && <DetailRow label="Held">Yes</DetailRow>}
{messageDisplay?.isTradeMemo && (
<DetailRow label="Parsed Message">
<div className="inline-flex items-start gap-1">
<span>{messageDisplay.computed}</span>
<ComputedMarker />
</div>
</DetailRow>
)}
{message && (
<DetailRow label="Message">
{messageDisplay?.raw ?? message}
</DetailRow>
)}
</DetailTable>
</div>
);
})}
</div>
)}
</div>
)}
</div>

View file

@ -3,12 +3,32 @@ import { decodeMessages } from 'src/server/utils/customRecords';
import { NodeService } from '../../node/node.service';
import { CurrentUser } from '../../security/security.decorators';
import { UserId } from '../../security/security.types';
import { GetInvoicesType, GetPaymentsType } from './transactions.types';
import {
GetInvoicesType,
GetPaymentsType,
InvoiceType,
PaymentType,
} from './transactions.types';
@Resolver()
export class TransactionsResolver {
constructor(private nodeService: NodeService) {}
@Query(() => InvoiceType)
async getInvoice(@CurrentUser() user: UserId, @Args('id') id: string) {
const invoice = await this.nodeService.getInvoice(user.id, { id });
return {
...invoice,
type: 'invoice',
date: invoice.confirmed_at || invoice.created_at,
payments: invoice.payments.map(p => ({
...p,
messages: decodeMessages(p.messages),
})),
};
}
@Query(() => GetInvoicesType)
async getInvoices(
@CurrentUser() user: UserId,
@ -31,6 +51,30 @@ export class TransactionsResolver {
return { next, invoices: mapped };
}
@Query(() => PaymentType)
async getPayment(@CurrentUser() user: UserId, @Args('id') id: string) {
const result = await this.nodeService.getPayment(user.id, { id });
const payment = result?.payment;
if (!payment) {
throw new Error('Payment not found');
}
return {
...payment,
type: 'payment',
date: payment.created_at,
is_confirmed: result.is_confirmed ?? false,
is_outgoing: true,
safe_fee: payment.fee,
safe_tokens: payment.tokens,
destination_node: { publicKey: payment.destination },
hops: payment.hops.map((hop: { public_key: string }) => ({
publicKey: hop.public_key,
})),
};
}
@Query(() => GetPaymentsType)
async getPayments(
@CurrentUser() user: UserId,

View file

@ -294,10 +294,12 @@ export interface LightningProvider {
options: GetRouteToDestinationOptions
): Promise<GetRouteToDestinationResult>;
decodePaymentRequest(connection: any, request: string): Promise<any>;
getPayment(connection: any, options: { id: string }): Promise<any>;
getPayments(connection: any, options: GetPaymentsOptions): Promise<any>;
// ── Invoices ──
createInvoice(connection: any, options: CreateInvoiceOptions): Promise<any>;
getInvoice(connection: any, options: { id: string }): Promise<any>;
getInvoices(connection: any, options: GetInvoicesOptions): Promise<any>;
subscribeToInvoice(connection: any, id: string): EventEmitter;

View file

@ -210,6 +210,10 @@ export class LitdService implements LightningProvider, TaprootAssetsProvider {
);
}
async getPayment(connection: LitdConnection, options: { id: string }) {
return this.lndService.getPayment(this.getLnd(connection), options);
}
async getPayments(connection: LitdConnection, options: GetPaymentsOptions) {
return this.lndService.getPayments(this.getLnd(connection), options);
}
@ -223,6 +227,10 @@ export class LitdService implements LightningProvider, TaprootAssetsProvider {
return this.lndService.createInvoice(this.getLnd(connection), options);
}
async getInvoice(connection: LitdConnection, options: { id: string }) {
return this.lndService.getInvoice(this.getLnd(connection), options);
}
async getInvoices(connection: LitdConnection, options: GetInvoicesOptions) {
return this.lndService.getInvoices(this.getLnd(connection), options);
}

View file

@ -32,11 +32,13 @@ import {
payViaRoutes,
getRouteToDestination,
createInvoice,
getInvoice,
getChannel,
closeChannel,
openChannel,
updateRoutingFees,
getForwards,
getPayment,
getPayments,
getInvoices,
subscribeToInvoice,
@ -304,10 +306,18 @@ export class LndService implements LightningProvider {
return to(getForwards({ lnd, ...options } as any));
}
async getPayment(lnd: AuthenticatedLnd, options: { id: string }) {
return to(getPayment({ lnd, ...options } as any));
}
async getPayments(lnd: AuthenticatedLnd, options: GetPaymentsOptions) {
return to(getPayments({ lnd, ...options } as any));
}
async getInvoice(lnd: AuthenticatedLnd, options: { id: string }) {
return to(getInvoice({ lnd, ...options } as any));
}
async getInvoices(lnd: AuthenticatedLnd, options: GetInvoicesOptions) {
return to(getInvoices({ lnd, ...options } as any));
}

View file

@ -253,11 +253,21 @@ export class NodeService {
return provider.getForwards(account.connection, options);
}
async getPayment(id: string, options: { id: string }) {
const { account, provider } = this.getAccountAndProvider(id);
return provider.getPayment(account.connection, options);
}
async getPayments(id: string, options: GetPaymentsOptions) {
const { account, provider } = this.getAccountAndProvider(id);
return provider.getPayments(account.connection, options);
}
async getInvoice(id: string, options: { id: string }) {
const { account, provider } = this.getAccountAndProvider(id);
return provider.getInvoice(account.connection, options);
}
async getInvoices(id: string, options: GetInvoicesOptions) {
const { account, provider } = this.getAccountAndProvider(id);
return provider.getInvoices(account.connection, options);