agentic_payments/ap2/
mod.rs

1//! Agent Payments Protocol (AP2) Implementation
2//!
3//! AP2 provides a standardized protocol for agent-to-agent payments with:
4//! - Verifiable Credentials (W3C VC) with Ed25519 signatures
5//! - Intent Mandates for user authorization
6//! - Cart Mandates for explicit purchase authorization
7//! - Payment Mandates for payment network signaling
8//! - DID (Decentralized Identifiers) integration
9//! - Multi-agent consensus verification
10
11pub mod credentials;
12pub mod did;
13pub mod mandates;
14pub mod verification;
15
16pub use credentials::{
17    CredentialSubject, Proof, ProofPurpose, VerifiableCredential, VerificationMethod,
18};
19pub use did::{DidDocument, DidManager, DidResolver, ServiceEndpoint};
20pub use mandates::{
21    CartItem, CartMandate, IntentMandate, Mandate, MandateStatus, MandateType, PaymentMandate,
22    PaymentMethod, Permission,
23};
24pub use verification::{
25    ConsensusVerification, VerificationResult, VerificationWorkflow, VerifierNode,
26};
27
28use chrono::{DateTime, Utc};
29use serde::{Deserialize, Serialize};
30use std::collections::HashMap;
31use thiserror::Error;
32
33/// AP2 Protocol Version
34pub const AP2_VERSION: &str = "1.0.0";
35
36/// AP2 Error Types
37#[derive(Debug, Error)]
38pub enum Ap2Error {
39    #[error("Invalid credential: {0}")]
40    InvalidCredential(String),
41
42    #[error("Signature verification failed: {0}")]
43    SignatureVerificationFailed(String),
44
45    #[error("DID resolution failed: {0}")]
46    DidResolutionFailed(String),
47
48    #[error("Mandate validation failed: {0}")]
49    MandateValidationFailed(String),
50
51    #[error("Consensus verification failed: {0}")]
52    ConsensusVerificationFailed(String),
53
54    #[error("Expired credential or mandate")]
55    Expired,
56
57    #[error("Insufficient authorization: {0}")]
58    InsufficientAuthorization(String),
59
60    #[error("Serialization error: {0}")]
61    SerializationError(String),
62
63    #[error("Cryptographic error: {0}")]
64    CryptographicError(String),
65}
66
67pub type Result<T> = std::result::Result<T, Ap2Error>;
68
69/// AP2 Context - Standard W3C Verifiable Credentials context
70pub const VC_CONTEXT: &str = "https://www.w3.org/2018/credentials/v1";
71pub const AP2_CONTEXT: &str = "https://ap2.protocol/v1";
72
73/// Agent Identity representation
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
75pub struct AgentIdentity {
76    pub did: String,
77    pub public_key: Vec<u8>,
78    pub metadata: HashMap<String, String>,
79}
80
81impl AgentIdentity {
82    pub fn new(did: String, public_key: Vec<u8>) -> Self {
83        Self {
84            did,
85            public_key,
86            metadata: HashMap::new(),
87        }
88    }
89
90    pub fn with_metadata(mut self, key: String, value: String) -> Self {
91        self.metadata.insert(key, value);
92        self
93    }
94}
95
96/// Payment authorization with credential chain
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct PaymentAuthorization {
99    pub intent_mandate: VerifiableCredential,
100    pub cart_mandate: VerifiableCredential,
101    pub payment_mandate: VerifiableCredential,
102    pub authorization_chain: Vec<VerifiableCredential>,
103    pub timestamp: DateTime<Utc>,
104}
105
106impl PaymentAuthorization {
107    pub fn new(
108        intent_mandate: VerifiableCredential,
109        cart_mandate: VerifiableCredential,
110        payment_mandate: VerifiableCredential,
111    ) -> Self {
112        Self {
113            intent_mandate: intent_mandate.clone(),
114            cart_mandate: cart_mandate.clone(),
115            payment_mandate: payment_mandate.clone(),
116            authorization_chain: vec![intent_mandate, cart_mandate, payment_mandate],
117            timestamp: Utc::now(),
118        }
119    }
120
121    /// Verify the complete authorization chain
122    pub fn verify_chain(&self, did_resolver: &DidResolver) -> Result<bool> {
123        for credential in &self.authorization_chain {
124            if !credential.verify(did_resolver)? {
125                return Ok(false);
126            }
127        }
128        Ok(true)
129    }
130
131    /// Check if authorization is still valid (not expired)
132    pub fn is_valid(&self) -> bool {
133        let now = Utc::now();
134        self.authorization_chain
135            .iter()
136            .all(|vc| vc.expiration_date.map_or(true, |exp| exp > now))
137    }
138}
139
140/// AP2 Protocol Handler
141#[derive(Debug)]
142pub struct Ap2Protocol {
143    did_manager: DidManager,
144    did_resolver: DidResolver,
145    verification_workflow: VerificationWorkflow,
146}
147
148impl Ap2Protocol {
149    pub fn new() -> Self {
150        Self {
151            did_manager: DidManager::new(),
152            did_resolver: DidResolver::new(),
153            verification_workflow: VerificationWorkflow::new(),
154        }
155    }
156
157    /// Register a new agent identity
158    pub fn register_agent(&mut self, agent_id: &str, public_key: Vec<u8>) -> Result<AgentIdentity> {
159        let did = self.did_manager.create_did(agent_id, public_key.clone())?;
160        Ok(AgentIdentity::new(did, public_key))
161    }
162
163    /// Create an intent mandate (user authorizes agent action)
164    pub fn create_intent_mandate(
165        &self,
166        issuer: &AgentIdentity,
167        subject_agent: &str,
168        intent_description: &str,
169        private_key: &[u8],
170    ) -> Result<VerifiableCredential> {
171        let mandate = IntentMandate::new(
172            issuer.did.clone(),
173            subject_agent.to_string(),
174            intent_description.to_string(),
175        );
176
177        let subject = CredentialSubject {
178            id: subject_agent.to_string(),
179            claims: serde_json::to_value(mandate)
180                .map_err(|e| Ap2Error::SerializationError(e.to_string()))?,
181        };
182
183        VerifiableCredential::new(issuer.did.clone(), subject, private_key)
184    }
185
186    /// Create a cart mandate (explicit purchase authorization)
187    pub fn create_cart_mandate(
188        &self,
189        issuer: &AgentIdentity,
190        items: Vec<CartItem>,
191        total_amount: u64,
192        currency: &str,
193        private_key: &[u8],
194    ) -> Result<VerifiableCredential> {
195        let mandate = CartMandate::new(issuer.did.clone(), items, total_amount, currency.to_string());
196
197        let subject = CredentialSubject {
198            id: issuer.did.clone(),
199            claims: serde_json::to_value(mandate)
200                .map_err(|e| Ap2Error::SerializationError(e.to_string()))?,
201        };
202
203        VerifiableCredential::new(issuer.did.clone(), subject, private_key)
204    }
205
206    /// Create a payment mandate (payment network signal)
207    pub fn create_payment_mandate(
208        &self,
209        issuer: &AgentIdentity,
210        recipient: &str,
211        amount: u64,
212        currency: &str,
213        payment_method: &str,
214        private_key: &[u8],
215    ) -> Result<VerifiableCredential> {
216        let mandate = PaymentMandate::new(
217            issuer.did.clone(),
218            recipient.to_string(),
219            amount,
220            currency.to_string(),
221            payment_method.to_string(),
222        );
223
224        let subject = CredentialSubject {
225            id: recipient.to_string(),
226            claims: serde_json::to_value(mandate)
227                .map_err(|e| Ap2Error::SerializationError(e.to_string()))?,
228        };
229
230        VerifiableCredential::new(issuer.did.clone(), subject, private_key)
231    }
232
233    /// Verify a complete payment authorization with multi-agent consensus
234    pub async fn verify_payment_authorization(
235        &self,
236        authorization: &PaymentAuthorization,
237        verifier_nodes: Vec<VerifierNode>,
238    ) -> Result<VerificationResult> {
239        // First verify the credential chain
240        if !authorization.verify_chain(&self.did_resolver)? {
241            return Err(Ap2Error::ConsensusVerificationFailed(
242                "Credential chain verification failed".to_string(),
243            ));
244        }
245
246        // Then perform multi-agent consensus verification
247        self.verification_workflow
248            .verify_with_consensus(
249                &authorization.payment_mandate,
250                verifier_nodes,
251                &self.did_resolver,
252            )
253            .await
254    }
255
256    /// Resolve a DID to its document
257    pub fn resolve_did(&self, did: &str) -> Result<DidDocument> {
258        self.did_resolver.resolve(did)
259    }
260
261    /// Get DID resolver reference
262    pub fn did_resolver(&self) -> &DidResolver {
263        &self.did_resolver
264    }
265
266    /// Get DID manager reference
267    pub fn did_manager(&self) -> &DidManager {
268        &self.did_manager
269    }
270}
271
272impl Default for Ap2Protocol {
273    fn default() -> Self {
274        Self::new()
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281
282    #[test]
283    fn test_agent_identity_creation() {
284        let public_key = vec![1, 2, 3, 4];
285        let identity = AgentIdentity::new("did:example:123".to_string(), public_key.clone());
286
287        assert_eq!(identity.did, "did:example:123");
288        assert_eq!(identity.public_key, public_key);
289    }
290
291    #[test]
292    fn test_ap2_protocol_initialization() {
293        let protocol = Ap2Protocol::new();
294        assert!(protocol.did_resolver.resolve("test").is_err());
295    }
296
297    #[test]
298    fn test_payment_authorization_validity() {
299        // This is a placeholder test - actual implementation would need valid credentials
300        let protocol = Ap2Protocol::new();
301
302        // Test would create proper credentials and verify the authorization chain
303        assert!(protocol.did_resolver.resolve("did:example:test").is_err());
304    }
305}