Added display of credits

This commit is contained in:
Dries Augustyns
2025-12-05 07:59:47 +01:00
parent 8c348d3580
commit 04a64ef83a
4 changed files with 62 additions and 1 deletions
+20
View File
@@ -476,6 +476,25 @@ export class Users {
// This is normal for new subscriptions or if there's no usage yet
}
// Get customer to retrieve balance (credits)
const customer = await stripe.customers.retrieve(project.customer);
let credits = null;
if (!customer.deleted) {
// Stripe stores balance in cents as a negative number (credit) or positive (debit)
// A negative balance means the customer has credits
const balance = customer.balance || 0;
const hasCredits = balance < 0;
const creditAmount = hasCredits ? Math.abs(balance) : 0;
credits = {
balance,
hasCredits,
creditAmount,
currency: customer.currency || 'usd',
};
}
return res.status(200).json({
period: {
start: startOfMonth.toISOString(),
@@ -485,6 +504,7 @@ export class Users {
total: totalUsage,
records: [], // Deprecated in favor of invoice line items
},
credits,
upcomingInvoice: upcomingInvoice
? {
amountDue: upcomingInvoice.amount_due,
+5
View File
@@ -276,6 +276,11 @@ export class Webhooks {
},
});
// add 1 eur/usd in credit in return for onboarding fee
await stripe.customers.update(customerId, {
balance: -100,
});
signale.success(`[WEBHOOK] Checkout completed for project ${projectId}`);
// Send notification about subscription started
+29 -1
View File
@@ -1,5 +1,5 @@
import {Card, CardContent, CardDescription, CardHeader, CardTitle, Alert} from '@plunk/ui';
import {AlertCircle, TrendingUp} from 'lucide-react';
import {AlertCircle, TrendingUp, Coins} from 'lucide-react';
import {useBillingConsumption} from '../lib/hooks/useBillingConsumption';
import {useConfig} from '../lib/hooks/useConfig';
import {useCallback} from 'react';
@@ -119,6 +119,26 @@ export function BillingConsumption({projectId, hasSubscription}: BillingConsumpt
</div>
</div>
{/* Account Credits */}
{consumptionData.credits && consumptionData.credits.hasCredits && (
<div className="border border-green-200 rounded-lg p-6 bg-green-50">
<div className="flex items-center gap-3 mb-2">
<div className="p-2 bg-white rounded-lg shadow-sm border border-green-200">
<Coins className="h-5 w-5 text-green-600" />
</div>
<div>
<p className="text-sm text-green-700">Account Credits</p>
<p className="text-2xl font-bold text-green-900">
{formatCurrency(consumptionData.credits.creditAmount, consumptionData.credits.currency)}
</p>
</div>
</div>
<p className="text-xs text-green-700 mt-2">
Credits will be automatically applied to your upcoming invoices
</p>
</div>
)}
{/* Upcoming Invoice */}
{consumptionData.upcomingInvoice && (
<div className="border border-neutral-200 rounded-lg p-6">
@@ -137,6 +157,14 @@ export function BillingConsumption({projectId, hasSubscription}: BillingConsumpt
{formatCurrency(consumptionData.upcomingInvoice.subtotal, consumptionData.upcomingInvoice.currency)}
</span>
</div>
{consumptionData.credits && consumptionData.credits.hasCredits && (
<div className="flex justify-between items-center">
<span className="text-sm text-green-600">Credits Applied</span>
<span className="text-sm font-medium text-green-600">
-{formatCurrency(consumptionData.credits.creditAmount, consumptionData.credits.currency)}
</span>
</div>
)}
<div className="border-t border-neutral-200 pt-3 mt-3">
<div className="flex justify-between items-center">
<span className="text-base font-semibold text-neutral-900">Amount Due</span>
@@ -19,12 +19,20 @@ export interface UpcomingInvoice {
total: number;
}
export interface AccountCredits {
balance: number;
hasCredits: boolean;
creditAmount: number;
currency: string;
}
export interface BillingConsumptionData {
period: BillingPeriod;
usage: {
total: number;
records: UsageRecord[];
};
credits: AccountCredits | null;
upcomingInvoice: UpcomingInvoice | null;
}