amazon_spapi/client_apis/
fees_api.rs

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