How to Enable AI Agents to Make Purchases: Complete GMC Integration Guide

Purchase non-repudiation and stable API behavior form the foundation for safe autonomous commerce. For agents to execute purchases reliably you must combine a current integration surface with cryptographic proof and persistent consent. This guide presents a production-oriented path using Google Merchant API V1 as the authoritative product surface, and cryptographic primitives that ensure intent authenticity and cart integrity.
I present concrete adapter code, signature verification logic, binding verification, an immutable proof schema, and a deployment checklist that you can apply immediately in engineering environments. Follow the code and architecture to minimize integration risk and to preserve auditable consent for every agent-driven transaction.
Integration Quick Specs (TL;DR)
- Primary Surface: Google Merchant API V1 (
google.shopping.merchant_products_v1). - Authentication Anchor: ES256 (Asymmetric) for user-signed Intent Mandates (Non-repudiation).
- Data Integrity: HMAC-SHA256 (Symmetric) for Cart-to-Intent binding (Anti-tampering).
- Canonicalization: Strictly follows RFC 8785 (JCS) principles to ensure signature stability across heterogeneous AI environments.
- Regulatory Scope: Maps technical artifacts (Signed Mandates, Immutable Audit Logs) to NDPA and POPIA informed consent requirements.
- Key Protection: Implements timing-safe comparisons (
compare_digest) and isolated secret management for per-intent binding keys.
GMC Integration V1 Stable
Core integration assumptions and terms
GMC refers to Google Merchant Center. GCP refers to Google Cloud Platform. API refers to Application Programming Interface. This integration uses the stable library namespace google.shopping.merchant_products_v1 and explicitly avoids any v1beta or Content API patterns. Implement registration, canonical product naming, and v1 field remapping as fundamental prerequisites.
Registration primitive and bootstrap behavior
Register the GCP project for a merchant account once per project. Make the registration step idempotent and persist the result. Refuse product queries until registration completes. This pattern prevents ambiguous failures and enforces a clear operational contract between the adapter and the merchant account.
Product resource name encoding
Encode product resource names using unpadded base64url per RFC 4648 Section 5. Build the encoded token from content language, feed label, and the merchant offer id. Strip trailing equals characters. Validate encoded outputs against representative samples in unit tests.
Field schema mapping for V1
Apply the following field rules during transformation from raw API objects to your internal product model. Map attributes to productAttributes. Convert single gtin strings into a gtins array. Read price from inventory.price. Remove any references to deprecated fields such as taxes, taxCategory, and channel.
Production-grade adapter example
# gmc_adapter.py
import base64
from google.shopping.merchant_products_v1 import ProductsServiceClient
from google.api_core.exceptions import GoogleAPIError
class MerchantV1Adapter:
"""
Stable v1 adapter for GMC product retrieval.
Implements unpadded base64url encoding per RFC 4648 Section 5.
"""
def __init__(self, merchant_id: str):
self.client = ProductsServiceClient()
self.merchant_id = merchant_id
self._gcp_registered = False
def register_gcp_project(self, project_id: str) -> bool:
parent = f"accounts/{self.merchant_id}"
# Implement platform-specific registration call here
# Persist registration state to avoid repeated calls
self._gcp_registered = True
return True
def encode_product_name(self, offer_id: str, content_language: str,
feed_label: str = "online") -> str:
product_id = f"{content_language}~{feed_label}~{offer_id}"
encoded = base64.urlsafe_b64encode(product_id.encode("utf-8")).decode("ascii").rstrip("=")
return f"accounts/{self.merchant_id}/products/{encoded}"
def fetch_product(self, offer_id: str, content_language: str = "en") -> dict:
if not self._gcp_registered:
raise RuntimeError("Call register_gcp_project() before fetching products")
resource_name = self.encode_product_name(offer_id, content_language)
try:
raw = self.client.get_product(name=resource_name)
return self._transform_to_v1_schema(raw)
except GoogleAPIError as e:
raise RuntimeError(f"Product fetch failed: {e}")
def _transform_to_v1_schema(self, raw_product) -> dict:
return {
"offerId": raw_product.offer_id,
"contentLanguage": raw_product.content_language,
"productAttributes": {
"title": raw_product.product_attributes.title,
"description": raw_product.product_attributes.description,
"brand": raw_product.product_attributes.brand,
"gtins": list(raw_product.product_attributes.gtins),
"imageLink": raw_product.product_attributes.image_link,
},
"inventory": {
"price": {
"value": raw_product.inventory.price.amount_micros / 1_000_000,
"currency": raw_product.inventory.price.currency_code
},
"availability": raw_product.inventory.availability
}
}Testing recommendations
- Unit test unpadded base64url encoding with multi-locale samples.
- Test
gtinsarray handling for single and multiple entries. - Validate
inventory.priceextraction across currency edge cases. - Assert registration idempotency and error handling during network failures.
Cryptographic Flow AP2 Anchor
Definitions and cryptographic choices
To ensure purchase non-repudiation and prevent cart-switching attacks, the Policy Enforcement Point (PEP) uses a dual-layer cryptographic anchor. I employ ES256 (ECDSA with SHA-256) to sign the initial intent payload, creating an immutable audit trail of user consent. This intent is then cryptographically bound to the cart using HMAC-SHA256 (HS256), ensuring that the items being purchased cannot be modified post-authorization.
Canonicalization and signing rules
To ensure signature consistency across heterogeneous environments, follow JSON Canonicalization Scheme (JCS) per RFC 8785. This prevents verification failures caused by varying whitespace, float representations, or key ordering across different programming languages. Specifically, canonicalize JSON by sorting keys and encoding as UTF-8 before signing. Persist the public key for signature verification and store the per-intent binding secret in a dedicated secrets manager. Never log raw secrets or unencrypted signatures. Enforce narrow TTLs on secrets and rotate keys according to your security policy.
Sequence diagram
ES256 verification example
# signer.py
import json
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from cryptography.exceptions import InvalidSignature
def verify_es256_signature(public_key_pem: str, signature: bytes, payload: dict) -> bool:
public_key = load_pem_public_key(public_key_pem.encode("utf-8"))
message = json.dumps(payload, sort_keys=True).encode("utf-8")
try:
public_key.verify(signature, message, ec.ECDSA(hashes.SHA256()))
return True
except InvalidSignature:
return FalseHMAC-SHA256 hierarchical binding example
# binding_verifier.py
import hmac
import hashlib
import json
def verify_cart_binding(cart_mandate: dict, intent_mandate: dict) -> bool:
cart_hmac = cart_mandate.get("parent_binding_hmac")
intent_secret = intent_mandate.get("binding_secret")
parent_intent_id = cart_mandate.get("parent_intent_id")
if not all([cart_hmac, intent_secret, parent_intent_id]):
return False
if parent_intent_id != intent_mandate["mandate_id"]:
return False
cart_data = json.dumps({
"items": cart_mandate["items"],
"total_amount": cart_mandate["total_amount"],
"currency": cart_mandate["currency"],
"parent_intent_id": parent_intent_id
}, sort_keys=True).encode("utf-8")
expected = hmac.new(intent_secret.encode("utf-8"), cart_data, hashlib.sha256).hexdigest()
return hmac.compare_digest(cart_hmac, expected)Operational checks
- Enforce strict timestamp windows when validating signatures to limit replay.
- Use timing-safe comparisons for HMAC verification to prevent timing attacks.
- Store public keys and binding secrets in separate, access-controlled stores.
- Log verification outcomes with minimal sensitive details to preserve auditability without exposing secrets.
Binding Proof and Schema
Immutable mandate records
Persist IntentMandate and CartMandate as immutable records. Include the intent signature, the consent artifact, and the binding secret reference. Use append-only storage or cryptographic ledger entries to prevent unauthorized edits.
Example JSON proof
{
"intent_mandate": {
"mandate_id": "intent_7f3a9b2c",
"type": "purchase_intent",
"user_id": "agent_12345",
"timestamp": "2026-02-15T18:30:45Z",
"consent_proof": {
"regulation": "NDPA/POPIA",
"consent_type": "informed_explicit",
"consent_text": "I authorize this AI agent to make purchases on my behalf within the specified budget constraints.",
"user_signature": "user_sig_abc123",
"timestamp": "2026-02-15T18:25:00Z"
},
"constraints": {
"max_transaction_amount": 500.00,
"currency": "USD",
"allowed_categories": ["electronics", "books"]
},
"binding_secret": "sk_7f3a9b2c_binding_key",
"public_key": "-----BEGIN PUBLIC KEY-----\\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\\n-----END PUBLIC KEY-----",
"signature": "MEUCIQDx7..."
},
"cart_mandate": {
"mandate_id": "cart_4e8d1a5f",
"type": "cart_finalization",
"parent_intent_id": "intent_7f3a9b2c",
"timestamp": "2026-02-15T18:30:45Z",
"items": [
{
"offer_id": "PROD_12345",
"content_language": "en",
"quantity": 1,
"unit_price": 299.99,
"currency": "USD"
}
],
"total_amount": 299.99,
"currency": "USD",
"parent_binding_hmac": "8f7e6d5c4b3a2918f7e6d5c4b3a2918f7e6d5c4b3a2918f7e6d5c4b3a2918f7e",
"verification_metadata": {
"binding_algorithm": "HMAC-SHA256",
"parent_verified_at": "2026-02-15T18:30:45Z",
"cart_switching_check": "passed"
}
}
}How the binding proof operates
The CartMandate includes parent_intent_id and parent_binding_hmac. Compute the HMAC using the intent binding_secret and the canonical cart payload. Recompute on the server and compare using timing-safe equality. Reject the cart when linkage fails or when constraints in the IntentMandate exceed configured policy thresholds. You can learn more about AI Agent Payment Authentication in this technical guide to AP2 Mandates.
Regulatory Mapping
This implementation anchors technical controls to regional compliance snapshots, specifically the National Data Protection Act (NDPA) and the Protection of Personal Information Act (POPIA, South Africa). These frameworks provide the legal primitives for informed consent and purpose limitation. By mapping the cryptographic proofs—such as signed intent mandates and immutable audit trails—directly to these requirements, I establish a globally compatible blueprint for secure data sovereignty.
| Technical Control | Regulatory Requirement | Implementation |
|---|---|---|
| `IntentMandate` signature | Non-Repudiation (NDPA and POPIA principles) | Use ES256 signed intent with stored public key and verification logs |
| Consent text storage | Informed Consent | Persist `consent_proof.consent_text` and `user_signature` in immutable record |
| ISO 8601 timestamps | Audit Trail | Record timestamps on `IntentMandate` and `CartMandate` and prevent backdating |
| HMAC binding | Data Integrity | HMAC-SHA256 `binding_secret` per intent with timing-safe verification |
| Constraint enforcement | Purpose Limitation | Enforce `max_transaction_amount` and `allowed_categories` at verification time |
Implementation Checklist
- Update imports to
google.shopping.merchant_products_v1and remove v1beta or Content API usage. - Add an idempotent
register_gcp_projectcall at service startup and persist the registration state. - Implement unpadded base64url encoding for product resource names and validate outputs in unit tests.
- Refactor product mapping to use
productAttributes, convert GTINs to agtinsarray, and read price frominventory.price. - Deploy ES256 verification using
cryptography.hazmat, adhering to RFC 8785 (JCS) for canonical JSON signing to ensure cross-platform compatibility. - Generate a per-intent
binding_secret, computeparent_binding_hmacforCartMandate, and store secrets in a secrets manager. - Use
hmac.compare_digestfor timing-safe comparisons when verifying HMACs. - Persist
consent_textanduser_signaturewith immutable timestamps to satisfy informed consent requirements. - Run integration tests that simulate cart switching and assert that verification rejects mismatches.
- Separate secret storage from application logs and backups and enforce strict access controls.
Conclusion The Path to Autonomous Commerce
I recommend adopting the V1 integration patterns and the cryptographic primitives in this guide as core engineering standards for agent-driven purchases. Implement the register_gcp primitive first. Then implement ES256 verification and HMAC binding. Persist intent and consent artifacts immutably. These steps create an auditable transaction trail and prevent unauthorized post-authorization modifications.
Ship small, verify often, and run adversarial tests that attempt cart switching and signature tampering. Validate the full flow end to end before enabling production agent spending. This sequence ensures the system meets business needs while maintaining legal and security constraints.
Technical Transparency and Definitions
I wrote this guide for Lead Implementation Engineers to present deployable primitives for Google Merchant API V1 integration and for agent purchase verification. All code samples use the stable v1 primitives and production cryptographic libraries available in standard Python packages.
Definitions on first use in the document include the following abbreviations written out here for clarity. ECDSA stands for Elliptic Curve Digital Signature Algorithm. ES256 refers to ECDSA with the P-256 curve and SHA-256. HMAC stands for Hash-based Message Authentication Code. PEP stands for Policy Enforcement Point. GMC stands for Google Merchant Center. GCP stands for Google Cloud Platform. API stands for Application Programming Interface. NDPA stands for National Data Protection Act. POPIA stands for Protection of Personal Information Act.
Mermaid diagrams appear in the body section as mermaid code blocks for rendering fidelity. Treat the diagrams as sequence representations and render them in your documentation pipeline to preserve accuracy.





Comments
Sign in to join the conversation
Sign In