Skip to content

Instantly share code, notes, and snippets.

@turbotobias
Last active April 8, 2022 03:51
Show Gist options
  • Save turbotobias/8f0838625f487c83e3df3a53d0687ef8 to your computer and use it in GitHub Desktop.
Save turbotobias/8f0838625f487c83e3df3a53d0687ef8 to your computer and use it in GitHub Desktop.
import { round } from ".."
/**
* @example
*
* ```ts
* const fees = new PaymentAmount(1000)
* fees.getAmountToUser() // === 976.66
* fees.getAmountToStripe() // === 21
* ```
*/
export class PaymentAmount {
/**
* the amount used as the basis for fee calculations
*/
private _amount: number
/**
* the amount used as the basis for fee calculations
*/
public static _amountMin: number = 1
/**
* the amount used as the basis for fee calculations
*/
public static _amountMax: number = 50000
/**
* currency
*/
public static _currency: string = "nok"
/**
* how many we assume will donate to one wishlist on average, which dictates the fee amount
*/
private _estimatedDonorsOnAvg: number = 2
/**
*
* @todo create separate transfers and fees
*
*/
/**
* decimal places: the number of decimals included when getting values, rounded to its nearest or up when 5
*
* only used on exposed getters, not in calculations
*
* @example ./decimal-precision.test.ts
*/
private _decimals: number = 2
/**
* stripe fees
*/
private _stripePaymentAmountPercent: number = 2.9
private _stripePaymentAmountNOK: number = 0.3
private _stripeConnectFeePercent: number = 0.25
private _stripeConnectFeeNOK: number = 0.25
private _stripeConnectFeeMonthlyNOK: number = 20
/**
* stas fees
*/
private _stasFeeNOK: number = 6.25
/**
* @param amount the amount that fees are calculated from
*/
constructor(amount: number) {
if (amount < PaymentAmount._amountMin || amount > PaymentAmount._amountMax) this._amount = 1
this._amount = amount
}
public get stripePaymentAmountPercent() {
return this._stripePaymentAmountPercent
}
public get stripePaymentAmountNOK() {
return this._stripePaymentAmountNOK
}
public get stripeConnectFeePercent() {
return this._stripeConnectFeePercent
}
public get stripeConnectFeeNOK() {
return this._stripeConnectFeeNOK
}
public get stripeConnectFeeMonthlyNOK() {
return this._stripeConnectFeeMonthlyNOK
}
public get stasFeeNOK() {
return this._stasFeeNOK
}
public get decimals() {
return this._decimals
}
private get stripeFeePrPayment() {
const percent = this._amount * (this._stripePaymentAmountPercent / 100)
return percent + this._stripePaymentAmountNOK
}
private get stripeFeePrWishlist() {
const percent = this._amount * (this._stripeConnectFeePercent / 100)
const flat = this._stripeConnectFeeNOK + this._stripeConnectFeeMonthlyNOK
return percent + flat
}
private get stripeFeePrWishlistReduced() {
return this.stripeFeePrWishlist / this._estimatedDonorsOnAvg
}
private get stasFeePrPayment() {
return this._stasFeeNOK
}
/**
* change the number of decimal places used to round numbers (only on public getters)
*
* @example ./decimal-precision.test.ts
*/
public set decimalPlaces(decimalPlaces: number) {
this._decimals = decimalPlaces
}
public get amountMin() {
return PaymentAmount._amountMin
}
public get amountMax() {
return PaymentAmount._amountMax
}
/**
* all fees to stripe
*/
public get feesToStripe() {
return round(this.stripeFeePrPayment + this.stripeFeePrWishlistReduced, this._decimals)
}
/**
* all fees to stas
*/
public get feesToStas() {
return round(this.stasFeePrPayment, this._decimals)
}
/**
* the total amount of fees
*/
public get feesTotal() {
return this.feesToStripe + this.feesToStas
}
/**
* the % of all fees
*/
public get feesTotalPercent() {
const percent = (this.feesTotal / this._amount) * 100
return round(percent, this.decimals)
}
/**
* the total amount + all fees
*/
public get total() {
const total = Number(this._amount + this.feesTotal)
return round(total, this.decimals)
}
/**
* the total amount + all fees, formatted for Stripe (zero decimal currency support)
*/
public get totalToStripe() {
return PaymentAmount.toZeroDecimal(this.total)
}
/**
* the total amount + all fees, formatted from Stripe (with double decimal currency support)
*/
public totalFromStripe(amount: number) {
return PaymentAmount.toDecimal(amount)
}
/**
* the amount that all fees are calculated from
*/
public get amount() {
return this._amount
}
/**
* the amount that all fees are calculated from
*/
public get amountInSmallestCurrencyUnit() {
return PaymentAmount.toZeroDecimal(this._amount)
}
/**
* which currency this fee is in, probably "nok"
*
* @see https://stripe.com/docs/currencies?presentment-currency=NO
*/
public get currency() {
return PaymentAmount._currency.toLocaleLowerCase("nb-no")
}
/**
* get `1 250 550,00 NOK` from amount `1250550`
*
* @see https://stripe.com/docs/currencies?presentment-currency=NO
*/
public static toStringNOK(amount: number) {
return amount.toLocaleString("nb-no") + " NOK"
}
/**
* returns an integer representation of a double amount.
*
* `converts 51.50 to 5150`
*
* @param amount 1234.5678
* @returns 123457 (this is implicitly intended as being equal to 1234.57 of X)
*/
public static toZeroDecimal(amount: number) {
return round(amount * 100, 0)
}
/**
* converts `5150` to `51.50`
*
* returns a double representation of an integer amount (the opposit of PaymentAmount.toZeroDecimal)
*/
public static toDecimal(amount: number) {
return round(amount / 100, 2)
}
}
const intPow10 = (power: number) => {
if (power < 0 || power > 22) {
return Math.pow(10, power)
}
const powers = [
1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20,
1e21, 1e22,
]
return powers[power]
}
/**
*
* @example
* ```ts
* round(10.234, 2) // 10.23
* round(10.235, 2) // 10.24
* round(10.236999999, 2) // 10.24
* ```
*/
export const round = (num: number, decimalPlaces: number) => {
if (typeof num !== "number" || typeof decimalPlaces !== "number") return NaN
const power = intPow10(decimalPlaces || 0)
const n = num * power * (1 + Number.EPSILON)
return Math["round"](n) / power
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment