Build a fully custom storefront for your RMZ store using the Storefront SDK and Next.js. Your store handles products, payments, orders, and customer management — you control the entire frontend experience.
Prerequisites
- An RMZ store with products and at least one payment method configured
- API keys from Dashboard > Settings > API Keys (public key, and secret key for server-side operations)
- Node.js 18+ installed
- Your custom domain configured in the store dashboard
Quick Start with the Template
The fastest way to get started is to clone the official Next.js template:
Clone the template
git clone https://github.com/Dokan-E-Commerce/rmz-storefront-nextjs my-storefront
cd my-storefront
Install dependencies
The template already includes the Storefront SDK as a dependency. Configure environment variables
Create a .env.local file in the project root:NEXT_PUBLIC_RMZ_PUBLIC_KEY=pk_your_public_key_here
RMZ_SECRET_KEY=sk_your_secret_key_here
NEXT_PUBLIC_API_URL=https://front.rmz.gg/api
Never expose your RMZ_SECRET_KEY in client-side code. It should only be used in server-side API routes and server components.
Start the development server
Open http://localhost:3000 to see your storefront.
Starting from Scratch
If you prefer to build from an existing Next.js project:
1. Install the SDK
npm install rmz-storefront-sdk
2. Initialize the SDK
Create a shared SDK instance for your application:
// lib/rmz.ts
import { createStorefrontSDK } from 'rmz-storefront-sdk';
// Client-side SDK (safe for browser)
export const sdk = createStorefrontSDK({
publicKey: process.env.NEXT_PUBLIC_RMZ_PUBLIC_KEY!,
environment: 'production'
});
// Server-side SDK (with HMAC authentication)
export const serverSDK = createStorefrontSDK({
publicKey: process.env.NEXT_PUBLIC_RMZ_PUBLIC_KEY!,
secretKey: process.env.RMZ_SECRET_KEY!,
environment: 'production'
});
The SDK defaults to https://front.rmz.gg/api as the API URL. You only need to set apiUrl if you are using a custom domain.
3. Fetch and Display Products
// app/page.tsx
import { serverSDK } from '@/lib/rmz';
export default async function Home() {
const { data: products } = await serverSDK.products.getAll({ per_page: 12 });
return (
<div className="grid grid-cols-3 gap-6">
{products.map((product) => (
<div key={product.id} className="border rounded-lg p-4">
<img src={product.image} alt={product.name} className="w-full" />
<h2 className="text-xl font-bold mt-2">{product.name}</h2>
<p className="text-gray-600">{product.actual_price} {product.currency}</p>
<a href={`/products/${product.id}`} className="text-blue-600">
View Details
</a>
</div>
))}
</div>
);
}
4. Build the Cart
// components/AddToCart.tsx
'use client';
import { sdk } from '@/lib/rmz';
import { useState } from 'react';
export function AddToCart({ productId }: { productId: number }) {
const [loading, setLoading] = useState(false);
async function handleAddToCart() {
setLoading(true);
try {
const cart = await sdk.cart.addItem(productId, 1);
// Store cart token for subsequent requests
localStorage.setItem('cart_token', cart.cart_token);
alert('Added to cart!');
} catch (error) {
console.error('Failed to add to cart:', error);
} finally {
setLoading(false);
}
}
return (
<button onClick={handleAddToCart} disabled={loading}>
{loading ? 'Adding...' : 'Add to Cart'}
</button>
);
}
5. Implement Checkout
// app/checkout/page.tsx
'use client';
import { sdk } from '@/lib/rmz';
import { useState } from 'react';
export default function Checkout() {
const [loading, setLoading] = useState(false);
async function handleCheckout(paymentMethod: string) {
setLoading(true);
try {
const checkout = await sdk.checkout.create({
payment_method: paymentMethod,
});
if (checkout.redirect_url) {
window.location.href = checkout.redirect_url;
}
} catch (error) {
console.error('Checkout failed:', error);
} finally {
setLoading(false);
}
}
return (
<div>
<h1>Checkout</h1>
<button onClick={() => handleCheckout('card')}>Pay with Card</button>
<button onClick={() => handleCheckout('mada')}>Pay with Mada</button>
<button onClick={() => handleCheckout('applepay')}>Apple Pay</button>
</div>
);
}
6. Customer Authentication
// app/auth/page.tsx
'use client';
import { sdk } from '@/lib/rmz';
import { useState } from 'react';
export default function Auth() {
const [step, setStep] = useState<'phone' | 'otp' | 'done'>('phone');
const [phone, setPhone] = useState('');
const [otp, setOtp] = useState('');
async function startAuth() {
await sdk.auth.startOTP({
country_code: '966',
phone: phone,
});
setStep('otp');
}
async function verifyOTP() {
const result = await sdk.auth.verifyOTP({ code: otp });
// Store the Bearer token
localStorage.setItem('auth_token', result.token);
setStep('done');
}
if (step === 'phone') {
return (
<div>
<input value={phone} onChange={e => setPhone(e.target.value)} placeholder="Phone number" />
<button onClick={startAuth}>Send OTP</button>
</div>
);
}
if (step === 'otp') {
return (
<div>
<input value={otp} onChange={e => setOtp(e.target.value)} placeholder="Enter code" />
<button onClick={verifyOTP}>Verify</button>
</div>
);
}
return <p>Authenticated!</p>;
}
Deploying
Vercel (Recommended)
npm install -g vercel
vercel
Set your environment variables in the Vercel dashboard under Settings > Environment Variables.
The storefront is a standard Next.js app and can be deployed to any platform that supports Node.js:
- Netlify: Add a
netlify.toml with the Next.js plugin
- AWS Amplify: Connect your Git repository
- Docker: Use the official Next.js Docker example
- Self-hosted: Run
npm run build && npm start
Domain Configuration
After deploying, configure your custom domain:
- Go to Dashboard > Settings > Domains
- Add your storefront domain (e.g.,
store.yourdomain.com)
- Update your DNS records as instructed
- The Storefront API will accept requests from this domain automatically
For the best SEO and performance, use server-side rendering (SSR) or static site generation (SSG) for product and category pages. Use client-side rendering only for interactive elements like cart and checkout.
Next Steps
Storefront SDK Docs
Full SDK API reference with all available methods.
Storefront API
Direct API reference if you prefer raw HTTP requests.