A B2B repeat-purchase case study · KiranaClub
Vivek Singh60% of Kirana retailers reorder the same 20–30 products every time, yet spend 15–20 minutes browsing the catalogue on every order.
I designed a known-basket reordering flow that brought this down to 5–7 minutes in testing, while keeping the price-verification step retailers already trust.
Timings observed across 3 moderated prototype sessions. Directional, not production data.
A solo assignment, shown honestly. There was no cross-functional triad to claim.
Setting some context, before we begin…
Context · The current state
A B2B ordering platform that lets Kirana (neighbourhood retail) store owners buy wholesale stock, including groceries, snacks and household essentials, directly through a mobile app. It replaces manual distributor calls and shop visits.
Assignment-provided figures, treated as the starting hypothesis for this case study rather than independently verified metrics.
(from both business & user point of view)
same 5 steps, every single order… the first three are where the time goes15–20 minutes of repetitive browsing per order, for products the retailer already knows they want. This reads as friction, not choice, and it costs more on low-end devices with slower load times.
The browse-and-search phase (Steps 1 to 3) is the most likely point of drop-off, the place where time, effort and decision fatigue peak. For a platform whose stated goal is reducing retailer friction, this is where friction is currently highest.
Reasoned inference, not confirmed drop-off data.
1
2
3
Search bar, used fresh on every visit
Two full navigation blocks before any product appears
Product rails begin, still with no path for a retailer who already knows their list
Captured directly from the live KiranaClub app. Shown to illustrate the current browse-first flow, not as a validated usability finding.
Discovery · Research
Primary research budget was limited, so this stage combined two sources: the current-state data and screens provided by KiranaClub, and a self-built reorder prototype tested in person with 7 retailers, to check whether a simple "repeat last order" shortcut would actually work.
Findings below are directional, based on a small in-person test. They are not a statistically validated study.
I tested a simple one-tap "repeat last order" prototype in person with 7 retailers. The obvious fix didn't hold up.
Several retailers kept their own informal diary, a notebook where they'd jot down rough costs from previous orders. During the test, they pulled this out to manually cross-check whether prices had changed before trusting a "repeat" action. A one-tap reorder skipped a verification step they clearly weren't willing to give up.
The second concern was quantity, not price. Retailers didn't always want the exact same amount of every item repeated. Some wanted more of a fast-moving product that week, less of something slow-moving, and didn't trust the app to know that.
This reframes the design problem. It isn't "automate the repeat". It is "make reviewing and adjusting a repeat basket fast." A trustworthy shortcut, not a blind one.
The rejected approach. Fast, but it removed the check.
Before jumping into flows, I asked KiranaClub a basic but often-skipped question: who is actually using this, and what's their day-to-day context? The answer reframed the whole problem. This isn't a general e-commerce audience. Retailers using the app skew toward low digital literacy and low-end Android devices on limited mobile data.
That single question changed my design constraints from day one: no assumption of app fluency, no data-heavy interactions, high-contrast UI, large touch targets. A fast reorder flow that a low-literacy user on a slow connection can't actually use isn't a solution.
Sourced directly from KiranaClub on request. Not part of the original assignment brief.
No formal personas on this page. With 7 informal sessions there wasn't enough signal to build them honestly.
Problem definition
The research surfaced two things that don't get along on the surface: retailers want speed, because they're repeating the same basket, but they don't trust speed enough to skip verification, meaning checking price and adjusting quantity. A good solution has to earn trust while saving time, rather than trade one for the other.
How might we help retailers rebuild their known basket in seconds, while still giving them fast, trustworthy control over price changes and quantity adjustments?
"How might we make reordering faster?"
"How might we make reordering faster without removing the verification step retailers already trust?"
| Feature | Udaan | Jumbotail | ShopKirana | DealShare |
|---|---|---|---|---|
| One-tap repeat order | ✓ explicitly advertised | ✕ not shown | ✕ | ✕ |
| Editable quantity before repeat | not publicly confirmed | · | · | · |
| Price-change alert on repeat | not publicly confirmed | · | · | · |
| Simplified / multilingual UI | ✓ | ✓ strong focus | partial | ✓ |
| Built for low-end device / data | not confirmed | ✓ data-pack support | not confirmed | not confirmed |
Even where "repeat order" exists (Udaan), none of the platforms researched appear to solve the specific trust gap this research surfaced: a visible way to catch price changes and adjust quantity without re-browsing the full catalogue.
Based on publicly available app store listings and product marketing, not hands-on testing of competitor apps. Feature availability may differ in-app. Amazon "Buy Again" was considered as a consumer benchmark and deliberately dropped, keeping the comparison set B2B and India-specific.
Strategy · Scope
| # | Opportunity | Impact | Effort | Phase |
|---|---|---|---|---|
| 1 | A known-basket entry point: a route into reordering that doesn't start with browsing | High | Low | Phase 1 |
| 2 | Price-change visibility: surfacing what changed since last order | High | Med | Phase 1 |
| 3 | Fast quantity adjustment: editing amounts without re-entering the catalogue | High | Med | Phase 1 |
| 4 | Discovery that stays additive: deals and new margin items offered after the basket is built | Med | Med | Phase 2 |
| 5 | Seasonal and festival basket shifts: recognising that Diwali and Holi baskets differ structurally | High | High | Phase 3 |
Entry point, price-change visibility and quantity control. This is the minimum that makes reordering fast without removing the verification retailers already rely on. These aren't three features. They're one feature that needs three parts, and shipping the entry point alone would rebuild the exact prototype retailers rejected.
KiranaClub's business depends on retailers finding new and higher-margin stock. Discovery only becomes solvable after the basket is fast, because that's what frees up retailer attention. Doing it first would solve the business's problem before the user's.
Festival demand restructures the basket entirely. High value, but it needs order history across multiple seasons to be trustworthy, so it follows rather than leads.
Low digital literacy, low-end Android devices and limited mobile data aren't a Phase 3 concern to be handled later. They're a filter every phase has to pass through. A solution that only works on a fast connection isn't a Phase 1 solution that needs polishing. It's the wrong solution.
Effort estimates are my own inference. With engineering input from the KiranaClub team this prioritisation would be sharper, particularly around what basket data can be cached locally versus fetched per session. This page phases problems, not features: at this point in the narrative no solution has been chosen yet.
Ideation · Explorations
Phase 1 needs an entry point into the known basket that doesn't begin with browsing. But where that entry point sits changes what it means. A shortcut buried in a menu is a feature; a shortcut on the landing screen is a statement about who the app is for.
Bottom navigation gains a new tab beside Feed, Order, Category and Deals.
The known basket occupies a block on the landing screen, above the main category navigation.
A bottom sheet appears on entering Order or Bazaar: "Load your usual basket?" with a dismiss option.
Chosen: Option B, the reorder surface on the Bazaar landing screen.
Option A treats reordering as a destination the retailer must choose to visit. Option C treats it as an interruption. Only Option B treats it as the default mission, which is what the research showed it to be for 60% of orders.
The commercial cost is real. But Page 5's phasing answers it: discovery moves below the basket, not out of the app. A retailer who finishes procurement in 5 minutes instead of 20 has more attention left for deals, not less. That's the bet, and it's testable.
This choice is reasoned, not yet validated. The trade-off between landing-screen space and procurement speed is exactly the kind of decision that should be A/B tested rather than argued.
The block sits above टॉप कैटेगरीज़ but below the category icon row. The icon row is thin and scannable and costs little to leave in place. The full-height grid is what actually blocks a repeat mission.
1
2
3
Heading and item-count badge
50/50 split: three thumbnails from the last order against the price comparison
Full-width CTA
In place on the landing screen, sitting between the category icon row and टॉप कैटेगरीज़.
Arrow direction, colour and wording must always agree. An early version had a down arrow sitting on a red "costs more" tag. It was caught in review, and it's the kind of contradiction that quietly destroys trust in a number.
Rejected: बास्केट is an English loanword. It doesn't appear in the spoken Hindi of a low-literacy shopkeeper, and a word that has to be decoded is a word that costs time.
Final: ऑर्डर देखें reuses vocabulary the app already teaches: मेरे ऑर्डर, ऑर्डर details. It also still promises review, not commitment.
Choosing the entry point doesn't answer what happens inside it. These are interaction problems, not placement problems.
Interaction design · The core screen
Page 6 decided where the shortcut lives. This is what sits behind it: the screen the whole solution succeeds or fails on. The retailer arrives here with a known list in their head and, often, a diary in their hand. This screen has to be faster than the diary, and at least as trustworthy.
The basket review is built on the existing category-listing pattern retailers already use daily: left-hand group rail, product cards in the centre, ख़रीदें / बेचें / मार्जिन on every card, stepper controls for quantity, running total pinned to the bottom.
Nothing about how to operate this screen is new. Only what it contains is new: the retailer's own last basket instead of a catalogue.
For a low-digital-literacy user, familiarity outperforms clarity. A pattern parsed correctly by habit is worth more than a better pattern that has to be learned.
The left group rail carries over unchanged, grouping the retailer's own basket by category. Useful for a 24-item list, and again, no new interaction to learn.
Same skeleton, different contents.
Interaction design · Verification
1
2
3
4
| # | Element | Purpose |
|---|---|---|
| 1 | Product image, name, pack size (1 पैक = 40 यूनिट) | Unchanged from the existing pattern |
| 2 | Current ख़रीदें / बेचें / मार्जिन | What they'd pay today |
| 3 | Last order's ख़रीदें / बेचें / मार्जिन, directly beneath, muted | The diary, built into the card |
| 4 | Stepper pre-filled with last order's quantity | Speed by default, adjustment by tap |
Chosen: pre-fill quantities, with last order's price shown on every card.
Speed comes from the pre-fill. Trust comes from the card never hiding the previous number. The retailer doesn't have to rely on the system having flagged the right items. The comparison they were making manually is present on every line, whether or not anything changed.
The diary wasn't a workaround for a missing feature. It was the retailer's verification ritual. This card doesn't replace it. It performs it in the same place they'd have looked.
The category screen already has a horizontal filter row, and retailers already use it. Rather than introducing a new alert pattern, the same control is repurposed, filtering the basket by what changed rather than by brand.
A retailer in a hurry taps दाम बढ़े, reviews only the items that got more expensive, and moves on. A retailer who wants to check everything scrolls the full list, as they would today. The design doesn't decide which kind of retailer they are on a given day.
Engineering constraints
Low-end Android devices, limited mobile data, unreliable connectivity in semi-urban areas.
The retailer's own last order, a bounded payload of 20 to 30 items, rather than a catalogue query. Item names, images, pack sizes and last-order prices can persist locally. Only current prices need fetching.
Because every card shows current price above last price, the screen is only trustworthy once current prices have synced. If cached data renders instantly and live prices arrive a moment later, the retailer may begin reviewing against numbers that then change underneath them. That is a worse trust failure than a slow load.
The basket renders immediately from cache with current-price fields in a loading state, and the आगे बढ़ें action stays disabled until sync completes. The retailer sees their basket instantly; they just can't commit against stale numbers.

Basket from cache, current-price fields as skeletons, CTA disabled.

Current prices populated, changes visible, CTA enabled.

This is the real screen shown app-wide when offline. It appears before the retailer can reach any flow, including this one.
The app already gates all functionality behind this global offline screen at launch. The offline-basket behaviour originally proposed here, meaning read-only access with the order held locally, assumed the retailer could still open the basket while offline. In reality, they can't get past this screen to try.
This wasn't caught during initial design. It surfaced when checking the concept against the real app. Resolving it needs a product decision: either carve out an exception to this global gate for a cached reorder basket specifically, or accept that offline access to this flow isn't currently possible and drop it from Phase 1's requirements.
No connection means no current prices, which means no trustworthy reorder. Rather than fail, the basket would load read-only with prices marked as last-known, holding the order locally until connectivity returns. A retailer could prepare an order in a dead zone, just not commit one.
Stated as a proposal, not shipped behaviour. It is contingent on an app-wide architecture change to the existing offline gate described above, and that change has not been confirmed as feasible.
Two things would have made this section specific rather than principled, and I wasn't able to confirm either:
Is order history cached client-side, or fetched per session? This determines whether instant basket load is real or aspirational. I've designed assuming a bounded order-history payload can persist locally.
How often do wholesale prices actually change on the platform? If daily, change-visibility is the core feature. If monthly, it's closer to an edge case, and Phase 1 would be weighted differently.
Both decisions here are designed against reasonable assumptions, not a confirmed architecture. With access to KiranaClub's engineering team this section would be scoped rather than inferred, and I'd expect at least one of these choices to change.
Validation · Testing & iteration
I tested a clickable prototype of the basket review flow in person with 3 KiranaClub retailers. Each was given one instruction, "open the app and order your usual stock", with no explanation of the new screen.
3 participants, moderated in person. Small enough that findings are directional, not conclusive. Timings were observed, not instrumented.
All 3 retailers found the पिछला ऑर्डर block without prompting and understood what it would do before tapping it. One began adding items through the usual category flow out of habit, then noticed the block 2–3 seconds in and switched to it.
Habit briefly overrode the new path, but the block was prominent enough to interrupt it. This supports the landing-screen placement. A shortcut in a tab or a dismissible sheet would likely have lost that user.

All 3 retailers immediately understood that quantities were carried over from their last order and that the stepper let them adjust. None of them actually adjusted a quantity during the session.
This is ambiguous, and I'm treating it as unresolved. It may mean the test baskets genuinely needed no adjustment, or it may mean pre-filling encourages passive acceptance, the exact risk flagged during design. A short moderated session can't distinguish between the two. Resolving it needs either a test where the correct answer requires adjustment, or live data on how often quantities get edited.

The control everyone understood, and nobody touched.
Retailers did notice that current and previous prices were both shown. But the change filter chips (दाम बढ़े / दाम घटे) went unfound initially, and retailers asked for a stronger visual signal.
I replaced the plain chips with a solid colour treatment: red for price increased, green for decreased. After the change, retailers could spot changed items, though some still needed a small nudge before recognising what the colours meant.
Colour alone isn't sufficient for this user group. A next iteration should pair colour with an explicit label or icon rather than relying on it, and colour-only signalling is an accessibility problem regardless.
Retailers expected to verify prices in two places, not one. Per-item on the review screen, and again in aggregate at the cart: has my total changed since last time?
Their diaries tracked both: rough per-item costs, and what the order came to. The design solved only the first.
Add a comparison view at the cart, showing item name, quantity, current price and previous price in tabular form, with the previous order total displayed alongside the current one.
| Item | Qty | Now | Last |
|---|---|---|---|
| Cintu Nutty Star | 3 | ₹150 | ₹240 |
| Cintu Orange Fills | 4 | ₹152 | ₹120 |
| Dark Magic Cake | 3 | ₹150 | ₹240 |
All 3 said they'd keep their notebook. When asked why, the reason wasn't distrust of the prices shown. It was that a phone can be lost and app data can disappear, so a physical record has to exist.
The diary isn't a workaround for a missing feature. It's a backup against device and data loss, which no interface change can address. The design doesn't need to replace the notebook. It needs to be faster than consulting it. Measured against that goal, it succeeded.
to complete a full reorder, against a 15–20 minute baseline
Observed across 3 sessions, timed manually. Directional, not statistically meaningful.
Beyond the happy path
Everything so far assumes a retailer with order history, a working connection, and items that still exist. In practice none of those hold reliably. The users here are on low-end devices in semi-urban areas, ordering from a catalogue that changes underneath them.

Full basket, prices synced, CTA enabled. The happy path.

Basket from cache, price fields as skeletons, CTA disabled. Instant render, no commitment against stale prices.

The app's global offline screen, the same shot as 6c. See Page 7C for how this was discovered.

Not a new state, and the same shot as 2a. If there's no order history, the entry condition for this feature isn't met, and the retailer sees the current app unchanged.

Some items available, some not. The most common real state; it must not block the order.
Treated identically to a temporarily unavailable item, because the retailer's need is the same either way: see that the line is gone, and get a workable alternative without leaving the basket. It uses the same screen design as the Partial basket state above (8e), where the line is marked उपलब्ध नहीं, the last-order prices stay visible, and a substitute is offered inline with its own margin shown. The retailer decides; the system suggests.
A price that has moved sharply since the last order shouldn't just be flagged in colour. It warrants an explicit confirmation step before the order proceeds. A retailer protecting thin margins should never discover a significant increase after committing.
Page 8's testing showed the honest limit of inline signalling: retailers missed the change chips entirely on first pass, and even after the colour iteration some needed a nudge to read them. Scanning a list of 24 cards, a tag is something you might notice. A large price movement is too expensive to leave to chance-scrolling, so instead of competing for attention inside the list, it stops the flow and asks a direct question. The cost is an interruption; the alternative is a retailer discovering the increase after the money is committed.
The modal fires for price increases and not decreases. A large drop is welcome news; it carries no cash-flow risk and doesn't need a forced decision. It stays a green in-list indicator, where it belongs.
Trigger threshold remains an open question, listed below. I don't have the price-movement data to set it responsibly.

Fires before the retailer taps आगे बढ़ें, for items whose increase crosses the threshold.
Accessibility here isn't only about assistive technology. It's about a shopkeeper checking prices one-handed, in daylight, on a low-end screen, while serving a customer.
Testing surfaced that even after the colour indicators were added, retailers needed a small nudge to understand what the colours meant. That's not a colour-contrast failure. It's a learnability failure, and it argues for explicit labelling over relying on convention.
What price-change threshold should trigger explicit confirmation? Needs real distribution data on how much prices actually move.
How should substitutes be ranked? By margin, by category proximity, or by what other retailers in the same pincode bought? Each has different business implications, and I don't have the data to choose.
Screen-reader behaviour was not tested. Given the user group, voice and contrast were the higher-priority accessibility concerns, but this is a genuine gap rather than a deliberate exclusion.
Impact
Time to complete a full reorder
Observed across 3 moderated sessions, timed manually.
found the पिछला ऑर्डर entry point without prompting
One reverted to habit for 2–3 seconds before switching.
understood pre-filled quantities and how to adjust them
None adjusted a quantity. Unresolved, see Page 8.
noticed price changes after the colour iteration
Some still needed a nudge to interpret the colours.
3 participants, moderated, manually timed. These are directional findings from prototype testing, not production data, and not statistically meaningful. The sample is large enough to catch obvious usability failures and too small to claim an effect size.
The design's core bet is that faster procurement leaves more retailer attention for discovery, not less. If order value and add-on rate hold steady or rise while ordering time falls, the bet was right. If order value drops, the landing-screen trade-off from Page 6 was the wrong call, and that's the number I'd watch first.
KiranaClub's stated goal is reducing friction for retailers. The current flow works against that in its highest-frequency use case: 60% of orders repeat a known basket, yet every order pays the full cost of catalogue navigation.
Cutting that from 15–20 minutes to 5–7 removes roughly two-thirds of the time cost from the most common journey on the platform, while keeping the verification step retailers already trust, and keeping discovery in the app rather than removing it.
Drop-off improvement is the expected business outcome, but I inferred where drop-off occurs (Page 2) rather than measuring it. Confirming it against real funnel data is the first thing I'd do with platform access.
What's next
This design rests on one assumption: that a retailer's last order is a good predictor of their next one. For most of the year that holds, since 60% reorder the same 20–30 products. Around Diwali, Holi and local festivals, it stops holding entirely.
Festival baskets don't scale the regular basket up. They restructure it: different categories, different pack sizes, sharply different quantities, items that appear once a year. During those weeks, "पिछला ऑर्डर" points at the wrong basket, and a shortcut that's wrong at the busiest, highest-stakes time of year is worse than no shortcut, because the retailer has learned to trust it.
This isn't a missing feature. It's a failure mode of the current design, and the honest reason Phase 3 exists.
Surfaced in the original 7-retailer prototype test, where quantity fluctuation around festivals came up unprompted. Not yet quantified. I don't know how many orders fall in these periods or how far they deviate.
Rather than predicting a festival basket, the system recognises when the current period historically deviated and changes what it offers. During those weeks the entry point stops saying "your last order" and starts offering "your last Diwali order", comparing against the same period last year rather than last week.
Why this is the right first step: it needs no prediction and no machine learning. It needs one year of order history and a calendar. The retailer still reviews and adjusts exactly as they do now; only the starting basket changes. That keeps it inside the trust model this design already established.
What it needs: a full seasonal cycle of order history per retailer, and a definition of which periods count, which likely varies by region and community rather than following a national festival calendar.
Today the design answers "what did I order last time?" The more valuable question is "what should I order this time?", and the retailer already has the data to answer it, sitting in their order history: how fast a product is actually moving, which items they consistently over-order, which margins are quietly eroding.
Why it's a step up: this shifts the app from a faster ordering tool to a stock-planning tool. It also serves the behaviour the diary reveals, since retailers are already doing rough analysis on paper. The app is currently faster than the notebook, but not smarter than it.
The risk, stated plainly: this edges toward telling a retailer how to run their business. Testing showed retailers won't accept recommendations they can't verify. The same trust constraint that shaped Page 7 applies here, more sharply. Any suggestion would have to show its reasoning ("you ordered this 4 times and it's still in stock"), not just its conclusion.
Retailers in the same pincode face the same demand shifts, and often learn about them from each other. A signal like "stores near you are stocking this ahead of the festival" could surface timing information no individual order history contains.
Why I'm marking this speculative: it's the most commercially attractive idea here and the least validated. It assumes retailers want to know what nearby stores are doing, but nearby stores are also direct competitors. Whether this reads as useful signal or as exposure is an empirical question I have no data on.
Including this with the same confidence as the others would be dishonest. It's a hypothesis worth testing, not a roadmap item.
Answer the two engineering questions from Page 7. Whether order history is cached and how often prices move determines whether the current design is even correctly scoped. Everything else is downstream of this.
Resolve the quantity-edit question with live data. Page 8's unresolved finding is the biggest open risk in the design. If retailers don't adjust quantities in production, pre-filling is creating exactly the passive acceptance the research warned against, and Phase 1 needs rethinking before Phase 2 begins.
A/B test the landing-screen placement. The commercial trade-off from Page 6 was argued, not proven. It should ship as a test.
Then seasonality. Only once the base behaviour is verified, because a seasonal layer built on an unverified foundation compounds the error.
The obvious solution to this brief was a one-tap reorder button. I built it, tested it with 7 retailers, and it failed. Not because it was slow, but because it removed a verification step retailers had good reason to keep.
The design that worked wasn't faster than that prototype. It was slower and more trusted: it kept the review, and made the review quick. The diary finding was the turning point, because retailers weren't asking for automation. They were asking for their existing check to stop taking twenty minutes.
The strongest single result from testing wasn't the time saved. It was learning that all three retailers would keep their notebooks regardless, because a phone can be lost and app data can disappear. No interface change solves that, and recognising which problems aren't design problems mattered more here than any screen I drew.
Thanks to the KiranaClub team for providing user context and access, and to the 10 retailers who gave their time across both rounds of testing.