Skip to content

Commit dc29138

Browse files
authored
Merge pull request #8 from fadesany/POST-/v1/payment-requests/validate
feat: initialize NestJS project structure with Prisma, Supabase, and …
2 parents 40ba291 + 9d92519 commit dc29138

17 files changed

Lines changed: 833 additions & 22 deletions

README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,94 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
9393
- Website - [https://nestjs.com](https://nestjs.com/)
9494
- Twitter - [@nestframework](https://twitter.com/nestframework)
9595

96+
## Payment Request Validation
97+
98+
The backend supports validation and normalization of payment requests (including direct fields, serialized JSON strings, and URI schemes).
99+
100+
### Endpoint
101+
`POST /v1/payment-requests/validate`
102+
103+
### Curl Examples
104+
105+
#### 1. Direct Structured Request (Valid)
106+
```bash
107+
curl -X POST http://localhost:3000/v1/payment-requests/validate \
108+
-H "Content-Type: application/json" \
109+
-d '{
110+
"asset": "USDC",
111+
"recipient": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
112+
"amount": 250.0
113+
}'
114+
```
115+
116+
Response:
117+
```json
118+
{
119+
"valid": true,
120+
"normalizedPayload": {
121+
"asset": "USDC",
122+
"recipient": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
123+
"amount": 250
124+
}
125+
}
126+
```
127+
128+
#### 2. URI String Payment Request (Valid)
129+
```bash
130+
curl -X POST http://localhost:3000/v1/payment-requests/validate \
131+
-H "Content-Type: application/json" \
132+
-d '{
133+
"payload": "ethereum:0x742d35Cc6634C0532925a3b844Bc454e4438f44e?amount=150.0&asset=USDC"
134+
}'
135+
```
136+
137+
Response:
138+
```json
139+
{
140+
"valid": true,
141+
"normalizedPayload": {
142+
"asset": "USDC",
143+
"recipient": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
144+
"amount": 150
145+
}
146+
}
147+
```
148+
149+
#### 3. Request with Validation Errors (Invalid)
150+
```bash
151+
curl -X POST http://localhost:3000/v1/payment-requests/validate \
152+
-H "Content-Type: application/json" \
153+
-d '{
154+
"asset": "DOGE",
155+
"recipient": "0xInvalidEthAddress",
156+
"amount": -100
157+
}'
158+
```
159+
160+
Response:
161+
```json
162+
{
163+
"valid": false,
164+
"errors": [
165+
{
166+
"code": "INVALID_ASSET",
167+
"message": "Asset 'DOGE' is not supported. Supported assets are: USDC, USDT, ETH, BTC, SOL, USD, EUR.",
168+
"field": "asset"
169+
},
170+
{
171+
"code": "INVALID_RECIPIENT",
172+
"message": "Recipient address starts with 0x but is not a valid Ethereum address.",
173+
"field": "recipient"
174+
},
175+
{
176+
"code": "INVALID_AMOUNT",
177+
"message": "Amount must be greater than zero.",
178+
"field": "amount"
179+
}
180+
]
181+
}
182+
```
183+
96184
## License
97185

98186
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).

package-lock.json

Lines changed: 84 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"@nestjs/config": "^4.0.4",
2525
"@nestjs/core": "^11.0.1",
2626
"@nestjs/platform-express": "^11.0.1",
27-
"@prisma/client": "^7.8.0",
27+
"@prisma/client": "^6.3.0",
2828
"@supabase/supabase-js": "^2.104.1",
2929
"reflect-metadata": "^0.2.2",
3030
"rxjs": "^7.8.1"
@@ -45,6 +45,7 @@
4545
"globals": "^16.0.0",
4646
"jest": "^30.0.0",
4747
"prettier": "^3.4.2",
48+
"prisma": "^6.3.0",
4849
"source-map-support": "^0.5.21",
4950
"supertest": "^7.0.0",
5051
"ts-jest": "^29.2.5",

src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { SupabaseModule } from './supabase/supabase.module';
77
import { UsersModule } from './users/users.module';
88
import { PaymentsModule } from './payments/payments.module';
99
import { TransactionsModule } from './transactions/transactions.module';
10+
import { PaymentRequestsModule } from './payment-requests/payment-requests.module';
1011

1112
@Module({
1213
imports: [
@@ -18,6 +19,7 @@ import { TransactionsModule } from './transactions/transactions.module';
1819
UsersModule,
1920
PaymentsModule,
2021
TransactionsModule,
22+
PaymentRequestsModule,
2123
],
2224
controllers: [AppController],
2325
providers: [AppService],
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class ValidatePaymentRequestDto {
2+
asset?: string;
3+
recipient?: string;
4+
amount?: number | string;
5+
expiresAt?: string | number;
6+
payload?: any;
7+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { PaymentRequestsController } from './payment-requests.controller';
3+
import { PaymentRequestsService } from './payment-requests.service';
4+
5+
describe('PaymentRequestsController', () => {
6+
let controller: PaymentRequestsController;
7+
let service: PaymentRequestsService;
8+
9+
beforeEach(async () => {
10+
const module: TestingModule = await Test.createTestingModule({
11+
controllers: [PaymentRequestsController],
12+
providers: [PaymentRequestsService],
13+
}).compile();
14+
15+
controller = module.get<PaymentRequestsController>(
16+
PaymentRequestsController,
17+
);
18+
service = module.get<PaymentRequestsService>(PaymentRequestsService);
19+
});
20+
21+
it('should be defined', () => {
22+
expect(controller).toBeDefined();
23+
});
24+
25+
describe('validate', () => {
26+
it('should call service.validate and return result', () => {
27+
const dto = {
28+
asset: 'USDC',
29+
recipient: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
30+
amount: 100,
31+
};
32+
33+
const spy = jest.spyOn(service, 'validate');
34+
const result = controller.validate(dto);
35+
36+
expect(spy).toHaveBeenCalledWith(dto);
37+
expect(result.valid).toBe(true);
38+
expect(result.normalizedPayload?.amount).toBe(100);
39+
});
40+
});
41+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
2+
import {
3+
PaymentRequestsService,
4+
ValidationResult,
5+
} from './payment-requests.service';
6+
import { ValidatePaymentRequestDto } from './dto/validate-payment-request.dto';
7+
8+
@Controller('v1/payment-requests')
9+
export class PaymentRequestsController {
10+
constructor(
11+
private readonly paymentRequestsService: PaymentRequestsService,
12+
) {}
13+
14+
@Post('validate')
15+
@HttpCode(HttpStatus.OK)
16+
validate(@Body() dto: ValidatePaymentRequestDto): ValidationResult {
17+
return this.paymentRequestsService.validate(dto);
18+
}
19+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Module } from '@nestjs/common';
2+
import { PaymentRequestsService } from './payment-requests.service';
3+
import { PaymentRequestsController } from './payment-requests.controller';
4+
5+
@Module({
6+
providers: [PaymentRequestsService],
7+
controllers: [PaymentRequestsController],
8+
exports: [PaymentRequestsService],
9+
})
10+
export class PaymentRequestsModule {}

0 commit comments

Comments
 (0)