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
49 changes: 49 additions & 0 deletions revenue-refund-liability-reserve-guard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Revenue Refund Liability Reserve Guard

This module adds a focused refund liability reserve guard for the Revenue
Infrastructure bounty. It evaluates whether subscription, institutional
invoice, and compute-credit refund requests are financially safe before cash
leaves the platform or entitlements are reversed.

## Scope

The guard checks synthetic refund packets for:

- annual subscription refund eligibility and proration
- institutional invoice reversal evidence and approval status
- unused AI compute credit refund treatment
- non-refundable processor fees and tax handling
- entitlement rollback readiness before cash release
- refund evidence completeness
- reserve sufficiency by revenue stream and currency
- reviewer actions for hold, approve, or manual finance review decisions

This is not a generic quote approval, billing readiness, payment rail,
dunning, dispute, tax exemption, proration, prepaid credit breakage, or
collections module. It focuses specifically on refund liability exposure and
reserve sufficiency before money is returned.

## Issue #20 Requirement Mapping

- Tiered subscription billing: evaluates subscription refunds, annual-plan
proration, cancellation terms, and entitlement rollback readiness.
- AI compute billing: checks unused compute-credit refund requests against
consumed usage, restricted grants, and available reserve coverage.
- Institutional invoicing: validates invoice reversal approvals, PO evidence,
and tax/processor fee treatment.
- Revenue operations: emits deterministic finance review packets, reserve
impact, audit digests, and curator actions for revenue teams.

## Validation

```bash
node revenue-refund-liability-reserve-guard/test.js
node revenue-refund-liability-reserve-guard/demo.js
node revenue-refund-liability-reserve-guard/render-video.js
node --check revenue-refund-liability-reserve-guard/index.js
node --check revenue-refund-liability-reserve-guard/sample-data.js
node --check revenue-refund-liability-reserve-guard/test.js
node --check revenue-refund-liability-reserve-guard/demo.js
node --check revenue-refund-liability-reserve-guard/render-video.js
git diff --check
```
27 changes: 27 additions & 0 deletions revenue-refund-liability-reserve-guard/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const fs = require("fs");
const path = require("path");
const packets = require("./sample-data");
const {
evaluateRefundBatch,
renderMarkdownReport,
renderSvgReport,
} = require("./index");

const reportDir = path.join(__dirname, "reports");
fs.mkdirSync(reportDir, { recursive: true });

const report = evaluateRefundBatch(packets);
const jsonPath = path.join(reportDir, "refund-liability-review.json");
const markdownPath = path.join(reportDir, "refund-liability-review.md");
const svgPath = path.join(reportDir, "refund-liability-review.svg");

fs.writeFileSync(jsonPath, `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(markdownPath, renderMarkdownReport(report));
fs.writeFileSync(svgPath, renderSvgReport(report));

console.log(`wrote ${path.relative(process.cwd(), jsonPath)}`);
console.log(`wrote ${path.relative(process.cwd(), markdownPath)}`);
console.log(`wrote ${path.relative(process.cwd(), svgPath)}`);
console.log(
`summary: ${report.summary.hold} held refunds, ${report.summary.refundableAmount} refundable`
);
269 changes: 269 additions & 0 deletions revenue-refund-liability-reserve-guard/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
const crypto = require("crypto");

function money(value) {
return Math.round(Number(value || 0) * 100) / 100;
}

function requiredEvidenceFor(packet) {
const evidence = ["contract-term", "entitlement-rollback"];
if (packet.revenueStream === "institutional_invoice") {
evidence.push("finance-approval");
if (packet.requiresPOEvidence) {
evidence.push("po-or-invoice-evidence");
}
}
if (packet.revenueStream === "ai_compute") {
evidence.push("credit-ledger", "usage-meter-snapshot");
if (packet.restrictedGrantFunded) {
evidence.push("grant-refund-approval");
}
}
if (packet.taxCollected > 0) {
evidence.push("tax-refund-treatment");
}
return evidence;
}

function missingEvidence(packet) {
const present = new Set(packet.evidence || []);
return requiredEvidenceFor(packet).filter((item) => !present.has(item));
}

function refundableAmount(packet) {
const serviceRefund = Math.min(packet.requestedRefund, packet.unusedServiceValue);
const taxComponent = packet.taxRefundable ? packet.taxCollected : 0;
return money(Math.max(0, serviceRefund + taxComponent - packet.processorFee));
}

function reserveImpact(packet) {
return money(refundableAmount(packet));
}

function evaluateRefund(packet) {
const blockers = [];
const warnings = [];
const actions = [];
const missing = missingEvidence(packet);
const refundable = refundableAmount(packet);
const overRequest = money(Math.max(0, packet.requestedRefund - packet.unusedServiceValue));
const reserveAfter = money(packet.reserveAvailable - reserveImpact(packet));

if (missing.length > 0) {
blockers.push(`missing evidence: ${missing.join(", ")}`);
actions.push("hold refund until evidence packet is complete");
}

if (packet.approvalStatus !== "approved") {
blockers.push(`approval status is ${packet.approvalStatus}`);
actions.push("route to finance approver before cash release");
}

if (!packet.entitlementsRolledBack) {
blockers.push("entitlements are not rolled back");
actions.push("rollback entitlements before refund release");
}

if (overRequest > 0) {
warnings.push(`requested refund exceeds unused service value by ${overRequest} ${packet.currency}`);
actions.push("cap refundable service amount at unused value");
}

if (packet.processorFee > 0) {
warnings.push(`processor fee ${packet.processorFee} ${packet.currency} is non-refundable`);
}

if (packet.taxCollected > 0 && !packet.taxRefundable) {
warnings.push(`tax collected ${packet.taxCollected} ${packet.currency} requires non-refund treatment`);
}

if (packet.restrictedGrantFunded && !(packet.evidence || []).includes("grant-refund-approval")) {
blockers.push("restricted grant-funded compute lacks refund approval");
actions.push("hold refund for grant compliance review");
}

if (reserveAfter < 0) {
blockers.push(`refund reserve short by ${money(Math.abs(reserveAfter))} ${packet.currency}`);
actions.push("top up refund reserve before release");
} else if (reserveAfter < refundable * 0.25) {
warnings.push(`reserve after refund is low at ${reserveAfter} ${packet.currency}`);
actions.push("notify finance about low reserve coverage");
}

if (blockers.length === 0) {
actions.push("approve refund for payment queue");
actions.push("record reserve draw and revenue reversal");
}

const decision =
blockers.length > 0 ? "hold" : warnings.length > 0 ? "approve_with_adjustments" : "approve";

const result = {
refundId: packet.id,
customer: packet.customer,
revenueStream: packet.revenueStream,
currency: packet.currency,
requestedRefund: money(packet.requestedRefund),
refundableAmount: refundable,
reserveAvailable: money(packet.reserveAvailable),
reserveAfter,
decision,
blockers,
warnings,
actions: [...new Set(actions)],
};

return {
...result,
auditDigest: digest(result),
};
}

function evaluateRefundBatch(packets) {
const decisions = packets.map(evaluateRefund);
const summary = decisions.reduce(
(acc, item) => {
acc.total += 1;
acc[item.decision] += 1;
acc.requestedRefund = money(acc.requestedRefund + item.requestedRefund);
acc.refundableAmount = money(acc.refundableAmount + item.refundableAmount);
acc.reserveDraw = money(acc.reserveDraw + Math.max(0, item.refundableAmount));
if (item.decision === "hold") {
acc.heldRefunds = money(acc.heldRefunds + item.requestedRefund);
}
return acc;
},
{
total: 0,
approve: 0,
approve_with_adjustments: 0,
hold: 0,
requestedRefund: 0,
refundableAmount: 0,
reserveDraw: 0,
heldRefunds: 0,
}
);

const report = {
generatedAt: new Date("2026-05-31T00:00:00Z").toISOString(),
summary,
decisions,
};

return {
...report,
auditDigest: digest(report),
};
}

function renderMarkdownReport(report) {
const lines = [
"# Revenue Refund Liability Reserve Report",
"",
`Generated: ${report.generatedAt}`,
`Audit digest: ${report.auditDigest}`,
"",
"## Summary",
"",
`- Refund packets: ${report.summary.total}`,
`- Approve: ${report.summary.approve}`,
`- Approve with adjustments: ${report.summary.approve_with_adjustments}`,
`- Hold: ${report.summary.hold}`,
`- Requested refund: ${report.summary.requestedRefund}`,
`- Refundable amount: ${report.summary.refundableAmount}`,
`- Reserve draw: ${report.summary.reserveDraw}`,
`- Held refunds: ${report.summary.heldRefunds}`,
"",
"## Decisions",
"",
];

for (const item of report.decisions) {
lines.push(`### ${item.refundId}`);
lines.push("");
lines.push(`- Customer: ${item.customer}`);
lines.push(`- Stream: ${item.revenueStream}`);
lines.push(`- Decision: ${item.decision}`);
lines.push(`- Requested refund: ${item.requestedRefund} ${item.currency}`);
lines.push(`- Refundable amount: ${item.refundableAmount} ${item.currency}`);
lines.push(`- Reserve after: ${item.reserveAfter} ${item.currency}`);
lines.push(`- Blockers: ${item.blockers.join("; ") || "none"}`);
lines.push(`- Warnings: ${item.warnings.join("; ") || "none"}`);
lines.push(`- Actions: ${item.actions.join("; ")}`);
lines.push(`- Digest: ${item.auditDigest}`);
lines.push("");
}

while (lines[lines.length - 1] === "") {
lines.pop();
}

return `${lines.join("\n")}\n`;
}

function escapeXml(value) {
return String(value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}

function renderSvgReport(report) {
const cards = [
["Packets", report.summary.total, "#355c7d"],
["Approved", report.summary.approve, "#2a9d8f"],
["Adjusted", report.summary.approve_with_adjustments, "#e9c46a"],
["Held", report.summary.hold, "#e76f51"],
["Refundable", `$${report.summary.refundableAmount}`, "#457b9d"],
["Held value", `$${report.summary.heldRefunds}`, "#b56576"],
];

const cardSvg = cards
.map(([label, value, color], index) => {
const x = 72 + (index % 3) * 380;
const y = 188 + Math.floor(index / 3) * 150;
return [
`<rect x="${x}" y="${y}" width="320" height="108" rx="8" fill="#ffffff" stroke="#d7dde5"/>`,
`<rect x="${x}" y="${y}" width="10" height="108" rx="4" fill="${color}"/>`,
`<text x="${x + 30}" y="${y + 42}" font-size="24" font-family="Arial" fill="#2f3b4a">${escapeXml(label)}</text>`,
`<text x="${x + 30}" y="${y + 82}" font-size="34" font-family="Arial" font-weight="700" fill="${color}">${escapeXml(value)}</text>`,
].join("\n");
})
.join("\n");

const queueRows = report.decisions
.filter((item) => item.decision !== "approve")
.slice(0, 3)
.map((item, index) => {
const y = 545 + index * 32;
return `<text x="82" y="${y}" font-size="21" font-family="Arial" fill="#344054">${escapeXml(item.refundId)} - ${escapeXml(item.decision)} - ${escapeXml(item.actions[0])}</text>`;
})
.join("\n");

return `<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720">
<rect width="1280" height="720" fill="#f6f8fb"/>
<rect x="44" y="44" width="1192" height="632" rx="12" fill="#ffffff" stroke="#d7dde5"/>
<text x="72" y="105" font-size="38" font-family="Arial" font-weight="700" fill="#1d2939">Revenue Refund Liability Reserve Guard</text>
<text x="72" y="145" font-size="22" font-family="Arial" fill="#667085">Refund exposure and reserve sufficiency review</text>
${cardSvg}
<text x="72" y="500" font-size="27" font-family="Arial" font-weight="700" fill="#1d2939">Finance queue</text>
${queueRows}
<text x="72" y="655" font-size="18" font-family="Arial" fill="#667085">Audit digest ${escapeXml(report.auditDigest.slice(0, 32))}</text>
</svg>`;
}

function digest(value) {
return crypto.createHash("sha256").update(JSON.stringify(value)).digest("hex");
}

module.exports = {
evaluateRefund,
evaluateRefundBatch,
refundableAmount,
reserveImpact,
requiredEvidenceFor,
renderMarkdownReport,
renderSvgReport,
digest,
};
50 changes: 50 additions & 0 deletions revenue-refund-liability-reserve-guard/render-video.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const fs = require("fs");
const path = require("path");
const { execFileSync } = require("child_process");

const reportDir = path.join(__dirname, "reports");
const jsonPath = path.join(reportDir, "refund-liability-review.json");
const svgPath = path.join(reportDir, "refund-liability-review.svg");
const framePath = path.join(reportDir, "refund-liability-review.png");
const outputPath = path.join(reportDir, "demo.mp4");

if (!fs.existsSync(jsonPath) || !fs.existsSync(svgPath)) {
require("./demo");
}

const report = JSON.parse(fs.readFileSync(jsonPath, "utf8"));

execFileSync(
"rsvg-convert",
["--width", "1280", "--height", "720", "--output", framePath, svgPath],
{ stdio: "inherit" }
);

execFileSync(
"ffmpeg",
[
"-y",
"-loop",
"1",
"-framerate",
"25",
"-i",
framePath,
"-t",
"4",
"-pix_fmt",
"yuv420p",
"-vf",
"scale=1280:720",
"-movflags",
"+faststart",
outputPath,
],
{ stdio: "inherit" }
);

fs.unlinkSync(framePath);

console.log(
`wrote ${path.relative(process.cwd(), outputPath)} for ${report.summary.total} refund packets`
);
Binary file not shown.
Loading