amazon_spapi/client_apis/
product_fees_v0.rs

1use crate::client::{ApiEndpoint, ApiMethod, SpapiClient};
2use crate::models::product_fees_v0::{
3    FeesEstimateByIdRequest, FeesEstimateRequest, FeesEstimateResult, GetMyFeesEstimateRequest,
4    GetMyFeesEstimateResponse, IdType, MoneyType, OptionalFulfillmentProgram, PriceToEstimateFees,
5};
6use anyhow::Result;
7
8impl SpapiClient {
9    /// Get fees estimate for a product by seller SKU
10    pub async fn get_my_fees_estimate_for_sku(
11        &self,
12        sku: &str,
13        request: GetMyFeesEstimateRequest,
14    ) -> Result<GetMyFeesEstimateResponse> {
15        let endpoint = ApiEndpoint {
16            version: "product_fees_v0",
17            path: "/products/fees/v0/listings/{sku}/feesEstimate",
18            path_params: Some(vec![("sku", sku.to_string())]),
19            method: ApiMethod::Post,
20            rate: 1.0,
21            burst: 2,
22        };
23        let body = serde_json::to_string(&request)?;
24
25        let res = self.request(&endpoint, None, None, Some(&body)).await?;
26        Self::from_json(&res)
27    }
28
29    /// Get fees estimate for a product by ASIN
30    pub async fn get_my_fees_estimate_for_asin(
31        &self,
32        asin: &str,
33        request: GetMyFeesEstimateRequest,
34    ) -> Result<GetMyFeesEstimateResponse> {
35        let endpoint = ApiEndpoint {
36            version: "product_fees_v0",
37            path: "/products/fees/v0/items/{asin}/feesEstimate",
38            path_params: Some(vec![("asin", asin.to_string())]),
39            method: ApiMethod::Post,
40            rate: 1.0,
41            burst: 2,
42        };
43        let body = serde_json::to_string(&request)?;
44        let res = self.request(&endpoint, None, None, Some(&body)).await?;
45        Self::from_json(&res)
46    }
47
48    /// Get fees estimates for multiple products (batch operation)
49    pub async fn get_my_fees_estimates(
50        &self,
51        requests: Vec<FeesEstimateByIdRequest>,
52    ) -> Result<Vec<FeesEstimateResult>> {
53        let endpoint = ApiEndpoint {
54            version: "product_fees_v0",
55            path: "/products/fees/v0/feesEstimate",
56            path_params: None,
57            method: ApiMethod::Post,
58            rate: 0.5,
59            burst: 1,
60        };
61
62        // Convert the request array to JSON string for the POST body
63        let body = serde_json::to_string(&requests)?;
64
65        let res = self.request(&endpoint, None, None, Some(&body)).await?;
66        Self::from_json(&res)
67    }
68
69    /// Convenience method to get fees estimate for a single ASIN with price
70    pub async fn get_fee_for_asin(
71        &self,
72        asin: &str,
73        price: f64,
74        is_amazon_fulfilled: bool,
75    ) -> Result<f64> {
76        let request = GetMyFeesEstimateRequest {
77            fees_estimate_request: Some(Box::new(FeesEstimateRequest {
78                marketplace_id: self.get_marketplace_id().to_string(),
79                is_amazon_fulfilled: Some(is_amazon_fulfilled),
80                price_to_estimate_fees: Box::new(PriceToEstimateFees {
81                    listing_price: Box::new(MoneyType {
82                        currency_code: Some("USD".to_string()),
83                        amount: Some(price),
84                    }),
85                    shipping: None,
86                    points: None,
87                }),
88                identifier: asin.to_string(),
89                optional_fulfillment_program: Some(OptionalFulfillmentProgram::FbaCore),
90            })),
91        };
92
93        let response = self.get_my_fees_estimate_for_asin(asin, request).await?;
94
95        if let Some(payload) = response.payload {
96            if let Some(result) = payload.fees_estimate_result {
97                if result.status == Some("Success".to_string()) {
98                    if let Some(estimate) = result.fees_estimate {
99                        if let Some(total_fees) = estimate.total_fees_estimate {
100                            return Ok(total_fees.amount.unwrap_or(0.0));
101                        }
102                    }
103                }
104            }
105        }
106
107        Err(anyhow::anyhow!(
108            "Failed to get fee estimate for ASIN: {}",
109            asin
110        ))
111    }
112
113    /// Get fees estimates for multiple ASINs (batch operation)
114    pub async fn get_fees_for_asins(
115        &self,
116        asins_with_prices: Vec<(String, f64)>,
117        is_amazon_fulfilled: bool,
118    ) -> Result<Vec<(String, f64)>> {
119        let requests: Vec<FeesEstimateByIdRequest> = asins_with_prices
120            .iter()
121            .map(|(asin, price)| FeesEstimateByIdRequest {
122                fees_estimate_request: Some(Box::new(FeesEstimateRequest {
123                    marketplace_id: self.get_marketplace_id().to_string(),
124                    is_amazon_fulfilled: Some(is_amazon_fulfilled),
125                    price_to_estimate_fees: Box::new(PriceToEstimateFees {
126                        listing_price: Box::new(MoneyType {
127                            currency_code: Some("USD".to_string()),
128                            amount: Some(*price),
129                        }),
130                        shipping: None,
131                        points: None,
132                    }),
133                    identifier: asin.clone(),
134                    optional_fulfillment_program: None,
135                })),
136                id_type: IdType::Asin,
137                id_value: asin.clone(),
138            })
139            .collect();
140
141        let results = self.get_my_fees_estimates(requests).await?;
142
143        let mut fees: Vec<(String, f64)> = Vec::new();
144        for result in results {
145            if let Some(identifier) = result.fees_estimate_identifier {
146                let asin = identifier.id_value.unwrap_or_default();
147                let fee = if result.status == Some("Success".to_string()) {
148                    if let Some(estimate) = result.fees_estimate {
149                        if let Some(total_fees) = estimate.total_fees_estimate {
150                            total_fees.amount.unwrap_or(0.0)
151                        } else {
152                            0.0
153                        }
154                    } else {
155                        0.0
156                    }
157                } else {
158                    0.0
159                };
160                fees.push((asin, fee));
161            }
162        }
163
164        Ok(fees)
165    }
166
167    /// Convenience method to get fees estimate for a single SKU with price
168    pub async fn get_fee_for_sku(
169        &self,
170        sku: &str,
171        price: f64,
172        is_amazon_fulfilled: bool,
173    ) -> Result<f64> {
174        let request = GetMyFeesEstimateRequest {
175            fees_estimate_request: Some(Box::new(FeesEstimateRequest {
176                marketplace_id: self.get_marketplace_id().to_string(),
177                is_amazon_fulfilled: Some(is_amazon_fulfilled),
178                price_to_estimate_fees: Box::new(PriceToEstimateFees {
179                    listing_price: Box::new(MoneyType {
180                        currency_code: Some("USD".to_string()),
181                        amount: Some(price),
182                    }),
183                    shipping: None,
184                    points: None,
185                }),
186                identifier: sku.to_string(),
187                optional_fulfillment_program: Some(OptionalFulfillmentProgram::FbaCore),
188            })),
189        };
190
191        let response = self.get_my_fees_estimate_for_sku(sku, request).await?;
192
193        if let Some(payload) = response.payload {
194            if let Some(result) = payload.fees_estimate_result {
195                if result.status == Some("Success".to_string()) {
196                    if let Some(estimate) = result.fees_estimate {
197                        if let Some(total_fees) = estimate.total_fees_estimate {
198                            return Ok(total_fees.amount.unwrap_or(0.0));
199                        }
200                    }
201                }
202            }
203        }
204
205        Err(anyhow::anyhow!(
206            "Failed to get fee estimate for SKU: {}",
207            sku
208        ))
209    }
210
211    /// Get fees estimates for multiple SKUs (batch operation)
212    pub async fn get_fees_for_skus(
213        &self,
214        skus_with_prices: Vec<(String, f64)>,
215        is_amazon_fulfilled: bool,
216    ) -> Result<Vec<(String, f64)>> {
217        let requests: Vec<FeesEstimateByIdRequest> = skus_with_prices
218            .iter()
219            .map(|(sku, price)| FeesEstimateByIdRequest {
220                fees_estimate_request: Some(Box::new(FeesEstimateRequest {
221                    marketplace_id: self.get_marketplace_id().to_string(),
222                    is_amazon_fulfilled: Some(is_amazon_fulfilled),
223                    price_to_estimate_fees: Box::new(PriceToEstimateFees {
224                        listing_price: Box::new(MoneyType {
225                            currency_code: Some("USD".to_string()),
226                            amount: Some(*price),
227                        }),
228                        shipping: None,
229                        points: None,
230                    }),
231                    identifier: sku.clone(),
232                    optional_fulfillment_program: None,
233                })),
234                id_type: IdType::SellerSku,
235                id_value: sku.clone(),
236            })
237            .collect();
238        let results = self.get_my_fees_estimates(requests).await?;
239        let mut fees: Vec<(String, f64)> = Vec::new();
240        for result in results {
241            if let Some(identifier) = result.fees_estimate_identifier {
242                let sku = identifier.id_value.unwrap_or_default();
243                let fee = if result.status == Some("Success".to_string()) {
244                    if let Some(estimate) = result.fees_estimate {
245                        if let Some(total_fees) = estimate.total_fees_estimate {
246                            total_fees.amount.unwrap_or(0.0)
247                        } else {
248                            0.0
249                        }
250                    } else {
251                        0.0
252                    }
253                } else {
254                    0.0
255                };
256                fees.push((sku, fee));
257            }
258        }
259        Ok(fees)
260    }
261}