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.

Revisions

  1. turbotobias revised this gist Apr 8, 2022. 1 changed file with 0 additions and 43 deletions.
    43 changes: 0 additions & 43 deletions payment-amount.test.ts
    Original file line number Diff line number Diff line change
    @@ -1,43 +0,0 @@
    import { PaymentAmount } from "./"

    describe("PaymentAmount", () => {
    const PaymentAmount100 = new PaymentAmount(100)
    const PaymentAmount500 = new PaymentAmount(500)
    const PaymentAmount1000 = new PaymentAmount(1000)
    const PaymentAmount10000 = new PaymentAmount(10000)

    test("declare amount properly", () => {
    expect(PaymentAmount100.amount).toBe(100)
    expect(PaymentAmount500.amount).toBe(500)
    expect(PaymentAmount1000.amount).toBe(1000)
    expect(PaymentAmount10000.amount).toBe(10000)
    })

    test("get all fees feesToStripe", () => {
    expect(PaymentAmount100.feesToStripe).toBe(13.45)
    expect(PaymentAmount500.feesToStripe).toBe(25.55)
    expect(PaymentAmount1000.feesToStripe).toBe(40.68)
    expect(PaymentAmount10000.feesToStripe).toBe(312.93)
    })

    test("get all fees feesToStas", () => {
    expect(PaymentAmount100.feesToStas).toBe(6.25)
    expect(PaymentAmount500.feesToStas).toBe(6.25)
    expect(PaymentAmount1000.feesToStas).toBe(6.25)
    expect(PaymentAmount10000.feesToStas).toBe(6.25)
    })

    test("get total amount + all fees", () => {
    expect(PaymentAmount100.total).toBe(119.7)
    expect(PaymentAmount500.total).toBe(531.8)
    expect(PaymentAmount1000.total).toBe(1046.93)
    expect(PaymentAmount10000.total).toBe(10319.18)
    })

    test("get total of % fees", () => {
    expect(PaymentAmount100.feesTotalPercent).toBe(19.7)
    expect(PaymentAmount500.feesTotalPercent).toBe(6.36)
    expect(PaymentAmount1000.feesTotalPercent).toBe(4.69)
    expect(PaymentAmount10000.feesTotalPercent).toBe(3.19)
    })
    })
  2. turbotobias revised this gist Apr 7, 2022. 1 changed file with 28 additions and 0 deletions.
    28 changes: 28 additions & 0 deletions round.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    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
    }
  3. turbotobias revised this gist Apr 7, 2022. 1 changed file with 42 additions and 15 deletions.
    57 changes: 42 additions & 15 deletions payment-amount.ts
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,12 @@
    import { Payment } from "@stas/models"
    import { round } from ".."

    /**
    * @example
    *
    * ```ts
    * const fees = new PaymentAmount(1000)
    * fees.amount // 100
    * fees.total // 119.7
    * fees.feesTotalPercent // 19.7
    * fees.getAmountToUser() // === 976.66
    * fees.getAmountToStripe() // === 21
    * ```
    */
    export class PaymentAmount {
    @@ -20,17 +18,17 @@ export class PaymentAmount {
    /**
    * the amount used as the basis for fee calculations
    */
    private _amountMin: number = 1
    public static _amountMin: number = 1

    /**
    * the amount used as the basis for fee calculations
    */
    private _amountMax: number = 50000
    public static _amountMax: number = 50000

    /**
    * currency
    */
    private _currency: string = "nok"
    public static _currency: string = "nok"

    /**
    * how many we assume will donate to one wishlist on average, which dictates the fee amount
    @@ -70,10 +68,38 @@ export class PaymentAmount {
    * @param amount the amount that fees are calculated from
    */
    constructor(amount: number) {
    if (amount < this._amountMin || amount > this._amountMax) throw Error("payment amount out of bounds")
    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
    @@ -104,11 +130,11 @@ export class PaymentAmount {
    }

    public get amountMin() {
    return this._amountMin
    return PaymentAmount._amountMin
    }

    public get amountMax() {
    return this._amountMax
    return PaymentAmount._amountMax
    }

    /**
    @@ -138,14 +164,15 @@ export class PaymentAmount {
    public get feesTotalPercent() {
    const percent = (this.feesTotal / this._amount) * 100

    return round(percent, 2)
    return round(percent, this.decimals)
    }

    /**
    * the total amount + all fees
    */
    public get total() {
    return round(this._amount + this.feesTotal, this._decimals)
    const total = Number(this._amount + this.feesTotal)
    return round(total, this.decimals)
    }

    /**
    @@ -182,7 +209,7 @@ export class PaymentAmount {
    * @see https://stripe.com/docs/currencies?presentment-currency=NO
    */
    public get currency() {
    return this._currency.toLocaleLowerCase("nb-no")
    return PaymentAmount._currency.toLocaleLowerCase("nb-no")
    }

    /**
    @@ -207,9 +234,9 @@ export class PaymentAmount {
    }

    /**
    * returns a double representation of an integer amount (the opposit of PaymentAmount.toZeroDecimal)
    *
    * 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)
  4. turbotobias revised this gist Mar 17, 2022. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions payment-amount.ts
    Original file line number Diff line number Diff line change
    @@ -50,7 +50,7 @@ export class PaymentAmount {
    *
    * @example ./decimal-precision.test.ts
    */
    private _dp: number = 2
    private _decimals: number = 2

    /**
    * stripe fees
    @@ -100,7 +100,7 @@ export class PaymentAmount {
    * @example ./decimal-precision.test.ts
    */
    public set decimalPlaces(decimalPlaces: number) {
    this._dp = decimalPlaces
    this._decimals = decimalPlaces
    }

    public get amountMin() {
    @@ -115,14 +115,14 @@ export class PaymentAmount {
    * all fees to stripe
    */
    public get feesToStripe() {
    return round(this.stripeFeePrPayment + this.stripeFeePrWishlistReduced, this._dp)
    return round(this.stripeFeePrPayment + this.stripeFeePrWishlistReduced, this._decimals)
    }

    /**
    * all fees to stas
    */
    public get feesToStas() {
    return round(this.stasFeePrPayment, this._dp)
    return round(this.stasFeePrPayment, this._decimals)
    }

    /**
    @@ -145,7 +145,7 @@ export class PaymentAmount {
    * the total amount + all fees
    */
    public get total() {
    return round(this._amount + this.feesTotal, this._dp)
    return round(this._amount + this.feesTotal, this._decimals)
    }

    /**
  5. turbotobias revised this gist Mar 17, 2022. 2 changed files with 46 additions and 2 deletions.
    43 changes: 43 additions & 0 deletions payment-amount.test.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    import { PaymentAmount } from "./"

    describe("PaymentAmount", () => {
    const PaymentAmount100 = new PaymentAmount(100)
    const PaymentAmount500 = new PaymentAmount(500)
    const PaymentAmount1000 = new PaymentAmount(1000)
    const PaymentAmount10000 = new PaymentAmount(10000)

    test("declare amount properly", () => {
    expect(PaymentAmount100.amount).toBe(100)
    expect(PaymentAmount500.amount).toBe(500)
    expect(PaymentAmount1000.amount).toBe(1000)
    expect(PaymentAmount10000.amount).toBe(10000)
    })

    test("get all fees feesToStripe", () => {
    expect(PaymentAmount100.feesToStripe).toBe(13.45)
    expect(PaymentAmount500.feesToStripe).toBe(25.55)
    expect(PaymentAmount1000.feesToStripe).toBe(40.68)
    expect(PaymentAmount10000.feesToStripe).toBe(312.93)
    })

    test("get all fees feesToStas", () => {
    expect(PaymentAmount100.feesToStas).toBe(6.25)
    expect(PaymentAmount500.feesToStas).toBe(6.25)
    expect(PaymentAmount1000.feesToStas).toBe(6.25)
    expect(PaymentAmount10000.feesToStas).toBe(6.25)
    })

    test("get total amount + all fees", () => {
    expect(PaymentAmount100.total).toBe(119.7)
    expect(PaymentAmount500.total).toBe(531.8)
    expect(PaymentAmount1000.total).toBe(1046.93)
    expect(PaymentAmount10000.total).toBe(10319.18)
    })

    test("get total of % fees", () => {
    expect(PaymentAmount100.feesTotalPercent).toBe(19.7)
    expect(PaymentAmount500.feesTotalPercent).toBe(6.36)
    expect(PaymentAmount1000.feesTotalPercent).toBe(4.69)
    expect(PaymentAmount10000.feesTotalPercent).toBe(3.19)
    })
    })
    5 changes: 3 additions & 2 deletions PaymentFee.ts → payment-amount.ts
    Original file line number Diff line number Diff line change
    @@ -6,8 +6,9 @@ import { round } from ".."
    *
    * ```ts
    * const fees = new PaymentAmount(1000)
    * fees.getAmountToUser() // === 976.66
    * fees.getAmountToStripe() // === 21
    * fees.amount // 100
    * fees.total // 119.7
    * fees.feesTotalPercent // 19.7
    * ```
    */
    export class PaymentAmount {
  6. turbotobias revised this gist Mar 17, 2022. 1 changed file with 30 additions and 16 deletions.
    46 changes: 30 additions & 16 deletions PaymentFee.ts
    Original file line number Diff line number Diff line change
    @@ -1,28 +1,28 @@
    import { Payment } from "@stas/models"
    import { round } from "."
    import { round } from ".."

    /**
    * @example
    *
    * ```ts
    * const fees = new PaymentFee(1000)
    * const fees = new PaymentAmount(1000)
    * fees.getAmountToUser() // === 976.66
    * fees.getAmountToStripe() // === 21
    * ```
    */
    export class PaymentFee {
    export class PaymentAmount {
    /**
    * the amount used as the basis for fee calculations
    */
    private _amount: number

    /**
    * the minimum amount allowed
    * the amount used as the basis for fee calculations
    */
    private _amountMin: number = 50
    private _amountMin: number = 1

    /**
    * the maximum amount allowed
    * the amount used as the basis for fee calculations
    */
    private _amountMax: number = 50000

    @@ -34,10 +34,16 @@ export class PaymentFee {
    /**
    * how many we assume will donate to one wishlist on average, which dictates the fee amount
    */
    private _estimatedDonorsOnAvg: number = 3
    private _estimatedDonorsOnAvg: number = 2

    /**
    * decimal places: the number of decimals included when getting values, rounded up
    *
    * @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
    *
    @@ -48,8 +54,8 @@ export class PaymentFee {
    /**
    * stripe fees
    */
    private _stripePaymentFeePercent: number = 2.9
    private _stripePaymentFeeNOK: number = 0.3
    private _stripePaymentAmountPercent: number = 2.9
    private _stripePaymentAmountNOK: number = 0.3
    private _stripeConnectFeePercent: number = 0.25
    private _stripeConnectFeeNOK: number = 0.25
    private _stripeConnectFeeMonthlyNOK: number = 20
    @@ -68,8 +74,8 @@ export class PaymentFee {
    }

    private get stripeFeePrPayment() {
    const percent = this._amount * (this._stripePaymentFeePercent / 100)
    return percent + this._stripePaymentFeeNOK
    const percent = this._amount * (this._stripePaymentAmountPercent / 100)
    return percent + this._stripePaymentAmountNOK
    }

    private get stripeFeePrWishlist() {
    @@ -96,6 +102,14 @@ export class PaymentFee {
    this._dp = decimalPlaces
    }

    public get amountMin() {
    return this._amountMin
    }

    public get amountMax() {
    return this._amountMax
    }

    /**
    * all fees to stripe
    */
    @@ -137,14 +151,14 @@ export class PaymentFee {
    * the total amount + all fees, formatted for Stripe (zero decimal currency support)
    */
    public get totalToStripe() {
    return PaymentFee.toZeroDecimal(this._amount + this.feesTotal)
    return PaymentAmount.toZeroDecimal(this.total)
    }

    /**
    * the total amount + all fees, formatted from Stripe (with double decimal currency support)
    */
    public totalFromStripe(amount: number) {
    return PaymentFee.toDecimal(amount)
    return PaymentAmount.toDecimal(amount)
    }

    /**
    @@ -158,7 +172,7 @@ export class PaymentFee {
    * the amount that all fees are calculated from
    */
    public get amountInSmallestCurrencyUnit() {
    return PaymentFee.toZeroDecimal(this._amount)
    return PaymentAmount.toZeroDecimal(this._amount)
    }

    /**
    @@ -192,7 +206,7 @@ export class PaymentFee {
    }

    /**
    * returns a double representation of an integer amount (the opposit of PaymentFee.toZeroDecimal)
    * returns a double representation of an integer amount (the opposit of PaymentAmount.toZeroDecimal)
    *
    * converts `5150` to `51.50`
    */
  7. turbotobias revised this gist Mar 7, 2022. 1 changed file with 3 additions and 9 deletions.
    12 changes: 3 additions & 9 deletions PaymentFee.ts
    Original file line number Diff line number Diff line change
    @@ -17,12 +17,12 @@ export class PaymentFee {
    private _amount: number

    /**
    * the amount used as the basis for fee calculations
    * the minimum amount allowed
    */
    private _amountMin: number = 50

    /**
    * the amount used as the basis for fee calculations
    * the maximum amount allowed
    */
    private _amountMax: number = 50000

    @@ -37,13 +37,7 @@ export class PaymentFee {
    private _estimatedDonorsOnAvg: number = 3

    /**
    *
    * @todo create separate transfers and fees
    *
    */

    /**
    * decimal places: the number of decimals included when getting values, rounded to its nearest or up when 5
    * decimal places: the number of decimals included when getting values, rounded up
    *
    * only used on exposed getters, not in calculations
    *
  8. turbotobias created this gist Mar 7, 2022.
    208 changes: 208 additions & 0 deletions PaymentFee.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,208 @@
    import { Payment } from "@stas/models"
    import { round } from "."

    /**
    * @example
    *
    * ```ts
    * const fees = new PaymentFee(1000)
    * fees.getAmountToUser() // === 976.66
    * fees.getAmountToStripe() // === 21
    * ```
    */
    export class PaymentFee {
    /**
    * the amount used as the basis for fee calculations
    */
    private _amount: number

    /**
    * the amount used as the basis for fee calculations
    */
    private _amountMin: number = 50

    /**
    * the amount used as the basis for fee calculations
    */
    private _amountMax: number = 50000

    /**
    * currency
    */
    private _currency: string = "nok"

    /**
    * how many we assume will donate to one wishlist on average, which dictates the fee amount
    */
    private _estimatedDonorsOnAvg: number = 3

    /**
    *
    * @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 _dp: number = 2

    /**
    * stripe fees
    */
    private _stripePaymentFeePercent: number = 2.9
    private _stripePaymentFeeNOK: 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 < this._amountMin || amount > this._amountMax) throw Error("payment amount out of bounds")
    this._amount = amount
    }

    private get stripeFeePrPayment() {
    const percent = this._amount * (this._stripePaymentFeePercent / 100)
    return percent + this._stripePaymentFeeNOK
    }

    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._dp = decimalPlaces
    }

    /**
    * all fees to stripe
    */
    public get feesToStripe() {
    return round(this.stripeFeePrPayment + this.stripeFeePrWishlistReduced, this._dp)
    }

    /**
    * all fees to stas
    */
    public get feesToStas() {
    return round(this.stasFeePrPayment, this._dp)
    }

    /**
    * 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, 2)
    }

    /**
    * the total amount + all fees
    */
    public get total() {
    return round(this._amount + this.feesTotal, this._dp)
    }

    /**
    * the total amount + all fees, formatted for Stripe (zero decimal currency support)
    */
    public get totalToStripe() {
    return PaymentFee.toZeroDecimal(this._amount + this.feesTotal)
    }

    /**
    * the total amount + all fees, formatted from Stripe (with double decimal currency support)
    */
    public totalFromStripe(amount: number) {
    return PaymentFee.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 PaymentFee.toZeroDecimal(this._amount)
    }

    /**
    * which currency this fee is in, probably "nok"
    *
    * @see https://stripe.com/docs/currencies?presentment-currency=NO
    */
    public get currency() {
    return this._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)
    }

    /**
    * returns a double representation of an integer amount (the opposit of PaymentFee.toZeroDecimal)
    *
    * converts `5150` to `51.50`
    */
    public static toDecimal(amount: number) {
    return round(amount / 100, 2)
    }
    }