Skip to content

Commit 28cad96

Browse files
authored
Merge pull request #438 from iheomadev/feature/338-transactions-openapi
feat(transactions): add OpenAPI decorators and examples (#338)
2 parents 0c8d79e + 50f1d15 commit 28cad96

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

src/transactions/transactions.controller.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ import {
99
UseGuards,
1010
BadRequestException,
1111
} from '@nestjs/common';
12+
import {
13+
ApiTags,
14+
ApiSecurity,
15+
ApiOperation,
16+
ApiParam,
17+
ApiQuery,
18+
ApiResponse,
19+
ApiBody,
20+
} from '@nestjs/swagger';
1221
import { TransactionsService } from './transactions.service';
1322
import { StellarTransactionBuildService } from './stellar-transaction-build.service';
1423
import { CreateTransactionDto } from './dto/create-transaction.dto';
@@ -54,18 +63,96 @@ export class TransactionsController {
5463
* Build an unsigned Stellar payment transaction XDR.
5564
* The returned XDR must be signed before submission to the network.
5665
*/
66+
@ApiOperation({ summary: 'Build an unsigned Stellar payment transaction XDR' })
67+
@ApiBody({
68+
description: 'Payment build parameters',
69+
examples: {
70+
native: {
71+
summary: 'Native XLM payment',
72+
value: {
73+
sourcePublicKey: 'GABC1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEF',
74+
destinationPublicKey: 'GDEF1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEF',
75+
amount: '10.5',
76+
asset: { type: 'NATIVE' },
77+
memo: 'Payment for services',
78+
},
79+
},
80+
},
81+
})
82+
@ApiResponse({ status: 200, description: 'Returns unsigned transaction XDR string' })
5783
@Post('build')
5884
@SensitiveEndpoint()
5985
buildTransaction(@Body() dto: BuildTransactionDto) {
6086
return this.stellarBuildService.buildPayment(dto);
6187
}
6288

89+
@ApiOperation({ summary: 'Create a new transaction' })
90+
@ApiBody({
91+
description: 'Transaction creation payload',
92+
examples: {
93+
nativePayment: {
94+
summary: 'Native XLM payment',
95+
value: {
96+
amount: '10',
97+
asset: { type: 'NATIVE' },
98+
senderWalletId: '550e8400-e29b-41d4-a716-446655440000',
99+
receiverWalletId: '550e8400-e29b-41d4-a716-446655440001',
100+
memo: 'Payment for invoice #42',
101+
idempotencyKey: 'inv-42-pay-1',
102+
},
103+
},
104+
usdcPayment: {
105+
summary: 'USDC payment',
106+
value: {
107+
amount: '25.00',
108+
asset: {
109+
type: 'CREDIT_ALPHANUM4',
110+
code: 'USDC',
111+
issuer: 'GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN',
112+
},
113+
senderWalletId: '550e8400-e29b-41d4-a716-446655440000',
114+
receiverWalletId: '550e8400-e29b-41d4-a716-446655440001',
115+
},
116+
},
117+
},
118+
})
119+
@ApiResponse({ status: 201, description: 'Transaction created in PENDING state' })
63120
@Post()
64121
@SensitiveEndpoint()
65122
create(@Body() createTransactionDto: CreateTransactionDto) {
66123
return this.transactionsService.create(createTransactionDto);
67124
}
68125

126+
@ApiOperation({ summary: 'List transactions with optional filters and pagination' })
127+
@ApiQuery({ name: 'senderWalletId', required: false, description: 'Filter by sender wallet ID' })
128+
@ApiQuery({ name: 'receiverWalletId', required: false, description: 'Filter by receiver wallet ID' })
129+
@ApiQuery({ name: 'status', required: false, enum: TransactionStatus, description: 'Filter by transaction status' })
130+
@ApiQuery({ name: 'limit', required: false, description: 'Max records to return (1-100, default 20)', example: 20 })
131+
@ApiQuery({ name: 'offset', required: false, description: 'Number of records to skip (default 0)', example: 0 })
132+
@ApiResponse({
133+
status: 200,
134+
description: 'Paginated list of transactions',
135+
schema: {
136+
example: {
137+
data: [
138+
{
139+
id: '550e8400-e29b-41d4-a716-446655440002',
140+
amount: '10',
141+
assetType: 'NATIVE',
142+
status: 'PENDING',
143+
senderWalletId: '550e8400-e29b-41d4-a716-446655440000',
144+
receiverWalletId: '550e8400-e29b-41d4-a716-446655440001',
145+
createdAt: '2026-01-01T00:00:00.000Z',
146+
updatedAt: '2026-01-01T00:00:00.000Z',
147+
},
148+
],
149+
total: 1,
150+
limit: 20,
151+
offset: 0,
152+
hasMore: false,
153+
},
154+
},
155+
})
69156
@Get()
70157
findAll(
71158
@Query('senderWalletId') senderWalletId?: string,
@@ -83,6 +170,12 @@ export class TransactionsController {
83170
});
84171
}
85172

173+
@ApiOperation({ summary: 'List transactions for a specific wallet' })
174+
@ApiParam({ name: 'walletId', description: 'Wallet ID to query transactions for', example: '550e8400-e29b-41d4-a716-446655440000' })
175+
@ApiQuery({ name: 'limit', required: false, description: 'Max records to return (1-100, default 20)', example: 20 })
176+
@ApiQuery({ name: 'offset', required: false, description: 'Number of records to skip (default 0)', example: 0 })
177+
@ApiResponse({ status: 200, description: 'Paginated list of wallet transactions' })
178+
@ApiResponse({ status: 404, description: 'Wallet not found' })
86179
@Get('wallet/:walletId')
87180
findByWallet(
88181
@Param('walletId') walletId: string,
@@ -95,16 +188,57 @@ export class TransactionsController {
95188
});
96189
}
97190

191+
@ApiOperation({ summary: 'Find a transaction by Stellar transaction hash' })
192+
@ApiParam({ name: 'hash', description: 'Stellar transaction hash', example: 'a1b2c3d4e5f6...' })
193+
@ApiResponse({ status: 200, description: 'Transaction found' })
194+
@ApiResponse({ status: 200, description: 'Returns null if not found' })
98195
@Get('stellar/:hash')
99196
findByStellarHash(@Param('hash') hash: string) {
100197
return this.transactionsService.findByStellarHash(hash);
101198
}
102199

200+
@ApiOperation({ summary: 'Get a transaction by ID' })
201+
@ApiParam({ name: 'id', description: 'Transaction UUID', example: '550e8400-e29b-41d4-a716-446655440002' })
202+
@ApiResponse({ status: 200, description: 'Transaction found' })
203+
@ApiResponse({ status: 404, description: 'Transaction not found' })
103204
@Get(':id')
104205
findOne(@Param('id') id: string) {
105206
return this.transactionsService.findOne(id);
106207
}
107208

209+
@ApiOperation({ summary: 'Update transaction status' })
210+
@ApiParam({ name: 'id', description: 'Transaction UUID' })
211+
@ApiBody({
212+
description: 'Status update payload',
213+
examples: {
214+
submit: {
215+
summary: 'Mark as submitted to Stellar',
216+
value: {
217+
status: 'SUBMITTED',
218+
stellarHash: 'a1b2c3d4e5f6789abc...',
219+
},
220+
},
221+
confirm: {
222+
summary: 'Mark as confirmed on-chain',
223+
value: {
224+
status: 'CONFIRMED',
225+
stellarHash: 'a1b2c3d4e5f6789abc...',
226+
stellarLedger: 48750123,
227+
stellarFee: '100',
228+
},
229+
},
230+
fail: {
231+
summary: 'Mark as failed',
232+
value: {
233+
status: 'FAILED',
234+
statusReason: 'Insufficient fee',
235+
},
236+
},
237+
},
238+
})
239+
@ApiResponse({ status: 200, description: 'Status updated' })
240+
@ApiResponse({ status: 400, description: 'Invalid status transition' })
241+
@ApiResponse({ status: 404, description: 'Transaction not found' })
108242
@Patch(':id/status')
109243
@SensitiveEndpoint()
110244
updateStatus(

0 commit comments

Comments
 (0)