Wix Checking & Savings Folders With Automatic Deposits

I’m having trouble with
I am a small business owner (www.mabeeink.com) and I have used many different platforms for website and POS systems over the last 20 years. I came to Wix because it has almost 99% of the features I need to make my business turn key, but it lacks one feature that is critical and could be easily achieved.

That feature is checking account folders. I love the business account and transactions, and I am so stoked with the working capital features, without them I don’t don’t know where my business would be, BUT as it stands it is incomplete without savings folders and automatic percentage based deposits from sales.

This may seem like a small feature to fuss about but without it I have to have 5 other bank accounts and pay five different transfer fees to make sure my money gets split into, investment, savings, overhead, payroll and personal accounts etc. Wix would be 100% complete and dominate the sphere if they enabled this feature and put an Ai on it to help manage it so I don’t have to pay an assistant accountant to do it everyday/week/month.

But without it I have considered moving my systems to square who has this feature. I really don’t want to (that’s not a threat just a reality) please please make the feature! I love Wix and I have trained many people on Wix and I willingly promote Wix to all my entrepreneurial friends.

The images are just for reference not real (not exact either) just proposed UI

I love this @Tony_Mabee! And thanks for taking the time to share mockups :star_struck: Sending this straight to the team!

Wix won’t do the bank half of this from your site. There’s no API for opening folders inside the business account or moving money between them, so no amount of code gets you five sub-balances.

The ledger half you can have now, and it is the half that costs you the five transfer fees. If something inside your site tracks what each share of the balance is for, four of those five envelopes never have to leave the account. The money stays in one place and you make one real transfer, for whatever has to sit somewhere else.

Two collections do it. Envelopes holds a name, a percent and a running balance. Allocations holds one row per payment, which is what lets you audit it later and reverse a refund properly.

The first thing that will trip you up is that wixStores_onOrderPaid is deprecated and the docs now point at onOrderPaymentStatusUpdated() in wix-ecom-backend. The handler goes in backend/events.js:

import wixData from 'wix-data';

const ENVELOPES = ['Investment', 'Savings', 'Overhead', 'Payroll', 'Personal'];
const PERCENTS  = [12, 13, 29, 36, 10];

export async function wixEcom_onOrderPaymentStatusUpdated(event) {
    const order = event.data.order;
    if (order.paymentStatus !== 'PAID') return;
    if (event.data.previousPaymentStatus === 'PAID') return;

    // priceSummary amounts are strings ("40.00", "10.0", "0"), and the sales
    // tax in there is not your money, so it should not be split.
    const cents = (s) => Math.round(parseFloat(s || '0') * 100);
    const net = cents(order.priceSummary.totalPrice.amount)
              - cents(order.priceSummary.tax.amount);

    const parts = split(net, PERCENTS);

    await wixData.insert('Allocations', {
        orderId: order._id,
        eventId: event.metadata.id,
        paidAt: order._updatedDate,
        netCents: net,
        percents: PERCENTS,
        parts
    }, { suppressAuth: true });

    for (let i = 0; i < ENVELOPES.length; i++) {
        const { items } = await wixData.query('Envelopes')
            .eq('name', ENVELOPES[i]).find({ suppressAuth: true });
        const env = items[0];
        env.balanceCents = (env.balanceCents || 0) + parts[i];
        await wixData.update('Envelopes', env, { suppressAuth: true });
    }
}

The split itself is worth doing carefully:

function split(cents, percents) {
    const total = percents.reduce((a, b) => a + b, 0);
    const exact = percents.map((p) => (cents * p) / total);
    const parts = exact.map(Math.floor);
    const short = cents - parts.reduce((a, b) => a + b, 0);

    exact.map((v, i) => ({ i, frac: v - Math.floor(v) }))
         .sort((a, b) => (b.frac - a.frac) || (a.i - b.i))
         .slice(0, short)
         .forEach((x) => parts[x.i]++);

    return parts;
}

Rounding each percentage on its own is what breaks this. Five independently rounded shares don’t add up to the payment that came in, and the gap accumulates. Handing the leftover cents to the largest fractional shares means the five parts sum to the payment exactly, every time, with no line absorbing the difference.

Three more things will catch you out. Refunds and chargebacks have to come back out of the envelopes they went into, at the percentages that applied when that money arrived, so if you retune the split in July and refund a May session in August, clawing back at July’s percentages puts the money in the wrong envelopes and the total still reconciles. Backend events don’t fire in Preview, so the site has to be published before you can test any of it. And if you want a monthly statement out of the Allocations rows, a scheduled job in backend/jobs.config will write one, but the times in that file are UTC, not your local time.

If your money mostly arrives through Bookings rather than Stores, the event is a different one, but everything after it is the same. Log the event object once and read the property names off what actually arrives before you build against them.

I haven’t run this against a live business account, so treat it as a starting point rather than something already proven.