Start repository

This commit is contained in:
Francisco Pessano
2025-06-06 21:14:10 -03:00
commit 1bb5fcd022
72 changed files with 14106 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
/*
# Debt Negotiation AI Edge Function
This function generates FDCPA-compliant negotiation responses based on debt amounts:
- < $500: 30-day extension request
- $500-$2000: 3-month installment plan
- >= $2000: 60% lump-sum settlement offer
*/
import { createClient } from 'npm:@supabase/supabase-js@2';
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
};
interface DebtRecord {
id: string;
vendor: string;
amount: number;
raw_email: string;
}
Deno.serve(async (req) => {
try {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
const { record }: { record: DebtRecord } = await req.json();
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const supabase = createClient(supabaseUrl, supabaseKey);
// Generate negotiation strategy based on amount
let strategy = '';
let projectedSavings = 0;
if (record.amount < 500) {
strategy = 'extension';
projectedSavings = 0; // No savings, just time
} else if (record.amount >= 500 && record.amount < 2000) {
strategy = 'installment';
projectedSavings = record.amount * 0.1; // 10% savings from avoiding late fees
} else {
strategy = 'settlement';
projectedSavings = record.amount * 0.4; // 40% savings from 60% settlement
}
// Generate FDCPA-compliant response
const negotiatedPlan = generateNegotiationResponse(record, strategy);
// Update debt record
const { error: updateError } = await supabase
.from('debts')
.update({
negotiated_plan: negotiatedPlan,
projected_savings: projectedSavings,
status: 'negotiating'
})
.eq('id', record.id);
if (updateError) {
throw updateError;
}
// Log the action
await supabase
.from('audit_logs')
.insert({
debt_id: record.id,
action: 'negotiation_generated',
details: {
strategy,
amount: record.amount,
projected_savings: projectedSavings
}
});
return new Response(
JSON.stringify({ success: true, strategy, projected_savings: projectedSavings }),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
} catch (error) {
console.error('Negotiation function error:', error);
return new Response(
JSON.stringify({ error: error.message }),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
}
);
}
});
function generateNegotiationResponse(record: DebtRecord, strategy: string): string {
const vendorDomain = record.vendor.split('@')[1] || 'your company';
const companyName = vendorDomain.split('.')[0].toUpperCase();
const baseResponse = `Dear ${companyName} Collections Department,
I am writing in response to your recent communication regarding account balance of $${record.amount.toFixed(2)}.
I acknowledge this debt and am committed to resolving this matter in good faith. Due to current financial circumstances, I would like to propose the following arrangement:
`;
let proposal = '';
switch (strategy) {
case 'extension':
proposal = `I respectfully request a 30-day extension to arrange full payment. I anticipate being able to settle this account in full by ${getDateAfterDays(30)}.
During this extension period, I request that no additional fees or interest be applied to maintain the current balance.`;
break;
case 'installment':
const monthlyPayment = (record.amount / 3).toFixed(2);
proposal = `I would like to propose a 3-month payment plan with the following terms:
- Monthly payments of $${monthlyPayment}
- First payment due: ${getDateAfterDays(30)}
- Subsequent payments on the same date for the following 2 months
- No additional fees or interest during the payment plan period`;
break;
case 'settlement':
const settlementAmount = (record.amount * 0.6).toFixed(2);
proposal = `I would like to propose a lump-sum settlement offer of $${settlementAmount} (60% of the current balance) to resolve this matter completely.
This settlement would be paid within 10 business days of written acceptance of this offer. Upon payment, I request written confirmation that this account will be considered paid in full and closed.`;
break;
}
const closingResponse = `
Please confirm receipt of this correspondence and your acceptance of the proposed arrangement. I am committed to honoring any agreement we reach and appreciate your consideration of this proposal.
I look forward to your prompt response so we can resolve this matter efficiently.
Sincerely,
[Account Holder Name]
---
This correspondence is sent in accordance with the Fair Debt Collection Practices Act (FDCPA). If you are a debt collector, this serves as formal notice that I am disputing this debt and requesting validation as outlined under Section 809(b) of the FDCPA.`;
return baseResponse + proposal + closingResponse;
}
function getDateAfterDays(days: number): string {
const date = new Date();
date.setDate(date.getDate() + days);
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
}

View File

@@ -0,0 +1,94 @@
/*
# InboxNegotiator Database Schema
1. New Tables
- `debts`
- `id` (uuid, primary key)
- `created_at` (timestamp)
- `vendor` (text) - creditor email address
- `amount` (numeric) - debt amount
- `raw_email` (text) - full email content
- `status` (text) - current negotiation status
- `negotiated_plan` (text) - AI-generated response
- `projected_savings` (numeric) - estimated savings
- `updated_at` (timestamp)
- `audit_logs`
- `id` (uuid, primary key)
- `debt_id` (uuid, foreign key)
- `action` (text) - action performed
- `details` (jsonb) - additional details
- `created_at` (timestamp)
2. Security
- Enable RLS on both tables
- Add policies for authenticated users to access their data
- Create indexes for performance
3. Real-time
- Enable real-time for debts table
*/
-- Create debts table
CREATE TABLE IF NOT EXISTS debts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
vendor text NOT NULL,
amount numeric DEFAULT 0,
raw_email text,
status text DEFAULT 'received' CHECK (status IN ('received', 'negotiating', 'settled', 'failed', 'opted_out')),
negotiated_plan text,
projected_savings numeric DEFAULT 0
);
-- Create audit logs table
CREATE TABLE IF NOT EXISTS audit_logs (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
created_at timestamptz DEFAULT now(),
debt_id uuid REFERENCES debts(id) ON DELETE CASCADE,
action text NOT NULL,
details jsonb DEFAULT '{}'::jsonb
);
-- Enable RLS
ALTER TABLE debts ENABLE ROW LEVEL SECURITY;
ALTER TABLE audit_logs ENABLE ROW LEVEL SECURITY;
-- Create policies (for demo purposes, allowing all authenticated users)
CREATE POLICY "Allow all operations on debts"
ON debts
FOR ALL
TO authenticated
USING (true)
WITH CHECK (true);
CREATE POLICY "Allow all operations on audit_logs"
ON audit_logs
FOR ALL
TO authenticated
USING (true)
WITH CHECK (true);
-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_debts_status ON debts(status);
CREATE INDEX IF NOT EXISTS idx_debts_created_at ON debts(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_logs_debt_id ON audit_logs(debt_id);
-- Create function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create trigger for updated_at
DROP TRIGGER IF EXISTS update_debts_updated_at ON debts;
CREATE TRIGGER update_debts_updated_at
BEFORE UPDATE ON debts
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- Enable real-time for debts table
ALTER publication supabase_realtime ADD TABLE debts;