/**
* Shopify Agentic MCP Gateway — Fee Collector Middleware
*
* Automatic fee deduction from checkout transactions.
* In-memory ledger with a DynamoDB-ready interface.
*/
import { randomUUID } from "node:crypto";
import type { FeeRecord } from "../types.js";
export interface FeeCollectorConfig {
/** Fee rate as a decimal (e.g. 0.005 = 0.5%) */
feeRate: number;
/** Wallet address for fee collection */
walletAddress: string;
}
export class FeeCollector {
private readonly feeRate: number;
private readonly walletAddress: string;
private readonly ledger: FeeRecord[];
constructor(config: FeeCollectorConfig) {
this.feeRate = config.feeRate ?? 0.005;
this.walletAddress = config.walletAddress;
this.ledger = [];
}
/**
* Calculate the platform fee for a given gross amount.
* Creates a FeeRecord in "pending" status without persisting it.
*
* @param grossAmount - The gross transaction amount (minor units)
* @param currency - The ISO 4217 currency code
* @returns A new FeeRecord with calculated fee
*/
calculateFee(grossAmount: number, currency: string): FeeRecord {
const feeAmount = Math.round(grossAmount * this.feeRate);
return {
transaction_id: randomUUID(),
checkout_id: "",
order_id: "",
gross_amount: grossAmount,
fee_rate: this.feeRate,
fee_amount: feeAmount,
currency: currency.toUpperCase(),
wallet_address: this.walletAddress,
status: "pending",
created_at: new Date().toISOString(),
};
}
/**
* Mark a fee record as collected and store it in the ledger.
* In production, this would trigger an actual payment transfer
* to the wallet address (e.g. via Stripe Connect, crypto, etc.).
*
* @param feeRecord - The fee record to collect
* @returns The updated fee record with "collected" status
*/
async collect(feeRecord: FeeRecord): Promise<FeeRecord> {
const collectedRecord: FeeRecord = {
...feeRecord,
status: "collected",
};
this.ledger.push(collectedRecord);
return collectedRecord;
}
/**
* Query fee records from the in-memory ledger, optionally filtered by date range.
*
* @param startDate - ISO 8601 start date (inclusive)
* @param endDate - ISO 8601 end date (inclusive)
* @returns Array of matching fee records
*/
async getLedger(startDate?: string, endDate?: string): Promise<FeeRecord[]> {
if (!startDate && !endDate) {
return [...this.ledger];
}
const start = startDate ? new Date(startDate).getTime() : 0;
const end = endDate ? new Date(endDate).getTime() : Infinity;
return this.ledger.filter((record) => {
const recordTime = new Date(record.created_at).getTime();
return recordTime >= start && recordTime <= end;
});
}
/**
* Sum all collected fees, optionally filtered by currency.
*
* @param currency - Optional ISO 4217 currency code to filter by
* @returns Total fee amount in minor units
*/
async getTotalCollected(currency?: string): Promise<number> {
return this.ledger
.filter((record) => {
if (record.status !== "collected") {
return false;
}
if (currency && record.currency !== currency.toUpperCase()) {
return false;
}
return true;
})
.reduce((sum, record) => sum + record.fee_amount, 0);
}
}