> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tagada.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Path Remapping (React)

> Advanced URL customization with parameter extraction for SEO and UX optimization

# Path Remapping Guide for React Plugins

<CardGroup cols={2}>
  <Card title="Feature Overview" icon="sparkles">
    Path remapping allows you to customize external URLs while maintaining your plugin's internal routing logic. Perfect for SEO-optimized URLs, branded paths, and multi-market operations.
  </Card>

  <Card title="Framework" icon="react">
    **React SDK v2** - Requires React with TanStack Query
  </Card>
</CardGroup>

<Note>
  **React is Required**: This guide covers path remapping for React-based TagadaPay plugins. All code examples use React hooks and patterns from the TagadaPay SDK v2.
</Note>

## What is Path Remapping?

Path remapping lets you expose **custom, SEO-friendly URLs** to your users while your plugin continues to use its **original internal paths** internally.

### Example Scenario

Your plugin defines these internal paths:

```
/checkout
/checkout/step1
/checkout/step2
```

But you want customers to see:

```
/buy-now
/buy-now/shipping  
/buy-now/payment
```

**Path remapping makes this possible!** ✨

<CardGroup cols={2}>
  <Card title="SEO Benefits" icon="chart-line">
    Use keyword-rich URLs like `/buy-now` instead of `/checkout`
  </Card>

  <Card title="Brand Consistency" icon="palette">
    Match URLs to your brand language across markets
  </Card>

  <Card title="A/B Testing" icon="flask">
    Test different URL structures without changing plugin code
  </Card>

  <Card title="Multi-Market" icon="globe">
    Localized URLs: `/acheter` (FR) vs `/kaufen` (DE) vs `/checkout` (EN)
  </Card>
</CardGroup>

## How It Works

<Steps>
  <Step title="You configure the mapping in CRM">
    Tell TagadaPay: "When users visit `/custom/:id`, route to plugin's `/hello-with-param/:myparam`"
  </Step>

  <Step title="TagadaPay injects configuration">
    The platform automatically injects the mapping as a meta tag in your plugin's HTML
  </Step>

  <Step title="SDK handles routing">
    The SDK transparently handles URL matching and parameter extraction
  </Step>

  <Step title="Your plugin works unchanged">
    Your code continues to use internal paths - no modifications needed!
  </Step>
</Steps>

## Architecture

```mermaid theme={null}
graph LR
    A[User visits<br/>/buy-now/product-123] --> B[TagadaPay Platform]
    B --> C[Path Remap:<br/>external → internal]
    C --> D[Plugin receives<br/>/checkout/:productId]
    D --> E[Plugin renders<br/>CheckoutPage]
    
    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#f3e5f5
    style D fill:#e8f5e9
    style E fill:#fce4ec
```

## Configuration

### In Plugin Manifest

Mark which pages support remapping:

```json theme={null}
{
  "pluginId": "my-checkout-plugin",
  "pages": [
    {
      "path": "/",
      "remappable": false  // Static landing page
    },
    {
      "path": "/checkout",
      "remappable": true   // ✅ Can be remapped
    },
    {
      "path": "/checkout/:step",
      "remappable": true   // ✅ Can be remapped
    },
    {
      "path": "/thankyou/:orderId",
      "remappable": true   // ✅ Can be remapped
    }
  ]
}
```

### In CRM (Done by Merchant)

Merchants configure remapping when mounting your plugin:

```typescript theme={null}
// CRM UI allows configuring:
{
  from: "/checkout",      // Your plugin's original path
  to: "/buy-now",         // Custom external URL
  matcher: "^/buy-now.*"  // Route matcher pattern
}
```

## SDK Usage

### Basic Routing with `shouldMatchRoute()`

```tsx theme={null}
import { shouldMatchRoute } from '@tagadapay/plugin-sdk/v2';
import { Route, Routes } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path="/*" element={<RemappableRoutes />} />
    </Routes>
  );
}

function RemappableRoutes() {
  // SDK automatically handles remapping!
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
  
  if (shouldMatchRoute('/thankyou/:orderId')) {
    return <ThankYouPage />;
  }
  
  return <NotFound />;
}
```

**What happens:**

* User visits: `/buy-now` (external URL)
* SDK knows: "This is actually `/checkout`"
* `shouldMatchRoute('/checkout')` returns `true` ✅
* Your component renders correctly!

### Parameter Extraction with `matchRoute()`

<Note>
  **New in v2.7.14**: `matchRoute()` extracts URL parameters from remapped paths automatically!
</Note>

```tsx theme={null}
import { matchRoute } from '@tagadapay/plugin-sdk/v2';

function RemappableRoutes() {
  const location = useLocation();
  
  // Check and extract params in one call
  const thankYouMatch = matchRoute('/thankyou/:orderId');
  
  if (thankYouMatch.matched) {
    // ✅ Params are automatically extracted!
    return <ThankYouPage orderId={thankYouMatch.params.orderId} />;
  }
  
  const checkoutMatch = matchRoute('/checkout/:step');
  
  if (checkoutMatch.matched) {
    return <CheckoutPage step={checkoutMatch.params.step} />;
  }
  
  return <NotFound />;
}
```

**Example:**

```
External URL: /order-confirmation/ORD-12345
Internal path: /thankyou/:orderId
Result: { matched: true, params: { orderId: 'ORD-12345' } }
```

### Getting Path Information with `getPathInfo()`

```tsx theme={null}
import { getPathInfo } from '@tagadapay/plugin-sdk/v2';

function PathDebugger() {
  const pathInfo = getPathInfo();

  return (
    <div>
      <h2>Path Information</h2>
      <dl>
        <dt>External URL (what user sees):</dt>
        <dd>{pathInfo.externalPath}</dd>
        
        <dt>Internal Path (plugin's perspective):</dt>
        <dd>{pathInfo.internalPath || 'No remapping'}</dd>
        
        <dt>Is Remapped:</dt>
        <dd>{pathInfo.isRemapped ? 'Yes' : 'No'}</dd>
        
        <dt>Query Parameters:</dt>
        <dd>{pathInfo.query.toString()}</dd>
      </dl>
    </div>
  );
}
```

## Real-World Examples

### Example 1: E-commerce Checkout

**Plugin defines:**

```
/checkout           → Main checkout page
/checkout/shipping  → Shipping step
/checkout/payment   → Payment step
/checkout/confirm   → Review step
```

**Merchant wants:**

```
/secure-checkout           → Main checkout page
/secure-checkout/delivery  → Shipping step
/secure-checkout/pay       → Payment step
/secure-checkout/review    → Review step
```

**Your plugin code (unchanged!):**

```tsx theme={null}
function CheckoutRoutes() {
  const shippingMatch = matchRoute('/checkout/shipping');
  const paymentMatch = matchRoute('/checkout/payment');
  const confirmMatch = matchRoute('/checkout/confirm');
  
  if (shippingMatch.matched) return <ShippingPage />;
  if (paymentMatch.matched) return <PaymentPage />;
  if (confirmMatch.matched) return <ConfirmPage />;
  
  return <CheckoutLandingPage />;
}
```

**Result:** Works perfectly with both URL structures! ✅

### Example 2: Thank You Page with Order ID

**Plugin expects:**

```
/thankyou/:orderId
```

**Merchant wants:**

```
/order-complete/:orderId
```

**Your plugin code:**

```tsx theme={null}
function ThankYouRoutes() {
  const match = matchRoute('/thankyou/:orderId');
  
  if (match.matched) {
    // ✅ Works with both /thankyou/123 and /order-complete/123
    return <ThankYouPage orderId={match.params.orderId} />;
  }
  
  return <NotFound />;
}
```

### Example 3: Multi-Market Localization

**Plugin internal paths:**

```
/checkout → Universal checkout logic
```

**Different markets:**

```
🇺🇸 English: /checkout
🇫🇷 French:  /paiement
🇩🇪 German:  /kasse
🇪🇸 Spanish: /comprar
```

**Your plugin:**

```tsx theme={null}
// Same code works for all markets!
function App() {
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
}
```

Each market configures their preferred external URL in the CRM. Your plugin works everywhere! 🌍

### Example 4: Product Pages with Slugs

**Plugin:**

```
/product/:productId
```

**SEO-Friendly URLs:**

```
/p/amazing-wireless-headphones
/p/ultra-comfy-sneakers
/p/premium-coffee-beans
```

**Implementation:**

```tsx theme={null}
function ProductRoutes() {
  const match = matchRoute('/product/:productId');
  
  if (match.matched) {
    // ✅ productId = "amazing-wireless-headphones"
    return <ProductPage slug={match.params.productId} />;
  }
  
  return <ProductGrid />;
}
```

## Parameter Name Rules

<Warning>
  **Important:** When remapping paths with parameters, **parameter names must match** between external and internal paths.
</Warning>

### ✅ Valid Remapping

```json theme={null}
{
  "from": "/hello-with-param/:myparam",
  "to": "/custom/:myparam"
}
```

Both use `:myparam` → **Works!** ✅

### ❌ Invalid Remapping

```json theme={null}
{
  "from": "/hello-with-param/:myparam",
  "to": "/custom/:id"  
}
```

Parameter names don't match → **CRM will reject this** ❌

**Why?** This ensures reliable parameter extraction without complex name mapping logic.

## Development & Testing

### Local Development

Test path remapping in your local environment using `localStorage`:

```javascript theme={null}
// Open browser console and run:
localStorage.setItem('tagadapay-remap', JSON.stringify({
  externalPath: '/custom/:myparam',
  internalPath: '/hello-with-param/:myparam'
}));

// Then visit: http://localhost:5173/custom/test123
// Your plugin receives it as: /hello-with-param/test123
```

**Clear remapping:**

```javascript theme={null}
localStorage.removeItem('tagadapay-remap');
```

### Query Parameter Method

Alternative testing method:

```
http://localhost:5173/hello-with-param/123?__remap=/my-custom-path/123
```

### Debugging

Add a debug component to see what's happening:

```tsx theme={null}
import { getPathInfo, matchRoute } from '@tagadapay/plugin-sdk/v2';

function PathRemapDebugger() {
  const pathInfo = getPathInfo();
  const match = matchRoute('/your-internal-path/:param');
  
  return (
    <div className="debug-panel">
      <h3>🔍 Path Remap Debug Info</h3>
      
      <div>
        <strong>Current URL:</strong> {window.location.pathname}
      </div>
      
      <div>
        <strong>Is Remapped:</strong> {pathInfo.isRemapped ? '✅ Yes' : '❌ No'}
      </div>
      
      {pathInfo.isRemapped && (
        <>
          <div>
            <strong>External Path:</strong> {pathInfo.externalPath}
          </div>
          <div>
            <strong>Internal Path:</strong> {pathInfo.internalPath}
          </div>
        </>
      )}
      
      <div>
        <strong>Route Match:</strong> {match.matched ? '✅ Matched' : '❌ No match'}
      </div>
      
      {match.matched && match.params && (
        <div>
          <strong>Extracted Params:</strong>
          <pre>{JSON.stringify(match.params, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}
```

## Production Behavior

### Automatic Meta Tag Injection

In production, TagadaPay automatically injects remapping configuration:

```html theme={null}
<head>
  <meta name="tagadapay-path-remap" 
        content="%7B%22external%22%3A%22%2Fcustom%2F%3Amyparam%22%2C%22internal%22%3A%22%2Fhello-with-param%2F%3Amyparam%22%7D">
</head>
```

**Decoded:**

```json theme={null}
{
  "external": "/custom/:myparam",
  "internal": "/hello-with-param/:myparam"
}
```

The SDK reads this automatically - no action needed from you!

### CDN Distribution

Remapped plugins are served from CDN subdomains:

```
Original:  https://store-123.cdn.tagadapay.com/checkout
Remapped:  https://store-123.cdn.tagadapay.com/buy-now

(Both serve the same plugin, SDK handles the routing)
```

## Best Practices

### 1. Always Use SDK Functions

<Tip>
  **Don't parse `window.location` manually!** Always use SDK functions for path matching.
</Tip>

**❌ Don't do this:**

```tsx theme={null}
// This won't work with remapping!
function CheckoutPage() {
  const isCheckout = window.location.pathname === '/checkout';
  // ...
}
```

**✅ Do this:**

```tsx theme={null}
// This works with remapping!
function CheckoutPage() {
  const isCheckout = shouldMatchRoute('/checkout');
  // ...
}
```

### 2. Mark Pages as Remappable

In your `plugin.manifest.json`:

```json theme={null}
{
  "pages": [
    {
      "path": "/admin-panel",
      "remappable": false  // Internal tool, no need for remapping
    },
    {
      "path": "/checkout",
      "remappable": true   // Customer-facing, allow remapping
    }
  ]
}
```

### 3. Keep Internal Paths Descriptive

Even if merchants remap them, keep your internal paths clear:

**Good:**

```
/checkout
/checkout/shipping
/product/:productId
/thankyou/:orderId
```

**Avoid:**

```
/c
/s1
/p/:id
/ty/:oid
```

### 4. Handle Both Scenarios

Your plugin should work whether remapping is active or not:

```tsx theme={null}
function App() {
  // Works with:
  // - /checkout (no remapping)
  // - /buy-now (remapped to /checkout)
  // - /secure-payment (remapped to /checkout)
  
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
  
  return <HomePage />;
}
```

### 5. Test Both Paths

During development, test your plugin with:

1. **Original paths**: `http://localhost:5173/checkout`
2. **Remapped paths**: Use `localStorage` to simulate remapping

## API Reference

### `shouldMatchRoute(internalPath: string): boolean`

Check if the current URL matches the given internal path.

**Parameters:**

* `internalPath`: Your plugin's internal path pattern (e.g., `/checkout`, `/product/:id`)

**Returns:**

* `boolean`: `true` if current URL matches (considering remapping), `false` otherwise

**Example:**

```tsx theme={null}
if (shouldMatchRoute('/checkout/:step')) {
  return <CheckoutPage />;
}
```

***

### `matchRoute(internalPath: string): RouteMatchResult`

Check if route matches AND extract URL parameters.

**Parameters:**

* `internalPath`: Your plugin's internal path pattern

**Returns:**

```typescript theme={null}
{
  matched: boolean;
  params: Record<string, string>;
}
```

**Example:**

```tsx theme={null}
const result = matchRoute('/product/:productId');
// result.matched = true
// result.params = { productId: 'wireless-headphones' }

if (result.matched) {
  return <ProductPage productId={result.params.productId} />;
}
```

***

### `getPathInfo(): PathInfo`

Get detailed information about the current path and remapping status.

**Returns:**

```typescript theme={null}
{
  externalPath: string;        // URL user sees
  internalPath: string | null; // Your plugin's path (if remapped)
  isRemapped: boolean;          // Whether remapping is active
  query: URLSearchParams;       // Query parameters
  hash: string;                 // URL hash
}
```

**Example:**

```tsx theme={null}
const info = getPathInfo();

if (info.isRemapped) {
  console.log(`User sees: ${info.externalPath}`);
  console.log(`Plugin receives: ${info.internalPath}`);
}
```

***

### `getInternalPath(): string | null`

Get the current internal path (or `null` if no remapping).

**Returns:**

* `string`: Internal path if remapping is active
* `null`: If no remapping is active

**Example:**

```tsx theme={null}
const internalPath = getInternalPath();

if (internalPath) {
  console.log('Remapping active! Internal path:', internalPath);
} else {
  console.log('No remapping - using original paths');
}
```

## Troubleshooting

### Parameters are empty

**Problem:** `matchRoute()` returns `{ matched: true, params: {} }`

**Solution:** Ensure parameter names match in your manifest and CRM configuration:

```json theme={null}
// ✅ Correct
{
  "from": "/product/:productId",
  "to": "/p/:productId"
}

// ❌ Wrong
{
  "from": "/product/:productId",
  "to": "/p/:id"  // Different parameter name!
}
```

### Route not matching

**Problem:** `shouldMatchRoute()` returns `false` when it should match

**Checklist:**

1. Is the page marked as `remappable: true` in your manifest?
2. Is the internal path correct (check with `getPathInfo()`)?
3. Are you using the SDK function (not `window.location`)?
4. Try adding logging: `console.log(getPathInfo())`

### Works locally but not in production

**Problem:** Remapping works in development but fails on CDN

**Solution:**

1. Check that meta tag is injected: View page source and search for `tagadapay-path-remap`
2. Rebuild your plugin: `pnpm build`
3. Redeploy: `npx @tagadapay/plugin-cli deploy`
4. Clear browser cache and CDN cache

### Remapping not working locally

**Problem:** `localStorage` remapping doesn't work

**Solution:**

```javascript theme={null}
// 1. Clear any existing config
localStorage.clear();

// 2. Set new config (ensure it's valid JSON)
localStorage.setItem('tagadapay-remap', JSON.stringify({
  externalPath: '/your-external/:param',
  internalPath: '/your-internal/:param'
}));

// 3. Reload the page (hard refresh: Cmd+Shift+R or Ctrl+Shift+R)
```

## Migration Guide

### From Manual Routing

**Before (manual routing):**

```tsx theme={null}
function App() {
  const location = useLocation();
  
  // ❌ Doesn't work with remapping
  if (location.pathname === '/checkout') {
    return <CheckoutPage />;
  }
}
```

**After (SDK routing):**

```tsx theme={null}
import { shouldMatchRoute } from '@tagadapay/plugin-sdk/v2';

function App() {
  // ✅ Works with remapping!
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
}
```

### From React Router Params

**Before:**

```tsx theme={null}
import { useParams } from 'react-router-dom';

function ProductPage() {
  const { productId } = useParams(); // ❌ Might be empty with remapping
  return <Product id={productId} />;
}
```

**After:**

```tsx theme={null}
import { matchRoute } from '@tagadapay/plugin-sdk/v2';

function ProductRoutes() {
  const match = matchRoute('/product/:productId');
  
  if (match.matched) {
    // ✅ Always works, even with remapping
    return <ProductPage id={match.params.productId} />;
  }
  
  return <NotFound />;
}
```

## Advanced Patterns

### Nested Routes with Remapping

```tsx theme={null}
function App() {
  const checkoutMatch = matchRoute('/checkout/:step');
  
  if (checkoutMatch.matched) {
    return <CheckoutFlow step={checkoutMatch.params.step} />;
  }
  
  return <HomePage />;
}

function CheckoutFlow({ step }: { step: string }) {
  switch (step) {
    case 'shipping':
      return <ShippingStep />;
    case 'payment':
      return <PaymentStep />;
    case 'review':
      return <ReviewStep />;
    default:
      return <CheckoutLanding />;
  }
}
```

### Conditional Remapping Display

```tsx theme={null}
function PathBreadcrumbs() {
  const pathInfo = getPathInfo();
  
  if (pathInfo.isRemapped) {
    return (
      <nav>
        <span>You are here: {pathInfo.externalPath}</span>
        <small>(Internal: {pathInfo.internalPath})</small>
      </nav>
    );
  }
  
  return <nav>{window.location.pathname}</nav>;
}
```

### SEO Meta Tags with Remapping

```tsx theme={null}
import { Helmet } from 'react-helmet';

function CheckoutPage() {
  const pathInfo = getPathInfo();
  
  // Use external path for canonical URL (what user/Google sees)
  const canonicalUrl = `https://example.com${pathInfo.externalPath}`;
  
  return (
    <>
      <Helmet>
        <link rel="canonical" href={canonicalUrl} />
        <meta property="og:url" content={canonicalUrl} />
      </Helmet>
      <div>Checkout content...</div>
    </>
  );
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/developer-tools/sdk/api">
    Complete SDK API documentation
  </Card>

  <Card title="Examples" icon="lightbulb" href="/developer-tools/sdk/examples">
    Real-world plugin examples
  </Card>

  <Card title="Best Practices" icon="star" href="/developer-tools/sdk/best-practices">
    Optimization tips and patterns
  </Card>

  <Card title="Tutorial" icon="rocket" href="/developer-tools/sdk/tutorial">
    Build your first plugin
  </Card>
</CardGroup>

## Need Help?

* **Documentation**: [https://docs.tagadapay.com](https://docs.tagadapay.com)
* **Support**: [developer-support@tagadapay.com](mailto:developer-support@tagadapay.com)
* **Community**: Join our developer Discord
