← Semua picks

Bundle Recipe Recommended

Bundle: E-commerce SMB Indonesia dengan Medusa + Astro

Stack untuk e-commerce SMB Indonesia: Medusa backend + Astro storefront + Midtrans payment. Lebih murah dari Shopify, lebih scalable dari WooCommerce.

24 Mei 2026 · 9 menit ·Use case: E-commerce SMB skala kecil-menengah Indonesia
MedusaAstroPostgreSQLMidtransCloudflare Pages

Konteks

Klien e-commerce yang saya handle sejak 2024:

  • Toko fashion online (200 SKU, 80-150 order/bulan)
  • Toko buku indie (500 SKU, 30-60 order/bulan)
  • Toko bibit tanaman (120 SKU, 50-100 order/bulan)

3 dari 3 saya rekomendasi pindah dari Shopify atau WooCommerce ke Medusa. Reason: cost + control.

The stack

Backend (admin + API):    Medusa 2.x (Node.js)
Storefront (customer):    Astro 6 + React island untuk cart
Database:                 PostgreSQL 16 (Neon atau self-hosted)
Payment:                  Midtrans Snap (existing Indonesia integration)
File storage:             Cloudflare R2
Image CDN:                Cloudflare Images (atau bunny.net)
Email:                    Resend
Search:                   Meilisearch (self-hosted) atau Algolia
Hosting:                  
  - Medusa: Hetzner CX21 ($5/bulan)
  - Astro storefront: Cloudflare Pages (free)

Mengapa Medusa (bukan Shopify atau WooCommerce)

vs Shopify

AspectShopify BasicMedusa self-host
Monthly cost$29 ($39 nanti)$5 (VPS)
Transaction fee2% (kalau Anda tidak pakai Shopify Payments)0% (Anda bayar Midtrans saja 2.5%)
Custom developmentPlugin/app expensiveFull control
Indonesia paymentStripe-based, butuh workaroundMidtrans native
HostingShopify-lockedAnywhere

Saving untuk klien 100 order/bulan @ Rp 500rb avg:

  • Shopify cost: $29 + 2% × Rp 50jt = $29 + $66 = ~Rp 1.5 juta/bulan
  • Medusa cost: $5 + (Midtrans 2.5% = $83) = ~Rp 1.4 juta/bulan
  • Saving small di volume kecil, tapi scaling lebih murah.

Plus: Medusa adalah open-source, Anda own code.

vs WooCommerce

AspectWooCommerceMedusa
StackPHP + MySQL + WordPressNode + Postgres
PerformanceOK, tapi WP plugin can slowFaster (modern stack)
Headless supportPossible but awkwardNative (API-first)
CustomizationPlugin + custom themeFull code-level
HostingcPanel hosting (Niagahoster, dll)VPS or PaaS

WooCommerce winner kalau team Anda WordPress-native. Medusa winner kalau Anda comfortable dengan modern stack + butuh headless.

Setup step

1. Backend (Medusa)

# Setup di Hetzner VPS Singapore
ssh root@your-vps

# Install Node 22 + PostgreSQL
curl -fsSL https://deb.nodesource.com/setup_22.x | bash
apt install -y nodejs postgresql

# Clone Medusa starter
npx create-medusa-app@latest --skip-db --skip-env
cd my-medusa-store

# Configure env
cat > .env <<EOF
DATABASE_URL=postgresql://medusa:password@localhost:5432/medusa_store
REDIS_URL=redis://localhost:6379
COOKIE_SECRET=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)
ADMIN_CORS=https://admin.tokoanda.id
STORE_CORS=https://tokoanda.id
EOF

# Run migrations + seed
yarn medusa migrations run
yarn seed

# Start dev
yarn dev

Admin UI di http://localhost:9000/admin. Production: setup Nginx + systemd untuk run di background.

2. Midtrans payment integration

Medusa native support Stripe/PayPal. Untuk Midtrans, butuh custom plugin atau API integration via Webhook.

// modules/payment-midtrans/service.ts
import { AbstractPaymentProvider } from '@medusajs/framework/utils';
import midtransClient from 'midtrans-client';

class MidtransPaymentProvider extends AbstractPaymentProvider {
  static identifier = 'midtrans';
  private snap = new midtransClient.Snap({
    isProduction: process.env.NODE_ENV === 'production',
    serverKey: process.env.MIDTRANS_SERVER_KEY!,
  });

  async initiatePayment(context) {
    const { amount, currency_code, resource_id } = context;
    
    const transaction = await this.snap.createTransaction({
      transaction_details: {
        order_id: resource_id,
        gross_amount: Math.round(amount), // Midtrans expects IDR integer
      },
      enabled_payments: ['bca_va', 'bni_va', 'mandiri_va', 'gopay', 'qris'],
    });

    return {
      session_data: {
        token: transaction.token,
        redirect_url: transaction.redirect_url,
      },
    };
  }

  // Webhook handler (separate route)
  async authorizePayment(payment, context) {
    // Verify signature dari Midtrans webhook
    // Update payment status
  }
}

3. Storefront (Astro)

---
// src/pages/produk/[handle].astro
import StorefrontLayout from '~/layouts/StorefrontLayout.astro';
import AddToCart from '~/components/AddToCart.jsx'; // React island

export async function getStaticPaths() {
  const res = await fetch(`${import.meta.env.MEDUSA_URL}/store/products`);
  const { products } = await res.json();
  return products.map((p) => ({
    params: { handle: p.handle },
    props: { product: p },
  }));
}

const { product } = Astro.props;
---
<StorefrontLayout title={product.title}>
  <article>
    <img src={product.thumbnail} alt={product.title} />
    <h1>{product.title}</h1>
    <p>Rp {product.variants[0].calculated_price.toLocaleString('id-ID')}</p>
    <p>{product.description}</p>
    <AddToCart productId={product.id} client:visible />
  </article>
</StorefrontLayout>

Storefront static (Astro generate semua product page at build). Trigger rebuild saat product update via Medusa webhook.

4. Image handling

Klien upload product image via Medusa admin → Medusa upload ke Cloudflare R2 (S3-compatible). Storefront fetch image via Cloudflare Images variant untuk auto-resize/format.

Original (admin upload): 4000x3000 JPEG, 2MB
↓ (Cloudflare Images)
Thumbnail: 400x300 AVIF, 25KB
Product detail: 1200x900 AVIF, 80KB
Zoom view: 2400x1800 AVIF, 250KB

Cost: Cloudflare Images $5/bulan untuk 100K stored + unlimited delivery. Saving signifikan vs Shopify image bandwidth charge.

Performance untuk customer Indonesia

Bench dari klien fashion (200 SKU):

  • Homepage LCP (Jakarta 4G): 1.1s
  • Product detail LCP: 1.3s
  • Add to cart latency: 180ms
  • Checkout to payment redirect: 800ms

Untuk e-commerce kompetitif, ini metric yang win conversion (vs Shopify default 2.5-3.5s LCP).

Common pain point

Inventory sync: kalau klien juga jualan di marketplace (Tokopedia, Shopee), butuh sync inventory manual atau pakai middleware (Shipper, dll). Medusa belum punya official integration untuk Indonesian marketplace.

SEO untuk product page: pastikan setiap product punya unique title, meta description, schema Product markup. Saya generate via Astro frontmatter dari Medusa data.

Customer account: Medusa default punya customer login. Untuk SMB Indonesia, sometimes lebih simple guest checkout (kebanyakan Indonesian customer prefer not login). Configure di Medusa region settings.

Shipping rate: Indonesia shipping (JNE, J&T, SiCepat, Tiki) butuh integration. Pakai RajaOngkir API atau Biteship API untuk live shipping rate.

// Get shipping rate untuk order
const res = await fetch('https://api.biteship.com/v1/rates/couriers', {
  headers: { Authorization: `Bearer ${BITESHIP_KEY}` },
  // ... courier_code, origin, destination, weight
});

Cost summary

Untuk klien e-commerce 100 order/bulan, 200 SKU:

ItemCost (USD)
Hetzner CX21 (Medusa backend)$5
Cloudflare Pages (Astro storefront)$0
Neon Postgres atau self-hosted$0-19
Cloudflare R2 + Images$5
Resend email$0
Biteship API$0-50 (per shipment fee)
Midtrans fee2.5% per transaksi
Total fixed$10-29/bulan

vs Shopify Basic $29 + 2% transaction fee + apps subscriptions. Medusa saving signifikan di scale.

Verdict

Recommended untuk SMB Indonesia e-commerce yang:

  • Bersedia self-host backend (atau hire dev untuk maintain)
  • Sudah 50+ order/bulan (di bawah ini, Shopify Basic acceptable)
  • Mau control penuh + customization

Skip kalau:

  • Anda solo founder tanpa dev skill (Shopify lebih plug-and-play)
  • Order volume sangat tinggi (>500/hari) — perlu evaluasi infra serious
  • Team Anda WordPress-only — WooCommerce easier transition

Untuk SEO lokal toko fisik yang juga online, prioritaskan: GBP optimization, schema LocalBusiness markup, dan review velocity yang natural. Tools-stack di atas tetap relevan, dengan tambahan local SEO audit setiap kuartal.

Ditulis oleh Asti Larasati

// Pick Bundle Recipe lain


← Semua picks RSS feed