@billing-io/designs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing.js +880 -0
- package/dist/brand.d.ts +35 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +43 -0
- package/dist/brand.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/overlay/constants.d.ts +60 -0
- package/dist/overlay/constants.d.ts.map +1 -0
- package/dist/overlay/constants.js +77 -0
- package/dist/overlay/constants.js.map +1 -0
- package/dist/overlay/index.d.ts +3 -0
- package/dist/overlay/index.d.ts.map +1 -0
- package/dist/overlay/index.js +2 -0
- package/dist/overlay/index.js.map +1 -0
- package/dist/plans.d.ts +82 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +277 -0
- package/dist/plans.js.map +1 -0
- package/package.json +61 -0
- package/src/billing-js/billing.js +876 -0
- package/src/brand.ts +62 -0
- package/src/index.ts +68 -0
- package/src/overlay/constants.ts +99 -0
- package/src/overlay/index.ts +17 -0
- package/src/plans.ts +359 -0
package/src/brand.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// @billing-io/designs — Brand assets
|
|
2
|
+
// Canonical SVG badge and brand constants for billing.io
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Full SVG logo badge (the "b." glyph) used in billing.js overlay headers.
|
|
6
|
+
* This is the production brand mark — use this in all customer-facing contexts.
|
|
7
|
+
*/
|
|
8
|
+
export const BRAND_SVG_VIEWBOX = "0 0 1024 1024";
|
|
9
|
+
|
|
10
|
+
export const BRAND_SVG_PATHS = {
|
|
11
|
+
background: { fill: "#000", d: "" },
|
|
12
|
+
letterB: {
|
|
13
|
+
fill: "#fff",
|
|
14
|
+
d: "M648.077,530.65c0,46.2-18.34,81.95-55,107.25-25.3,17.239-51.7,25.851-79.2,25.851-10.27,0-25.119-3.301-44.55-9.9-10.27-3.661-18.149-5.5-23.649-5.5-9.9,0-19.989,3.67-30.25,11-2.2,1.461-3.851,2.2-4.95,2.2-2.569,0-4.039-1.101-4.4-3.3-.369-2.2-.55-8.801-.55-19.801v-322.85c0-9.53-1.719-15.761-5.156-18.7-3.438-2.931-10.948-4.4-22.533-4.4-9.771,0-14.66-1.28-14.66-3.85s2.234-3.85,6.694-3.85c41.653,0,77.919-4.581,108.797-13.75l.559,8.8v162.25c14.618-20.161,34.899-30.25,60.853-30.25,31.427,0,57.285,11.095,77.567,33.274,20.281,22.189,30.43,50.695,30.43,85.525ZM566.127,532.851c0-71.131-14.85-106.7-44.55-106.7-16.5,0-28.6,7.7-36.3,23.1-2.939,5.87-4.675,12.745-5.225,20.625-.551,7.89-.825,30.345-.825,67.375v33.551c0,34.469,2.286,57.294,6.875,68.475,4.58,11.189,13.836,16.775,27.774,16.775,19.062,0,32.536-9.351,40.426-28.051,7.88-18.699,11.824-50.41,11.824-95.149Z",
|
|
15
|
+
},
|
|
16
|
+
dot: {
|
|
17
|
+
fill: "#fff",
|
|
18
|
+
d: "M692.003,642.041c0,7.235-2.336,13.186-7,17.851-4.671,4.665-10.621,7-17.851,7s-13.3-2.396-18.2-7.175c-4.899-4.78-7.35-10.676-7.35-17.676,0-7.229,2.45-13.3,7.35-18.199s10.965-7.351,18.2-7.351c6.765,0,12.601,2.45,17.5,7.351,4.9,4.899,7.351,10.97,7.351,18.199Z",
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Build an inline-HTML brand badge at the given size.
|
|
24
|
+
* This is the full SVG version used in the canonical billing.js.
|
|
25
|
+
*/
|
|
26
|
+
export type BadgeSize = "sm" | "md" | "lg";
|
|
27
|
+
|
|
28
|
+
export interface BadgeDimensions {
|
|
29
|
+
size: number;
|
|
30
|
+
borderRadius: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const BADGE_DIMENSIONS: Record<BadgeSize, BadgeDimensions> = {
|
|
34
|
+
sm: { size: 18, borderRadius: 4 },
|
|
35
|
+
md: { size: 20, borderRadius: 5 },
|
|
36
|
+
lg: { size: 24, borderRadius: 6 },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function buildBrandBadgeHTML(size: BadgeSize = "md"): string {
|
|
40
|
+
const { size: dim, borderRadius: br } = BADGE_DIMENSIONS[size];
|
|
41
|
+
return (
|
|
42
|
+
`<div style="width:${dim}px;height:${dim}px;background:#000;border-radius:${br}px;display:flex;align-items:center;justify-content:center;overflow:hidden;">` +
|
|
43
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${BRAND_SVG_VIEWBOX}" width="${dim}" height="${dim}">` +
|
|
44
|
+
`<rect width="1024" height="1024" fill="#000"/>` +
|
|
45
|
+
`<path fill="${BRAND_SVG_PATHS.letterB.fill}" d="${BRAND_SVG_PATHS.letterB.d}"/>` +
|
|
46
|
+
`<path fill="${BRAND_SVG_PATHS.dot.fill}" d="${BRAND_SVG_PATHS.dot.d}"/>` +
|
|
47
|
+
`</svg></div>`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the full SVG markup for the brand logo (no container div).
|
|
53
|
+
*/
|
|
54
|
+
export function getBrandSVG(width: number = 24, height: number = 24): string {
|
|
55
|
+
return (
|
|
56
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${BRAND_SVG_VIEWBOX}" width="${width}" height="${height}">` +
|
|
57
|
+
`<rect width="1024" height="1024" fill="#000"/>` +
|
|
58
|
+
`<path fill="${BRAND_SVG_PATHS.letterB.fill}" d="${BRAND_SVG_PATHS.letterB.d}"/>` +
|
|
59
|
+
`<path fill="${BRAND_SVG_PATHS.dot.fill}" d="${BRAND_SVG_PATHS.dot.d}"/>` +
|
|
60
|
+
`</svg>`
|
|
61
|
+
);
|
|
62
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// @billing-io/designs — Centralized design definitions for billing.io
|
|
2
|
+
//
|
|
3
|
+
// This package is the single source of truth for:
|
|
4
|
+
// - Plan/pricing definitions and types
|
|
5
|
+
// - Brand assets (SVG badge, logo)
|
|
6
|
+
// - Checkout overlay design constants (variations, animations, dimensions)
|
|
7
|
+
// - Billing system constants (thresholds, event types)
|
|
8
|
+
//
|
|
9
|
+
// All billing.io projects should import from this package instead of
|
|
10
|
+
// duplicating these values.
|
|
11
|
+
|
|
12
|
+
// Plans, pricing, and billing constants
|
|
13
|
+
export {
|
|
14
|
+
PLANS,
|
|
15
|
+
MARKETING_PLANS,
|
|
16
|
+
MARKETING_SANDBOX,
|
|
17
|
+
COMPARISON_PLANS,
|
|
18
|
+
COMPARISON_DATA,
|
|
19
|
+
PRICING_FAQS,
|
|
20
|
+
MID_CYCLE_THRESHOLD_USD,
|
|
21
|
+
PAST_DUE_RESTRICT_HOURS,
|
|
22
|
+
RESTRICT_SUSPEND_HOURS,
|
|
23
|
+
USAGE_WARNING_THRESHOLD,
|
|
24
|
+
BILLING_EMAIL_EVENTS,
|
|
25
|
+
BILLING_EVENT_TYPES,
|
|
26
|
+
} from "./plans.js";
|
|
27
|
+
|
|
28
|
+
export type {
|
|
29
|
+
PlanId,
|
|
30
|
+
BillingStatus,
|
|
31
|
+
PlanConfig,
|
|
32
|
+
MarketingPlan,
|
|
33
|
+
MarketingSandboxPlan,
|
|
34
|
+
ComparisonValue,
|
|
35
|
+
ComparisonFeature,
|
|
36
|
+
ComparisonSection,
|
|
37
|
+
FaqEntry,
|
|
38
|
+
} from "./plans.js";
|
|
39
|
+
|
|
40
|
+
// Brand assets
|
|
41
|
+
export {
|
|
42
|
+
BRAND_SVG_VIEWBOX,
|
|
43
|
+
BRAND_SVG_PATHS,
|
|
44
|
+
BADGE_DIMENSIONS,
|
|
45
|
+
buildBrandBadgeHTML,
|
|
46
|
+
getBrandSVG,
|
|
47
|
+
} from "./brand.js";
|
|
48
|
+
|
|
49
|
+
export type { BadgeSize, BadgeDimensions } from "./brand.js";
|
|
50
|
+
|
|
51
|
+
// Overlay design constants
|
|
52
|
+
export {
|
|
53
|
+
OVERLAY_Z_INDEX,
|
|
54
|
+
EASING,
|
|
55
|
+
MSG_PREFIX,
|
|
56
|
+
VARIATION_ALIASES,
|
|
57
|
+
VARIATIONS,
|
|
58
|
+
CLOSE_BUTTON,
|
|
59
|
+
BACKDROP,
|
|
60
|
+
CARD,
|
|
61
|
+
PANEL,
|
|
62
|
+
BOTTOM_SHEET,
|
|
63
|
+
POPUP,
|
|
64
|
+
FULLSCREEN,
|
|
65
|
+
SUCCESS_AUTO_CLOSE_MS,
|
|
66
|
+
} from "./overlay/index.js";
|
|
67
|
+
|
|
68
|
+
export type { VariationName, VariationMeta } from "./overlay/index.js";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @billing-io/designs — Checkout overlay design constants
|
|
2
|
+
// Shared values used across all 5 overlay variations in billing.js
|
|
3
|
+
|
|
4
|
+
export const OVERLAY_Z_INDEX = 2147483647;
|
|
5
|
+
|
|
6
|
+
// Spring-like easing curves for overlay animations
|
|
7
|
+
export const EASING = {
|
|
8
|
+
OUT_EXPO: "cubic-bezier(0.16, 1, 0.3, 1)",
|
|
9
|
+
OUT_QUINT: "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
10
|
+
IN_EXPO: "cubic-bezier(0.7, 0, 0.84, 0)",
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
// PostMessage protocol
|
|
14
|
+
export const MSG_PREFIX = "billing:";
|
|
15
|
+
|
|
16
|
+
// Variation name -> number mapping
|
|
17
|
+
export const VARIATION_ALIASES: Record<string, number> = {
|
|
18
|
+
centered: 1, card: 1,
|
|
19
|
+
panel: 2, side: 2, sidepanel: 2,
|
|
20
|
+
bottom: 3, sheet: 3, bottomsheet: 3,
|
|
21
|
+
fullscreen: 4, full: 4, takeover: 4,
|
|
22
|
+
popup: 5, floating: 5, pill: 5,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type VariationName = "centered" | "panel" | "bottom" | "fullscreen" | "popup";
|
|
26
|
+
|
|
27
|
+
export interface VariationMeta {
|
|
28
|
+
id: number;
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
/** Primary alias for this variation */
|
|
32
|
+
alias: VariationName;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const VARIATIONS: VariationMeta[] = [
|
|
36
|
+
{ id: 1, name: "Centered Card", description: "Classic centered modal, backdrop blur", alias: "centered" },
|
|
37
|
+
{ id: 2, name: "Side Panel", description: "Right-anchored slide-in sheet", alias: "panel" },
|
|
38
|
+
{ id: 3, name: "Bottom Sheet", description: "Mobile-friendly bottom sheet", alias: "bottom" },
|
|
39
|
+
{ id: 4, name: "Fullscreen", description: "Immersive full-screen overlay", alias: "fullscreen" },
|
|
40
|
+
{ id: 5, name: "Floating Popup", description: "Compact bottom-right popup", alias: "popup" },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Close button dimensions
|
|
44
|
+
export const CLOSE_BUTTON = {
|
|
45
|
+
size: 32,
|
|
46
|
+
borderRadius: 10,
|
|
47
|
+
background: "#f3f4f6",
|
|
48
|
+
hoverBackground: "#e5e7eb",
|
|
49
|
+
color: "#6b7280",
|
|
50
|
+
iconSize: 14,
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
// Backdrop defaults
|
|
54
|
+
export const BACKDROP = {
|
|
55
|
+
color: "rgba(0,0,0,0.4)",
|
|
56
|
+
blur: "12px",
|
|
57
|
+
lightColor: "rgba(0,0,0,0.3)",
|
|
58
|
+
lightBlur: "6px",
|
|
59
|
+
scrimColor: "rgba(0,0,0,0.08)",
|
|
60
|
+
} as const;
|
|
61
|
+
|
|
62
|
+
// Card dimensions shared across variations
|
|
63
|
+
export const CARD = {
|
|
64
|
+
maxWidth: 460,
|
|
65
|
+
maxHeight: 680,
|
|
66
|
+
borderRadius: 20,
|
|
67
|
+
minIframeHeight: 600,
|
|
68
|
+
} as const;
|
|
69
|
+
|
|
70
|
+
// Side panel dimensions
|
|
71
|
+
export const PANEL = {
|
|
72
|
+
width: 460,
|
|
73
|
+
} as const;
|
|
74
|
+
|
|
75
|
+
// Bottom sheet dimensions
|
|
76
|
+
export const BOTTOM_SHEET = {
|
|
77
|
+
maxWidth: 500,
|
|
78
|
+
maxHeight: "92vh",
|
|
79
|
+
minIframeHeight: 580,
|
|
80
|
+
handleWidth: 36,
|
|
81
|
+
handleHeight: 4,
|
|
82
|
+
} as const;
|
|
83
|
+
|
|
84
|
+
// Floating popup dimensions
|
|
85
|
+
export const POPUP = {
|
|
86
|
+
width: 420,
|
|
87
|
+
height: 640,
|
|
88
|
+
offset: 24,
|
|
89
|
+
borderRadius: 16,
|
|
90
|
+
} as const;
|
|
91
|
+
|
|
92
|
+
// Fullscreen background
|
|
93
|
+
export const FULLSCREEN = {
|
|
94
|
+
background: "rgba(247,248,250,1)",
|
|
95
|
+
backgroundTransparent: "rgba(247,248,250,0)",
|
|
96
|
+
} as const;
|
|
97
|
+
|
|
98
|
+
// Auto-close delay after successful payment (ms)
|
|
99
|
+
export const SUCCESS_AUTO_CLOSE_MS = 2500;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
OVERLAY_Z_INDEX,
|
|
3
|
+
EASING,
|
|
4
|
+
MSG_PREFIX,
|
|
5
|
+
VARIATION_ALIASES,
|
|
6
|
+
VARIATIONS,
|
|
7
|
+
CLOSE_BUTTON,
|
|
8
|
+
BACKDROP,
|
|
9
|
+
CARD,
|
|
10
|
+
PANEL,
|
|
11
|
+
BOTTOM_SHEET,
|
|
12
|
+
POPUP,
|
|
13
|
+
FULLSCREEN,
|
|
14
|
+
SUCCESS_AUTO_CLOSE_MS,
|
|
15
|
+
} from "./constants.js";
|
|
16
|
+
|
|
17
|
+
export type { VariationName, VariationMeta } from "./constants.js";
|
package/src/plans.ts
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// @billing-io/designs — Canonical plan definitions
|
|
2
|
+
// All billing.io projects should import plan data from this package.
|
|
3
|
+
|
|
4
|
+
export type PlanId = "free" | "starter" | "growth" | "enterprise";
|
|
5
|
+
|
|
6
|
+
export type BillingStatus = "active" | "past_due" | "restricted" | "suspended";
|
|
7
|
+
|
|
8
|
+
export interface PlanConfig {
|
|
9
|
+
id: PlanId;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
monthlyMinimum: number; // USD
|
|
13
|
+
volumeBps: number; // basis points (30 = 0.30%)
|
|
14
|
+
perPaymentFee: number; // USD per successful payment
|
|
15
|
+
includedVolumeUsd: number; // free volume threshold
|
|
16
|
+
features: string[];
|
|
17
|
+
requiresCard: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const PLANS: Record<PlanId, PlanConfig> = {
|
|
21
|
+
free: {
|
|
22
|
+
id: "free",
|
|
23
|
+
name: "Free",
|
|
24
|
+
description: "For development and testing",
|
|
25
|
+
monthlyMinimum: 0,
|
|
26
|
+
volumeBps: 0,
|
|
27
|
+
perPaymentFee: 0,
|
|
28
|
+
includedVolumeUsd: 2_000,
|
|
29
|
+
features: [
|
|
30
|
+
"Up to $2,000/month processed",
|
|
31
|
+
"Limited API rate",
|
|
32
|
+
"No production SLA",
|
|
33
|
+
"No webhook retries",
|
|
34
|
+
],
|
|
35
|
+
requiresCard: false,
|
|
36
|
+
},
|
|
37
|
+
starter: {
|
|
38
|
+
id: "starter",
|
|
39
|
+
name: "Starter",
|
|
40
|
+
description: "For production applications",
|
|
41
|
+
monthlyMinimum: 49,
|
|
42
|
+
volumeBps: 30, // 0.30%
|
|
43
|
+
perPaymentFee: 0.02,
|
|
44
|
+
includedVolumeUsd: 5_000,
|
|
45
|
+
features: [
|
|
46
|
+
"0.30% of settled volume",
|
|
47
|
+
"$0.02 per successful payment",
|
|
48
|
+
"First $5,000/month included",
|
|
49
|
+
"Standard webhooks",
|
|
50
|
+
"Email support",
|
|
51
|
+
],
|
|
52
|
+
requiresCard: true,
|
|
53
|
+
},
|
|
54
|
+
growth: {
|
|
55
|
+
id: "growth",
|
|
56
|
+
name: "Growth",
|
|
57
|
+
description: "For scaling businesses",
|
|
58
|
+
monthlyMinimum: 199,
|
|
59
|
+
volumeBps: 20, // 0.20%
|
|
60
|
+
perPaymentFee: 0.02,
|
|
61
|
+
includedVolumeUsd: 50_000,
|
|
62
|
+
features: [
|
|
63
|
+
"0.20% of settled volume",
|
|
64
|
+
"$0.02 per successful payment",
|
|
65
|
+
"First $50,000/month included",
|
|
66
|
+
"Webhook retries + logs",
|
|
67
|
+
"Team access",
|
|
68
|
+
"Priority email support",
|
|
69
|
+
],
|
|
70
|
+
requiresCard: true,
|
|
71
|
+
},
|
|
72
|
+
enterprise: {
|
|
73
|
+
id: "enterprise",
|
|
74
|
+
name: "Enterprise",
|
|
75
|
+
description: "Custom pricing and SLA",
|
|
76
|
+
monthlyMinimum: 2_000,
|
|
77
|
+
volumeBps: 10, // 0.10-0.15%, configurable
|
|
78
|
+
perPaymentFee: 0.02,
|
|
79
|
+
includedVolumeUsd: 500_000,
|
|
80
|
+
features: [
|
|
81
|
+
"0.10-0.15% volume rate",
|
|
82
|
+
"Custom minimums",
|
|
83
|
+
"SLA guarantee",
|
|
84
|
+
"Dedicated infrastructure",
|
|
85
|
+
"Custom confirmations",
|
|
86
|
+
],
|
|
87
|
+
requiresCard: true,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Marketing-facing plan display data
|
|
93
|
+
// These are the customer-facing descriptions used on the pricing page.
|
|
94
|
+
// They reference the canonical PLANS above but add marketing copy.
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
export interface MarketingPlan {
|
|
98
|
+
id: PlanId;
|
|
99
|
+
name: string;
|
|
100
|
+
price: string; // display string, e.g. "$49"
|
|
101
|
+
period: string; // e.g. "/month"
|
|
102
|
+
badge?: string;
|
|
103
|
+
subheadline: string;
|
|
104
|
+
features: string[];
|
|
105
|
+
ctaLabel: string;
|
|
106
|
+
ctaHref: string;
|
|
107
|
+
ctaExternal?: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const MARKETING_PLANS: MarketingPlan[] = [
|
|
111
|
+
{
|
|
112
|
+
id: "starter",
|
|
113
|
+
name: "Starter",
|
|
114
|
+
price: `$${PLANS.starter.monthlyMinimum}`,
|
|
115
|
+
period: "/month",
|
|
116
|
+
badge: "Most common",
|
|
117
|
+
subheadline: `${(PLANS.starter.volumeBps / 100).toFixed(2)}% of volume + $${PLANS.starter.perPaymentFee.toFixed(2)} per payment.`,
|
|
118
|
+
features: [
|
|
119
|
+
"Branded checkout on your domain",
|
|
120
|
+
"Embeddable checkout",
|
|
121
|
+
"SDK access",
|
|
122
|
+
"Signed webhooks",
|
|
123
|
+
"Dashboard and analytics",
|
|
124
|
+
"Email support",
|
|
125
|
+
],
|
|
126
|
+
ctaLabel: "Go live",
|
|
127
|
+
ctaHref: "https://app.billing.io/signup",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "growth",
|
|
131
|
+
name: "Growth",
|
|
132
|
+
price: `$${PLANS.growth.monthlyMinimum}`,
|
|
133
|
+
period: "/month",
|
|
134
|
+
subheadline: `${(PLANS.growth.volumeBps / 100).toFixed(2)}% of volume + $${PLANS.growth.perPaymentFee.toFixed(2)} per payment.`,
|
|
135
|
+
features: [
|
|
136
|
+
"Everything in Starter",
|
|
137
|
+
"Webhook retries and logs",
|
|
138
|
+
"Team access (up to 5)",
|
|
139
|
+
"Priority support",
|
|
140
|
+
],
|
|
141
|
+
ctaLabel: "Upgrade to Growth",
|
|
142
|
+
ctaHref: "https://app.billing.io/signup",
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "enterprise",
|
|
146
|
+
name: "Scale",
|
|
147
|
+
price: `$${PLANS.enterprise.monthlyMinimum}`,
|
|
148
|
+
period: "/month",
|
|
149
|
+
subheadline: `${(PLANS.enterprise.volumeBps / 100).toFixed(2)}% of volume + $${PLANS.enterprise.perPaymentFee.toFixed(2)} per payment.`,
|
|
150
|
+
features: [
|
|
151
|
+
"Everything in Growth",
|
|
152
|
+
"Higher throughput and billing caps",
|
|
153
|
+
"Unlimited team members",
|
|
154
|
+
"Direct support channel",
|
|
155
|
+
],
|
|
156
|
+
ctaLabel: "Contact sales",
|
|
157
|
+
ctaHref: "https://wa.me/message/ZTXL4WR3SWZQP1",
|
|
158
|
+
ctaExternal: true,
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
export interface MarketingSandboxPlan {
|
|
163
|
+
name: string;
|
|
164
|
+
price: string;
|
|
165
|
+
period: string;
|
|
166
|
+
features: string[];
|
|
167
|
+
ctaLabel: string;
|
|
168
|
+
ctaHref: string;
|
|
169
|
+
footnote: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const MARKETING_SANDBOX: MarketingSandboxPlan = {
|
|
173
|
+
name: "Sandbox (development only)",
|
|
174
|
+
price: "$0",
|
|
175
|
+
period: "/month",
|
|
176
|
+
features: [
|
|
177
|
+
"Testnet access only",
|
|
178
|
+
"Full SDK access",
|
|
179
|
+
"Simulated webhooks",
|
|
180
|
+
"Dashboard preview",
|
|
181
|
+
"No card required",
|
|
182
|
+
"No time limit",
|
|
183
|
+
],
|
|
184
|
+
ctaLabel: "Start in sandbox",
|
|
185
|
+
ctaHref: "https://app.billing.io/signup",
|
|
186
|
+
footnote: "Production usage requires a paid plan.",
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// Plan comparison table data
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
export type ComparisonValue = boolean | string | Record<string, boolean | string>;
|
|
194
|
+
|
|
195
|
+
export interface ComparisonFeature {
|
|
196
|
+
name: string;
|
|
197
|
+
value: ComparisonValue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface ComparisonSection {
|
|
201
|
+
title: string;
|
|
202
|
+
features: ComparisonFeature[];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const COMPARISON_PLANS = ["Sandbox", "Starter", "Growth", "Scale"] as const;
|
|
206
|
+
|
|
207
|
+
export const COMPARISON_DATA: ComparisonSection[] = [
|
|
208
|
+
{
|
|
209
|
+
title: "Payments",
|
|
210
|
+
features: [
|
|
211
|
+
{ name: "Testnet access", value: true },
|
|
212
|
+
{
|
|
213
|
+
name: "Mainnet payments",
|
|
214
|
+
value: { Sandbox: false, Starter: true, Growth: true, Scale: true },
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: "Hosted checkout",
|
|
218
|
+
value: { Sandbox: false, Starter: true, Growth: true, Scale: true },
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "Embeddable checkout",
|
|
222
|
+
value: { Sandbox: false, Starter: true, Growth: true, Scale: true },
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
title: "Developer tools",
|
|
228
|
+
features: [
|
|
229
|
+
{ name: "SDK access", value: true },
|
|
230
|
+
{
|
|
231
|
+
name: "Webhooks",
|
|
232
|
+
value: { Sandbox: "Simulated", Starter: "Signed", Growth: "Signed", Scale: "Signed" },
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "Webhook retries",
|
|
236
|
+
value: { Sandbox: false, Starter: false, Growth: true, Scale: true },
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "Dashboard",
|
|
240
|
+
value: { Sandbox: "Preview", Starter: true, Growth: true, Scale: true },
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
title: "Team and support",
|
|
246
|
+
features: [
|
|
247
|
+
{
|
|
248
|
+
name: "Team members",
|
|
249
|
+
value: { Sandbox: "1", Starter: "1", Growth: "Up to 5", Scale: "Unlimited" },
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "Support level",
|
|
253
|
+
value: { Sandbox: "Docs only", Starter: "Email", Growth: "Priority", Scale: "Direct channel" },
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
title: "Billing and limits",
|
|
259
|
+
features: [
|
|
260
|
+
{
|
|
261
|
+
name: "Billing cap",
|
|
262
|
+
value: { Sandbox: "N/A", Starter: "$200", Growth: "$500", Scale: "$1,500" },
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: "Fiat access eligibility",
|
|
266
|
+
value: { Sandbox: false, Starter: "After volume", Growth: "After volume", Scale: "After volume" },
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// FAQ data
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
export interface FaqEntry {
|
|
277
|
+
id: string;
|
|
278
|
+
question: string;
|
|
279
|
+
answer: string;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export const PRICING_FAQS: FaqEntry[] = [
|
|
283
|
+
{
|
|
284
|
+
id: "faq-1",
|
|
285
|
+
question: "Do I need a credit card to start?",
|
|
286
|
+
answer: "Not for sandbox. You can build and test without a card. A payment method is required when you move to production.",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: "faq-2",
|
|
290
|
+
question: "When do I start paying?",
|
|
291
|
+
answer: "You can build and test for free in sandbox. Production usage is billed according to your plan when you process real payments.",
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "faq-3",
|
|
295
|
+
question: "When do you charge me?",
|
|
296
|
+
answer: "Your plan fee is charged at the start of each billing cycle. Usage fees are charged when they hit a cap or at month-end.",
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
id: "faq-4",
|
|
300
|
+
question: "What happens if my card fails?",
|
|
301
|
+
answer: "We notify you and retry. If payment stays unresolved, production features may be paused until it is sorted out.",
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: "faq-5",
|
|
305
|
+
question: "Do you hold my funds?",
|
|
306
|
+
answer: "No. Payments settle directly to your wallet. billing.io is non-custodial.",
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "faq-6",
|
|
310
|
+
question: "Can I upgrade or downgrade plans?",
|
|
311
|
+
answer: "Yes. We recommend upgrades automatically when they save you money at your current volume.",
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: "faq-7",
|
|
315
|
+
question: "When can I access fiat payments?",
|
|
316
|
+
answer: "Fiat access becomes available after you process meaningful volume. It is not included by default.",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: "faq-8",
|
|
320
|
+
question: "Is billing.io right for low-volume projects?",
|
|
321
|
+
answer: "If you are still building, use the sandbox. Production plans are designed for businesses processing real payments.",
|
|
322
|
+
},
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// Billing system constants (thresholds, events)
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
|
|
329
|
+
export const MID_CYCLE_THRESHOLD_USD = 100;
|
|
330
|
+
|
|
331
|
+
export const PAST_DUE_RESTRICT_HOURS = 48;
|
|
332
|
+
export const RESTRICT_SUSPEND_HOURS = 168; // 7 days
|
|
333
|
+
|
|
334
|
+
export const USAGE_WARNING_THRESHOLD = 0.8; // 80%
|
|
335
|
+
|
|
336
|
+
export const BILLING_EMAIL_EVENTS = {
|
|
337
|
+
CARD_REQUIRED: "card_required",
|
|
338
|
+
USAGE_APPROACHING_LIMIT: "usage_approaching_limit",
|
|
339
|
+
MID_CYCLE_CHARGE: "mid_cycle_charge",
|
|
340
|
+
PAYMENT_FAILED: "payment_failed",
|
|
341
|
+
SERVICE_RESTRICTED: "service_restricted",
|
|
342
|
+
SERVICE_SUSPENDED: "service_suspended",
|
|
343
|
+
PAYMENT_RECOVERED: "payment_recovered",
|
|
344
|
+
} as const;
|
|
345
|
+
|
|
346
|
+
export const BILLING_EVENT_TYPES = {
|
|
347
|
+
PLAN_CHANGED: "plan_changed",
|
|
348
|
+
SUBSCRIPTION_CREATED: "subscription_created",
|
|
349
|
+
USAGE_RECORDED: "usage_recorded",
|
|
350
|
+
INVOICE_CREATED: "invoice_created",
|
|
351
|
+
INVOICE_PAID: "invoice_paid",
|
|
352
|
+
INVOICE_FAILED: "invoice_failed",
|
|
353
|
+
MID_CYCLE_INVOICE: "mid_cycle_invoice",
|
|
354
|
+
STATUS_CHANGED: "status_changed",
|
|
355
|
+
PAYMENT_METHOD_UPDATED: "payment_method_updated",
|
|
356
|
+
ACCOUNT_RESTRICTED: "account_restricted",
|
|
357
|
+
ACCOUNT_SUSPENDED: "account_suspended",
|
|
358
|
+
ACCOUNT_RECOVERED: "account_recovered",
|
|
359
|
+
} as const;
|