Skip to main content

Natually

Natually sells natural skincare and haircare products online in India. In 2026 Dcrayon moved its store from Shopify to a Laravel platform that Dcrayon builds and maintains.Natually's range runs from...

Natually

Natually's re-platform at a glance

The main facts of Natually's move from Shopify to Dcrayon's Laravel platform.

ItemDetail
Client and industryNatually, natural skincare and haircare brand selling direct to consumers
Market and audienceIndia; online shoppers buying skincare and haircare
Engagement periodCatalogue import Mar 2026; move to its own server May 2026; maintained on the platform since
Our scopeTheme, catalogue import, link handling, checkout and payment integrations, shipping and tracking, product feeds and hosting move
Platform and stackLaravel 12, PostgreSQL, Redis, Blade, Tailwind CSS, Alpine.js, Vite; Razorpay; Shiprocket; AWS in the Mumbai region
Source: Dcrayon project records and the live store, checked 13 Oct 2026.

Challenge

Natually sells natural skincare and haircare products online in India. In 2026 Dcrayon moved its store from Shopify to a Laravel platform that Dcrayon builds and maintains.

Natually's range runs from face serums, day and night creams, cleansers and kojic soaps to hair colour, hair removal wax, lip care and facial kits, most of them priced for everyday buying. The store ran on Shopify, and Dcrayon had customised its Shopify theme since 2025 (see Natually's Shopify storefront work). In March 2026 the catalogue was imported into Dcrayon's own e-commerce platform, built on Laravel 12, and by May 2026 the store was running on its own server under the brand's domain.

What Natually's store had to keep doing after leaving Shopify

Shopping features were spread across separate Shopify apps.

The archived storefront from 6 March 2026 loads a third-party one-page checkout and a shoppable-video app, and a capture from February 2025 shows further apps for free gifts, volume discounts and an Instagram feed. Each app brought its own scripts, its own settings screen and its own subscription, and none of them shared rules with the others. On the new platform, offers, cart rules and the connections to payment and shipping providers had to be managed from one codebase.

About 170 products and their old links had to survive the move.

A platform change can break every product link that search engines, ads and saved bookmarks point to. Natually's old store used Shopify's /products/ and /collections/ addresses, and the domain had been live since at least 2022. Some products had been renamed over the years and some had been discontinued. The new store had to keep each address working, redirect it deliberately, or retire it in a way search engines understand.

Checkout had to match how Indian shoppers pay.

The store needed UPI, cards, net banking and wallets, a form of cash on delivery, and a way for customers to follow their parcel after dispatch. It also had to keep selling when an outside checkout window failed to load.

Offers change often and must show everywhere at once.

Natually runs standing offers: a discount on prepaid orders, free shipping, and free gifts above set order values (on 13 October 2026, a free cap above Rs 999 and a free facial kit above Rs 1,699). Shoppers need to see these in the announcement bar, on product pages and in the cart, along with how far they are from the next gift.

One codebase serving several brands must keep each brand's details apart.

Dcrayon's platform runs more than one brand from the same code. The team's own notes record the risk plainly: a default value written for one brand, such as a phone number, could surface in another brand's footer. Natually needed its own theme, settings, payment keys and data without inheriting anyone else's.

Solution

A multi-tenant Laravel platform with a theme per brand

The platform is multi-tenant: one Laravel 12 codebase serves several brands, each one a tenant with its own PostgreSQL database. While Natually ran on the shared platform, the stancl/tenancy package switched the database connection, prefixed cache keys, scoped file storage, tagged queued jobs and loaded tenant-specific settings, such as payment keys, on every request, based on the domain. With a database per tenant rather than shared tables, one brand's orders and customers cannot appear in another brand's query.

On the shared platform, Natually got its own storefront without a separate codebase. A ResolveTenantFrontend middleware resolves views in a fixed order: the tenant's own folder first (frontends/natually/views), then the shared storefront defaults (frontends/default/views), then the platform's original views. Natually's header, footer, home page and product page live in its own folder with its own CSS and JavaScript, while account pages, emails and the admin panel stay shared and receive every platform fix.

In May 2026 Natually moved from the shared platform to its own AWS server in the Mumbai region, running its own copy of the platform code, with the database, uploaded files and theme carried across. The brand's domain now points to that server.

Keeping old Shopify product links alive

Products, categories, blog posts and content pages were imported into the new database: the local import of 27 March 2026 holds 175 products, 29 categories and 75 blog posts. Addresses kept Shopify's pattern, products at /products/ and collections at /collections/, and product slugs stayed the same wherever the product itself was unchanged.

The product route then makes one decision per request. An active product with that slug is shown. A product that exists but has been soft-deleted (marked deleted with a timestamp rather than removed from the database) returns 410 Gone, which tells search engines the page was removed on purpose rather than lost. An old slug from the Shopify era that maps to a renamed product returns a permanent 301 redirect to the new address. A singular /product/ address redirects to the plural form or returns 410 for a deleted item. Category pages follow the same rule, returning 410 for an inactive collection. The results section shows how this holds up on a sample of old links, and our website redesign page covers migrations like this one.

Offers, free gifts and the cart drawer

The cart is a side drawer, not a separate page. It shows each item with quantity controls, a "You may also like" row, and a message that tells the shopper how much more to add to qualify for the next free gift. The same thresholds appear in the announcement bar at the top of every page and in a dismissible reminder, so the offer reads the same wherever the shopper meets it.

On the browser side, cart and wishlist state live in Alpine.js stores (Alpine.js is a small JavaScript library for interactive page parts). The shared _core front end provides stores for the cart, the wishlist, the sign-in modal, product pack selection and toast messages, and every Natually page reads from the same cart store, so the header count, the drawer and the product page never disagree. Collections for combos and free gifts are ordinary categories in the catalogue, so the Natually team can add or retire an offer collection from the admin panel without code.

A checkout that holds stock and survives a failed payment window

A Buy Now button opens Shiprocket's hosted one-page checkout. Between the moment a shopper starts that checkout and the moment the order comes back, a few minutes can pass with no stock reserved, so the StockHoldService places a soft hold on the item in Redis, set atomically so two shoppers cannot both take the last unit. Holds expire after ten minutes if the shopper abandons the checkout. If the Shiprocket window fails to load for any reason, the page adds the product to the store's own cart and sends the shopper to the store's own checkout instead, so the sale is not lost.

The store's own checkout re-checks every cart line against current product data before it accepts an order. If stock has fallen, the shopper is told how many are left; if the price has changed since the item was added, the cart is updated and the shopper is asked to review it. The order is then written inside a database transaction that locks each product or variant row, decrements its stock only if enough remains, and marks the item out of stock when it reaches zero.

Payments run through a PaymentGatewayInterface, introduced so that the rest of the code never talks to a payment provider directly. Each gateway implements the same methods (create a payment attempt, verify a payment, handle a webhook, issue a refund) and the GatewayRegistry holds four implementations: Razorpay, Cashfree, cash on delivery and partial cash on delivery. Partial cash on delivery asks for a small online advance, Rs 50 per the store's product FAQ, with the rest paid at the door. Amounts pass between gateways as a Money value object holding integer paise, never floating-point rupees, and results come back as typed objects such as PaymentAttemptResult, VerificationResult and RefundResult. This is the e-commerce side of our API development work.

Shipping, tracking and product feeds

Courier integrations for Delhivery and Blue Dart implement a ShippingProviderInterface with the same operations: check a pincode, calculate a cost, book a delivery, track a waybill, cancel, print a label and request a pickup. A ShippingManager picks the provider for each order, and Shiprocket is integrated separately for checkout and shipments: when an order is placed, a PushOrderToShiprocket listener sends it on.

The platform also publishes a Google Merchant Center feed and a Meta catalogue feed from the same product records, so shopping ads list the same products and prices as the store. On 13 October 2026 the Google Merchant feed carried 143 products.

Contracts, services and typed data in the platform

Each responsibility has one home in the codebase, which the table below lists. Requests pass through middleware for tenancy and theme resolution, security headers, response caching and authentication before reaching controllers. Controllers validate input and hand anything that touches money, stock or a courier to a service. Services that talk to outside providers depend on contracts, PaymentGatewayInterface and ShippingProviderInterface, rather than on a provider's own client, and they exchange typed data objects such as CheckoutSessionData and ShiprocketWebhookEventData instead of loose arrays. Order events trigger listeners such as SendOrderNotification, PushOrderToShiprocket, CheckOrderFraud and UpdateRecommendationData, each handling one follow-up task after an order is placed.

The database stores money in decimal columns with two places: an order carries its subtotal, discount, tax, shipping cost, total and paid amount, each as decimal(12,2), while the gateway layer converts to integer paise before any provider sees a figure. Order lifecycle times have their own columns, confirmed_at, shipped_at, delivered_at and cancelled_at, and every status change is also written to order_status_history, so the team can see who moved an order and when.

The storefront's Blade components, sections and Alpine stores

Natually's pages are assembled from shared Blade components rather than copied markup. product-card renders a product in every grid, product-schema writes its structured data, breadcrumb handles navigation, and recommended-products, similar-products and recently-viewed fill the product page. Smaller components cover trust-badges, faq-section, whatsapp-button, chatbot-widget, modal, alert and form input.

The platform's section library, used by the shared storefront, includes hero_banner, category_grid, product_carousel, countdown_timer, brand_logos, instagram_reels, newsletter, text_with_image, trust_badges and custom_html. Natually's own home page template draws its promotional product banners from homepage_sections, where each section has a position and an active flag, so the team can edit, reorder or switch off a banner from the admin panel without code. The Natually theme's settings file names the theme, its rose-gold accent and the settings and sections it supports. Our e-commerce solutions page describes this kind of storefront.

Git hooks, CI checks and guarded releases

Work on the platform moves through feature branches and pull requests. Local Git hooks refuse a commit with a PHP syntax or Blade error and refuse a direct push to the main or development branches. Every pull request to the main or development branch then runs a continuous integration workflow that checks the syntax of every PHP file, compiles every Blade template, validates routes and configuration, builds the front-end assets with Vite, runs the PHPUnit suite, and fails the build if it finds a brand-specific default hard-coded into shared code, the guard against one brand's phone number appearing in another's footer.

While Natually ran on the shared platform, releases went through a script that refuses to continue without a fresh database snapshot, verifies each uploaded file by checksum, runs smoke tests across every brand, watches site health for five minutes and rolls back automatically if either check fails.

Two-factor admin access, signed webhooks and security headers

The admin panel uses its own login, separate from customer accounts. Every admin route requires two-factor verification, re-checked every 12 hours, and passes through an audit middleware that logs admin actions. Admins have a role, and anyone other than a super admin sees only the sections their permission list allows; twelve admin areas are guarded this way. Repeated failed sign-in attempts are locked out for a period.

Payment and messaging webhooks are verified before they are trusted. A Razorpay webhook must carry a signature that matches an HMAC-SHA256 of its body, compared in constant time, and Meta webhooks have their own signature check. Card and UPI details are entered only on the payment providers' own pages, so they never reach Natually's server. Prices and stock are re-checked on the server at checkout, whatever the browser sends.

Response headers come from a SecurityHeaders middleware. On 13 October 2026 the live store redirected plain HTTP to HTTPS and sent Strict-Transport-Security for one year with subdomains and preload, X-Content-Type-Options set to nosniff, X-Frame-Options set to SAMEORIGIN, a strict-origin-when-cross-origin Referrer-Policy, a Permissions-Policy that turns off the camera and microphone, and a Cross-Origin-Opener-Policy. Its session cookie was marked Secure, HttpOnly and SameSite=Lax.

Product structured data and split sitemaps

Product pages carry structured data for the product and its brand, an offer with price and availability, shipping details with delivery time and region, a merchant return policy and a breadcrumb trail. The home page publishes Organization data with contact details, WebSite data with a site search action, and FAQ data. The XML sitemap is split into pages, products, categories, brands and blog posts, and the robots file keeps the cart, checkout and account pages out of search. On 13 October 2026 the sitemaps listed 166 product pages and 16 collections. This is the technical SEO layer of the migration.

A separate AWS server with Redis and response caching

Natually's server on AWS runs nginx, PHP 8.3 and PostgreSQL, with Redis holding the cache, sessions and the job queue; our AWS services page covers this kind of setup. Product listing pages use a response cache: for visitors who are not signed in, a listing is served from cache for five minutes before it is rebuilt. On 13 October 2026 the home page served its product images in WebP format and lazy-loaded 62 of them below the fold. The store also ships a web app manifest, so shoppers can add it to a phone's home screen like an app.

Natually platform layers

Where each responsibility sits in the platform code behind Natually's store.

Application partWhat it does in this project
Tenancy and theme resolutionResolveTenantFrontend serves Natually's own header, footer, home and product views before the shared defaults
MiddlewareSecurityHeaders, CacheResponse, admin two-factor and audit, webhook signature checks
ControllersValidate input, re-check price and stock at checkout, hand money and courier work to services
Payment contractPaymentGatewayInterface with Razorpay, Cashfree, COD and partial COD gateways in the GatewayRegistry
Shipping contractShippingProviderInterface and ShippingManager for pincode checks, costs, bookings, tracking and labels
Typed dataMoney in integer paise, CheckoutSessionData, PaymentAttemptResult, VerificationResult, RefundResult, ShiprocketWebhookEventData
Stock protectionStockHoldService soft holds in Redis during hosted checkout; row locks when the order is written
Source: Natually platform repository, checked 13 Oct 2026.

Natually's order and catalogue tables

Real table names from the platform database and what each one holds for Natually.

EntityTableWhat it holds
Product and variantproducts and product_variantsCatalogue items, price, MRP, stock quantity and stock status
CollectioncategoriesCollections shown at /collections/, including combos and free gifts
Cartcarts and cart_itemsItems with the price captured when added, re-checked at checkout
Orderorders and order_itemsSubtotal, discount, tax, shipping, total and paid amount as decimal(12,2), with item price snapshots
Order historyorder_status_historyEvery status change on an order
Payment and shipmentpayments and order_shipmentsGateway payment records and courier shipments per order
Home pagehomepage_sectionsThe sections the Natually team arranges on the home page
Source: Natually platform migrations and local database copy, checked 13 Oct 2026.

How Natually's store is protected

The controls on Natually's checkout, admin panel and responses, and how each was checked.

ControlWhere it appliesHow it was checked
Separate admin login with two-factor verification and audit logEvery admin routeAdmin route middleware reviewed in the repository
Section permissionsTwelve admin areasSection middleware and admin permission list reviewed
Webhook signature verificationRazorpay and Meta webhooksHMAC-SHA256 check with constant-time comparison reviewed
Server-side price and stock re-checkCheckoutCheckout code reviewed: price comparison, transaction and row locks
Security headersEvery responseLive check: HSTS with preload, nosniff, SAMEORIGIN, Referrer-Policy, Permissions-Policy
Session cookie and HTTPSWhole storeLive check: HTTP redirects to HTTPS; cookie Secure, HttpOnly, SameSite=Lax
Source: Natually platform repository and response headers of the live store, checked 13 Oct 2026.

Results

What Natually's shoppers and team have today

Natually now runs on code that Dcrayon maintains. Shoppers get a category menu across the full range, search that matches product names, descriptions, SKUs, categories and brands, a wishlist, a cart drawer that shows free-gift progress, Shiprocket's one-page checkout with a fallback to the store's own checkout, Razorpay payments and partial cash on delivery, and a Track Order page. The Natually team arranges the home page from sections, adds offer collections from the admin panel, and lists the same catalogue on Google and Meta from one set of product records.

To test the migration, we took the 54 product links on the archived Shopify homepage from 6 March 2026 and requested each one on the live store on 13 October 2026. 40 open the same product at the same address, 5 redirect permanently to a current product page, and 9 return 410 Gone for discontinued items. None of the 54 returns a 404 error.

A companion mobile app for Natually, built with Expo (React Native) and its own API, is in development in a separate codebase. Commercial figures for the move are not published.

Old Shopify links on the new Natually store

How old Shopify product links behave on the Laravel store.

MetricBaselineFinalPeriodSource
Old product links still reaching a product54 product links on the archived homepage45 of 54: 40 at the same address, 5 redirected with 301Checked 13 Oct 2026Archive capture and live requests
Old product links for discontinued itemsSame 54 links9 return 410 Gone; 0 return 404Checked 13 Oct 2026Live requests
Source: Internet Archive capture of the Shopify homepage (6 Mar 2026) and live HTTP requests on 13 Oct 2026. Sample: the 54 product links on that page.

Key takeaways

What Natually's move shows about leaving Shopify

A platform move is judged by what does not break: links, payment options and offers. Shoppers notice a missing payment method or a dead product link long before a new design, so those are the first things to plan and the first things to test after cutover.

A deliberate 410 is better than a silent 404. Telling search engines that a discontinued product is gone on purpose keeps crawling tidy, while a redirect to an unrelated product can confuse the shopper who clicked. And shared code needs guardrails: running several brands on one codebase saves work only if a setting written for one brand can never surface on another, which is why that check runs on every pull request.

If you are weighing Shopify against a custom build, our decision framework for D2C Shopify architecture sets out the trade-offs, and UPI payment architecture for India covers the checkout side. Ayurvexa's store runs on the same platform; read the Ayurvexa case study. To plan a re-platform of your own, talk to our team.

CategoryWeb Development

Subscribe to Our Newsletter

Get the latest insights on digital marketing, web development, and business growth delivered straight to your inbox.