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

# Payment Method Geography → Market Personas

> Create regional market personas from Stripe charge geography and payment method data

### Scenario

Analyze charges with expanded payment methods to extract billing countries. Aggregate by region and create Mavera personas for each market, enriched with language preferences and cultural context.

### Architecture

```mermaid theme={"dark"}
flowchart LR
A["GET /v1/charges with payment_method"] --> B["Extract country + aggregate by region"] --> C["POST /api/v1/personas per region"]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, time, stripe, requests
  from collections import defaultdict

  stripe.api_key = os.environ["STRIPE_API_KEY"]
  MAVERA_API_KEY = os.environ["MAVERA_API_KEY"]
  MAVERA_BASE = "https://app.mavera.io/api/v1"

  REGION_MAP = {"US": "North America", "CA": "North America", "MX": "North America",
      "GB": "Europe", "DE": "Europe", "FR": "Europe", "NL": "Europe",
      "JP": "Asia-Pacific", "AU": "Asia-Pacific", "SG": "Asia-Pacific",
      "BR": "Latin America", "AR": "Latin America", "CO": "Latin America"}

  def aggregate_by_region():
      regions = defaultdict(lambda: {"count": 0, "revenue": 0, "countries": defaultdict(int)})
      for ch in stripe.Charge.list(limit=100, expand=["data.payment_method"]).auto_paging_iter():
          country = (ch.payment_method_details or {}).get("card", {}).get("country") or \
                    ((ch.billing_details or {}).get("address") or {}).get("country")
          if not country:
              continue
          region = REGION_MAP.get(country, "Other")
          regions[region]["count"] += 1
          regions[region]["revenue"] += ch.amount / 100
          regions[region]["countries"][country] += 1
      return dict(regions)

  def create_market_persona(region, stats):
      top = sorted(stats["countries"].items(), key=lambda x: -x[1])[:5]
      resp = requests.post(f"{MAVERA_BASE}/personas", json={
          "name": f"{region} Market Persona",
          "description": f"{region}: {stats['count']} charges, ${stats['revenue']:,.0f} revenue. Top: {', '.join(c for c, _ in top)}.",
          "attributes": {"region": region, "total_charges": stats["count"],
                          "total_revenue": round(stats["revenue"], 2), "country_breakdown": dict(top)},
      }, headers={"Authorization": f"Bearer {MAVERA_API_KEY}"})
      resp.raise_for_status()
      return resp.json()

  for region, stats in aggregate_by_region().items():
      if stats["count"] >= 10:
          r = create_market_persona(region, stats)
          print(f"Created: {r['id']} — {region} ({stats['count']} charges)")
          time.sleep(0.2)
  ```

  ```javascript JavaScript theme={"dark"}
  const STRIPE_API_KEY = process.env.STRIPE_API_KEY;
  const MAVERA_API_KEY = process.env.MAVERA_API_KEY;
  const MAVERA_BASE = "https://app.mavera.io/api/v1";

  const REGION_MAP = { US: "North America", CA: "North America", MX: "North America",
    GB: "Europe", DE: "Europe", FR: "Europe", NL: "Europe",
    JP: "Asia-Pacific", AU: "Asia-Pacific", SG: "Asia-Pacific",
    BR: "Latin America", AR: "Latin America", CO: "Latin America" };

  async function stripeGet(path, params = {}) {
    const url = new URL(`https://api.stripe.com/v1/${path}`);
    Object.entries(params).forEach(([k, v]) => url.searchParams.append(k, v));
    const res = await fetch(url, { headers: { Authorization: `Bearer ${STRIPE_API_KEY}` } });
    if (!res.ok) throw new Error(`Stripe ${res.status}: ${await res.text()}`);
    return res.json();
  }

  async function aggregateByRegion() {
    const regions = {};
    const data = await stripeGet("charges", { limit: "100", "expand[]": "data.payment_method" });
    for (const ch of data.data) {
      const country = ch.payment_method_details?.card?.country || ch.billing_details?.address?.country;
      if (!country) continue;
      const region = REGION_MAP[country] || "Other";
      if (!regions[region]) regions[region] = { count: 0, revenue: 0, countries: {} };
      regions[region].count++;
      regions[region].revenue += ch.amount / 100;
      regions[region].countries[country] = (regions[region].countries[country] || 0) + 1;
    }
    return regions;
  }

  async function createMarketPersona(region, stats) {
    const top = Object.entries(stats.countries).sort((a, b) => b[1] - a[1]).slice(0, 5);
    const res = await fetch(`${MAVERA_BASE}/personas`, {
      method: "POST",
      headers: { Authorization: `Bearer ${MAVERA_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        name: `${region} Market Persona`,
        description: `${region}: ${stats.count} charges, $${stats.revenue.toFixed(0)} total. Top: ${top.map(([c]) => c).join(", ")}.`,
        attributes: { region, total_charges: stats.count, total_revenue: Math.round(stats.revenue * 100) / 100,
          country_breakdown: Object.fromEntries(top) },
      }),
    });
    if (!res.ok) throw new Error(`Mavera ${res.status}: ${await res.text()}`);
    return res.json();
  }

  (async () => {
    const regions = await aggregateByRegion();
    for (const [region, stats] of Object.entries(regions)) {
      if (stats.count >= 10) {
        const r = await createMarketPersona(region, stats);
        console.log(`Created: ${r.id} — ${region} (${stats.count} charges)`);
      }
    }
  })();
  ```
</CodeGroup>

### Example Output

```json theme={"dark"}
{
  "id": "per_g7h8i9j0",
  "name": "Europe Market Persona",
  "description": "Customers from Europe: 312 charges, $87,430 total revenue. Top: GB, DE, FR, NL, ES.",
  "attributes": { "region": "Europe", "total_charges": 312, "total_revenue": 87430.0,
    "country_breakdown": { "GB": 124, "DE": 89, "FR": 52, "NL": 31, "ES": 16 } },
  "created_at": "2026-03-17T14:35:12Z"
}
```

### Error Handling

<AccordionGroup>
  <Accordion title="Stripe: Payment method not expandable">
    Legacy charges may lack `payment_method_details`. Fall back to `billing_details.address.country` and skip charges with no geographic signal.
  </Accordion>

  <Accordion title="Mavera: Duplicate persona name (409)">
    Use `PUT` to update existing personas, or append a timestamp for versioned snapshots (e.g., `"Europe Market Persona — 2026-03"`).
  </Accordion>
</AccordionGroup>
