Skip to content
Open
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
33 changes: 33 additions & 0 deletions fluxapay_backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,36 @@ SOROBAN_RPC_URL="https://soroban-testnet.stellar.org"
SOROBAN_NETWORK_PASSPHRASE="Test SDF Network ; September 2015"
MERCHANT_REGISTRY_CONTRACT_ID="C..."
ADMIN_SECRET_KEY="S..."

# ─── Settlement Batch ──────────────────────────────────────────────────────────

# Exchange / payout partner: "yellowcard" | "anchor" | "mock" (default: mock)
EXCHANGE_PARTNER="mock"

# Yellow Card (https://yellowcard.io) — required when EXCHANGE_PARTNER=yellowcard
YELLOWCARD_API_KEY="yc_your_api_key_here"
YELLOWCARD_API_URL="https://api.yellowcard.io"

# Anchor (https://anchorusd.com) — required when EXCHANGE_PARTNER=anchor
ANCHOR_API_KEY="anc_your_api_key_here"
ANCHOR_API_URL="https://api.anchorusd.com"

# Settlement fee (percentage charged by FluxaPay, default: 2)
SETTLEMENT_FEE_PERCENT="2"

# Cron expression for settlement batch (UTC). Default: daily at midnight UTC.
# Examples:
# "0 0 * * *" → every day at 00:00 UTC (default)
# "0 0 * * 1" → every Monday at 00:00 UTC (weekly)
# "*/5 * * * *" → every 5 minutes (testing only)
SETTLEMENT_CRON="0 0 * * *"

# Max number of Payment rows processed per batch run (prevents OOM on huge datasets)
SETTLEMENT_BATCH_LIMIT="500"

# Set to "true" to disable all cron jobs (useful in test/CI environments)
DISABLE_CRON="false"

# Internal admin secret for /api/admin/* endpoints (X-Admin-Secret header)
ADMIN_INTERNAL_SECRET="your-strong-internal-secret-here"

30 changes: 18 additions & 12 deletions fluxapay_backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions fluxapay_backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"express-validator": "^7.3.1",
"jsonwebtoken": "^9.0.3",
"multer": "^2.0.2",
"node-cron": "^4.2.1",
"node-fetch": "^3.3.2",
"resend": "^6.8.0",
"swagger-jsdoc": "^6.2.8",
Expand All @@ -50,6 +51,7 @@
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.0.0",
"@types/node": "^25.0.9",
"@types/node-cron": "^3.0.11",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.8",
"@types/uuid": "^10.0.0",
Expand Down
70 changes: 58 additions & 12 deletions fluxapay_backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,29 @@ model Merchant {
settlement_currency String
password String
status MerchantStatus @default(pending_verification)
webhook_url String?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
otps OTP[]
settlements Settlement[]
kyc MerchantKYC?
webhookLogs WebhookLog[]
payments Payment[]
bankAccount BankAccount?
}

model BankAccount {
id String @id @default(cuid())
merchant Merchant @relation(fields: [merchantId], references: [id])
merchantId String @unique
account_name String
account_number String
bank_name String
bank_code String?
currency String
country String
created_at DateTime @default(now())
updated_at DateTime @updatedAt
}

model OTP {
Expand All @@ -48,16 +64,31 @@ model Settlement {
id String @id @default(cuid())
merchant Merchant @relation(fields: [merchantId], references: [id])
merchantId String
// USDC gross amount received
usdc_amount Decimal
// Fiat gross before fees
amount Decimal
currency String
status SettlementStatus @default(pending)
// FluxaPay settlement fee (fiat)
fees Decimal
// Fiat net amount transferred to merchant
net_amount Decimal
status SettlementStatus @default(pending)
breakdown Json?
// Exchange / payout partner details
exchange_partner String?
exchange_rate Decimal?
exchange_ref String?
// Bank transfer / payout reference
bank_transfer_id String?
// payment IDs included in this batch (JSON array of strings)
payment_ids Json @default("[]")
scheduled_date DateTime
processed_date DateTime?
failure_reason String?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
payments Payment[]
}

model MerchantKYC {
Expand Down Expand Up @@ -128,17 +159,30 @@ model WebhookRetryAttempt {
}

model Payment {
id String @id @default(cuid())
merchant Merchant @relation(fields: [merchantId], references: [id])
merchantId String
amount Decimal
currency String
customer_email String
metadata Json
expiration DateTime
status String
checkout_url String
createdAt DateTime @default(now())
id String @id @default(cuid())
merchant Merchant @relation(fields: [merchantId], references: [id])
merchantId String
amount Decimal
currency String
customer_email String
metadata Json
expiration DateTime
status String
checkout_url String
// sweeping fields
swept Boolean @default(false)
swept_at DateTime?
sweep_tx_hash String?
// settlement fields
settled Boolean @default(false)
settled_at DateTime?
settlement_ref String?
settlement_fiat_amount Decimal?
settlement_fiat_currency String?
// link to settlement batch
settlement Settlement? @relation(fields: [settlementId], references: [id])
settlementId String?
createdAt DateTime @default(now())
}

// ...existing code...
Expand Down Expand Up @@ -193,6 +237,8 @@ enum WebhookEventType {
subscription_created
subscription_cancelled
subscription_renewed
settlement_completed
settlement_failed
}

enum WebhookStatus {
Expand Down
2 changes: 2 additions & 0 deletions fluxapay_backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import merchantRoutes from "./routes/merchant.route";
import settlementRoutes from "./routes/settlement.route";
import kycRoutes from "./routes/kyc.route";
import webhookRoutes from "./routes/webhook.route";
import settlementBatchRoutes from "./routes/settlementBatch.route";

const app = express();
const prisma = new PrismaClient();
Expand All @@ -21,6 +22,7 @@ app.use("/api/merchants", merchantRoutes);
app.use("/api/settlements", settlementRoutes);
app.use("/api/merchants/kyc", kycRoutes);
app.use("/api/webhooks", webhookRoutes);
app.use("/api/admin/settlement", settlementBatchRoutes);

// Basic health check
app.get("/health", (req, res) => {
Expand Down
5 changes: 5 additions & 0 deletions fluxapay_backend/src/generated/client/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export * from './enums';
*
*/
export type Merchant = Prisma.MerchantModel
/**
* Model BankAccount
*
*/
export type BankAccount = Prisma.BankAccountModel
/**
* Model OTP
*
Expand Down
5 changes: 5 additions & 0 deletions fluxapay_backend/src/generated/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ path.join(process.cwd(), "src/generated/client/libquery_engine-darwin-arm64.dyli
*
*/
export type Merchant = Prisma.MerchantModel
/**
* Model BankAccount
*
*/
export type BankAccount = Prisma.BankAccountModel
/**
* Model OTP
*
Expand Down
Loading
Loading