Skip to main content
Glama

place_order

Submit the USDC transaction hash on Base, quote ID, variant ID, and shipping details to verify payment and place a physical hat order.

Instructions

Place an order after sending USDC payment on Base. Verifies the on-chain transaction and creates the order.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
quote_idYesQuote ID from get_quote
tx_hashYesTransaction hash of USDC payment on Base
variant_idYesShopify variant ID
shipping_nameYes
shipping_address1Yes
shipping_cityYes
shipping_stateYes
shipping_zipYes
shipping_countryNo

Implementation Reference

  • main.py:354-428 (handler)
    The place_order handler function (FastAPI POST /tools/place_order). Receives quote_id, tx_hash, variant_id, quantity, shipping_address, and email. Validates quote, verifies USDC payment on Base via eth_getTransactionReceipt, then creates a Shopify order with financial_status=paid and a USDC transaction record. Returns order details including order_id and payment_verified status.
    @app.post("/tools/place_order")
    async def place_order(request: Request):
        body = await request.json()
        quote_id = body.get("quote_id")
        tx_hash = body.get("tx_hash")
        variant_id = body.get("variant_id")
        quantity = body.get("quantity", 1)
        address = body.get("shipping_address", {})
        email = body.get("email", "")
    
        # Validate quote
        quote = _quotes.get(quote_id)
        if not quote:
            raise HTTPException(400, "Quote not found. Call get_quote first.")
        if time.time() > quote["expires_at"]:
            raise HTTPException(400, "Quote expired. Call get_quote again.")
    
        expected_usdc = quote["total_usdc"]
    
        # Verify payment on Base
        verification = await verify_usdc_payment(tx_hash, expected_usdc, PAYMENT_WALLET)
        if not verification["verified"]:
            raise HTTPException(402, f"Payment not verified: {verification['reason']}")
    
        # Build Shopify order (already paid)
        name_parts = address.get("name", "Agent Buyer").split(" ", 1)
        order_payload = {
            "order": {
                "line_items": [{"variant_id": int(variant_id), "quantity": quantity}],
                "financial_status": "paid",
                "fulfillment_status": None,
                "send_receipt": bool(email),
                "send_fulfillment_receipt": True,
                "note": f"Paid via USDC on Base | tx: {tx_hash} | quote: {quote_id}",
                "shipping_address": {
                    "first_name": name_parts[0],
                    "last_name": name_parts[1] if len(name_parts) > 1 else "",
                    "address1": address.get("address1", ""),
                    "address2": address.get("address2", ""),
                    "city": address.get("city", ""),
                    "province": address.get("province", ""),
                    "zip": address.get("zip", ""),
                    "country_code": address.get("country_code", "US"),
                    "phone": address.get("phone", ""),
                },
                "transactions": [
                    {
                        "kind": "sale",
                        "status": "success",
                        "amount": str(expected_usdc),
                        "gateway": "USDC on Base",
                    }
                ],
            }
        }
        if email:
            order_payload["order"]["email"] = email
    
        result = await shopify_post("orders.json", order_payload)
        order = result["order"]
    
        # Remove used quote
        _quotes.pop(quote_id, None)
    
        return {
            "success": True,
            "order_id": str(order["id"]),
            "order_number": order.get("order_number"),
            "status": order.get("fulfillment_status") or "unfulfilled",
            "payment_verified": True,
            "tx_hash": tx_hash,
            "amount_paid_usdc": verification.get("amount_usdc", expected_usdc),
            "shipping_to": address.get("name"),
            "message": "Order created. Printful will produce and ship your item. Use get_order_status to track.",
        }
  • First tool definition schema for place_order in MCP_TOOLS list (lines ~78-167). Declares name, description, and inputSchema with required fields: quote_id, tx_hash, variant_id, shipping_address, and optional email.
    "name": "place_order",
    "description": (
        "Place a fully paid order. "
        "Send the exact USDC amount from get_quote to the payment wallet on Base, "
        "then call this tool with the transaction hash. "
        "Server verifies payment on-chain and creates the order. "
        "Printful produces and ships the item."
    ),
    "price": "product price in USDC (see get_quote)",
    "inputSchema": {
        "type": "object",
        "properties": {
            "quote_id": {"type": "string", "description": "From get_quote"},
            "tx_hash": {"type": "string", "description": "Base transaction hash of USDC payment"},
            "variant_id": {"type": "string"},
            "quantity": {"type": "integer", "default": 1},
            "shipping_address": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "address1": {"type": "string"},
                    "address2": {"type": "string"},
                    "city": {"type": "string"},
                    "province": {"type": "string"},
                    "zip": {"type": "string"},
                    "country_code": {"type": "string"},
                    "phone": {"type": "string"},
                },
                "required": ["name", "address1", "city", "zip", "country_code"],
            },
            "email": {"type": "string", "description": "Optional — for order confirmation"},
        },
        "required": ["quote_id", "tx_hash", "variant_id", "shipping_address"],
    },
  • Second tool definition schema for place_order (lines ~507-524). An alternative schema variant with flat shipping fields (shipping_name, shipping_address1, etc.) instead of a nested shipping_address object.
    {
        "name": "place_order",
        "description": "Place an order after sending USDC payment on Base. Verifies the on-chain transaction and creates the order.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "quote_id": {"type": "string", "description": "Quote ID from get_quote"},
                "tx_hash": {"type": "string", "description": "Transaction hash of USDC payment on Base"},
                "variant_id": {"type": "integer", "description": "Shopify variant ID"},
                "shipping_name": {"type": "string"},
                "shipping_address1": {"type": "string"},
                "shipping_city": {"type": "string"},
                "shipping_state": {"type": "string"},
                "shipping_zip": {"type": "string"},
                "shipping_country": {"type": "string"}
            },
            "required": ["quote_id", "tx_hash", "variant_id", "shipping_name", "shipping_address1", "shipping_city", "shipping_state", "shipping_zip"]
        }
  • main.py:574-578 (registration)
    Registration of place_order in the REST-to-MCP routing map. Maps the tool name 'place_order' to the REST endpoint '/tools/place_order' for the tools/call JSON-RPC method.
    rest_map = {
        "search_products": "/tools/search_products",
        "get_product": "/tools/get_product",
        "get_quote": "/tools/get_quote",
        "place_order": "/tools/place_order",
  • The verify_usdc_payment helper function used by place_order. Verifies a USDC transfer on Base by decoding the transaction receipt logs, checking for a Transfer event to the payment wallet with the expected amount (allowing 1% slippage).
    async def verify_usdc_payment(tx_hash: str, expected_amount_usdc: float, to_wallet: str) -> dict:
        """Verify a USDC transfer on Base via eth_getTransactionReceipt."""
        # USDC Transfer event topic
        TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
        
        async with httpx.AsyncClient(timeout=20) as client:
            # Get transaction receipt
            r = await client.post(BASE_RPC, json={
                "jsonrpc": "2.0",
                "method": "eth_getTransactionReceipt",
                "params": [tx_hash],
                "id": 1,
            })
            receipt = r.json().get("result")
            
            if not receipt:
                return {"verified": False, "reason": "Transaction not found or not confirmed yet"}
            
            if receipt.get("status") != "0x1":
                return {"verified": False, "reason": "Transaction failed on-chain"}
            
            # Check logs for USDC Transfer to our wallet
            expected_usdc_wei = int(expected_amount_usdc * 1_000_000)  # USDC has 6 decimals
            to_wallet_padded = "0x000000000000000000000000" + to_wallet[2:].lower()
            
            for log in receipt.get("logs", []):
                if (log.get("address", "").lower() == USDC_CONTRACT.lower()
                    and len(log.get("topics", [])) >= 3
                    and log["topics"][0].lower() == TRANSFER_TOPIC.lower()
                    and log["topics"][2].lower() == to_wallet_padded.lower()):
                    
                    amount_hex = log.get("data", "0x0")
                    amount = int(amount_hex, 16)
                    
                    # Allow up to 1% slippage
                    if amount >= int(expected_usdc_wei * 0.99):
                        return {
                            "verified": True,
                            "amount_usdc": amount / 1_000_000,
                            "block": int(receipt.get("blockNumber", "0x0"), 16),
                        }
            
            return {"verified": False, "reason": f"No USDC transfer found to {to_wallet} in this transaction"}
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses that it verifies the on-chain transaction and creates the order, but with no annotations provided, it does not address idempotency, error conditions, or side effects beyond creation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences front-load the primary purpose and key verification step, with no extraneous words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description fails to mention return values (no output schema), error handling, or detailed prerequisites. For a tool with 9 parameters and 8 required, this lacks completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 33% (3 of 9 parameters have descriptions). The tool description does not add any additional semantic meaning for the undocumented shipping parameters, leaving a significant gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Place' on the resource 'order', specifies the context of sending USDC payment on Base, and distinguishes from sibling tools (which are queries).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies prerequisites ('after sending USDC payment') and mentions verifying the on-chain transaction, but lacks explicit guidance on when not to use or alternatives to consider.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/masonicGIT/shop-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server