Six working days is not a record and it is not marketing. It is the timeline you get when the scope is known in advance and the decisions about money are taken before the start. Below is a breakdown of what those six days are made of, using a pre-order system for a chain of coffee shops as the example.
Day zero: the decisions you cannot postpone
Three questions have to be closed before any code. Putting any of them off costs days of waiting.
Who receives the money. If the payment goes straight to the client’s account, you need their acquiring contract and their keys. Connecting YooKassa takes anywhere from one day to a week, and that week passes on the bank’s side. Starting development without having begun that procedure means running into it on day five.
How the receipt is issued. 54-FZ (Russia’s cash-register law) requires fiscalisation, and that is not something you add at the end. Either the payment provider generates the receipt, or the client has a cloud till, or a separate service is needed. The choice affects how the order is structured in the database.
What happens when something fails. Refund, partial refund, cancelled order, double charge. Refund logic is half the code of a payment module, and it is designed together with the main flow, not after it.
Days 1–2: the storefront
A Mini App is a web application inside the messenger. The phrase “bot with buttons” misses it, and two things follow. First: you have a real interface, so a catalogue with modifiers does not turn into twenty messages in a row. Second: it is ordinary frontend, which has to be adapted to a small screen and to the theme the user chose in Telegram.
User data arrives in initData—a signed string that must be verified on the
server. This is the first place bots break: the parameters are taken from the
request as they are, which hands anyone the ability to place an order in someone
else’s name.
import { createHmac } from 'node:crypto'
/** Проверка подписи initData. Без неё любой параметр можно подделать. */
export function verifyInitData(initData: string, botToken: string): boolean {
const params = new URLSearchParams(initData)
const hash = params.get('hash')
if (!hash) return false
params.delete('hash')
const dataCheckString = [...params.entries()]
.map(([key, value]) => `${key}=${value}`)
.sort()
.join('\n')
const secret = createHmac('sha256', 'WebAppData').update(botToken).digest()
const computed = createHmac('sha256', secret).update(dataCheckString).digest('hex')
return computed === hash
}Day 3: payment
The scheme is simple, but it has one non-obvious node. The customer taps “pay”,
the application creates an order with status pending and receives a link from
the provider. From there the user goes off to the bank’s page—and may not come
back. Closed the tab, lost the network, switched to another app.
So the source of truth about payment status is the provider’s webhook, not the
user’s redirect. The redirect shows a “thank you” screen; the webhook moves the
order to paid. If you rely on the redirect alone, some paid orders will sit in
pending and some unpaid ones will go into production.
The webhook has to be idempotent: the provider is entitled to send the same notification twice. The idempotency key is the payment id. Using the order id here breaks on the first partial refund.
Day 4: the operator screen
The guest sees a status, but someone has to press “ready”. For a coffee shop that is a tablet at the counter: the order queue, time in the queue, one large button. No password login—there is one tablet and it stands behind the counter, so access is by a one-time link with a long token.
This is also where the first performance requirement appears, the one people usually forget: the screen has to refresh itself. Polling every five seconds is more honest and more reliable than a websocket when orders come in tens rather than thousands. Less code, fewer states, and it survives a dropped connection without reconnecting.
Day 5: reports and small things
A nightly export of sales into the manager’s spreadsheet. A notification to the guest when the order is ready. Handling the case where the guest never turns up. Separate wording for when the location is closed.
Small things are half the time. And that is normal: they are exactly what separates a product from a prototype.
Day 6: a pilot at one location
Not across the whole chain. One location, one day, a live barista standing next to it. That day usually surfaces two or three phrasings the guest does not understand, and one scenario nobody planned for—a takeaway order for three people with different ready times, say.
What can really move the deadline
- Acquiring. Waiting for the connection is the most common reason. Start with it.
- Content. A menu with descriptions, photos, modifiers. Without it, development stalls and waits.
- One person in charge. Not a committee. What slips a project is usually the approvals rather than the code.
Six days is what you get when everything above is ready. If it is not, count honestly: development time plus waiting time. We try to name both.