Skip to content
← All articles
commerceAdvanced14 min readLast updated: Apr 30, 2026

Agentic Commerce Architecture: Wiring ACP, UCP/AP2, and MPP Together

Let an agent discover a product on your store and complete payment. The ACP + UCP/AP2 + MPP + product feed stack, and AIDE's commerce-* checks.

  • #agentic-commerce
  • #acp
  • #ucp
  • #ap2
  • #mpp
  • #advanced

Agentic commerce needs four protocol layers working together: product discovery, product context, payment dispatch, settlement. This guide shows how to wire all four, what each one solves, and exactly what AIDE's commerce-* family checks.

Architecture map

┌─────────────┐
│  Agent      │
│  (Claude /  │
│   ChatGPT)  │
└──────┬──────┘
       │
       │  1. Product discovery
       ▼
┌─────────────┐      ┌─────────────────────┐
│ Product     │◄─────│ Google Merchant     │
│ Feed (.xml/ │      │ Center / Schema.org │
│ schema.org) │      │ Product             │
└──────┬──────┘      └─────────────────────┘
       │
       │  2. Context — price, stock, fees
       ▼
┌─────────────┐      ┌─────────────────────┐
│ ACP         │◄─────│ Stripe Agent Toolkit│
│ /agent/     │      │ + product-context   │
│ products    │      └─────────────────────┘
└──────┬──────┘
       │
       │  3. Payment dispatch — wallet, amount
       ▼
┌─────────────┐      ┌─────────────────────┐
│ UCP/AP2     │◄─────│ Google Pay Agent    │
│ /agent/     │      │ Payments / Apple    │
│ checkout    │      │ Agent Wallet        │
└──────┬──────┘      └─────────────────────┘
       │
       │  4. Settlement
       ▼
┌─────────────┐      ┌─────────────────────┐
│ MPP         │◄─────│ Visa Trusted Agent  │
│ /merchant/  │      │ + Mastercard Agent  │
│ payment     │      │ Pay                 │
└─────────────┘      └─────────────────────┘

Four layers, four well-known paths, four AIDE checks. All required — drop one and the agent's flow breaks at that step.

Layer 1 — Product feed (commerce-product-feed)

Agents need to find the product first. Two standards run side by side:

Schema.org Product (HTML inline)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "AIDE Pro Plan",
  "description": "100 scans/mo + leaderboard access",
  "sku": "PLAN-PRO-MONTHLY",
  "offers": {
    "@type": "Offer",
    "price": "49.00",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "url": "https://aide.tr/pricing/pro"
  }
}
</script>

XML feed — Google Merchant compatible

<!-- /products.xml -->
<rss xmlns:g="http://base.google.com/ns/1.0">
  <channel>
    <item>
      <g:id>PLAN-PRO-MONTHLY</g:id>
      <g:title>AIDE Pro Plan</g:title>
      <g:link>https://aide.tr/pricing/pro</g:link>
      <g:price>49.00 USD</g:price>
      <g:availability>in stock</g:availability>
      <g:condition>new</g:condition>
      <g:google_product_category>Software</g:google_product_category>
    </item>
  </channel>
</rss>

Publish both — different dispatchers prefer one over the other.

commerce-product-feed checks: feed URL reachable, ≥1 product, valid price + currency.

Layer 2 — ACP (commerce-acp)

Agentic Commerce Protocol — the layer that answers "what's the context for this product, is the price still valid?".

# /agent/products endpoint
from fastapi import APIRouter, Query

router = APIRouter()


@router.get("/agent/products/{sku}")
async def get_product_context(sku: str, agent_id: str | None = Query(None)):
    """ACP-compatible product context."""
    product = await db.products.find_one({"sku": sku})

    return {
        "sku": product.sku,
        "title": product.title,
        "description": product.description,
        "current_price": {
            "amount": str(product.price),
            "currency": product.currency,
            "valid_until": (datetime.utcnow() + timedelta(minutes=15)).isoformat(),
        },
        "availability": "in_stock" if product.stock > 0 else "out_of_stock",
        "shipping": {
            "estimated_days": product.shipping_days,
            "regions": ["TR", "EU", "US"],
        },
        "agent_fees": {
            "fee_share_pct": 5.0,  # commission %
            "tracking_pixel": f"https://aide.tr/agent/track?sku={sku}&agent={agent_id}",
        },
        "checkout_endpoint": "/agent/checkout",  # hand-off to UCP
    }

valid_until matters — prices live 15 minutes, then the agent must refresh.

agent_fees.fee_share_pct — incentive for the agent. The Stripe Agent Toolkit auto-tracks it.

commerce-acp checks: /agent/products exists, returns a valid context, valid_until looks sane.

Layer 3 — UCP/AP2 (commerce-ucp-ap2)

Universal Commerce Protocol (Apple) + AP2 (Google) — the payment dispatch layer. "Which wallet, how much, to whom?"

@router.post("/agent/checkout")
async def initiate_checkout(req: CheckoutRequest):
    """UCP/AP2-compatible checkout initiation."""
    # Is the price the agent picked still valid?
    if not await validate_price(req.sku, req.amount, req.currency):
        return {"error": "price_changed", "current": await get_current_price(req.sku)}

    payment_intent = await create_intent(
        amount=req.amount,
        currency=req.currency,
        sku=req.sku,
    )

    return {
        "intent_id": payment_intent.id,
        "supported_methods": [
            {"type": "card", "scheme": "visa-trusted-agent"},
            {"type": "card", "scheme": "mastercard-agent-pay"},
            {"type": "wallet", "scheme": "apple-agent-pay"},
            {"type": "wallet", "scheme": "google-pay-agent"},
            {"type": "crypto", "scheme": "x402-base-usdc"},
        ],
        "merchant_payment_endpoint": f"https://aide.tr/merchant/payment/{payment_intent.id}",
        "ttl_seconds": 600,
    }

Five methods — if one is down, another picks up. merchant_payment_endpoint hands off to MPP.

commerce-ucp-ap2 checks: /agent/checkout POST returns 200/400 (auth-dependent), ≥2 methods, sane TTL.

Layer 4 — MPP (commerce-mpp)

Merchant Payment Protocol — settlement + reconciliation. After the agent dispatcher kicks off payment, this is where the merchant says "yes, settled, fulfilling".

@router.post("/merchant/payment/{intent_id}/settle")
async def settle_payment(intent_id: str, settlement: SettlementProof):
    """MPP-compatible settlement endpoint."""
    intent = await db.intents.find_one({"id": intent_id})

    if not await verify_settlement(settlement, intent):
        raise HTTPException(400, "settlement_invalid")

    await fulfill_order(intent.sku, intent.user_id)
    await db.intents.update({"id": intent_id}, {"$set": {"settled_at": datetime.utcnow()}})

    return {
        "status": "settled",
        "order_id": f"ORD-{intent_id}",
        "fulfillment": {
            "type": "instant",  # or "shipped" + tracking
            "delivery_url": f"https://aide.tr/orders/ORD-{intent_id}",
        },
        "receipt": {
            "amount": str(intent.amount),
            "currency": intent.currency,
            "tx_hash": settlement.tx_hash,
            "settled_at": datetime.utcnow().isoformat(),
        },
    }

commerce-mpp checks: the /merchant/payment/* pattern is recognized, settlement-proof verification is wired, the receipt object shape is correct.

Which layers do you need?

By product type:

| Product type | Layer priority | |---|---| | Digital subscription (SaaS) | 1 → 2 → 4 (UCP optional; subscriptions settle instantly) | | Physical good (e-commerce) | 1 → 2 → 3 → 4 (all four) | | Pay-per-API-call | x402 + 1 (skip UCP/MPP, on-chain settles it) | | Marketplace (Etsy-like) | 1 → 2 → 3 → 4 + multi-merchant routing |

aide.tr: SaaS subscription, so 1 + 2 + 4. x402 added for per-call pricing.

Common mistakes

| Mistake | Symptom | Fix | |---|---|---| | Schema.org only, no XML feed | Some dispatchers expect XML | Publish both — overhead minimal | | ACP /agent/products returns HTML | Agent parser fails | Force Content-Type: application/json | | valid_until 5 seconds | Agent's network latency makes it always expired | Minimum 5 min, average 15 | | UCP exposes only 1 payment method | One outage breaks the flow | At least 3 methods (card + wallet + alternative) | | MPP settle endpoint synchronous | Slow payments time out | Async pattern + webhook callback |

Production hardening

  • Idempotency: Honor Idempotency-Key headers everywhere. Agents retry on timeout — never double-charge.
  • Per-agent rate limits: Throttle by agent_id so a misbehaving agent can't starve the dispatcher.
  • Refund flow: ACP/UCP/MPP have no standard refund. Publish your own /merchant/refund/{order_id} and surface it via refund_endpoint in ACP responses.
  • Multi-currency: Agent dispatchers usually quote in USD. If your internal currency is TRY, expose live FX rate + freshness on every endpoint.
  • Telemetry: agentic_commerce_* metric family — which layer fails, which agent drops most often. Grafana dashboard is mandatory.

End-to-end test

# 1. Product feed
curl -s https://your-site.com/products.xml | head -20

# 2. ACP context
curl -s 'https://your-site.com/agent/products/PLAN-PRO?agent_id=test'

# 3. UCP checkout init
curl -X POST https://your-site.com/agent/checkout \
  -H 'Content-Type: application/json' \
  -d '{"sku":"PLAN-PRO","amount":"49.00","currency":"USD"}'

# 4. MPP settle (settlement proof obtained from a real flow)
curl -X POST https://your-site.com/merchant/payment/$INTENT_ID/settle \
  -H 'Content-Type: application/json' \
  -d '{"tx_hash":"0x...","signature":"..."}'

If all four respond correctly, AIDE PASSes commerce-acp, commerce-ucp-ap2, commerce-mpp, and commerce-product-feed.

Related resources

1284 subscribers

Weekly AI-Readiness newsletter

New articles, industry trends, check updates — one email a week.

Sitenizde deneyin

Tek bir tıklamayla bu kontrolü çalıştırın.

Which check do you want?
Agentic Commerce Architecture: Wiring ACP, UCP/AP2, and MPP Together | AIDE