@go-avro/avro-js 0.0.51 → 0.0.53
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/fees.d.ts +0 -41
- package/dist/billing/fees.js +0 -146
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/billing/fees.d.ts
CHANGED
|
@@ -1,43 +1,2 @@
|
|
|
1
1
|
import { PaymentType } from '../types/api/PaymentType';
|
|
2
|
-
export interface PassOnFeeBreakdown {
|
|
3
|
-
/** Stripe processing fee on the grossed-up amount, in cents. */
|
|
4
|
-
stripeFee: number;
|
|
5
|
-
/** Avro application fee on the grossed-up amount, in cents. */
|
|
6
|
-
avroFee: number;
|
|
7
|
-
/** Total fee added on top of the subtotal: stripeFee + avroFee. */
|
|
8
|
-
totalFee: number;
|
|
9
|
-
/** Gross amount the customer is charged: subtotal + totalFee. */
|
|
10
|
-
gross: number;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Compute Stripe's processing fee on a gross amount, in cents.
|
|
14
|
-
* Ceiling matches Stripe's worst-case rounding so the merchant is never short.
|
|
15
|
-
*/
|
|
16
|
-
export declare function computeStripeFee(grossCents: number, method: PaymentType): number;
|
|
17
|
-
/**
|
|
18
|
-
* Compute Avro's application fee on a gross amount, in cents.
|
|
19
|
-
*
|
|
20
|
-
* Avro's fee is now computed against the gross (matching Stripe's basis)
|
|
21
|
-
* rather than the post-Stripe net. This keeps the pass-on gross-up
|
|
22
|
-
* exactly solvable in closed form.
|
|
23
|
-
*/
|
|
24
|
-
export declare function computeAvroFee(grossCents: number): number;
|
|
25
|
-
/**
|
|
26
|
-
* Compute the pass-on fee (in cents) such that, after Stripe and Avro deduct
|
|
27
|
-
* their fees from the gross amount the customer is charged, the merchant
|
|
28
|
-
* receives at least `subtotalCents`.
|
|
29
|
-
*
|
|
30
|
-
* Returns `ZERO_FEE` when `subtotalCents <= 0`.
|
|
31
|
-
*
|
|
32
|
-
* Closed form: a finite set of candidate grosses is derived from each fee
|
|
33
|
-
* regime (avro at the 5¢ floor vs. percentage; ACH stripe at the $5 cap vs.
|
|
34
|
-
* percentage). The smallest candidate that satisfies the invariant
|
|
35
|
-
* `gross - stripeFee(gross) - avroFee(gross) >= subtotal` is selected.
|
|
36
|
-
*/
|
|
37
|
-
export declare function computePassOnFee(subtotalCents: number, method: PaymentType): PassOnFeeBreakdown;
|
|
38
|
-
/**
|
|
39
|
-
* Pick the payment method to use when computing the persisted ("default")
|
|
40
|
-
* fee line item amount on a bill. Card if enabled (worst case for the
|
|
41
|
-
* customer), otherwise ACH, otherwise null (no fee should be persisted).
|
|
42
|
-
*/
|
|
43
2
|
export declare function defaultPassOnFeeMethod(enabledPaymentMethods: PaymentType[] | null | undefined): PaymentType | null;
|
package/dist/billing/fees.js
CHANGED
|
@@ -1,150 +1,4 @@
|
|
|
1
1
|
import { PaymentType } from '../types/api/PaymentType';
|
|
2
|
-
/**
|
|
3
|
-
* Pass-on (surcharge) fee calculations.
|
|
4
|
-
*
|
|
5
|
-
* When `pass_on_fees` is enabled on a bill, the customer is charged a gross
|
|
6
|
-
* amount that, after Stripe's processing fees and Avro's application fee, leaves
|
|
7
|
-
* the merchant with at least the bill's subtotal. The fee added on top of the
|
|
8
|
-
* subtotal is exposed as a single read-only line item on the bill.
|
|
9
|
-
*
|
|
10
|
-
* Stripe US standard pricing (online):
|
|
11
|
-
* - Card: 2.9% of gross + 30¢
|
|
12
|
-
* - ACH (us_bank): 0.8% of gross, capped at $5.00
|
|
13
|
-
* Avro application fee:
|
|
14
|
-
* - 0.4% of gross, minimum 5¢
|
|
15
|
-
*
|
|
16
|
-
* All amounts are integer cents. All math is closed-form (no iteration).
|
|
17
|
-
*
|
|
18
|
-
* Because both Stripe and Avro fees are charged against the same base (gross),
|
|
19
|
-
* the gross-up reduces to the standard form `G = (B + fixed) / (1 - rate)`
|
|
20
|
-
* within each regime. The integer-ceiling on each candidate guarantees the
|
|
21
|
-
* merchant nets at least the requested subtotal. In rare regime-boundary cases
|
|
22
|
-
* the merchant may net up to 2¢ more than requested — never less.
|
|
23
|
-
*/
|
|
24
|
-
// --- Rate constants (per 1000 for integer math where applicable) ---
|
|
25
|
-
const CARD_PCT_PER_1000 = 29; // 2.9%
|
|
26
|
-
const CARD_FIXED_CENTS = 30; // 30¢
|
|
27
|
-
const ACH_PCT_PER_1000 = 8; // 0.8%
|
|
28
|
-
const ACH_CAP_CENTS = 500; // $5.00
|
|
29
|
-
const AVRO_PCT_PER_1000 = 4; // 0.4%
|
|
30
|
-
const AVRO_MIN_CENTS = 5; // 5¢
|
|
31
|
-
const ZERO_FEE = {
|
|
32
|
-
stripeFee: 0,
|
|
33
|
-
avroFee: 0,
|
|
34
|
-
totalFee: 0,
|
|
35
|
-
gross: 0,
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Compute Stripe's processing fee on a gross amount, in cents.
|
|
39
|
-
* Ceiling matches Stripe's worst-case rounding so the merchant is never short.
|
|
40
|
-
*/
|
|
41
|
-
export function computeStripeFee(grossCents, method) {
|
|
42
|
-
if (grossCents <= 0)
|
|
43
|
-
return 0;
|
|
44
|
-
if (method === PaymentType.CARD) {
|
|
45
|
-
return Math.ceil((CARD_PCT_PER_1000 * grossCents) / 1000) + CARD_FIXED_CENTS;
|
|
46
|
-
}
|
|
47
|
-
// us_bank_account: percentage capped at $5.00
|
|
48
|
-
return Math.min(Math.ceil((ACH_PCT_PER_1000 * grossCents) / 1000), ACH_CAP_CENTS);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Compute Avro's application fee on a gross amount, in cents.
|
|
52
|
-
*
|
|
53
|
-
* Avro's fee is now computed against the gross (matching Stripe's basis)
|
|
54
|
-
* rather than the post-Stripe net. This keeps the pass-on gross-up
|
|
55
|
-
* exactly solvable in closed form.
|
|
56
|
-
*/
|
|
57
|
-
export function computeAvroFee(grossCents) {
|
|
58
|
-
if (grossCents <= 0)
|
|
59
|
-
return AVRO_MIN_CENTS;
|
|
60
|
-
return Math.max(Math.ceil((AVRO_PCT_PER_1000 * grossCents) / 1000), AVRO_MIN_CENTS);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Compute the pass-on fee (in cents) such that, after Stripe and Avro deduct
|
|
64
|
-
* their fees from the gross amount the customer is charged, the merchant
|
|
65
|
-
* receives at least `subtotalCents`.
|
|
66
|
-
*
|
|
67
|
-
* Returns `ZERO_FEE` when `subtotalCents <= 0`.
|
|
68
|
-
*
|
|
69
|
-
* Closed form: a finite set of candidate grosses is derived from each fee
|
|
70
|
-
* regime (avro at the 5¢ floor vs. percentage; ACH stripe at the $5 cap vs.
|
|
71
|
-
* percentage). The smallest candidate that satisfies the invariant
|
|
72
|
-
* `gross - stripeFee(gross) - avroFee(gross) >= subtotal` is selected.
|
|
73
|
-
*/
|
|
74
|
-
export function computePassOnFee(subtotalCents, method) {
|
|
75
|
-
if (subtotalCents <= 0)
|
|
76
|
-
return { ...ZERO_FEE };
|
|
77
|
-
const B = subtotalCents;
|
|
78
|
-
const candidates = method === PaymentType.CARD ? cardCandidates(B) : achCandidates(B);
|
|
79
|
-
let best = Number.POSITIVE_INFINITY;
|
|
80
|
-
for (const g of candidates) {
|
|
81
|
-
const stripe = computeStripeFee(g, method);
|
|
82
|
-
const avro = computeAvroFee(g);
|
|
83
|
-
if (g - stripe - avro >= B && g < best) {
|
|
84
|
-
best = g;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (!Number.isFinite(best)) {
|
|
88
|
-
// Should never happen — the candidate set covers every regime — but
|
|
89
|
-
// fall back to a safe overshoot rather than throwing.
|
|
90
|
-
best = B + Math.ceil(B * 0.05) + CARD_FIXED_CENTS + AVRO_MIN_CENTS;
|
|
91
|
-
}
|
|
92
|
-
const stripeFee = computeStripeFee(best, method);
|
|
93
|
-
const avroFee = computeAvroFee(best);
|
|
94
|
-
return {
|
|
95
|
-
stripeFee,
|
|
96
|
-
avroFee,
|
|
97
|
-
totalFee: best - B,
|
|
98
|
-
gross: best,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Card regime candidates.
|
|
103
|
-
*
|
|
104
|
-
* stripe = ceil(0.029 * G) + 30; avro = max(ceil(0.004 * G), 5).
|
|
105
|
-
*
|
|
106
|
-
* Both ceil()s can each add up to 1¢ relative to the exact rational form, so
|
|
107
|
-
* each candidate carries a baked-in 2¢ (percentage regime) or 1¢ (floor
|
|
108
|
-
* regime) safety margin to guarantee the invariant holds for every B in
|
|
109
|
-
* integer cents — no post-hoc iteration required.
|
|
110
|
-
*
|
|
111
|
-
* 1) Percentage avro: G * 0.967 ≥ B + 30 + 2
|
|
112
|
-
* => G = ceil((1000 * B + 32_000) / 967)
|
|
113
|
-
* 2) Floor avro (5¢): G * 0.971 ≥ B + 30 + 5 + 1
|
|
114
|
-
* => G = ceil((1000 * B + 36_000) / 971)
|
|
115
|
-
*/
|
|
116
|
-
function cardCandidates(B) {
|
|
117
|
-
return [Math.ceil((1000 * B + 32000) / 967), Math.ceil((1000 * B + 36000) / 971)];
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* ACH regime candidates.
|
|
121
|
-
*
|
|
122
|
-
* stripe = min(ceil(0.008 * G), 500); avro = max(ceil(0.004 * G), 5).
|
|
123
|
-
*
|
|
124
|
-
* Same ceiling-safety logic as card. Stripe at the $5 cap is a fixed integer
|
|
125
|
-
* so its ceiling-slack disappears in the capped regimes.
|
|
126
|
-
*
|
|
127
|
-
* 1) No-cap percentage avro: G * 0.988 ≥ B + 2
|
|
128
|
-
* => G = ceil((1000 * (B + 2)) / 988)
|
|
129
|
-
* 2) No-cap floor avro: G * 0.992 ≥ B + 5 + 1
|
|
130
|
-
* => G = ceil((1000 * (B + 6)) / 992)
|
|
131
|
-
* 3) Capped percentage avro: G * 0.996 ≥ B + 500 + 1
|
|
132
|
-
* => G = ceil((1000 * (B + 501)) / 996)
|
|
133
|
-
* 4) Capped floor avro: G = B + 505
|
|
134
|
-
*/
|
|
135
|
-
function achCandidates(B) {
|
|
136
|
-
return [
|
|
137
|
-
Math.ceil((1000 * (B + 2)) / 988),
|
|
138
|
-
Math.ceil((1000 * (B + 6)) / 992),
|
|
139
|
-
Math.ceil((1000 * (B + 501)) / 996),
|
|
140
|
-
B + 505,
|
|
141
|
-
];
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Pick the payment method to use when computing the persisted ("default")
|
|
145
|
-
* fee line item amount on a bill. Card if enabled (worst case for the
|
|
146
|
-
* customer), otherwise ACH, otherwise null (no fee should be persisted).
|
|
147
|
-
*/
|
|
148
2
|
export function defaultPassOnFeeMethod(enabledPaymentMethods) {
|
|
149
3
|
if (!enabledPaymentMethods || enabledPaymentMethods.length === 0)
|
|
150
4
|
return null;
|
package/dist/index.d.ts
CHANGED
|
@@ -32,8 +32,7 @@ import './client/hooks/proposal';
|
|
|
32
32
|
import './client/hooks/timecards';
|
|
33
33
|
import './client/hooks/waivers';
|
|
34
34
|
import './client/hooks/email';
|
|
35
|
-
export {
|
|
36
|
-
export type { PassOnFeeBreakdown } from './billing/fees';
|
|
35
|
+
export { defaultPassOnFeeMethod } from './billing/fees';
|
|
37
36
|
export * from './types/api';
|
|
38
37
|
export * from './types/auth';
|
|
39
38
|
export * from './types/cache';
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import './client/hooks/proposal';
|
|
|
30
30
|
import './client/hooks/timecards';
|
|
31
31
|
import './client/hooks/waivers';
|
|
32
32
|
import './client/hooks/email';
|
|
33
|
-
export {
|
|
33
|
+
export { defaultPassOnFeeMethod } from './billing/fees';
|
|
34
34
|
export * from './types/api';
|
|
35
35
|
export * from './types/auth';
|
|
36
36
|
export * from './types/cache';
|