Skip to content
E-commerce

Building an online store in Romania with Medusa: couriers, payments and invoices

Learn how to set up a Romanian e‑commerce store using Medusa, integrate local couriers, payment gateways, and generate compliant invoices step by step.

Romanian boutique owner configuring a Medusa online store with parcels and payment screens
Illustration generated with AI.

Romanian merchants often struggle to find a headless e‑commerce stack that works with local couriers, payment methods, and invoicing requirements.

The answer is to use Medusa together with CarphaCom’s ready‑made integrations, then host the solution on Vultr for fast, scalable performance.

Why choose Medusa for a Romanian headless store

Open‑source, API‑first design

Medusa is released under the MIT license and provides a fully documented REST/GraphQL API that abstracts product, cart and order logic from the front‑end. Because the API is language‑agnostic, Romanian developers can pair it with Next.js, Vue, or even legacy PHP templates without rewriting business rules. The core services run on Node.js and use PostgreSQL for persistence, which aligns with the expertise of most local development teams and with the database choice of the CarphaCom platform.

All extensions—tax calculation, shipping rules, discount engines—are implemented as plug‑ins that register themselves through the same API surface. This modularity lets merchants add Romanian‑specific features, such as VAT‑M0 compliance or carrier‑specific rate tables, without forking the core codebase. Updates from the Medusa community can be applied with a single npm install medusa@latest command, preserving custom plug‑ins.

Fit for Romanian merchants

Romanian e‑commerce faces distinct regulatory and logistical constraints: mandatory electronic invoicing, local courier networks (Fan Courier, Urgent Cargus, Sameday) and payment providers (Netopia, PayU, MobilPay). Medusa’s headless nature allows these services to be called directly from the back‑end, ensuring that tax rates, shipping zones and payment callbacks respect Romanian law. Because the platform does not enforce a specific front‑end, merchants can maintain brand‑consistent UI while still leveraging the same checkout flow across desktop, mobile apps and marketplace integrations.

Performance is another advantage. Medusa processes cart operations in memory and persists only final states, reducing database load during high‑traffic sales events such as Black Friday. The stateless architecture works seamlessly with Vultr’s load balancers and auto‑scaling groups, which many Romanian retailers already use to handle seasonal spikes.

Proven deployment with CarphaCom

The CarphaCom e‑commerce platform demonstrates Medusa’s readiness for production in Romania. Built on Medusa v2, PostgreSQL and Next.js, CarphaCom integrates native Romanian courier APIs and payment gateways, and it is offered as a self‑hosted Developer Edition on the Vulval Marketplace (CarphaCom on Vultr). The listing includes a one‑click deployment script that provisions a 2‑CPU, 4 GB instance, configures the database, and installs the Medusa server with all required environment variables.

Retailers who launch CarphaCom gain immediate access to a fully tested Medusa stack, including pre‑built webhook handlers for Romanian logistics providers. The platform’s source code is maintained in the Qubitpage GitHub organization (github.com/qubitpage), allowing developers to audit security patches or contribute local enhancements.

Strategic advantage for local businesses

Choosing Medusa reduces time‑to‑market because the core commerce engine is already aligned with Romanian compliance requirements. The open‑source model eliminates licensing fees, a critical factor for SMEs operating on thin margins. Moreover, the active Medusa community provides ready‑made adapters for popular services, and Qubitpage’s expertise in custom software development ensures that any gap can be closed with a short‑term consulting engagement (contact us).

In practice, a Romanian merchant can spin up a Medusa‑based store on Vultr, connect CarphaCom’s courier plug‑ins, and start processing orders within a day, while retaining full control over the front‑end experience and data residency.

Setting up Medusa on Vultr Marketplace

Qubitpage’s CarphaCom Developer Edition provides a ready‑to‑run Medusa v2 stack that includes PostgreSQL, a Next.js storefront and pre‑configured Romanian courier and payment adapters. Deploying it from the Vultr Marketplace eliminates manual OS provisioning and lets you focus on product data and UI customisation.

  1. Log in to the Vultr control panel. Navigate to the CarphaCom Marketplace page and click Deploy Now. The wizard will prompt you to select a region; choose a data centre closest to your target customers, such as Frankfurt (DE) or Bucharest (RO), to minimise latency.
  2. Select an instance size. For a small‑to‑medium catalogue the vc2-2c/4gb plan (2 vCPU, 4 GB RAM, 80 GB SSD) is sufficient. If you anticipate high traffic or large product images, upgrade to vc2-4c/8gb. The Marketplace image includes the necessary Docker containers, so the size directly influences concurrency limits.
  3. Configure the PostgreSQL database. In the same wizard, enable the “Managed PostgreSQL” option and allocate at least 20 GB of storage. Set the database version to 15, which is the version tested with CarphaCom. Record the generated DB_HOST, DB_USER, and DB_PASSWORD values; you will need them for the Medusa environment file.
  4. Provide environment variables. After the instance boots, open the Vultr console and edit /etc/carphacom/.env. Insert the PostgreSQL credentials, set MEDUSA_BACKEND_URL=http://localhost:9000, and define NEXT_PUBLIC_STORE_URL=https://your-domain.com. Save the file and restart the Docker compose stack with docker compose up -d.
  5. Run the initial Medusa setup. Execute npx medusa migrations run inside the /usr/src/medusa directory to create the default tables. Then create an admin user with npx medusa user create --email admin@example.com --password StrongPass123. The command returns a JWT token that you can paste into the /admin UI.
  6. Verify the storefront. Open a browser and visit http://YOUR_IP. The Next.js front‑end should render the default “Welcome to CarphaCom” page. Log in at /admin using the credentials created in the previous step to confirm backend connectivity.
  7. Secure the deployment. Add an SSL certificate via Vultr’s “Add‑on” menu or use Let’s Encrypt with certbot. Update MEDUSA_BACKEND_URL and NEXT_PUBLIC_STORE_URL to use https:// URLs, then restart the containers.
  8. Enable automatic backups. In the Vultr dashboard, enable daily snapshots for both the compute instance and the managed PostgreSQL service. This protects your product catalog and order history against accidental loss.

Tip: After the first deployment, clone the repository from GitHub and create a separate staging instance. Running updates on staging first lets you test new plugins or courier integrations without affecting live sales.

With the CarphaCom Developer Edition live, you have a fully functional Medusa backend, a PostgreSQL store, and a Next.js front‑end ready for the next steps: connecting Romanian couriers, local payment gateways, and automated invoicing. For assistance with custom integrations, visit our contact page or explore related services under Software Development.

Integrating Romanian couriers with CarphaCom

Built‑in connector overview

CarphaCom ships with pre‑configured connectors for the three most used Romanian delivery services: Fan Courier, Sameday and DHL România. Each connector abstracts the provider’s REST API behind a uniform /courier/:provider/track and /courier/:provider/create endpoint. The Medusa service calls these endpoints through the carphacom-courier plugin, which automatically maps Medusa order fields to the required payload.

Configuring the connectors

First, add the plugin to medusa-config.js and supply the API keys obtained from the courier portals:

module.exports = { plugins: [ { resolve: `carphacom-courier`, options: { fanCourier: { apiKey: process.env.FAN_API_KEY }, sameday: { apiKey: process.env.SAMEDAY_API_KEY }, dhl: { apiKey: process.env.DHL_API_KEY } } } ] };

Environment variables are stored in the Vultr instance’s .env file, keeping credentials out of the source repository.

Creating a shipment

The Medusa order fulfillment hook invokes POST /courier/:provider/create. Below is a concrete request for Fan Courier:

POST https://api.carphacom.com/courier/fancourier/create Content-Type: application/json { "order_id": "medusa_12345", "recipient": { "name": "Ion Popescu", "phone": "+40 722 123 456", "address": "Strada Exemplului 10, București, 010101" }, "parcel": { "weight_kg": 2.3, "dimensions_cm": { "length": 30, "width": 20, "height": 10 }, "value_ron": 450 }, "service": "express" }

CarphaCom translates this payload into Fan Courier’s native format, sends the request, and returns a JSON response containing the tracking number and estimated delivery date. The response is stored in Medusa’s fulfillment record, making it instantly visible in the admin UI.

Tracking a shipment

To query the status of an existing shipment, use the generic tracking endpoint. The example below fetches the status for a Sameday parcel:

GET https://api.carphacom.com/courier/sameday/track?tracking_number=SM123456789

The API replies with a standardized object:

{ "tracking_number": "SM123456789", "status": "in_transit", "last_update": "2026-10-22T14:35:00Z", "estimated_delivery": "2026-10-24" }

Because the response schema is uniform across providers, Medusa can render a single tracking widget for all Romanian couriers without additional front‑end logic.

Handling errors and retries

If a provider returns an HTTP 4xx or 5xx, CarphaCom logs the error to the Vultr‑hosted syslog and retries up to three times with exponential back‑off. Failed shipments are flagged in Medusa with the error status, allowing store operators to intervene manually. Detailed logs are accessible via the CarphaCom Remote console on the Vultr Marketplace page.

Testing the integration locally

Before deploying to production, run the CarphaCom sandbox mode. Set CARPHACOM_ENV=sandbox in the .env file; the plugin then routes all calls to the providers’ test endpoints (e.g., https://sandbox.fancourier.ro/api). Use the following Medusa CLI command to simulate an order fulfillment:

medusa order create --email test@example.com --items sku123:2 --shipping_method fan_courier

Check the console output for the generated tracking number and verify that the order status updates to fulfilled. Once confirmed, switch the environment variable back to production and redeploy.

Connecting local payment gateways

Choosing the right Romanian providers

For a headless store targeting Romanian consumers, the most widely adopted payment processors are Netopia and PayU. Both support card‑present and card‑not‑present transactions, recurring billing, and have dedicated APIs for local banks. Netopia offers a sandbox environment at https://sandbox.netopia-payments.com, while PayU provides test credentials through its developer portal. Selecting a provider depends on the merchant’s preferred settlement cycle and the required fraud‑prevention features.

Preparing the Medusa server

Begin by pulling the latest CarphaCom payment module from the Qubitpage GitHub repository. The module resides in the carphacom/payments directory and includes adapters for Netopia and PayU.

  1. Clone the repository and navigate to the module folder:
git clone https://github.com/qubitpage/carphacom.git
cd carphacom/payments
  1. Install the required Node.js dependencies:
npm install
  1. Copy the payment-config.example.js to payment-config.js and fill in the credentials supplied by the chosen provider.

For Netopia, set API_KEY, MERCHANT_ID, and CALLBACK_URL. For PayU, provide POS_ID, MD5_KEY, and SECOND_KEY. The configuration file follows a simple JSON structure, for example:

{
  "netopia": {
    "apiKey": "YOUR_NETOPIA_API_KEY",
    "merchantId": "123456",
    "callbackUrl": "https://yourstore.com/api/payments/netopia/callback"
  },
  "payu": {
    "posId": "654321",
    "md5Key": "YOUR_MD5_KEY",
    "secondKey": "YOUR_SECOND_KEY",
    "callbackUrl": "https://yourstore.com/api/payments/payu/callback"
  }
}

Registering the adapters in Medusa

Open the Medusa server configuration (medusa-config.js) and add the CarphaCom payment service to the modules array:

module.exports = {
  // … other settings
  modules: [
    // existing modules
    {
      resolve: "@carphacom/medusa-payment",
      options: {
        configPath: "./carphacom/payments/payment-config.js"
      }
    }
  ]
};

After saving, restart the Medusa service:

docker compose restart medusa

The new endpoints appear under /store/payments/netopia and /store/payments/payu. Test them with a POST request containing amount, currency, and order_id. Successful calls return a redirect URL that forwards the shopper to the provider’s checkout page.

Handling callbacks and order status

Both Netopia and PayU send asynchronous notifications to the callbackUrl defined earlier. Implement a webhook handler in src/api/payments/webhook.js that verifies the signature, updates the Medusa order status, and records the transaction ID.

router.post('/netopia/callback', async (req, res) => {
  if (!verifyNetopiaSignature(req.body)) {
    return res.status(400).send('Invalid signature');
  }
  await updateOrderStatus(req.body.order_id, req.body.status);
  res.send('OK');
});

Repeat the pattern for PayU, swapping the verification function. Once the order status changes to paid, Medusa can trigger fulfillment workflows, such as the CarphaCom robotised warehouse integration (learn more).

Tip: Enable the provider’s “test mode” during development, then switch to live credentials only after the webhook endpoints have been validated with real‑world traffic.

Automatic invoice generation and compliance

CarphaCom extends Medusa’s checkout flow with a built‑in invoicing engine that complies with the Romanian Fiscal Code (Legea nr. 227/2015). When an order reaches the order.completed event, the engine creates an invoice in both XML (required by ANAF) and JSON (convenient for downstream processing). The XML follows the Factură Fiscală schema, including mandatory nodes such as <CIF>, <DataEmiterii>, <TotalTVA>, and a digital signature generated with the merchant’s private key.

To activate the feature, add the carphacom-invoice plugin to your Medusa server:

npm install @carphacom/invoice-plugin
medusa plugins add @carphacom/invoice-plugin

After installation, configure the plugin in medusa-config.js with your company’s tax identification number, default currency, and the path to the X.509 certificate used for signing:

module.exports = {
  plugins: [
    {
      resolve: '@carphacom/invoice-plugin',
      options: {
        cif: 'RO12345678',
        currency: 'RON',
        certificatePath: '/etc/ssl/certs/merchant.pem',
        privateKeyPath: '/etc/ssl/private/merchant.key',
      },
    },
  ],
};

The plugin stores generated files in /var/lib/carphacom/invoices. You can retrieve an invoice via the REST endpoint /admin/invoices/:order_id or attach it automatically to the order confirmation email. The JSON version mirrors the XML structure, making it easy to import into ERP systems such as SAP Business One or the open‑source Carpathron Remote suite.

Customizing the invoice layout does not require code changes. CarphaCom ships with a Handlebars template (invoice-template.hbs) that defines the header, line items, and footer. Edit the template to add a logo, change language strings, or insert additional fields like purchase order numbers. After editing, run npm run build:templates to recompile the assets.

Below is a quick checklist comparing the default Romanian invoice format with a typical EU‑wide variant. Use it to verify that your custom template meets all statutory requirements.

Element Romanian mandatory EU optional CarphaCom default
CIF (Tax ID) Yes No Included
Data emiterii (Issue date) Yes Yes Included
TVA breakdown Yes Yes Included
Digital signature Yes No Generated
QR code for e‑invoicing Optional Optional Off by default
Custom footer notes Optional Optional Editable via template

When you need to adapt the invoice for a specific client, create a new Handlebars partial and reference it in invoice-template.hbs. The partial can pull data from custom order metadata, for example a projectCode field stored during checkout. This approach keeps the core plugin untouched, simplifying future upgrades.

Finally, test compliance by sending a sample invoice to the ANAF validation portal. The portal accepts the XML file and returns a confirmation code that you can store in the order record for audit purposes. By automating this loop, your Romanian store remains tax‑compliant without manual intervention.

Deploying and scaling the store on Vultr

Enable HTTPS with a free SSL certificate

After the CarphaCom Developer Edition is up, open the Vultr dashboard and navigate to Instances → Your Instance → SSL/TLS. Click Add Certificate and select Let’s Encrypt. Provide the domain name you configured for the storefront (e.g., shop.example.ro) and confirm the email address for renewal notices. Vultr will automatically issue and install the certificate, then redirect HTTP traffic to HTTPS. Verify the setup by visiting https://shop.example.ro and checking the lock icon in the browser.

Attach a CDN for faster content delivery

From the same instance view, select CDN and create a new distribution. Choose the origin as the public IP of your Medusa server, enable HTTP/2, and set the cache TTL to 300 seconds for dynamic API responses while keeping a longer TTL (86400 seconds) for static assets such as product images. After the CDN is provisioned, update the DNS A record to point the sub‑domain (e.g., cdn.shop.example.ro) to the CDN endpoint. This reduces latency for Romanian customers and offloads bandwidth from the core instance.

Configure monitoring and alerts

Vultr’s built-in monitoring panel can track CPU, memory, disk I/O, and network traffic. Enable Metrics for the instance and set thresholds: CPU usage above 80 % for five minutes, memory consumption over 75 %, or disk write latency exceeding 100 ms. Add email alerts to contact@qubitpage.com or integrate with a Slack webhook for real‑time notifications. For deeper insight, install the node_exporter package inside the server and connect it to a Grafana dashboard hosted on a separate Vultr instance.

Vertical scaling: adjusting resources on the fly

When traffic spikes—such as during a flash sale—use the Vultr control panel to increase CPU cores, RAM, or SSD size without redeploying. Select the instance, click Resize, choose a higher‑performance plan (for example, from 2 vCPU / 4 GB RAM to 4 vCPU / 8 GB RAM), and confirm. The operation completes within minutes and retains the existing IP address, so no DNS changes are required.

Horizontal scaling with load balancers

For sustained growth, deploy a Vultr Load Balancer in front of two or more Medusa instances. In the dashboard, create a new load balancer, assign a public IP, and add your existing instances as backend nodes. Enable session persistence based on cookies to keep user carts intact across requests. Configure health checks to ping /admin/health on each node; unhealthy nodes are automatically removed until they recover.

Automate scaling with the API

Vultr provides a REST API that can be called from a CI/CD pipeline or a custom script. Example Bash snippet to add a node when average CPU exceeds 70 %:

#!/bin/bash
TOKEN="YOUR_VULTR_API_TOKEN"
AVG=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "https://api.vultr.com/v2/instances/$INSTANCE_ID/metrics?metric=cpu" \
  | jq '.data | map(.value) | add / length')
if (( $(echo "$AVG > 70" | bc -l) )); then
  curl -X POST -H "Authorization: Bearer $TOKEN" \
    -d '{"region":"fra","plan":"vc2-2c-4gb","os_id":167}" \
    https://api.vultr.com/v2/instances
fi

This script can be scheduled with cron or triggered by a monitoring webhook, ensuring the storefront automatically adapts to demand.

Next steps and support

With SSL, CDN, monitoring, and scaling configured, the Medusa store is ready for production traffic across Romania. For custom automation, integration with Qubitpage’s AI agents, or assistance with robotics‑enabled fulfillment, visit the Products page or contact our team via the Contact form.

Frequently asked questions

How does Medusa handle product catalogs compared to traditional platforms?

Medusa stores product data in a PostgreSQL database, allowing direct SQL queries and granular indexing. Unlike monolithic SaaS solutions, you can extend the schema with custom fields for Romanian attributes such as cod_fiscal or unitate_de_masura. The headless architecture also lets you serve catalog data to multiple front‑ends, for example a Next.js storefront and a mobile app, without duplicating content.

Can I use CarphaCom’s courier connectors for any Romanian delivery service?

CarphaCom ships with ready‑made integrations for the major Romanian couriers: Fan Courier, Sameday, and DHL Romania. Each connector maps the carrier’s API to Medusa’s fulfillment workflow, handling tracking numbers and status callbacks automatically. If you need a smaller regional provider, you can clone an existing connector and adjust the endpoint and authentication parameters.

What payment providers are supported out of the box in CarphaCom?

The platform includes native modules for PayU Romania, Netopia, and Stripe, all of which expose a unified createPaymentSession function to Medusa. These modules handle 3‑D Secure redirects, recurring subscriptions, and currency conversion to RON. Adding another PSP requires installing its SDK and registering a new payment plugin in the CarphaCom configuration file.

Do I need a developer to customize invoice templates for Romanian tax rules?

CarphaCom provides a default invoice template that complies with basic Romanian invoicing requirements, such as VAT identification and fiscal year format. To add mandatory fields like „Număr de ordine fiscal” or to change the layout for electronic signatures, a developer must edit the Handlebars template located in /templates/invoice.hbs. The template system supports conditional sections, so you can toggle extra rows based on the customer’s tax status without rewriting the whole file.

Is the Vultr Marketplace deployment suitable for high‑traffic Romanian stores?

The Developer Edition on Vultr provisions a 2‑vCPU, 4 GB RAM instance with SSD storage and a PostgreSQL cluster in the same region, which is sufficient for launch phases and moderate traffic spikes. For sustained high traffic, you can scale vertically by selecting a larger instance type or horizontally by adding a load balancer and read replicas. All scaling actions are performed through the Vultr dashboard, and the underlying Docker composition of CarphaCom automatically detects the new resources.

Next steps for launching your Romanian Medua store

Begin by cloning the CarphaCom repository from GitHub and deploying the Developer Edition on a Vultr instance. Run docker compose up -d to start Medusa, PostgreSQL, and the Next.js storefront, then verify connectivity to the Romanian courier APIs by issuing a test rate request with curl. Adjust the medusa-config.js file to include your payment gateway credentials, and enable the invoice generator module provided in the plugins folder.

After confirming order flow in a staging environment, create a DNS record pointing your domain to the Vultr public IP and enable HTTPS with Let’s Encrypt. Finally, run the built‑in medusa seed command to populate product data, and perform a live checkout to ensure invoices are generated and emailed correctly.

When the store passes all functional tests, switch the instance to production mode and monitor logs with docker logs -f. If you need custom integrations or AI‑driven recommendations, our team can extend the platform further. Explore how Qubitpage can help you scale.

Written by

Mircea S. Rusu

Founder and engineer of Qubitpage. Support engineer since 2005, CMS developer, former technical consultant at Niguarda Ca' Granda Hospital in Milan, and author of the book “The Architect”.

More about the author →