Prishtinë, Kosovo · CET · open to work
Gent Hulaj
Full-stack developer who builds Shopware 6 commerce platforms end to end — PHP and Symfony underneath, Vue and React on top, and the plugins that wire them to payment gateways, address validation, warehouse systems and revenue recovery.
Currently at Solution25 on Shopware 6 storefronts for US retailers — including one carrying 30,000+ items. Most of my work is plugins: three payment gateways, address validation, warehouse sync, abandoned-cart recovery, product bundles and shipping protection.
4+
years, paid
9
shipped, all live
10
Shopware plugins
3
payment gateways
Core stack
- React4.5y
- SQL / MySQL3.5y
- TypeScript2.5y
- Shopware 6
- PHP
- Symfony
- Vue.js
- Seeking
- Full-stack developer · Backend-leaning full-stack · Shopware / e-commerce engineer
- Availability
- Open to new work
- Based
- Prishtinë, Kosovo
- Hours
- CET (UTC+1) — overlaps a full working day with the EU and UK
- Languages
- Albanian (native), English (professional)
Or check the claims yourself
4 tables · 48 rows · SELECT only
Every write-up here lands on the same point: the render is almost never the problem, the query is. Asserting that on a page of prose seemed hollow, so the CV is a relational database and this is a client for it — engine written for this page, no dependencies. Click a question, or ask your own. Everything below is the same data, printed in full.
OK · 9 rows · read 9
| # | title | kind | year |
|---|---|---|---|
| 1 | This Site | Personal site, V2 | 2026 |
| 2 | Miniature Market | High-catalogue retail | 2025 |
| 3 | Utah Airguns | Specialist retail | 2025 |
| 4 | Lorik Zekaj Portfolio | Client portfolio | 2025 |
| 5 | VaporBeast | Regulated e-commerce platform | 2024 |
| 6 | Innovemia Academy | Education platform | 2024 |
| 7 | Portfolio V1 | Previous personal site | 2024 |
| 8 | MetDaan | Digital media publication | 2023 |
| 9 | Pabau | Clinic & booking platform | 2022 |
SELECT · JOIN · WHERE · GROUP BY · ORDER BY · LIMIT · COUNT · MIN · MAX · AVG · SUM · LIKE · IN · IS NULL · EXPLAIN. Arrow keys walk your history.
Why me, with the evidence attached
4 written-up projects · every link is production
I ship commerce that has to satisfy regulators, not just users
VaporBeast is age-gated nicotine retail under the US PACT Act: adult-signature delivery, carrier restrictions, and address accuracy as a legal requirement rather than a conversion tweak.
Read how it is built →I extend platforms instead of forking them
10 Shopware 6 plugins on documented extension points — payment handlers, cart processors, event subscribers, custom entities with their own admin modules, scheduled tasks and the message queue. Three are payment gateways, because processing is the part of a store that most needs a second route.
See the plugins →I fix the query, not the symptom
At Pabau a slow calendar looked like a React problem. It was five chained requests per view. Collapsing them into one shaped query and subscribing for changes is what actually fixed it.
Read the case study →Schema
4 tables · 2 foreign keys
- companytext
- titletext
- startedint
- endedint
- currentbool
- locationtext
- slugtext
- titletext
- kindtext
- company↗text
→ roles.company
- yearint
- featuredbool
- writeupbool
- livetext
- nametext
- slugtext
- categorytext
- company↗text
→ roles.company
- storetext
- servicetext
- surfacetext
- platformtext
- nametext
- areatext
- leveltext
- rankint
- yearsint
projects.company→roles.companyJOIN roles ON projects.company = roles.companyplugins.company→roles.companyJOIN roles ON plugins.company = roles.company
Projects
table projects · 8 columns
9
9 shipped, 4 written up properly. The rows with writeup = true have a full record: the problem, what I did, and what I would do differently.
SELECT * FROM projectsOK · 9 rows| # | slug | title | kind | company | year | featured | writeup | live |
|---|---|---|---|---|---|---|---|---|
| 1 | vaporbeast | VaporBeast | Regulated e-commerce platform | Solution25 | 2024 | true | true | vaporbeast.com ↗ |
| 2 | miniaturemarket | Miniature Market | High-catalogue retail | Solution25 | 2025 | true | false | miniaturemarket.com ↗ |
| 3 | utahairguns | Utah Airguns | Specialist retail | Solution25 | 2025 | false | false | utahairguns.com ↗ |
| 4 | pabau | Pabau | Clinic & booking platform | Pabau Clinic Software | 2022 | true | true | pabau.com ↗ |
| 5 | metdaan | MetDaan | Digital media publication | MetDaan | 2023 | true | true | metdaan.media ↗ |
| 6 | innovemia | Innovemia Academy | Education platform | Solution25 | 2024 | true | true | innovemia.academy ↗ |
| 7 | lorik-zekaj | Lorik Zekaj Portfolio | Client portfolio | NULL | 2025 | false | false | lorikzekaj.info ↗ |
| 8 | portfolio-v1 | Portfolio V1 | Previous personal site | NULL | 2024 | false | false | gent-hulaj.vercel.app ↗ |
| 9 | pirrox | This Site | Personal site, V2 | NULL | 2026 | false | false | pirrox.dev ↗ |
Shopware plugins
table plugins · 8 columns
10
Extensions written at Solution25 across three client storefronts. Three are payment gateways. Processing is the part of a store that most needs a second route. The category column groups them by purpose; the detail below gives the constraint behind each one.
SELECT * FROM pluginsOK · 10 rows10 rows
| # | name | slug | category | company | store | service | surface | platform |
|---|---|---|---|---|---|---|---|---|
| 1 | Sales Agent | sales-agent | b2b | Solution25 | NULL | NULL | Custom entity and admin module, storefront context switching, order attribution | Shopware 6 |
| 2 | Curbstone Payments | curbstone-payments | payments | Solution25 | NULL | Curbstone | Payment handler, order transaction state machine, checkout finalisation | Shopware 6 |
| 3 | Smarty Address Validation | smarty-address-validation | checkout | Solution25 | NULL | Smarty (formerly SmartyStreets) | Checkout address validation, storefront JS, cart error handling | Shopware 6 |
| 4 | ShipHero Warehouse Integration | shiphero-integration | fulfilment | Solution25 | NULL | ShipHero | Scheduled tasks, message queue, GraphQL client, webhook endpoints | Shopware 6 |
| 5 | NMI Payments | nmi-payments | payments | Solution25 | NULL | NMI | Payment handler, browser-side tokenisation, order transaction state machine | Shopware 6 |
| 6 | Authorize.Net Payments | authorize-net-payments | payments | Solution25 | NULL | Authorize.Net | Payment handler, browser-side tokenisation, refunds and capture | Shopware 6 |
| 7 | Abandoned Cart Saver | abandoned-cart-saver | revenue | Solution25 | NULL | NULL | Scheduled task, message queue, cart persistence, mail templates, event subscribers | Shopware 6 |
| 8 | Sales-Channel Specific Content | sales-channel-content | merchandising | Solution25 | NULL | NULL | Entity extensions with sales-channel association, CMS data resolver, storefront subscribers | Shopware 6 |
| 9 | Product Bundle | product-bundle | merchandising | Solution25 | NULL | NULL | Custom entity and admin module, cart data collector and processor, price calculation | Shopware 6 |
| 10 | Shipping Protection | shipping-protection | revenue | Solution25 | NULL | NULL | Cart data collector and processor, custom line item type, storefront toggle | Shopware 6 |
10 write-ups
01
Sales Agent
b2b
Lets a sales representative work inside a customer's account — building and placing an order on their behalf, with the agent recorded on the order.
Wholesale accounts do not self-serve. Orders arrive by phone and email, and someone has to enter them against the right account, at the right pricing, without handing that account's credentials around.
Extension point
Custom entity and admin module, storefront context switching, order attribution
Written in
PHP, Symfony, Shopware Administration (Vue), MySQL
02
Curbstone Payments
payments
↳ Curbstone
Payment handler integrating Curbstone's gateway, so card data is tokenised remotely and never lands on the merchant's infrastructure.
Curbstone is a PCI-validated Level 1 service provider. Routing card capture through remote tokenisation keeps the store's own systems out of PCI scope — which matters far more to a merchant than a few lines of checkout code.
Extension point
Payment handler, order transaction state machine, checkout finalisation
Written in
PHP, Symfony, REST, Shopware payment API
03
Smarty Address Validation
checkout
↳ Smarty (formerly SmartyStreets)
Validates and normalises US addresses at checkout against USPS CASS-certified data, with autocomplete as the customer types.
For a regulated shipper a wrong address is not a missed delivery, it is a compliance exposure. Smarty also returns whether an address is residential or commercial and whether it is deliverable at all, which is what decides the carrier and service level downstream.
Extension point
Checkout address validation, storefront JS, cart error handling
Written in
PHP, Symfony, JavaScript, REST
04
ShipHero Warehouse Integration
fulfilment
↳ ShipHero
Two-way sync with the ShipHero warehouse management system: orders out for fulfilment, stock levels and tracking numbers back in.
Once fulfilment moves into a real WMS, the storefront stops being the source of truth for stock. Orders have to reach the warehouse reliably and inventory has to come back, or the catalogue starts selling things that are not there.
Extension point
Scheduled tasks, message queue, GraphQL client, webhook endpoints
Written in
PHP, Symfony, GraphQL, Message queue, MySQL
05
NMI Payments
payments
↳ NMI
Payment handler for the NMI gateway, with card details tokenised in the browser so they never reach the store's servers.
A merchant needs more than one route to take money. Gateways go down, rates get renegotiated, and a high-risk category can lose an account with little notice — so the store has to be able to switch or run two in parallel.
Extension point
Payment handler, browser-side tokenisation, order transaction state machine
Written in
PHP, Symfony, JavaScript, REST
06
Authorize.Net Payments
payments
↳ Authorize.Net
Payment handler for Authorize.Net, again tokenising in the browser and keeping the merchant out of PCI scope.
Authorize.Net is what a lot of established US merchants already process on. Meeting them where they are is faster and cheaper than asking them to migrate their processing to suit a new storefront.
Extension point
Payment handler, browser-side tokenisation, refunds and capture
Written in
PHP, Symfony, JavaScript, REST
07
Abandoned Cart Saver
revenue
Finds carts left behind after a set interval and emails the customer a link that restores the cart exactly as they left it.
Most carts are abandoned, and a recovered one is the cheapest order a store will take all day. The work is in the details: send once rather than repeatedly, stop the moment the order completes, and restore the cart rather than dumping the shopper on the homepage.
Extension point
Scheduled task, message queue, cart persistence, mail templates, event subscribers
Written in
PHP, Symfony, MySQL, Twig
08
Sales-Channel Specific Content
merchandising
Lets content, product data and category structure differ per sales channel, so one Shopware instance can serve several storefronts without duplicating the catalogue.
A merchant running more than one storefront off one installation still needs each to look and read like its own shop. Forking the catalogue per channel is the obvious move and the wrong one — it doubles the maintenance and the two copies drift apart within a month.
Extension point
Entity extensions with sales-channel association, CMS data resolver, storefront subscribers
Written in
PHP, Symfony, Shopware Administration (Vue), MySQL, Twig
09
Product Bundle
merchandising
Sells several products together as one purchasable bundle, with its own pricing and its own line in the cart.
Bundles raise basket size, but only if the maths is right. The bundle has to price as a unit while still drawing down stock on each component, and it has to survive partial refunds — which is why it belongs in the cart pipeline rather than in a template.
Extension point
Custom entity and admin module, cart data collector and processor, price calculation
Written in
PHP, Symfony, Shopware Administration (Vue), MySQL
10
Shipping Protection
revenue
Adds an optional shipping-protection line item at checkout, priced from the cart total and toggled by the customer.
It gives the customer a remedy when a parcel is lost or damaged, and the merchant a margin line that costs nothing to stock. It has to be added as a real line item so tax, totals, refunds and the order record all stay correct.
Extension point
Cart data collector and processor, custom line item type, storefront toggle
Written in
PHP, Symfony, JavaScript, Twig
Roles
table roles · 6 columns
3
3 teams since 2022: booking software, then publishing at scale, now commerce. Each move went further down the stack rather than sideways across it.
SELECT * FROM rolesOK · 3 rows| # | company | title | started | ended | current | location |
|---|---|---|---|---|---|---|
| 1 | Solution25 | Software Engineer | 2024 | NULL | true | Prishtinë, Kosovo |
| 2 | MetDaan | Software Engineer | 2023 | 2024 | false | Prishtinë, Kosovo |
| 3 | Pabau Clinic Software | Full-Stack Developer | 2022 | 2023 | false | Prishtinë, Kosovo |
Skills
table skills · 5 columns
26
Graded honestly, because a wall of identical 'expert' badges tells a reader nothing. rank 3 means it is in my hands every week; rank 1 means I have shipped with it and would want a week to warm up. The years column is computed from the dates of the roles and projects on this page, not typed in — so it cannot be inflated, and is blank where a tool is not itemised on a role.
SELECT * FROM skillsOK · 26 rows26 rows
| # | name | area | level | rank | years |
|---|---|---|---|---|---|
| 1 | TypeScript | Frontend | core | 3 | 2.5 |
| 2 | React | Frontend | core | 3 | 4.5 |
| 3 | Next.js | Frontend | core | 3 | 4.5 |
| 4 | Vue.js | Frontend | strong | 2 | 1.5 |
| 5 | Tailwind CSS | Frontend | core | 3 | 1.5 |
| 6 | GSAP | Frontend | core | 3 | NULL |
| 7 | Zustand | Frontend | strong | 2 | 1.5 |
| 8 | Three.js | Frontend | working | 1 | NULL |
| 9 | Shopware 6 | Backend & Commerce | core | 3 | 1.5 |
| 10 | Shopware plugins | Backend & Commerce | core | 3 | 1.5 |
| 11 | PHP | Backend & Commerce | strong | 2 | 1.5 |
| 12 | Symfony | Backend & Commerce | strong | 2 | 1.5 |
| 13 | Laravel | Backend & Commerce | working | 1 | 1.5 |
| 14 | Node.js | Backend & Commerce | strong | 2 | NULL |
| 15 | Python | Backend & Commerce | working | 1 | 1.5 |
| 16 | SQL / MySQL | Data | strong | 2 | 3.5 |
| 17 | GraphQL | Data | strong | 2 | 2.5 |
| 18 | Hasura | Data | strong | 2 | 1 |
| 19 | Prisma | Data | strong | 2 | 1 |
| 20 | REST design | Data | core | 3 | NULL |
| 21 | Payment / WMS APIs | Data | strong | 2 | 1.5 |
| 22 | Git / GitHub | Craft & Tooling | core | 3 | NULL |
| 23 | Accessibility | Craft & Tooling | strong | 2 | NULL |
| 24 | Core Web Vitals | Craft & Tooling | strong | 2 | NULL |
| 25 | i18n | Craft & Tooling | strong | 2 | 1.5 |
| 26 | Figma handoff | Craft & Tooling | strong | 2 | NULL |
Why a database
Every write-up here lands on the same point: the render is almost never the problem, the query is. Asserting that on a page of prose seemed hollow. So the CV is normalised into four tables with foreign keys between them, and the page is a client. EXPLAIN is the honest part — it shows how many rows were read to return the ones you asked for.
How it works
A tokeniser, a recursive-descent parser and an evaluator, hand-written for this page. It does inner joins with a hash build rather than a nested loop, GROUP BY with aggregates, qualified and aliased columns, three-valued NULL logic, and EXPLAIN. Shipping a megabyte of WASM to make a point about query cost would have been self-defeating.
It reads without JavaScript
Every table the console can query is server-rendered in full above. The console is the extra, never the way through — turn JavaScript off and the whole CV is still there, as tables.
Built with
Next.js and TypeScript. No component library, no icon library, no animation library — the entrances are CSS transitions driven by one shared scroll sweep. Source is on GitHub ↗.
Contact
open to work · replies within a day or two
Looking for full-stack work, and open to selected freelance — commerce platforms, Shopware extensions, or a backend that needs untangling. Everything gets an answer.
- gent-hulaj ↗
- github
- Genthh ↗
- located
- Prishtinë, Kosovo (CET)
INSERT INTO messages3 fields