StoreMigrate - Product Migration PHP SaaS Script
A complete PHP SaaS application for migrating product catalogs between major e-commerce platforms. Supports WooCommerce, Shopify, OpenCart 4, and ikas out of the box — covering all 16 platform-to-platform combinations. Built with a clean adapter pattern, so additional platforms can be added with a single file.
System Requirements
| Requirement | Minimum | Recommended |
|---|---|---|
| PHP | 8.1 | 8.3+ |
| MySQL / MariaDB | 8.0 / 10.4 | 8.0+ / 10.6+ |
| PHP Extensions | pdo_mysql · curl · json · mbstring · fileinfo | |
| Web Server | Apache (mod_rewrite) or Nginx | |
| Disk Space | 20 MB | 1 GB+ for payment proofs |
Installation
Upload Files
Extract the ZIP and upload all files to your web server's document root (or a subdirectory).
Set Permissions
chmod 755 config/
chmod 755 uploads/
chmod 755 uploads/payment_proofs/
Configure Apache
The included .htaccess file handles clean URLs automatically. Ensure mod_rewrite is enabled:
sudo a2enmod rewrite
sudo service apache2 restart
Run the Installer
Open https://yourdomain.com/install.php in your browser and complete the 3 steps:
- Requirements Check — all items must show ✓
- Database & Admin — enter your MySQL credentials and create the admin account
- Complete — installation is done!
Delete install.php
install.php from your server immediately after installation to prevent unauthorized reinstallation.First Login
Navigate to https://yourdomain.com/login and sign in with the admin email and password you created during installation.
The admin panel is accessible via the left sidebar under the Admin section (only visible to admin accounts).
Admin Settings
Go to Admin → Settings to configure:
| Setting | Description |
|---|---|
| Site Name | Application name shown in the header and emails |
| Admin/Support Email | Recipient for admin notification emails |
| Free Plan Limits | Default product and store limits for free users |
| Bank Info | Payment instructions shown to users when subscribing |
| Email Notifications | Toggle which automatic emails are sent |
| SMTP Settings | Configure SMTP for reliable email delivery |
| Migration Engine | Batch size, delay, and execution time limits |
Managing Plans
Go to Admin → Plans to create and manage subscription plans.
- Product Limit: Number of products the user can migrate. Set to
-1for unlimited. - Store Limit: Number of stores the user can connect. Set to
-1for unlimited. - Features: One feature per line — shown as bullet points on the plans page.
- Sort Order: Lower numbers appear first.
Email Configuration
StoreMigrate can send emails for:
- Password reset links
- Subscription approved/rejected notifications
- New subscription request alerts (to admin)
Without SMTP configuration, emails are sent via PHP's built-in mail() function. For reliable delivery, configure SMTP in Admin → Settings → Email Settings.
PHPMailer Setup (Recommended)
Download PHPMailer
Download the latest release from github.com/PHPMailer/PHPMailer/releases
Copy Files
From the downloaded ZIP's src/ folder, copy these 3 files to lib/phpmailer/:
lib/phpmailer/PHPMailer.php
lib/phpmailer/SMTP.php
lib/phpmailer/Exception.php
Configure SMTP
Go to Admin → Settings → Email Settings and enter your SMTP credentials.
Common settings for Gmail: Host: smtp.gmail.com, Port: 587, Encryption: TLS
WooCommerce API Setup
Enable Pretty Permalinks
In your WordPress admin: Settings → Permalinks → Post name → Save
This is required for the WooCommerce REST API to work.
Create API Keys
Go to WooCommerce → Settings → Advanced → REST API → Add key
- Description: StoreMigrate
- User: Administrator
- Permissions: Read/Write
Copy the Consumer Key and Consumer Secret.
Add the Store
In StoreMigrate: My Stores → Add Store → Select WooCommerce → Enter your store URL and API keys.
Shopify API Setup
dev.shopify.com. The old direct access token (shpat_...) is no longer available for new apps. Follow all 5 steps below exactly.Open Dev Dashboard
In Shopify Admin: Settings → Apps → Develop apps → Build apps in Dev Dashboard
This opens dev.shopify.com. Click Create app, enter a name (e.g. StoreMigrate) → Create.
Create a Version & Set API Scopes
Fill in the version form on your new app's page:
- App URL: leave as default (
https://shopify.dev/apps/default-app-home) - Access (API Scopes): enter
read_products,write_products
Click Release → Release.
Install the App on Your Store
In the left sidebar click Installs → Install app → select your store → click Install.
Copy Client ID & Secret
Click the Settings tab on your app page:
- Client ID → copy it
- Client Secret → click "Show" to reveal, then copy
Add Store in StoreMigrate
Go to My Stores → Add Store → Shopify:
| Field | Value |
|---|---|
| Store URL | https://yourstore.myshopify.com |
| Client ID | Paste Client ID from Step 4 |
| Client Secret | Paste Client Secret from Step 4 |
| Access Token | Leave blank |
Click Test Connection — you should see a success message.
• not installed → You skipped Step 3 (Installs → Install app)
• shop_not_permitted → Store and app are in different Shopify organizations
• 401 Invalid token → You filled in the Access Token field — leave it blank
• Invalid scopes → You did not Release a version in Step 2, or scopes were not entered
OpenCart 4 Setup
sm_oc4_bridge.php) that you upload to your OpenCart server. This file acts as a secure local API proxy between StoreMigrate and your OpenCart database.
Download the Bridge File
In StoreMigrate, go to My Stores → Add Store → OpenCart 4. A blue info box will appear with a Download sm_oc4_bridge.php button. Click it to download the file.
You can also download it directly while logged in at: https://yourdomain.com/bridge/download
Upload to OpenCart Root
Upload sm_oc4_bridge.php to your OpenCart root directory — the same folder that contains OpenCart's own index.php.
opencart-root/
├── index.php ← OpenCart entry point
├── sm_oc4_bridge.php ← Upload here
├── admin/
├── catalog/
└── system/
Set the API Key
Open sm_oc4_bridge.php in a text editor. Find and change the SM_API_KEY constant to a long, random string:
define('SM_API_KEY', 'your-long-random-secret-key-here');
Use a password manager or openssl rand -hex 32 in terminal to generate a strong key.
Test the Bridge
Open this URL in your browser (replace values with your own):
https://yourstore.com/sm_oc4_bridge.php?action=test&sm_key=YOUR_KEY
You should see a JSON response like:
{"success":true,"message":"StoreMigrate bridge connected","total_products":142}
If you get a blank page or error, check that the file is uploaded to the correct directory and that PHP can access your OpenCart config.php.
Add Store in StoreMigrate
Go to My Stores → Add Store → OpenCart 4 and fill in:
| Field | Value |
|---|---|
| Store URL | https://yourstore.com (OpenCart root URL) |
| Bridge API Key | The same key you set in SM_API_KEY |
| Language ID | Default OC4 language ID — usually 1. Check Admin → System → Localisation → Languages |
| Store ID | Main store = 0. Multi-store: Admin → System → Settings |
Click Test Connection to verify.
Delete Bridge After Migration
sm_oc4_bridge.php from your server after migration is complete. The file provides direct database access and should not remain on a public server permanently.ikas Setup
api.myikas.com. No bridge file is needed — StoreMigrate communicates directly with the ikas API using OAuth 2.0 client credentials.
Open ikas Dashboard
Log in to your ikas store and go to Apps → My Apps in the left sidebar.
Create a Private App
Click Private Apps → then Create Private App. Choose Standard App.
Set Required Scopes
Select the following API scopes:
product:read— required to read products (use as source)product:write— required to create/update products (use as target)
Click Create.
Copy Credentials
After creation, ikas shows your Client ID and Client Secret.
Add Store in StoreMigrate
Go to My Stores → Add Store → ikas and fill in:
| Field | Value |
|---|---|
| Store URL | Your store's public URL, e.g. https://mystore.myikas.com (informational only) |
| Client ID | Paste Client ID from Step 4 |
| Client Secret | Paste Client Secret from Step 4 |
Click Test Connection — you should see a success message with your product count.
Background Migration & Cron Job Setup
How the Migration System Works
When you click Run Migration:
- The migration is immediately added to a queue (takes <1 second)
- A background PHP process is spawned automatically
- The migration runs in the background — you can navigate freely
- The migration page polls for status updates every 2.5 seconds
- The cron job acts as a safety net to pick up any missed jobs
This architecture means your browser never waits for a migration to finish. 10,000 products or 100 — the behavior is the same.
Cron Job Setup
Add this cron job in your hosting control panel (cPanel, Plesk, DirectAdmin):
- Log in to your hosting control panel
- Go to Advanced → Cron Jobs (cPanel) or Scheduled Tasks (Plesk)
- Set frequency to Every Minute (
* * * * *) - Command:
php /home/YOUR_USER/htdocs/YOUR_DOMAIN/cron.php >> /dev/null 2>&1 - Replace the path with your actual file path
- Save
Cancelling a Migration
Click the Cancel button on the migration detail page. The system:
- If queued (not yet started): cancels immediately
- If running: requests a graceful stop. The worker stops after the current batch (~10 products)
- If the worker appears stuck (no activity for 5+ minutes): cancels immediately
All products successfully migrated before cancellation are kept on the target store.
Abuse Prevention & Limit Enforcement
- Only one migration per user can run at a time
- Product limits are checked before starting and during migration (every 10 products)
- If a user's plan limit is reached mid-migration, the migration stops gracefully with a log message
- Admin can adjust user limits in real time from the Users section — changes take effect within ~10 products
Stuck Migration Recovery
If a migration gets stuck in Running state (e.g., server crash, PHP memory exhaustion), the cron job automatically marks it as Failed after 15 minutes of inactivity. Users can then restart it.
Running a Migration
- Go to Migrations → New Migration
- Select a Source Store (products will be fetched from here)
- Select a Target Store (products will be created here)
- Choose which data to transfer: images, categories, prices, attributes, stock, descriptions
- Optionally set a product limit for this run
- Click Start Migration
The migration progress is shown in real-time with a progress bar and activity log. You can navigate away and return to the migration detail page — it will continue running and update automatically.
Subscription Management
For Users
- Go to Plans & Pricing
- Click Subscribe on the desired plan
- Upload your bank transfer receipt/dekont
- Click Submit Request
- Wait for admin approval (you'll receive an email if SMTP is configured)
For Admin
- Go to Admin → Subscriptions
- Click Activate on a pending request
- Review the confirmation dialog (shows current limits and what will be added)
- Confirm activation
Adding a New Platform
See ADDING_PLATFORMS.md in the package root for the full developer guide. Summary:
- Create
platforms/YourPlatform.phpimplementingPlatformInterface - Register in
platforms/PlatformFactory.php$registryarray
That's it — the platform will automatically appear in the store connection form.
Nginx Configuration
server {
listen 80;
server_name yourdomain.com;
root /var/www/storemigrate;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Block access to sensitive directories
location ~ ^/(core|models|controllers|platforms|config|lib)/ {
deny all; return 404;
}
# Block PHP execution in uploads
location ~ ^/uploads/.*\.php$ {
deny all; return 404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
Security Notes
| Feature | Implementation |
|---|---|
| CSRF Protection | Every state-changing form includes a signed token validated server-side |
| Password Hashing | bcrypt with cost factor 12 (via password_hash) |
| SQL Injection | All queries use PDO prepared statements — no string concatenation |
| XSS Prevention | All view output escaped with htmlspecialchars() |
| Brute Force | Login locked for 15 min after 5 failed attempts per IP/email |
| Session Security | ID regeneration on login, httponly+samesite cookies, status check every request |
| File Uploads | MIME type + size validated; PHP execution blocked in uploads/ |
| Path Traversal | Payment proof paths validated against allowed directory before display |
| Sensitive Dirs | core/, config/, controllers/ etc. blocked via .htaccess |
| Store Credentials | Stored as JSON — for stronger security, configure openssl_encrypt in StoreController |
Frequently Asked Questions
Do I need to keep my browser open during a migration?
No. Migrations run in the background via the cron job. Once you click Run Migration, you can safely close the browser. Progress is saved to the database and you can check status anytime from the Migrations page.
Can I run migrations for multiple stores at the same time?
Yes. Each migration runs in its own isolated background worker. One user's migration does not block another's.
Why are images not showing in OpenCart 4 after migration?
The bridge file downloads images from the source store and saves them to /image/catalog/storemigrate/ on your OpenCart server. Make sure the bridge file is running on the same server as OpenCart and that the /image/ directory is writable (755 permissions). If the download fails, the raw URL is stored as a fallback.
Why are ikas variations not appearing correctly?
Make sure your ikas Private App has both product:read and product:write scopes. ikas requires at least one variant per product — StoreMigrate handles this automatically. If a product shows no variants, check that the Client Secret was entered correctly (it's only shown once at creation).
What happens if a product already exists at the target store?
StoreMigrate checks for existing products by SKU. If a match is found, the product is updated instead of duplicated. If no SKU match exists, a new product is created.
Stripe says "test mode" but I'm using live keys — why?
In Admin → Settings → Payment Methods → Stripe, make sure Test Mode is unchecked and your Live keys (not test keys) are entered in the Live Key fields. Test keys start with sk_test_ and live keys start with sk_live_.
How do I know the cron job is working?
Run a test migration with a small product limit (e.g., 1–5 products). If the migration status changes from queued to running to completed within a minute or two, the cron is working. You can also check Admin → Settings → Cron Configuration for the exact command and verify it matches what's set in your cPanel or server crontab.
Migration is stuck at 0% — what's wrong?
Check that: (1) Both stores are connected and the test passes. (2) Your server allows outbound cURL requests. (3) WooCommerce has pretty permalinks enabled. (4) The API key has Read/Write permissions.
Images are not being copied
Enable the Product Images option when creating the migration. Images are downloaded from the source store and re-uploaded to the target. Ensure both servers can reach each other.
Categories are showing as flat (no hierarchy)
Enable the Categories option when creating the migration. The engine fetches the full category tree from the source and re-creates the parent→child relationships on the target.
Can a user buy the same plan twice?
Yes. When a new subscription is activated while the user has remaining quota, the unused products are carried over as a bonus. Example: 300 remaining + new 1000 plan = 1300 total available.
How do I reset a user's migration count?
Activate a new subscription for the user via Admin → Subscriptions. The counter resets from the activation moment.
Password reset emails are not arriving
Configure SMTP settings in Admin → Settings. Without SMTP, emails rely on PHP's mail() which many hosting providers block.
Payment System
StoreMigrate supports three payment methods, all configurable from Admin → Settings → Payment Methods:
| Method | Approval | Test Mode | When to use |
|---|---|---|---|
| 💳 Stripe | Automatic | ✅ Built-in | Credit/debit card payments |
| 🅿 PayPal | Automatic | ✅ Built-in | PayPal balance or card via PayPal |
| 🏦 Bank Transfer | Manual (admin) | N/A | Wire transfer / local bank payment |
Stripe Setup
Create a Stripe Account
Go to dashboard.stripe.com and create an account.
Get Your API Keys
Dashboard → Developers → API keys. You need two keys:
- Publishable key (pk_live_... or pk_test_...)
- Secret key (sk_live_... or sk_test_...)
Start with test keys, switch to live keys when ready.
Set Up a Webhook
Dashboard → Developers → Webhooks → Add endpoint
- Endpoint URL:
https://yourdomain.com/payment/stripe/webhook - Events:
checkout.session.completed,payment_intent.payment_failed
Copy the Signing secret (whsec_...) and enter it in Admin → Settings.
Configure in Admin Panel
Admin → Settings → Payment Methods → Stripe:
- Enable Stripe ✓
- Paste Live/Test keys
- Paste Webhook signing secret
- Toggle Test Mode as needed
4242 4242 4242 4242, any future date, any CVCPayPal Setup
Create a PayPal Developer Account
Go to developer.paypal.com and log in or sign up.
Create an App
Dashboard → My Apps & Credentials → Create App (both Sandbox and Live tabs).
Copy the Client ID and Secret from each app.
Add Webhook
In your PayPal Live App: Webhooks → Add Webhook
- URL:
https://yourdomain.com/payment/paypal/webhook - Events:
PAYMENT.CAPTURE.COMPLETED,CHECKOUT.ORDER.APPROVED
Configure in Admin Panel
Admin → Settings → Payment Methods → PayPal:
- Enable PayPal ✓
- Paste Sandbox and Live Client ID/Secret
- Toggle Sandbox Mode as needed
Bank Transfer
Bank transfer requires manual admin approval:
- User selects "Bank Transfer" and uploads their payment receipt
- Admin sees the pending subscription in Admin → Subscriptions
- Admin views the uploaded receipt and clicks Activate
- User receives an email notification (if SMTP is configured)
Configure your bank account details in Admin → Settings → Bank Transfer Details.
The bank info text is shown to users in the payment modal when they select bank transfer.
Credits & Licenses
| Component | License | Used for |
|---|---|---|
| PHP 8.1+ | PHP License | Server-side runtime |
| PHPMailer (optional) | LGPL v2.1 | SMTP email sending (not bundled — install separately) |
| WooCommerce REST API | GPL v3 | Source/target platform integration |
| Shopify Admin API | Shopify API Terms | Source/target platform integration |
| OpenCart 4 (Bridge) | GPL v3 | Source/target platform via bridge file |
| ikas GraphQL API v2 | ikas API Terms | Source/target platform integration |
All other code is original and licensed under the Codester Regular & Extended License.
© 2026 StoreMigrate - Product Migration PHP SaaS Script. All rights reserved.