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

# Integrate

> Call the API from the visitor's browser, show the local price, and pass the returned Price ID to Checkout.

The integration is one request from the browser and one changed value on the server. Keep your current hard-coded prices in place; they are the fallback.

## 1. Fetch from the browser

Call `/v1/price` once per page load with every Price ID shown on the page (up to 50). Call it from the visitor's browser, not from your server: the country comes from whoever makes the request, and your server is not in the visitor's country.

```js pricing.js theme={null}
const KEY = "ep_live_…";
const IDS = ["price_1Pz9Kx", "price_1Pz9Ky"]; // your base Price IDs

export async function localizedPrices() {
  const url = `https://api.easyppp.com/v1/price?key=${KEY}&prices=${IDS.join(",")}`;
  const ctrl = new AbortController();
  const timer = setTimeout(() => ctrl.abort(), 2000);
  try {
    const res = await fetch(url, { signal: ctrl.signal });
    if (!res.ok) return null;
    const { data } = await res.json();
    return Object.fromEntries(data.filter((e) => e.known).map((e) => [e.base_price, e]));
  } catch {
    return null;
  } finally {
    clearTimeout(timer);
  }
}
```

That returns a map from your base Price ID to its entry, or `null` when anything went wrong.

## 2. Show the price

Render `display`. It is already formatted for the visitor's currency with the right number of decimals, so do not divide `amount` by 100 yourself.

```jsx PricingCard.jsx theme={null}
function PricingCard({ baseId, basePrice, localized }) {
  const entry = localized?.[baseId];
  const price = entry?.price ?? baseId;         // what Checkout charges
  const label = entry?.display ?? basePrice;    // what the visitor sees

  return (
    <div>
      <h3>Pro</h3>
      <p>{label}</p>
      {entry?.localized && entry.discount_percent > 0 && (
        <small>Pricing for {entry.country}, {entry.discount_percent}% off</small>
      )}
      <button onClick={() => startCheckout(price)}>Start now</button>
    </div>
  );
}
```

Patterns that work well:

* Render the base price immediately and swap in `display` when the response arrives. Never block the page on the request.
* When `localized` is true and `discount_percent` is above zero, say so. A short "Pricing for {country}" line is enough. Showing the base price struck through next to it is common.
* When `localized` is false, show nothing extra. The visitor is paying your normal price.
* In a premium market `pay` is above 100 and `discount_percent` is 0. Show `display` and skip the discount line.

## 3. Send the Price ID to your server

Wherever you create the Checkout Session, Subscription or Payment Intent today with a hard-coded Price ID, use the `price` value from the response instead. The sibling is a real Price in your account, so Stripe treats it like any other.

```js theme={null}
// server
const session = await stripe.checkout.sessions.create({
  mode: "subscription",
  line_items: [{ price: req.body.price, quantity: 1 }],
  success_url: "https://example.com/thanks",
  cancel_url: "https://example.com/pricing",
});
```

Stripe charges the sibling's amount in your base currency. The `display` value on the page is an estimate in the visitor's currency for them to read; it is not what Stripe bills. If you use Stripe's Adaptive Pricing, Checkout may present its own converted amount.

## Fallbacks

Your page must look and work exactly as it does today whenever EasyPPP is not reachable or has nothing to say.

| Situation | What to do |
| - | - |
| Request fails, times out or returns a non-200 status | Keep the base price on screen and charge the base Price ID. |
| Entry has `known: false` | That ID is not localized. Treat it as your base price. |
| Entry has `localized: false` | Visitor pays your base price. `display` is still useful: it is your price in their currency. |
| `meta.paused` is true | Localization is paused for your account. Entries already contain the base price. |

The response carries `Cache-Control: no-store`. Fetch once per page load and keep the result in memory while the visitor is on the page; there is no limit on requests.

## Keys and environments

Use the `ep_test_` or `ep_sbx_` key while developing and switch to `ep_live_` when you deploy. Each key returns Prices from its own Stripe environment, so a test key never hands you a live Price ID. Keys are public and safe to ship in client code.

When you are developing, the country is yours. To see a localized result you need to request from a localized country or test with a teammate who is in one.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.