Proration Preview
Interactive component that shows billing adjustments when users change subscription plans, featuring cost breakdowns, credits, and prorated charges with immediate or next-cycle options.
Default
Review the charges and credits for your plan change
Pro
$29.99/monthly
Enterprise
$99.99/monthly
Billing Breakdown
Your plan will change immediately. You'll be charged $35.00.
"use client";
import { ProrationPreview } from "@/components/billingsdk/proration-preview";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
const currentPlan = {
plan: {
id: "pro",
title: "Pro",
description: "Best for small teams",
monthlyPrice: "29.99",
yearlyPrice: "299.99",
currency: "$",
buttonText: "Current Plan",
features: [
{ name: "Advanced features", icon: "check" },
{ name: "Priority support", icon: "check" },
],
},
type: "monthly" as const,
price: "29.99",
nextBillingDate: "2024-01-15",
paymentMethod: "•••• 4242",
status: "active" as const,
};
const yearlyCurrentPlan = {
plan: {
id: "pro-yearly",
title: "Pro",
description: "Best for small teams",
monthlyPrice: "29.99",
yearlyPrice: "299.99",
currency: "$",
buttonText: "Current Plan",
features: [
{ name: "Advanced features", icon: "check" },
{ name: "Priority support", icon: "check" },
],
},
type: "yearly" as const,
price: "299.99",
nextBillingDate: "2024-12-15",
paymentMethod: "•••• 4242",
status: "active" as const,
};
const basicPlan = {
id: "basic",
title: "Basic",
description: "Perfect for getting started",
monthlyPrice: "9.99",
yearlyPrice: "99.99",
currency: "$",
buttonText: "Downgrade",
features: [
{ name: "Basic features", icon: "check" },
{ name: "Email support", icon: "check" },
],
};
const enterprisePlan = {
id: "enterprise",
title: "Enterprise",
description: "For large organizations",
monthlyPrice: "99.99",
yearlyPrice: "999.99",
currency: "$",
buttonText: "Upgrade",
features: [
{ name: "All features", icon: "check" },
{ name: "Priority support", icon: "check" },
{ name: "Custom integrations", icon: "check" },
],
};
const customPlan = {
id: "custom",
title: "Custom",
description: "Tailored for your needs",
monthlyPrice: "Custom",
yearlyPrice: "Custom",
currency: "$",
buttonText: "Contact Sales",
features: [
{ name: "Custom features", icon: "check" },
{ name: "Dedicated support", icon: "check" },
],
};
export function ProrationPreviewDemo() {
return (
<div className="space-y-8">
<Tabs defaultValue="upgrade" className="w-full">
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="upgrade">Upgrade</TabsTrigger>
<TabsTrigger value="downgrade">Downgrade</TabsTrigger>
<TabsTrigger value="cycle-change">Cycle Change</TabsTrigger>
<TabsTrigger value="next-cycle">Next Cycle</TabsTrigger>
</TabsList>
<TabsContent value="upgrade" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Monthly to Enterprise Upgrade</CardTitle>
<CardDescription>
Upgrading from Pro monthly to Enterprise monthly with 15 days
remaining
</CardDescription>
</CardHeader>
<CardContent>
<ProrationPreview
currentPlan={currentPlan}
newPlan={enterprisePlan}
billingCycle="monthly"
daysRemaining={15}
effectiveDate="immediately"
theme="minimal"
onConfirm={() => {}}
onCancel={() => {}}
/>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="downgrade" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Enterprise to Basic Downgrade</CardTitle>
<CardDescription>
Downgrading from Enterprise to Basic with account credit
</CardDescription>
</CardHeader>
<CardContent>
<ProrationPreview
currentPlan={{
plan: enterprisePlan,
type: "monthly",
price: "99.99",
nextBillingDate: "2024-01-15",
paymentMethod: "•••• 4242",
status: "active",
}}
newPlan={basicPlan}
billingCycle="monthly"
daysRemaining={20}
effectiveDate="immediately"
theme="minimal"
onConfirm={() => {}}
onCancel={() => {}}
/>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="cycle-change" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Monthly to Yearly Switch</CardTitle>
<CardDescription>
Switching from monthly to yearly billing for the same plan
</CardDescription>
</CardHeader>
<CardContent>
<ProrationPreview
currentPlan={currentPlan}
newPlan={currentPlan.plan}
billingCycle="yearly"
daysRemaining={10}
effectiveDate="immediately"
theme="minimal"
onConfirm={() => {}}
onCancel={() => {}}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Yearly to Monthly Switch</CardTitle>
<CardDescription>
Switching from yearly to monthly billing
</CardDescription>
</CardHeader>
<CardContent>
<ProrationPreview
currentPlan={yearlyCurrentPlan}
newPlan={yearlyCurrentPlan.plan}
billingCycle="monthly"
daysRemaining={120}
effectiveDate="immediately"
theme="classic"
onConfirm={() => {}}
onCancel={() => {}}
/>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="next-cycle" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Next Cycle Change</CardTitle>
<CardDescription>
Plan change effective at the next billing cycle with no
immediate charge
</CardDescription>
</CardHeader>
<CardContent>
<ProrationPreview
currentPlan={currentPlan}
newPlan={enterprisePlan}
billingCycle="monthly"
daysRemaining={25}
effectiveDate="next billing cycle"
theme="minimal"
onConfirm={() => {}}
onCancel={() => {}}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Custom Pricing Scenario</CardTitle>
<CardDescription>
Handling custom/enterprise pricing that doesn't follow standard
rates
</CardDescription>
</CardHeader>
<CardContent>
<ProrationPreview
currentPlan={currentPlan}
newPlan={customPlan}
billingCycle="monthly"
daysRemaining={12}
effectiveDate="next billing cycle"
theme="classic"
onConfirm={() => {}}
onCancel={() => {}}
/>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
}Installation
yarn dlx @billingsdk/cli add proration-previewStep 1: Install the following dependencies:
npm install motion lucide-react class-variance-authorityStep 2: Copy and paste the following code into your project.
"use client";
import { motion } from "motion/react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import {
ArrowRight,
Calendar,
CreditCard,
Calculator,
Clock,
} from "lucide-react";
import { type Plan, type CurrentPlan } from "@/lib/billingsdk-config";
import { cn } from "@/lib/utils";
import { cva, type VariantProps } from "class-variance-authority";
const prorationPreviewVariants = cva("w-full max-w-3xl mx-auto", {
variants: {
theme: {
minimal: "",
classic:
"relative overflow-hidden rounded-xl border border-border/50 bg-gradient-to-br from-card/50 to-muted/30 backdrop-blur-sm",
},
size: {
small: "text-sm",
medium: "text-base",
large: "text-lg",
},
},
defaultVariants: {
theme: "minimal",
size: "medium",
},
});
const cardVariants = cva("transition-all duration-300", {
variants: {
theme: {
minimal: "border border-border bg-card",
classic:
"border border-border/30 bg-gradient-to-br from-card/80 to-muted/20 backdrop-blur-sm shadow-md",
},
},
defaultVariants: {
theme: "minimal",
},
});
/**
* Props for `ProrationPreview`.
*
* Combines visual variants from `prorationPreviewVariants` with
* the billing context needed to compute proration math.
*
* - `currentPlan` and `newPlan` supply pricing and labels
* - `billingCycle` controls the target cycle for the new plan
* - `daysRemaining` and `effectiveDate` influence credit/charge math
* - `onConfirm`/`onCancel` wire user actions
*/
export interface ProrationPreviewProps
extends VariantProps<typeof prorationPreviewVariants> {
className?: string;
currentPlan: CurrentPlan;
newPlan: Plan;
billingCycle: "monthly" | "yearly";
daysRemaining?: number;
effectiveDate?: string;
onConfirm?: () => void;
onCancel?: () => void;
confirmText?: string;
cancelText?: string;
}
/**
* ProrationPreview
*
* Renders a detailed, accessible preview of billing changes when a user switches
* subscription plans. It calculates and displays credits for unused time,
* prorated charges for the new plan, and the resulting net amount, with clear
* visual hierarchy and responsive styles.
*
* Key UI sections:
* - From/To plan summary with upgrade/downgrade badges
* - Billing breakdown (credit, prorated charge, net amount)
* - Timeline note describing when changes take effect
* - Primary/secondary actions to confirm or cancel
*
* Props accept both monthly and yearly cycles and support custom pricing
* scenarios (e.g., enterprise). Visual appearance can be adjusted via
* `theme` and `size` variants.
*
* @param props.className Optional container className override.
* @param props.currentPlan Current subscription context including plan and cycle.
* @param props.newPlan Target plan the user is moving to.
* @param props.billingCycle Billing cycle for the new plan (monthly/yearly).
* @param props.daysRemaining Remaining days in the current cycle (defaults to 15).
* @param props.effectiveDate When the change takes effect (e.g., "immediately" or "next billing cycle").
* @param props.onConfirm Callback invoked when user confirms the change.
* @param props.onCancel Callback invoked when user cancels the change.
* @param props.confirmText Custom label for the confirm action.
* @param props.cancelText Custom label for the cancel action.
* @param props.theme Visual theme variant (minimal | classic). Defaults to minimal.
* @param props.size Component size (small | medium | large). Defaults to medium.
*/
export function ProrationPreview({
className,
currentPlan,
newPlan,
billingCycle,
daysRemaining = 15,
effectiveDate = "immediately",
onConfirm,
onCancel,
confirmText = "Confirm Change",
cancelText = "Cancel",
theme = "minimal",
size = "medium",
}: ProrationPreviewProps) {
// Prices & proration (robust) - Fixed CodeRabbit issues
const currentCycleDays = currentPlan.type === "yearly" ? 365 : 30;
const newCycleDays = billingCycle === "yearly" ? 365 : 30;
const isNumericValue = (v?: string) => {
if (v == null) return false;
const s = String(v).replace(/[^\d.\-]/g, "");
const n = Number.parseFloat(s);
return Number.isFinite(n);
};
const toNumber = (v?: string) => {
if (v == null) return undefined;
const s = String(v).replace(/[^\d.\-]/g, "");
const n = Number.parseFloat(s);
return Number.isFinite(n) ? n : undefined;
};
const currentRaw =
currentPlan.type === "monthly"
? currentPlan.plan.monthlyPrice
: currentPlan.type === "yearly"
? currentPlan.plan.yearlyPrice
: currentPlan.price;
const newRaw =
billingCycle === "monthly" ? newPlan.monthlyPrice : newPlan.yearlyPrice;
const currentPrice = toNumber(currentRaw);
const newPrice = toNumber(newRaw);
const isCustomCurrent = !isNumericValue(currentRaw);
const isCustomNew = !isNumericValue(newRaw);
const chargeCurrency = newPlan.currency ?? currentPlan.plan.currency ?? "$";
const creditCurrency = currentPlan.plan.currency ?? newPlan.currency ?? "$";
const clampedUnusedDays = Math.max(
0,
Math.min(daysRemaining, currentCycleDays),
);
const isNextCycle =
typeof effectiveDate === "string" &&
effectiveDate.toLowerCase().includes("next");
const prorationDays = isNextCycle ? 0 : clampedUnusedDays;
const canCompute =
!isNextCycle &&
!isCustomCurrent &&
!isCustomNew &&
currentPrice !== undefined &&
newPrice !== undefined;
const creditAmount = canCompute
? (currentPrice! / currentCycleDays) * clampedUnusedDays
: 0;
const proratedCharge = canCompute
? (newPrice! / newCycleDays) * prorationDays
: 0;
const netAmount = proratedCharge - creditAmount;
const normalizedCurrentMonthly =
currentPlan.type === "yearly" && currentPrice !== undefined
? currentPrice / 12
: (currentPrice ?? 0);
const normalizedNewMonthly =
billingCycle === "yearly" && newPrice !== undefined
? newPrice / 12
: (newPrice ?? 0);
const hasComparablePrices =
!isCustomCurrent &&
!isCustomNew &&
currentPrice !== undefined &&
newPrice !== undefined;
const isUpgrade =
hasComparablePrices && normalizedNewMonthly > normalizedCurrentMonthly;
const isDowngrade =
hasComparablePrices && normalizedNewMonthly < normalizedCurrentMonthly;
return (
<div className={cn(prorationPreviewVariants({ theme, size }), className)}>
{theme === "classic" && (
<>
<div className="bg-grid-pattern absolute inset-0 opacity-5" />
<div className="bg-primary/5 absolute top-1/2 left-1/2 h-64 w-64 -translate-x-1/2 -translate-y-1/2 rounded-full blur-3xl" />
</>
)}
<Card className={cn(cardVariants({ theme }))}>
<CardHeader className="px-4 pb-3 sm:px-6 sm:pb-4">
<CardTitle className="flex items-center gap-2 text-base sm:text-lg">
<div className="bg-primary/10 ring-primary/20 rounded-lg p-2 ring-1">
<Calculator className="text-primary h-4 w-4 sm:h-5 sm:w-5" />
</div>
Plan Change Preview
</CardTitle>
<p className="text-muted-foreground text-sm">
Review the charges and credits for your plan change
</p>
</CardHeader>
<CardContent className="space-y-4 px-4 sm:space-y-6 sm:px-6">
{/* From/To Plans */}
<div className="grid grid-cols-1 items-center gap-3 sm:gap-4 md:grid-cols-3">
{/* Current Plan */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3 }}
className={cn(
"rounded-lg border p-3 sm:p-4",
theme === "classic"
? "from-muted/50 to-background/50 border-border/50 bg-gradient-to-br"
: "bg-muted/50 border-border",
)}
>
<div className="mb-2 flex items-center gap-2">
<Badge variant="outline" className="text-[10px] sm:text-xs">
Current
</Badge>
{isDowngrade && (
<Badge variant="secondary" className="text-[10px] sm:text-xs">
Downgrading
</Badge>
)}
</div>
<h3 className="text-base font-semibold sm:text-lg">
{currentPlan.plan.title}
</h3>
<p className="text-muted-foreground mb-3 text-xs sm:text-sm">
{isCustomCurrent
? "Custom"
: `${creditCurrency}${currentPrice}/${currentPlan.type}`}
</p>
<div className="text-muted-foreground flex items-center gap-1 text-xs">
<Clock className="h-3 w-3 sm:h-4 sm:w-4" />
{daysRemaining} days remaining
</div>
</motion.div>
{/* Arrow */}
<div className="flex justify-center">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ duration: 0.3, delay: 0.2 }}
className={cn(
"rounded-full p-2",
theme === "classic"
? "from-primary to-primary/80 text-primary-foreground bg-gradient-to-r shadow-lg"
: "bg-primary/10 text-primary",
)}
>
<ArrowRight className="h-4 w-4 sm:h-5 sm:w-5" />
</motion.div>
</div>
{/* New Plan */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: 0.1 }}
className={cn(
"rounded-lg border p-3 sm:p-4",
theme === "classic"
? "from-primary/5 to-primary/10 border-primary/30 bg-gradient-to-br"
: "bg-primary/5 border-primary/30",
)}
>
<div className="mb-2 flex items-center gap-2">
<Badge variant="default" className="text-[10px] sm:text-xs">
New Plan
</Badge>
{isUpgrade && (
<Badge variant="secondary" className="text-[10px] sm:text-xs">
Upgrading
</Badge>
)}
</div>
<h3 className="text-base font-semibold sm:text-lg">
{newPlan.title}
</h3>
<p className="text-muted-foreground mb-3 text-xs sm:text-sm">
{isCustomNew
? "Custom"
: `${chargeCurrency}${newPrice}/${billingCycle}`}
</p>
<div className="text-muted-foreground flex items-center gap-1 text-xs">
<Calendar className="h-3 w-3 sm:h-4 sm:w-4" />
Effective {effectiveDate}
</div>
</motion.div>
</div>
<Separator
className={cn(
theme === "classic" &&
"via-border bg-gradient-to-r from-transparent to-transparent",
)}
/>
{/* Calculation Breakdown */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.4 }}
className={cn(
"rounded-lg border p-3 sm:p-4",
theme === "classic"
? "from-muted/30 to-background/30 border-border/50 bg-gradient-to-br"
: "bg-muted/30 border-border",
)}
>
<h4 className="mb-3 flex items-center gap-2 font-medium sm:mb-4">
<CreditCard className="h-4 w-4 sm:h-5 sm:w-5" />
Billing Breakdown
</h4>
<div className="space-y-3">
<div className="flex items-center justify-between text-xs sm:text-sm">
<span className="text-muted-foreground">
Credit for unused time
</span>
<span className="font-medium text-green-600">
{canCompute
? `-${creditCurrency}${Math.abs(creditAmount).toFixed(2)}`
: "—"}
</span>
</div>
<div className="flex items-center justify-between text-xs sm:text-sm">
<span className="text-muted-foreground">
Prorated charge ({prorationDays} days)
</span>
<span className="font-medium">
{canCompute
? `+${chargeCurrency}${proratedCharge.toFixed(2)}`
: "—"}
</span>
</div>
<Separator className="my-2" />
<div className="flex items-center justify-between font-semibold">
<span>
{canCompute
? netAmount >= 0
? "Amount to charge"
: "Credit to account"
: "Amount due will be calculated at checkout"}
</span>
<span
className={cn(
"text-base sm:text-lg",
canCompute
? netAmount >= 0
? "text-foreground"
: "text-green-600"
: "text-muted-foreground",
)}
>
{canCompute
? `${netAmount >= 0 ? "+" : ""}${chargeCurrency}${netAmount.toFixed(2)}`
: "—"}
</span>
</div>
</div>
</motion.div>
{/* Timeline */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3, delay: 0.6 }}
className="bg-muted/20 border-border/50 rounded-lg border p-3 text-center sm:p-4"
>
<p className="text-muted-foreground text-xs sm:text-sm">
Your plan will change {effectiveDate}.
{isNextCycle
? " No immediate charge."
: hasComparablePrices
? netAmount >= 0
? ` You'll be charged ${chargeCurrency}${Math.abs(netAmount).toFixed(2)}.`
: ` You'll receive a ${chargeCurrency}${Math.abs(netAmount).toFixed(2)} credit.`
: " Amount will be finalized at checkout."}
</p>
</motion.div>
{/* Action Buttons */}
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.8 }}
className="flex flex-col gap-3 pt-3 sm:flex-row sm:pt-4"
>
<Button
onClick={onConfirm}
className={cn(
"flex-1",
theme === "classic" &&
"from-primary to-primary/90 bg-gradient-to-r transition-all duration-200 hover:shadow-md active:scale-95",
)}
size="lg"
>
{confirmText}
<ArrowRight className="ml-2 h-4 w-4 sm:h-5 sm:w-5" />
</Button>
<Button
variant="outline"
onClick={onCancel}
className="flex-1"
size="lg"
>
{cancelText}
</Button>
</motion.div>
</CardContent>
</Card>
</div>
);
}Step 3: Update the import paths to match your project setup.
Usage
import { ProrationPreview } from "@/components/billingsdk/proration-preview"<ProrationPreview
currentPlan={{
plan: {
id: "pro",
title: "Pro",
description: "Best for small teams",
monthlyPrice: "29.99",
yearlyPrice: "299.99",
currency: "$",
buttonText: "Current Plan",
features: [
{ name: "Advanced features", icon: "check" },
{ name: "Priority support", icon: "check" },
],
},
type: "monthly",
price: "29.99",
nextBillingDate: "2024-01-15",
paymentMethod: "•••• 4242",
status: "active",
}}
newPlan={{
id: "enterprise",
title: "Enterprise",
description: "For large organizations",
monthlyPrice: "99.99",
yearlyPrice: "999.99",
currency: "$",
buttonText: "Upgrade",
features: [
{ name: "All features", icon: "check" },
{ name: "Priority support", icon: "check" },
{ name: "Custom integrations", icon: "check" },
],
}}
billingCycle="monthly"
daysRemaining={15}
effectiveDate="immediately"
onConfirm={() => console.log("Plan change confirmed")}
onCancel={() => console.log("Plan change cancelled")}
/>Examples
Upgrade Scenario
Shows cost breakdown when upgrading from Pro to Enterprise with remaining billing cycle.
Downgrade Scenario
Displays credit calculations when downgrading plans with unused time refunds.
Billing Cycle Changes
Handles switching between monthly and yearly billing for the same plan.
Next Cycle Changes
Shows plan changes that take effect at the next billing cycle with no immediate charges.
API Reference
ProrationPreview
| Prop | Type | Default | Description |
|---|---|---|---|
currentPlan | CurrentPlan | - | The user's current subscription plan details |
newPlan | Plan | - | The target plan they want to switch to |
billingCycle | 'monthly' | 'yearly' | - | The billing cycle for the new plan |
daysRemaining | number | 15 | Days remaining in current billing cycle |
effectiveDate | string | "immediately" | When the plan change takes effect |
onConfirm | () => void | - | Callback when user confirms the change |
onCancel | () => void | - | Callback when user cancels the change |
confirmText | string | "Confirm Change" | Text for the confirm button |
cancelText | string | "Cancel" | Text for the cancel button |
theme | 'minimal' | 'classic' | 'minimal' | Visual theme variant |
size | 'small' | 'medium' | 'large' | 'medium' | Component size |
className | string | - | Additional CSS classes |
CurrentPlan
interface CurrentPlan {
plan: Plan;
type: 'monthly' | 'yearly' | 'custom';
price?: string;
nextBillingDate: string;
paymentMethod: string;
status: 'active' | 'inactive' | 'past_due' | 'cancelled';
}Plan
interface Plan {
id: string;
title: string;
description: string;
monthlyPrice: string;
yearlyPrice: string;
currency?: string;
buttonText: string;
features: {
name: string;
icon: string;
iconColor?: string;
}[];
}Features
- Accurate Proration Calculations - Handles complex billing scenarios with precise math
- Credit & Charge Breakdown - Shows detailed billing adjustments and credits
- Immediate vs Next Cycle - Supports both immediate and next-cycle plan changes
- Theme Variants - Minimal and classic visual themes
- Responsive Design - Works seamlessly across all device sizes
- Accessibility Ready - Full keyboard navigation and screen reader support
- Animation Effects - Smooth transitions using Motion
- Custom Pricing Support - Handles non-standard pricing like "Custom" or "Enterprise"
- Robust Error Handling - Graceful handling of invalid pricing data
Use Cases
- Plan Upgrades - Show costs when upgrading to higher-tier plans
- Plan Downgrades - Display credits when downgrading plans
- Billing Cycle Changes - Handle monthly/yearly billing switches
- Enterprise Sales - Support custom pricing and next-cycle changes
- Billing Transparency - Provide clear breakdown of charges and credits
Billing Logic
The component implements sophisticated proration logic:
- Credits - Calculated based on unused time in current plan (remaining days, not elapsed)
- Prorated Charges - New plan charges based on remaining cycle days
- Net Amount - Final charge or credit after calculations
- Cycle Handling - Different logic for immediate vs next-cycle changes
- Price Validation - Robust parsing of pricing strings with fallbacks
- Currency Consistency - Uses current plan currency for credits, new plan currency for charges