tap_msg/examples/
invoice_examples.rs

1//! Examples for using the Invoice and PaymentRequest functionality.
2
3use crate::error::Result;
4use crate::message::invoice::{Invoice, LineItem, TaxCategory, TaxSubtotal, TaxTotal};
5use crate::message::tap_message_trait::TapMessageBody;
6use crate::message::types::Participant;
7use crate::message::PaymentRequest;
8use didcomm::Message;
9use std::collections::HashMap;
10
11/// Example of creating a basic invoice with line items
12pub fn create_basic_invoice_example() -> Result<Invoice> {
13    // Create line items
14    let line_items = vec![
15        LineItem {
16            id: "1".to_string(),
17            description: "Widget A".to_string(),
18            quantity: 5.0,
19            unit_code: Some("EA".to_string()),
20            unit_price: 10.0,
21            line_total: 50.0,
22            tax_category: None,
23        },
24        LineItem {
25            id: "2".to_string(),
26            description: "Widget B".to_string(),
27            quantity: 10.0,
28            unit_code: Some("EA".to_string()),
29            unit_price: 5.0,
30            line_total: 50.0,
31            tax_category: None,
32        },
33    ];
34
35    // Calculate the total
36    let total = line_items.iter().map(|item| item.line_total).sum();
37
38    // Create a basic invoice
39    let invoice = Invoice::new(
40        "INV001".to_string(),
41        "2023-09-01".to_string(),
42        "USD".to_string(),
43        line_items,
44        total,
45    );
46
47    // Validate the invoice
48    invoice.validate()?;
49
50    Ok(invoice)
51}
52
53/// Example of creating an invoice with tax information
54pub fn create_invoice_with_tax_example() -> Result<Invoice> {
55    // Create line items
56    let line_items = vec![
57        LineItem {
58            id: "1".to_string(),
59            description: "Widget A".to_string(),
60            quantity: 5.0,
61            unit_code: Some("EA".to_string()),
62            unit_price: 10.0,
63            line_total: 50.0,
64            tax_category: None,
65        },
66        LineItem {
67            id: "2".to_string(),
68            description: "Widget B".to_string(),
69            quantity: 10.0,
70            unit_code: Some("EA".to_string()),
71            unit_price: 5.0,
72            line_total: 50.0,
73            tax_category: None,
74        },
75    ];
76
77    // Calculate the subtotal
78    let sub_total = line_items.iter().map(|item| item.line_total).sum();
79
80    // Create tax information
81    let tax_category = TaxCategory {
82        id: "S".to_string(),
83        percent: 15.0,
84        tax_scheme: "VAT".to_string(),
85    };
86
87    let tax_amount = sub_total * (tax_category.percent / 100.0);
88    let total = sub_total + tax_amount;
89
90    let tax_subtotal = TaxSubtotal {
91        taxable_amount: sub_total,
92        tax_amount,
93        tax_category,
94    };
95
96    let tax_total = TaxTotal {
97        tax_amount,
98        tax_subtotal: Some(vec![tax_subtotal]),
99    };
100
101    // Create the invoice with tax information
102    let invoice = Invoice {
103        id: "INV001".to_string(),
104        issue_date: "2023-09-01".to_string(),
105        currency_code: "USD".to_string(),
106        line_items,
107        tax_total: Some(tax_total),
108        total,
109        sub_total: Some(sub_total),
110        due_date: Some("2023-10-01".to_string()),
111        note: None,
112        payment_terms: Some("NET30".to_string()),
113        accounting_cost: None,
114        order_reference: None,
115        additional_document_reference: None,
116        metadata: HashMap::new(),
117    };
118
119    // Validate the invoice
120    invoice.validate()?;
121
122    Ok(invoice)
123}
124
125/// Example of creating a PaymentRequest with an embedded invoice
126pub fn create_payment_request_with_invoice_example(
127    merchant_did: &str,
128    customer_did: Option<&str>,
129) -> Result<Message> {
130    // Create merchant participant
131    let merchant = Participant {
132        id: merchant_did.to_string(),
133        role: Some("merchant".to_string()),
134        policies: None,
135        leiCode: None,
136    };
137
138    // Create a merchant agent (e.g., a payment processor)
139    let agent = Participant {
140        id: "did:example:payment_processor".to_string(),
141        role: Some("agent".to_string()),
142        policies: None,
143        leiCode: None,
144    };
145
146    // Create an invoice with tax
147    let invoice = create_invoice_with_tax_example()?;
148
149    // Create a PaymentRequest with the invoice
150    let mut payment_request = PaymentRequest::with_currency(
151        invoice.currency_code.clone(),
152        format!("{:.2}", invoice.total),
153        merchant.clone(),
154        vec![agent],
155    );
156
157    // Add the invoice
158    payment_request.invoice = Some(invoice);
159
160    // Add customer information if provided
161    if let Some(cust_did) = customer_did {
162        payment_request.customer = Some(Participant {
163            id: cust_did.to_string(),
164            role: Some("customer".to_string()),
165            policies: None,
166            leiCode: None,
167        });
168    }
169
170    // Add expiry (e.g., 30 days)
171    payment_request.expiry = Some("2023-10-01T00:00:00Z".to_string());
172
173    // Convert to a DIDComm message
174    let recipients = if let Some(cust_did) = customer_did {
175        vec![cust_did]
176    } else {
177        vec![]
178    };
179
180    let message =
181        payment_request.to_didcomm_with_route(Some(merchant_did), recipients.iter().copied())?;
182
183    Ok(message)
184}
185
186/// Example of extracting and validating an invoice from a PaymentRequest message
187pub fn process_payment_request_with_invoice_example(message: &Message) -> Result<()> {
188    // Extract the PaymentRequest
189    let payment_request = PaymentRequest::from_didcomm(message)?;
190
191    // Validate the PaymentRequest
192    payment_request.validate()?;
193
194    // Check if it has an invoice
195    if let Some(invoice) = &payment_request.invoice {
196        println!("Invoice ID: {}", invoice.id);
197        println!("Currency: {}", invoice.currency_code);
198        println!("Total amount: {:.2}", invoice.total);
199
200        // Print line items
201        println!("Line items:");
202        for (i, item) in invoice.line_items.iter().enumerate() {
203            println!(
204                "  {}: {} x {} @ {:.2} = {:.2}",
205                i + 1,
206                item.quantity,
207                item.description,
208                item.unit_price,
209                item.line_total
210            );
211        }
212
213        // Print tax information if present
214        if let Some(tax_total) = &invoice.tax_total {
215            println!("Tax amount: {:.2}", tax_total.tax_amount);
216
217            if let Some(tax_subtotals) = &tax_total.tax_subtotal {
218                for (i, subtotal) in tax_subtotals.iter().enumerate() {
219                    println!(
220                        "  Tax {}: {:.2}% {} on {:.2} = {:.2}",
221                        i + 1,
222                        subtotal.tax_category.percent,
223                        subtotal.tax_category.tax_scheme,
224                        subtotal.taxable_amount,
225                        subtotal.tax_amount
226                    );
227                }
228            }
229        }
230
231        println!(
232            "Due date: {}",
233            invoice.due_date.as_deref().unwrap_or("Not specified")
234        );
235    } else {
236        println!("Payment request does not contain an invoice");
237    }
238
239    Ok(())
240}