What we solved
Selling food supplies online in Saudi Arabia without the usual surplus-to-landfill problem meant building three things into one mobile app: a catalogue and checkout with RTL Arabic, a rewards program that routes excess stock to repeat buyers (300 riyals credit per 1,000 spent), and utility bill pay so the app becomes the wallet, not just the grocery. Add a direct bank-to-bank money transfer to close the loop.
The system at a glance
A Flutter 2.7 app for iOS and Android from one codebase. Arabic RTL renders natively; the same app serves both platforms without a fork. GetX manages state; Dio handles API calls. Stripe processes card, online-banking, and wallet payments. The backend (FastAPI + Postgres assumed) runs the catalogue, orders, rewards ledger, and integrates bill-pay and DMT through third-party APIs.
What the user experiences
- Open the app. Arabic RTL, top-to-bottom.
- Browse the catalogue - groceries and food supplies, photos, prices in Saudi riyals.
- Add to cart; live cart totals update as you go.
- Check out through Stripe - cards, online banking, and wallets on one checkout surface.
- Earn rewards - 300 riyals credit for every 1,000 spent, automatically credited.
- Pay electricity, water, and gas bills inside the app.
- Send money bank-to-bank via DMT without switching to a banking app.
How we built the pieces
RTL - native, not retrofitted
Flutter’s internationalisation handles RTL at the framework level. Lay out once, flip once; text, icons, and gestures mirror correctly. Retrofitting RTL after the fact costs two weeks of layout surprises; shipping it RTL-first costs a day.
The rewards loop - inventory lever, not marketing
Rewards are not a coupon code. They’re a ledger entry tied to every order, credited automatically. The operator can reroute surplus stock into the rewards pool - customers redeeming rewards buy items the operator wants to move, not just anything. Loop closed.
Payments - Stripe
Stripe fronts cards, online banking, and wallets with one integration. The cart POSTs the order, Stripe tokenises and charges, a webhook marks the order paid. The app never touches card details; PCI scope stays with Stripe.
Bill pay + DMT - aggregation, not direct integration
Building 3 utility integrations + the banking network takes quarters. Using a licensed aggregator (or local bill-pay API) takes days. The app sees one API; the aggregator handles the compliance and the 20 downstream connections.
State - GetX, tight codebase
GetX keeps the cart, checkout, and rewards state in one place without the ceremony of a bigger framework. For a commerce app this size, the ratio of code to feature matters.
Flutter 2.7 - deliberately held
Flutter 2.7 is older than the current stable release. The client’s deliberate decision was not to chase Flutter version upgrades - the app is stable, the integrations are stable, and the cost of migrating outweighs the benefit right now. This is a reasonable call on a shipped commerce app; migration will happen when there is a forcing function (a breaking SDK, a new platform requirement), not just for its own sake.
The rewards loop holds it together
Arabic-first commerce apps usually die because the team builds a catalogue, bolts on RTL, bolts on payments, and never ships rewards or bill-pay. REZQ Basket ships all four as one product - the user does groceries, pays the electricity bill, and sends money home from the same app. That is why the rewards loop actually moves stock.
Results
- Shipped to Google Play in 2020; iOS build available.
- Arabic RTL across catalogue, cart, checkout, rewards, and bill-pay.
- Rewards automatically credited on qualifying orders.
- Utility bill pay + DMT integrated in-app.
- Stripe checkout integrated for cards + banking + wallets.
What the client said
“Techy Panther did an excellent job building my e-commerce app, REZQ Basket. They listened to my needs, delivered a beautiful and user-friendly app, and seamlessly integrated payment processing.”
- Client, REZQ Basket
What an engineering team should take from this
If you are shipping commerce in a non-English-primary market:
- RTL from day one. Not day 60.
- Rewards tied to inventory, not just spend. Couples the retention loop to a real business lever.
- Aggregators for bill pay and DMT. You are not going to beat a licensed aggregator on either compliance or integration speed.
- Don’t chase framework upgrades without a forcing function. A stable Flutter 2.7 app that works and ships revenue is worth more than a half-migrated Flutter 3.x app that doesn’t.
Tech stack
- Mobile: Flutter 2.7 (iOS + Android, Arabic RTL)
- State: GetX
- HTTP: Dio
- Backend: FastAPI + Postgres (inferred)
- Payments: Stripe (cards, banking, wallets)
- Bill pay: utility aggregator
- DMT: bank API
Screens
