1use crate::didcomm::PlainMessage;
4use crate::error::Result;
5use crate::message::invoice::{Invoice, LineItem, TaxCategory, TaxSubtotal, TaxTotal};
6use crate::message::payment::PaymentBuilder;
7use crate::message::tap_message_trait::TapMessageBody;
8use crate::message::{Participant, Payment};
9use std::collections::HashMap;
10use std::str::FromStr;
11use tap_caip::AssetId;
12
13pub fn create_basic_invoice_example() -> Result<Invoice> {
15 let line_items = vec![
17 LineItem {
18 id: "1".to_string(),
19 description: "Widget A".to_string(),
20 quantity: 5.0,
21 unit_code: Some("EA".to_string()),
22 unit_price: 10.0,
23 line_total: 50.0,
24 tax_category: None,
25 },
26 LineItem {
27 id: "2".to_string(),
28 description: "Widget B".to_string(),
29 quantity: 10.0,
30 unit_code: Some("EA".to_string()),
31 unit_price: 5.0,
32 line_total: 50.0,
33 tax_category: None,
34 },
35 ];
36
37 let total = line_items.iter().map(|item| item.line_total).sum();
39
40 let invoice = Invoice::new(
42 "INV001".to_string(),
43 "2023-09-01".to_string(),
44 "USD".to_string(),
45 line_items,
46 total,
47 );
48
49 invoice.validate()?;
51
52 Ok(invoice)
53}
54
55pub fn create_invoice_with_tax_example() -> Result<Invoice> {
57 let line_items = vec![
59 LineItem {
60 id: "1".to_string(),
61 description: "Widget A".to_string(),
62 quantity: 5.0,
63 unit_code: Some("EA".to_string()),
64 unit_price: 10.0,
65 line_total: 50.0,
66 tax_category: None,
67 },
68 LineItem {
69 id: "2".to_string(),
70 description: "Widget B".to_string(),
71 quantity: 10.0,
72 unit_code: Some("EA".to_string()),
73 unit_price: 5.0,
74 line_total: 50.0,
75 tax_category: None,
76 },
77 ];
78
79 let sub_total = line_items.iter().map(|item| item.line_total).sum();
81
82 let tax_category = TaxCategory {
84 id: "S".to_string(),
85 percent: 15.0,
86 tax_scheme: "VAT".to_string(),
87 };
88
89 let tax_amount = sub_total * (tax_category.percent / 100.0);
90 let total = sub_total + tax_amount;
91
92 let tax_subtotal = TaxSubtotal {
93 taxable_amount: sub_total,
94 tax_amount,
95 tax_category,
96 };
97
98 let tax_total = TaxTotal {
99 tax_amount,
100 tax_subtotal: Some(vec![tax_subtotal]),
101 };
102
103 let invoice = Invoice {
105 id: "INV001".to_string(),
106 issue_date: "2023-09-01".to_string(),
107 currency_code: "USD".to_string(),
108 line_items,
109 tax_total: Some(tax_total),
110 total,
111 sub_total: Some(sub_total),
112 due_date: Some("2023-10-01".to_string()),
113 note: None,
114 payment_terms: Some("NET30".to_string()),
115 accounting_cost: None,
116 order_reference: None,
117 additional_document_reference: None,
118 metadata: HashMap::new(),
119 };
120
121 invoice.validate()?;
123
124 Ok(invoice)
125}
126
127pub fn create_payment_request_with_invoice_example(
129 merchant_did: &str,
130 customer_did: Option<&str>,
131) -> Result<PlainMessage> {
132 let merchant = Participant {
134 id: merchant_did.to_string(),
135 role: Some("merchant".to_string()),
136 policies: None,
137 leiCode: None,
138 name: None,
139 };
140
141 let agent = Participant {
143 id: "did:example:payment_processor".to_string(),
144 role: Some("agent".to_string()),
145 policies: None,
146 leiCode: None,
147 name: None,
148 };
149
150 let invoice = create_invoice_with_tax_example()?;
152
153 let asset =
155 AssetId::from_str("eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
156
157 let transaction_id = uuid::Uuid::new_v4().to_string();
159
160 let customer = customer_did.map(|cust_did| Participant {
162 id: cust_did.to_string(),
163 role: Some("customer".to_string()),
164 policies: None,
165 leiCode: None,
166 name: None,
167 });
168
169 let mut payment_request = PaymentBuilder::default()
171 .transaction_id(transaction_id)
172 .asset(asset)
173 .amount(format!("{:.2}", invoice.total))
174 .currency_code(invoice.currency_code.clone())
175 .merchant(merchant.clone())
176 .add_agent(agent)
177 .build();
178
179 payment_request.customer = customer;
181
182 payment_request.invoice = Some(invoice);
184
185 payment_request.expiry = Some("2023-10-01T00:00:00Z".to_string());
187
188 let recipients = if let Some(cust_did) = customer_did {
190 vec![cust_did]
191 } else {
192 vec![]
193 };
194
195 let message =
196 payment_request.to_didcomm_with_route(merchant_did, recipients.iter().copied())?;
197
198 Ok(message)
199}
200
201pub fn process_payment_request_with_invoice_example(message: &PlainMessage) -> Result<()> {
203 let payment_request = Payment::from_didcomm(message)?;
205
206 payment_request.validate()?;
208
209 println!("Merchant: {}", payment_request.merchant.id);
211
212 if let Some(customer) = &payment_request.customer {
214 println!("Customer: {}", customer.id);
215 } else {
216 println!("Customer: Not specified");
217 }
218
219 println!("Amount: {}", payment_request.amount);
220
221 if let Some(currency) = &payment_request.currency_code {
222 println!("Currency: {}", currency);
223 }
224
225 if let Some(asset) = &payment_request.asset {
226 println!("Asset: {}", asset);
227 }
228
229 if let Some(invoice) = &payment_request.invoice {
231 println!("Invoice ID: {}", invoice.id);
232 println!("Currency: {}", invoice.currency_code);
233 println!("Total amount: {:.2}", invoice.total);
234
235 println!("Line items:");
237 for (i, item) in invoice.line_items.iter().enumerate() {
238 println!(
239 " {}: {} x {} @ {:.2} = {:.2}",
240 i + 1,
241 item.quantity,
242 item.description,
243 item.unit_price,
244 item.line_total
245 );
246 }
247
248 if let Some(tax_total) = &invoice.tax_total {
250 println!("Tax amount: {:.2}", tax_total.tax_amount);
251
252 if let Some(tax_subtotals) = &tax_total.tax_subtotal {
253 for (i, subtotal) in tax_subtotals.iter().enumerate() {
254 println!(
255 " Tax {}: {:.2}% {} on {:.2} = {:.2}",
256 i + 1,
257 subtotal.tax_category.percent,
258 subtotal.tax_category.tax_scheme,
259 subtotal.taxable_amount,
260 subtotal.tax_amount
261 );
262 }
263 }
264 }
265
266 println!(
267 "Due date: {}",
268 invoice.due_date.as_deref().unwrap_or("Not specified")
269 );
270 } else {
271 println!("Payment request does not contain an invoice");
272 }
273
274 if let Some(expiry) = &payment_request.expiry {
276 println!("Expires: {}", expiry);
277 }
278
279 Ok(())
280}