Skip to content

Commit f4798f9

Browse files
committed
feat: added pagination in payments
Change response type + test cases TICKET: BTC-2110
1 parent 81ff48a commit f4798f9

File tree

4 files changed

+123
-7
lines changed

4 files changed

+123
-7
lines changed

examples/ts/btc/lightning/list-payments.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async function main(): Promise<void> {
5454
}
5555

5656
// List payments with the provided filters
57-
const payments = await lightning.listPayments(queryParams);
57+
const { payments } = await lightning.listPayments(queryParams);
5858

5959
// Display payment summary
6060
console.log(`\nFound ${payments.length} payments:`);

modules/abstract-lightning/src/codecs/api/payment.ts

+19
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,23 @@ export const PaymentInfo = t.intersection(
7474

7575
export type PaymentInfo = t.TypeOf<typeof PaymentInfo>;
7676

77+
export const ListPaymentsResponse = t.intersection(
78+
[
79+
t.type({
80+
payments: t.array(PaymentInfo),
81+
}),
82+
t.partial({
83+
/**
84+
* This is the paymentHash of the last Payment in the last iteration.
85+
* Providing this value as the prevId in the next request will return the next batch of payments.
86+
* */
87+
nextBatchPrevId: t.string,
88+
}),
89+
],
90+
'ListPaymentsResponse'
91+
);
92+
export type ListPaymentsResponse = t.TypeOf<typeof ListPaymentsResponse>;
93+
7794
/**
7895
* Payment query parameters
7996
*/
@@ -83,6 +100,8 @@ export const PaymentQuery = t.partial(
83100
limit: BigIntFromString,
84101
startDate: DateFromISOString,
85102
endDate: DateFromISOString,
103+
/** paymentHash provided by nextBatchPrevId in the previous list */
104+
prevId: t.string,
86105
},
87106
'PaymentQuery'
88107
);

modules/abstract-lightning/src/wallet/lightning.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
LightningOnchainWithdrawParams,
2828
LightningOnchainWithdrawResponse,
2929
ListInvoicesResponse,
30+
ListPaymentsResponse,
3031
} from '../codecs';
3132
import { LightningPaymentIntent, LightningPaymentRequest } from '@bitgo/public-types';
3233

@@ -144,7 +145,7 @@ export interface ILightningWallet {
144145
* @param {bigint} [params.limit] The maximum number of invoices to return
145146
* @param {Date} [params.startDate] The start date for the query
146147
* @param {Date} [params.endDate] The end date for the query
147-
* @param {Date} [params.prevId] Continue iterating (provided by nextBatchPrevId in the previous list)
148+
* @param {string} [params.prevId] Continue iterating (provided by nextBatchPrevId in the previous list)
148149
* @returns {Promise<ListInvoicesResponse>} List of invoices and nextBatchPrevId
149150
*/
150151
listInvoices(params: InvoiceQuery): Promise<ListInvoicesResponse>;
@@ -183,9 +184,10 @@ export interface ILightningWallet {
183184
* @param {bigint} [params.limit] The maximum number of payments to return
184185
* @param {Date} [params.startDate] The start date for the query
185186
* @param {Date} [params.endDate] The end date for the query
186-
* @returns {Promise<PaymentInfo[]>} List of payments
187+
* @param {string} [params.prevId] Continue iterating (provided by nextBatchPrevId in the previous list)
188+
* @returns {Promise<ListPaymentsResponse>} List of payments and nextBatchPrevId
187189
*/
188-
listPayments(params: PaymentQuery): Promise<PaymentInfo[]>;
190+
listPayments(params: PaymentQuery): Promise<ListPaymentsResponse>;
189191
/**
190192
* Get transaction details by ID
191193
* @param {string} txId - Transaction ID to lookup
@@ -366,12 +368,12 @@ export class LightningWallet implements ILightningWallet {
366368
});
367369
}
368370

369-
async listPayments(params: PaymentQuery): Promise<PaymentInfo[]> {
371+
async listPayments(params: PaymentQuery): Promise<ListPaymentsResponse> {
370372
const response = await this.wallet.bitgo
371373
.get(this.wallet.bitgo.url(`/wallet/${this.wallet.id()}/lightning/payment`, 2))
372374
.query(PaymentQuery.encode(params))
373375
.result();
374-
return decodeOrElse(t.array(PaymentInfo).name, t.array(PaymentInfo), response, (error) => {
376+
return decodeOrElse(ListPaymentsResponse.name, ListPaymentsResponse, response, (error) => {
375377
throw new Error(`Invalid payment list response: ${error}`);
376378
});
377379
}

modules/bitgo/test/v2/unit/lightning/lightningWallets.ts

+96-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
getLightningAuthKeychains,
1717
updateWalletCoinSpecific,
1818
LightningOnchainWithdrawParams,
19+
PaymentInfo,
20+
PaymentQuery,
1921
} from '@bitgo/abstract-lightning';
2022

2123
import { BitGo, common, GenerateLightningWalletOptions, Wallet, Wallets } from '../../../../src';
@@ -313,7 +315,7 @@ describe('Lightning wallets', function () {
313315
it('listInvoices should throw error if wp response is invalid', async function () {
314316
const listInvoicesNock = nock(bgUrl)
315317
.get(`/api/v2/wallet/${wallet.wallet.id()}/lightning/invoice`)
316-
.reply(200, [{ valueMsat: '1000' }]);
318+
.reply(200, { invoices: [{ valueMsat: '1000' }] });
317319
await assert.rejects(async () => await wallet.listInvoices({}), /Invalid list invoices response/);
318320
listInvoicesNock.done();
319321
});
@@ -503,6 +505,99 @@ describe('Lightning wallets', function () {
503505
});
504506
});
505507

508+
describe('payments', function () {
509+
let wallet: LightningWallet;
510+
beforeEach(function () {
511+
wallet = getLightningWallet(
512+
new Wallet(bitgo, basecoin, {
513+
id: 'walletId',
514+
coin: 'tlnbtc',
515+
subType: 'lightningCustody',
516+
coinSpecific: { keys: ['def', 'ghi'] },
517+
})
518+
) as LightningWallet;
519+
});
520+
521+
it('should get payments', async function () {
522+
const payment: PaymentInfo = {
523+
paymentHash: 'foo',
524+
walletId: wallet.wallet.id(),
525+
txRequestId: 'txReqId',
526+
status: 'settled',
527+
invoice: 'tlnfoobar',
528+
destination: 'destination',
529+
feeLimitMsat: 100n,
530+
amountMsat: 1000n,
531+
createdAt: new Date(),
532+
updatedAt: new Date(),
533+
};
534+
const query = {
535+
status: 'settled',
536+
startDate: new Date(),
537+
limit: 100n,
538+
} as PaymentQuery;
539+
const listPaymentsNock = nock(bgUrl)
540+
.get(`/api/v2/wallet/${wallet.wallet.id()}/lightning/payment`)
541+
.query(PaymentQuery.encode(query))
542+
.reply(200, { payments: [PaymentInfo.encode(payment)] });
543+
const listPaymentsResponse = await wallet.listPayments(query);
544+
assert.strictEqual(listPaymentsResponse.payments.length, 1);
545+
assert.deepStrictEqual(listPaymentsResponse.payments[0], payment);
546+
listPaymentsNock.done();
547+
});
548+
549+
it('should work properly with pagination while listing payments', async function () {
550+
const payment1: PaymentInfo = {
551+
paymentHash: 'foo1',
552+
walletId: wallet.wallet.id(),
553+
txRequestId: 'txReqId1',
554+
status: 'settled',
555+
invoice: 'tlnfoobar1',
556+
destination: 'destination',
557+
feeLimitMsat: 100n,
558+
amountMsat: 1000n,
559+
createdAt: new Date(),
560+
updatedAt: new Date(),
561+
};
562+
const payment2: PaymentInfo = {
563+
...payment1,
564+
paymentHash: 'foo2',
565+
txRequestId: 'txReqId2',
566+
invoice: 'tlnfoobar2',
567+
};
568+
const payment3: PaymentInfo = {
569+
...payment1,
570+
paymentHash: 'foo3',
571+
txRequestId: 'txReqId3',
572+
invoice: 'tlnfoobar3',
573+
};
574+
const allPayments = [PaymentInfo.encode(payment1), PaymentInfo.encode(payment2), PaymentInfo.encode(payment3)];
575+
const query = {
576+
status: 'settled',
577+
startDate: new Date(),
578+
limit: 2n,
579+
} as PaymentQuery;
580+
const listPaymentsNock = nock(bgUrl)
581+
.get(`/api/v2/wallet/${wallet.wallet.id()}/lightning/payment`)
582+
.query(PaymentQuery.encode(query))
583+
.reply(200, { payments: allPayments.slice(0, 2), nextBatchPrevId: payment2.paymentHash });
584+
const listPaymentsResponse = await wallet.listPayments(query);
585+
assert.strictEqual(listPaymentsResponse.payments.length, 2);
586+
assert.deepStrictEqual(listPaymentsResponse.payments[0], payment1);
587+
assert.deepStrictEqual(listPaymentsResponse.payments[1], payment2);
588+
assert.strictEqual(listPaymentsResponse.nextBatchPrevId, payment2.paymentHash);
589+
listPaymentsNock.done();
590+
});
591+
592+
it('listPayments should throw error if wp response is invalid', async function () {
593+
const listPaymentsNock = nock(bgUrl)
594+
.get(`/api/v2/wallet/${wallet.wallet.id()}/lightning/payment`)
595+
.reply(200, { payments: [{ amountMsat: '1000' }] });
596+
await assert.rejects(async () => await wallet.listPayments({}), /Invalid payment list response/);
597+
listPaymentsNock.done();
598+
});
599+
});
600+
506601
describe('Get lightning key(s)', function () {
507602
const walletData = {
508603
id: 'fakeid',

0 commit comments

Comments
 (0)