Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions packages/payments-engine/src/config/config.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { envValidationSchema } from './env.validation';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validationSchema: envValidationSchema,
validationOptions: {
abortEarly: true,
allowUnknown: true,
},
}),
],
exports: [ConfigModule],
})
export class AppConfigModule {}
11 changes: 11 additions & 0 deletions packages/payments-engine/src/config/env.validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as Joi from 'joi';

export const envValidationSchema = Joi.object({
DATABASE_URL: Joi.string().uri().required(),
JWT_SECRET: Joi.string().min(16).required(),
TREASURY_PRIVATE_KEY: Joi.string().required(),
STELLAR_RPC_URL: Joi.string().uri().required(),
BTC_NODE_URL: Joi.string().uri().required(),
ETH_NODE_URL: Joi.string().uri().required(),
REDIS_URL: Joi.string().uri().required(),
});
15 changes: 14 additions & 1 deletion packages/payments-engine/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
export * from './stellar.service';
import { Module } from '@nestjs/common';
import { AppConfigModule } from './config/config.module';
import { StellarService } from './stellar.service';
// ... other imports

@Module({
imports: [
AppConfigModule,
// ... other modules
],
providers: [StellarService],
// ...
})
export class PaymentsEngineModule {}
61 changes: 12 additions & 49 deletions packages/payments-engine/src/stellar.service.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,19 @@
import * as StellarSdk from 'stellar-sdk';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class StellarService {
private server: StellarSdk.Horizon.Server;
private sourceKeypair!: StellarSdk.Keypair;

constructor() {
// Default to testnet if not explicitly set
const networkUrl = process.env.STELLAR_NETWORK_URL || 'https://horizon-testnet.stellar.org';
this.server = new StellarSdk.Horizon.Server(networkUrl);

// In production, this must be securely injected
const secret =
process.env.STELLAR_STORAGE_SECRET ||
'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; // Replace with a valid testnet secret for local dev

try {
this.sourceKeypair = StellarSdk.Keypair.fromSecret(secret);
} catch {
console.warn('Invalid STELLAR_STORAGE_SECRET. Stellar operations will fail.');
}
constructor(private configService: ConfigService) {
// validation at startup ensures all required env vars are present
}

/**
* Sends funds from the operational storage to a destination address
*/
async sendFunds(destinationAddress: string, amount: string): Promise<string> {
try {
const sourceAccount = await this.server.loadAccount(this.sourceKeypair.publicKey());

const transaction = new StellarSdk.TransactionBuilder(sourceAccount, {
fee: StellarSdk.BASE_FEE,
networkPassphrase: process.env.STELLAR_NETWORK_URL?.includes('public')
? StellarSdk.Networks.PUBLIC
: StellarSdk.Networks.TESTNET,
})
.addOperation(
StellarSdk.Operation.payment({
destination: destinationAddress,
asset: StellarSdk.Asset.native(),
amount: amount,
}),
)
.setTimeout(30)
.build();

transaction.sign(this.sourceKeypair);
private getStellarRpcUrl(): string {
return this.configService.get<string>('STELLAR_RPC_URL');
}

const response = await this.server.submitTransaction(transaction);
return response.hash;
} catch (error) {
console.error('Stellar transaction failed:', error);
throw error; // Rethrow to let the worker handle the failure state
}
private getTreasuryPrivateKey(): string {
return this.configService.get<string>('TREASURY_PRIVATE_KEY');
}

// Additional methods using ConfigService
}
Loading