Figures as reported by the operator. Transaction volumes, client names and screenshots are not published.
The problem
In commodity murabaha (tawarruq) a bank buys a listed commodity on the customer's behalf, sells it to the customer on deferred terms and — if the customer chooses — sells it again for cash. Each deal passes through staff of the bank and of the broker in a fixed order, under limits that differ from bank to bank. Every step has to be attributable to a person, and the documents printed at the end have to match what was approved.
The requirement was one system for all banks — not one copy of the code per bank.
The transaction cycle
- Bank employeePrice quoteAmount entered; commodity and quantity calculated
- Bank employeePurchase requestThe quote becomes an order for the customer
- Bank managerApprovalCan be skipped per bank, by configuration
- BrokerPurchase executedCommodity bought and allocated
- Bank employeeSale to the customerContract documents printed in Arabic and English
Customer decision
Side paths are first-class: orders waiting for the customer, amendments and deletions — deleted orders are kept and reported, never erased.
What we built
ASP.NET MVC application for every role and every bank: quotes, orders, role-specific work queues, approvals, document printing, bank and user administration, reports.
REST/JSON API over the same cycle, for banks that drive it from their own systems: commodities, quotes, orders, approvals, sales, reports.
REST/JSON API secured with OAuth 2.0 for a bank's digital channel: a second transaction cycle in which the bank's system checks available commodities, places purchase, transfer and sale orders, and receives each result by push callback.
One SQL Server database behind all three, holding the history of every deal.
Engineering decisions
Configuration per bank, not code per bank
Currency, maximum order value, commission model (a fixed amount up to a threshold and a rate above it, or a corporate scheme per amount band), second factor on or off, IP allow-list, whether the manager approval step applies, and which document templates print. A new bank is a configuration, not a release.
One set of screens, scoped by role
Bank employees see their own orders, managers see their bank — or additional banks they are explicitly granted — and the broker sees only the steps that need action. Pending, in-process, done and waiting-for-customer queues are the same screen with different scopes.
Security layered per bank
SMS one-time passwords as a second factor, network allow-lists per bank, sensitive switches that only a system administrator can change and nobody else can see, and separate client credentials for each digital partner.
Pricing the commodity
Commodity prices are kept in the market's currency and converted with a maintained conversion factor; the quantity follows from the financed amount, and each commodity is offered only within its configured amount range.
Nothing disappears
Every step records who did it and when. Deleted orders are retained and have their own report; done transactions and employee activity are reportable by bank, employee and date.
Six years of change
The system was extended without a rewrite as the banks' needs changed. Examples from the change log:
- A per-bank option to route orders straight from the bank employee to the broker, skipping manager approval.
- A second transaction cycle and the OAuth 2.0 partner API for one bank's digital channel.
- A dedicated queue for orders waiting for the customer's action.
- Document printing changed from the last five quotes to all of today's quotes, at the branches' request.
What we would do differently today
Six years is long enough to see a design's weak spots. Starting today, we would:
- Model the cycle as an explicit state machine with named states and allowed transitions. Today each work queue is a filter over status values, so adding a state touches many screens — which is why docflow is event-sourced and names every refusal.
- Keep commission and pricing rules as versioned rule tables pinned by hand-written test cases, the way docrecon pins its 21 rules, instead of fields on the bank record.
- Use one authorisation model for every integration from the first day, rather than introducing it with the second API.
What this page does not show
No client or bank names, no screenshots, no transaction volumes and no documents from the project. References can be arranged on request.
Stack
- C# / .NET
- ASP.NET MVC
- SQL Server
- REST / JSON APIs
- OAuth 2.0
- SMS one-time passwords
- Arabic / English UI and documents