Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
3e32ca4
Merge pull request #28 from supreme2580/issue-23
MissBlue00 Mar 24, 2026
095f6e4
feat(api): implement JWT authentication strategy and guards
supreme2580 Mar 24, 2026
f1bc378
fix: lint error
SamixYasuke Mar 24, 2026
8bf70fc
Merge pull request #30 from supreme2580/issue-6
MissBlue00 Mar 24, 2026
9024a2b
feat(api): implement Proof-of-Reserves (PoR) API
supreme2580 Mar 24, 2026
fb5ed91
Merge origin/main into issue-19
supreme2580 Mar 24, 2026
444c38d
Merge pull request #33 from supreme2580/issue-19
MissBlue00 Mar 24, 2026
caa8f79
fix: merge conflict
SamixYasuke Mar 24, 2026
34b1b70
fix lint and merge conflict
SamixYasuke Mar 24, 2026
14acbe0
feat(api): implement rate limiting middleware
supreme2580 Mar 24, 2026
4dbd3af
test: add comprehensive integration e2e test suite
devJaja Mar 24, 2026
cf8b8a5
Merge pull request #34 from supreme2580/issue-21
MissBlue00 Mar 24, 2026
1dd0af7
feat(infra): configure multi-stage pnpm docker builds
Code-Paragon Mar 24, 2026
0d415af
feat: enhance CI pipeline with test coverage and Codecov integration
OperaCode Mar 24, 2026
769b60d
fix: enable CI for all branches
OperaCode Mar 24, 2026
d805c8c
fix: enabled CI pipelines to main
OperaCode Mar 24, 2026
e9564cf
fixed lint errors
OperaCode Mar 24, 2026
b233f63
multi-chain-validator
Mar 25, 2026
7bffcdb
Add disaster recovery documentation and DR drill scripts
Mar 25, 2026
1c2efde
feat(api): implement centralized audit logging
David-patrick-chuks Mar 25, 2026
f428b34
Add PaymentIntentType interface to payments-engine
Ugasutun Mar 25, 2026
60dccb9
Remove package-lock.json to fix pnpm conflict
Ugasutun Mar 25, 2026
af0d5bc
feat: setup global request validation and payment DTOs
Wisdom2788 Mar 25, 2026
8d0ec36
Merge pull request #27 from SamixYasuke/feat/integrate-swagger--opena…
MissBlue00 Mar 27, 2026
5ef9f00
Merge pull request #105 from Code-Paragon/feature/docker-containers
MissBlue00 Mar 27, 2026
568efd7
fix: resolve frontend linting issues
Mar 27, 2026
2d5f020
Merge branch 'main' into feat/disaster-recovery-procedures
Muhammadcodes112 Mar 27, 2026
d61d3c2
fix: CI failure fix
devJaja Mar 27, 2026
d56b05f
Merge branch 'main' into test/integration-test-suite
devJaja Mar 27, 2026
0caca65
chore: resolve merge conflicts
OperaCode Mar 27, 2026
9dd6977
Merge pull request #36 from devJaja/test/integration-test-suite
MissBlue00 Mar 27, 2026
3d8ed4e
feat: implement withdrawal processor worker for pending redemptions
Mar 24, 2026
683c2c2
Merge branch 'MissBlue00:main' into main
Ugasutun Mar 27, 2026
cd94e23
feat(database): add prisma migrations, deploy scripts, and seed data
Samaro1 Mar 25, 2026
c29099a
Merge pull request #31 from AbuJulaybeeb/feat/issue-20-withdrawal-pro…
MissBlue00 Mar 28, 2026
72bbb68
multi-chain-validator
Mar 28, 2026
5927673
Merge branch 'main' into main
Ugasutun Mar 28, 2026
7e81624
fix(frontend): escape apostrophes and remove Math.random from render
Wisdom2788 Mar 29, 2026
17f5937
fix(frontend): resolve lint failures
David-patrick-chuks Apr 27, 2026
a0ef9f7
merge upstream/main
David-patrick-chuks Apr 27, 2026
eba574c
docs: add comprehensive API documentation for StellarPay backend
Apr 29, 2026
02ef0d2
docs: add comprehensive SDK documentation for @stellar-pay/sdk-js
Apr 29, 2026
b5a9e7d
docs: add comprehensive smart contracts documentation
Apr 29, 2026
62337c5
docs: add deployment and configuration guide
Apr 29, 2026
10968f6
Merge pull request #137 from supreme2580/issue-133
MissBlue00 Apr 29, 2026
913ea41
Merge pull request #138 from supreme2580/issue-134
MissBlue00 Apr 29, 2026
8cbe131
Merge pull request #139 from supreme2580/issue-135
MissBlue00 Apr 29, 2026
e3d22e8
Merge pull request #140 from supreme2580/issue-136
MissBlue00 Apr 29, 2026
cce9a69
Merge pull request #126 from Wisdom2788/feature/backend-validation
MissBlue00 May 26, 2026
68bba6a
Merge pull request #125 from Ugasutun/main
MissBlue00 May 26, 2026
561086e
Merge pull request #123 from Samaro1/chore/database-migrations
MissBlue00 May 26, 2026
c49a738
Merge pull request #122 from David-patrick-chuks/feat/issue-22-audit-…
MissBlue00 May 26, 2026
9b69ddd
Merge pull request #119 from Muhammadcodes112/feat/disaster-recovery-…
MissBlue00 May 26, 2026
5e2ec7d
Merge pull request #116 from rosemary21/multi-chain-validator
MissBlue00 May 26, 2026
d5bc124
Merge pull request #114 from OperaCode/feat/ci-cd-pipeline
MissBlue00 May 26, 2026
b5165b5
feat(payments): implement payment intent creation endpoint
supreme2580 May 28, 2026
5ebe1ab
Merge pull request #161 from supreme2580/feat/payment-intent-endpoint
MissBlue00 May 28, 2026
7aff7a6
feat(treasury): implement asset redemption and burn endpoint
supreme2580 May 28, 2026
a7468bb
Merge pull request #162 from supreme2580/feat/treasury-redeem-endpoint
MissBlue00 May 28, 2026
4bbb52f
feat(worker): implement blockchain transaction watcher
supreme2580 May 28, 2026
b486851
Merge branch 'main' into feat/blockchain-transaction-watcher
supreme2580 May 28, 2026
20a01f0
Merge pull request #163 from supreme2580/feat/blockchain-transaction-…
MissBlue00 May 28, 2026
7ce7f60
feat(payments): implement crypto deposit address generation
supreme2580 May 28, 2026
b0841ba
Merge pull request #164 from supreme2580/feat/deposit-address-generation
MissBlue00 May 28, 2026
6d11b45
feat(db): initialize Prisma with core PostgreSQL schema
omosvico May 28, 2026
9f473de
Merge pull request #165 from omosvico/feat/prisma-initialize-core-schema
MissBlue00 May 28, 2026
67ba85a
feat(anchor): implement processAnchorRefund function
omosvico May 28, 2026
9e01d1e
Merge pull request #166 from omosvico/feat/process-anchor-refund
MissBlue00 May 28, 2026
9971c42
feat(anchor): implement createSep31DirectPayment function
omosvico May 28, 2026
6053d35
Merge branch 'main' into feat/sep31-direct-payment
omosvico May 28, 2026
1c19699
Merge pull request #167 from omosvico/feat/sep31-direct-payment
MissBlue00 May 28, 2026
d258c7b
feat(anchor): implement submitCustomerInfo function for SEP-12
omosvico May 28, 2026
f086cea
Merge branch 'main' into feat/submit-customer-info
omosvico May 28, 2026
dfaf10d
Merge pull request #168 from omosvico/feat/submit-customer-info
MissBlue00 May 28, 2026
4efb5ab
Implement blue-green deployment strategy
vstudio79 May 28, 2026
b40edae
Merge pull request #169 from vstudio79/feat/blue-green-deployment
MissBlue00 May 28, 2026
75fec88
Add calculateAnchorFee function
vstudio79 May 28, 2026
101a940
Merge pull request #170 from vstudio79/feat/calculate-anchor-fee
MissBlue00 May 28, 2026
ed11f55
Add checkKycStatus function
vstudio79 May 28, 2026
3948ffc
Merge branch 'main' into feat/check-kyc-status
vstudio79 May 28, 2026
4946825
Merge pull request #171 from vstudio79/feat/check-kyc-status
MissBlue00 May 28, 2026
5c9c178
Implement Redis-backed webhook retry queue
vstudio79 May 28, 2026
8c58a29
Merge pull request #172 from vstudio79/feat/webhook-redis-retry
MissBlue00 May 28, 2026
b619072
feat(auth): add merchant register/login with JWT and bcrypt
AbilityTechy May 29, 2026
c059425
Merge branch 'main' into feat/auth-register-login
AbilityTechy Jun 1, 2026
162473f
Merge branch 'main' into feat/auth-register-login
AbilityTechy Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@nestjs/common": "^11.0.1",
"@nestjs/core": "^11.0.1",
"@nestjs/passport": "^11.0.5",
"@nestjs/jwt": "^11.0.0",
"@nestjs/platform-express": "^11.0.1",
"@nestjs/schedule": "^6.1.1",
"@nestjs/swagger": "^11.2.6",
Expand All @@ -45,6 +46,7 @@
"ioredis": "^5.11.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"bcrypt": "^5.1.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.1"
Expand Down
82 changes: 11 additions & 71 deletions apps/api/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,22 @@
import {
Controller,
Post,
Body,
HttpCode,
HttpStatus,
BadRequestException,
UnauthorizedException,
} from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { RegisterDto } from './dto/register.dto';
import { LoginDto } from './dto/login.dto';
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { RegisterMerchantDto } from './dto/register-merchant.dto';
import { LoginMerchantDto } from './dto/login-merchant.dto';
import { Public } from './decorators/public.decorator';
import * as jwt from 'jsonwebtoken';
import { randomBytes, scryptSync } from 'crypto';

function hashPassword(password: string): string {
const salt = randomBytes(16).toString('hex');
const derived = scryptSync(password, salt, 64).toString('hex');
return `${salt}:${derived}`;
}

function verifyPassword(password: string, stored: string): boolean {
const [salt, key] = stored.split(':');
if (!salt || !key) return false;
const derived = scryptSync(password, salt, 64).toString('hex');
return derived === key;
}

@Controller('auth')
export class AuthController {
constructor(private readonly prisma: PrismaService) {}
constructor(private readonly authService: AuthService) {}

@Post('register')
@Public()
@HttpCode(HttpStatus.CREATED)
async register(@Body() dto: RegisterDto) {
const existing = await this.prisma.merchant.findUnique({ where: { email: dto.email } });
if (existing) {
throw new BadRequestException('Email already registered');
}

const passwordHash = hashPassword(dto.password);
const merchant = await this.prisma.merchant.create({
data: { email: dto.email, passwordHash },
});

const token = jwt.sign(
{ merchant_id: merchant.id },
process.env.JWT_SECRET ?? 'default-secret-change-me',
{
expiresIn: '1h',
},
);

return { access_token: token };
@Post('register')
async register(@Body() dto: RegisterMerchantDto) {
return this.authService.register(dto);
}

@Post('login')
@Public()
@HttpCode(HttpStatus.OK)
async login(@Body() dto: LoginDto) {
const merchant = await this.prisma.merchant.findUnique({ where: { email: dto.email } });
if (!merchant) {
throw new UnauthorizedException('Invalid credentials');
}

if (!merchant.passwordHash || !verifyPassword(dto.password, merchant.passwordHash)) {
throw new UnauthorizedException('Invalid credentials');
}

const token = jwt.sign(
{ merchant_id: merchant.id },
process.env.JWT_SECRET ?? 'default-secret-change-me',
{
expiresIn: '1h',
},
);

return { access_token: token };
@Post('login')
async login(@Body() dto: LoginMerchantDto) {
return this.authService.login(dto);
}
}
15 changes: 12 additions & 3 deletions apps/api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './strategies/jwt.strategy';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { PrismaModule } from '../prisma/prisma.module';

@Module({
imports: [PassportModule.register({ defaultStrategy: 'jwt' }), PrismaModule],
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret: process.env.JWT_SECRET ?? 'default-secret-change-me',
signOptions: { expiresIn: process.env.JWT_EXPIRES_IN || '1h' },
}),
PrismaModule,
],
controllers: [AuthController],
providers: [JwtStrategy, JwtAuthGuard],
exports: [JwtAuthGuard],
providers: [JwtStrategy, JwtAuthGuard, AuthService],
exports: [JwtAuthGuard, AuthService],
})
export class AuthModule {}
52 changes: 52 additions & 0 deletions apps/api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Injectable, BadRequestException, UnauthorizedException } from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from '../prisma/prisma.service';
import { RegisterMerchantDto } from './dto/register-merchant.dto';
import { LoginMerchantDto } from './dto/login-merchant.dto';

@Injectable()
export class AuthService {
constructor(private readonly prisma: PrismaService, private readonly jwtService: JwtService) {}

private parseExpiresInToSeconds(value: string): number {
// Accepts values like '1h', '30m', or plain seconds '3600'
if (!value) return 3600;
if (/^\d+$/.test(value)) return parseInt(value, 10);
const m = value.match(/^(\d+)([smh])$/);
if (!m) return 3600;
const n = parseInt(m[1], 10);
const unit = m[2];
if (unit === 'h') return n * 3600;
if (unit === 'm') return n * 60;
return n;
}

async register(dto: RegisterMerchantDto) {
const existing = await this.prisma.merchant.findUnique({ where: { email: dto.email } });
if (existing) throw new BadRequestException('Email already in use');

const hash = await bcrypt.hash(dto.password, 12);

const merchant = await this.prisma.merchant.create({
data: { email: dto.email, passwordHash: hash },
});

return { merchant_id: merchant.id, email: merchant.email };
}

async login(dto: LoginMerchantDto) {
const merchant = await this.prisma.merchant.findUnique({ where: { email: dto.email } });
if (!merchant) throw new UnauthorizedException('Invalid credentials');

const ok = await bcrypt.compare(dto.password, merchant.passwordHash);
if (!ok) throw new UnauthorizedException('Invalid credentials');

const payload = { merchant_id: merchant.id };
const expiresInEnv = process.env.JWT_EXPIRES_IN || '1h';
const access_token = this.jwtService.sign(payload, { expiresIn: expiresInEnv });
const expires_in = this.parseExpiresInToSeconds(expiresInEnv);

return { access_token, expires_in };
}
}
9 changes: 9 additions & 0 deletions apps/api/src/auth/dto/login-merchant.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IsEmail, IsString } from 'class-validator';

export class LoginMerchantDto {
@IsEmail()
email: string;

@IsString()
password: string;
}
13 changes: 13 additions & 0 deletions apps/api/src/auth/dto/register-merchant.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IsEmail, IsString, MinLength } from 'class-validator';

export class RegisterMerchantDto {
@IsEmail()
email: string;

@IsString()
@MinLength(8)
password: string;

@IsString()
name?: string;
}