python-bestbuy 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_bestbuy-0.1.0/.gitignore +77 -0
- python_bestbuy-0.1.0/LICENSE +21 -0
- python_bestbuy-0.1.0/PKG-INFO +544 -0
- python_bestbuy-0.1.0/README.md +516 -0
- python_bestbuy-0.1.0/bestbuy/__init__.py +0 -0
- python_bestbuy-0.1.0/bestbuy/clients/__init__.py +9 -0
- python_bestbuy-0.1.0/bestbuy/clients/base.py +178 -0
- python_bestbuy-0.1.0/bestbuy/clients/catalog.py +64 -0
- python_bestbuy-0.1.0/bestbuy/clients/commerce.py +74 -0
- python_bestbuy-0.1.0/bestbuy/configs/__init__.py +5 -0
- python_bestbuy-0.1.0/bestbuy/configs/base.py +14 -0
- python_bestbuy-0.1.0/bestbuy/configs/catalog.py +6 -0
- python_bestbuy-0.1.0/bestbuy/configs/commerce.py +10 -0
- python_bestbuy-0.1.0/bestbuy/exceptions.py +59 -0
- python_bestbuy-0.1.0/bestbuy/loggers.py +11 -0
- python_bestbuy-0.1.0/bestbuy/models/__init__.py +205 -0
- python_bestbuy-0.1.0/bestbuy/models/catalog.py +948 -0
- python_bestbuy-0.1.0/bestbuy/models/commerce.py +603 -0
- python_bestbuy-0.1.0/bestbuy/operations/__init__.py +50 -0
- python_bestbuy-0.1.0/bestbuy/operations/base.py +10 -0
- python_bestbuy-0.1.0/bestbuy/operations/catalog.py +1443 -0
- python_bestbuy-0.1.0/bestbuy/operations/commerce.py +1208 -0
- python_bestbuy-0.1.0/bestbuy/operations/pagination.py +237 -0
- python_bestbuy-0.1.0/bestbuy/py.typed +0 -0
- python_bestbuy-0.1.0/bestbuy/typing.py +4 -0
- python_bestbuy-0.1.0/bestbuy/utils/__init__.py +9 -0
- python_bestbuy-0.1.0/bestbuy/utils/encryption.py +103 -0
- python_bestbuy-0.1.0/bestbuy/utils/errors.py +164 -0
- python_bestbuy-0.1.0/pyproject.toml +73 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
|
|
5
|
+
# C extensions
|
|
6
|
+
*.so
|
|
7
|
+
|
|
8
|
+
# Distribution / packaging
|
|
9
|
+
.Python
|
|
10
|
+
venv/
|
|
11
|
+
.env
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
# Usually these files are written by a python script from a template
|
|
29
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.coverage
|
|
41
|
+
.coverage.*
|
|
42
|
+
.cache
|
|
43
|
+
nosetests.xml
|
|
44
|
+
coverage.xml
|
|
45
|
+
*,cover
|
|
46
|
+
cover/
|
|
47
|
+
|
|
48
|
+
# Translations
|
|
49
|
+
#*.mo
|
|
50
|
+
*.pot
|
|
51
|
+
|
|
52
|
+
# Django stuff:
|
|
53
|
+
*.db
|
|
54
|
+
*.log
|
|
55
|
+
|
|
56
|
+
# Sphinx documentation
|
|
57
|
+
docs/_build/
|
|
58
|
+
|
|
59
|
+
# PyBuilder
|
|
60
|
+
target/
|
|
61
|
+
|
|
62
|
+
# PyCharm
|
|
63
|
+
.idea/
|
|
64
|
+
|
|
65
|
+
# Node
|
|
66
|
+
node_modules/
|
|
67
|
+
_build
|
|
68
|
+
|
|
69
|
+
/media
|
|
70
|
+
/static
|
|
71
|
+
.vscode/
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
.DS_Store
|
|
75
|
+
tags
|
|
76
|
+
CLAUDE.md
|
|
77
|
+
.python-version
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Michael Lavers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-bestbuy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client library for Best Buy's Catalog and Commerce APIs
|
|
5
|
+
Project-URL: Homepage, https://github.com/bbify/python-bestbuy
|
|
6
|
+
Project-URL: Repository, https://github.com/bbify/python-bestbuy
|
|
7
|
+
Project-URL: Issues, https://github.com/bbify/python-bestbuy/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/bbify/python-bestbuy/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Michael Lavers <kolanos@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api,bestbuy,catalog,commerce,ecommerce
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: cryptography>=35.0.0
|
|
25
|
+
Requires-Dist: httpx>=0.23.0
|
|
26
|
+
Requires-Dist: pydantic-xml>=2.0.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# python-bestbuy
|
|
30
|
+
|
|
31
|
+
[](https://github.com/bbify/python-bestbuy/actions/workflows/ci.yml)
|
|
32
|
+
|
|
33
|
+
A Python client library for Best Buy's APIs, providing synchronous and asynchronous HTTP clients for interacting with Best Buy's Catalog and Commerce services.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Dual API Support**: Full support for both the Catalog API (product information, categories, stores, recommendations) and Commerce API (orders, fulfillment, pricing)
|
|
38
|
+
- **Sync and Async Clients**: Both synchronous and asynchronous clients for each API
|
|
39
|
+
- **Automatic Pagination**: Built-in paginators for iterating over large result sets
|
|
40
|
+
- **Type Safety**: Full Pydantic model validation for requests and responses
|
|
41
|
+
- **Session Management**: Automatic session handling for Commerce API authentication
|
|
42
|
+
- **Payment Encryption**: Built-in utilities for encrypting credit card data for guest orders
|
|
43
|
+
- **Sandbox Support**: Easy switching between production and sandbox environments
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install python-bestbuy
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or using uv:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv add python-bestbuy
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Requirements
|
|
58
|
+
|
|
59
|
+
- Python 3.10+
|
|
60
|
+
- httpx
|
|
61
|
+
- pydantic-xml
|
|
62
|
+
- cryptography
|
|
63
|
+
|
|
64
|
+
## Usage
|
|
65
|
+
|
|
66
|
+
### Catalog API
|
|
67
|
+
|
|
68
|
+
The Catalog API provides access to Best Buy's product catalog, categories, store locations, and product recommendations.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from bestbuy.clients.catalog import CatalogClient, AsyncCatalogClient
|
|
72
|
+
|
|
73
|
+
# Initialize the client
|
|
74
|
+
client = CatalogClient(api_key="your-api-key")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Products
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# Get a single product by SKU
|
|
81
|
+
product = client.products.get(sku=6487435)
|
|
82
|
+
print(f"{product.name}: ${product.sale_price}")
|
|
83
|
+
|
|
84
|
+
# Get specific attributes only
|
|
85
|
+
product = client.products.get(sku=6487435, show=["name", "salePrice", "manufacturer"])
|
|
86
|
+
|
|
87
|
+
# Search for products
|
|
88
|
+
results = client.products.search(
|
|
89
|
+
query="manufacturer=apple&salePrice<1000",
|
|
90
|
+
show=["sku", "name", "salePrice"],
|
|
91
|
+
sort="salePrice",
|
|
92
|
+
sort_order="asc",
|
|
93
|
+
page=1,
|
|
94
|
+
page_size=10
|
|
95
|
+
)
|
|
96
|
+
for product in results.products:
|
|
97
|
+
print(f"{product.sku}: {product.name}")
|
|
98
|
+
|
|
99
|
+
# List all products
|
|
100
|
+
results = client.products.list(page=1, page_size=10)
|
|
101
|
+
|
|
102
|
+
# Get warranties for a product
|
|
103
|
+
warranties = client.products.get_warranties(sku=6487435)
|
|
104
|
+
|
|
105
|
+
# Paginate through search results
|
|
106
|
+
for page in client.products.search_pages(query="onSale=true", page_size=100):
|
|
107
|
+
for product in page.products:
|
|
108
|
+
print(product.name)
|
|
109
|
+
|
|
110
|
+
# Iterate over individual products across all pages
|
|
111
|
+
for product in client.products.search_pages(query="onSale=true").items():
|
|
112
|
+
print(product.name)
|
|
113
|
+
|
|
114
|
+
# Limit pagination to a maximum number of pages
|
|
115
|
+
for product in client.products.search_pages(query="onSale=true", max_pages=5).items():
|
|
116
|
+
print(product.name)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Categories
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Get a single category
|
|
123
|
+
category = client.categories.get(category_id="abcat0100000")
|
|
124
|
+
print(f"{category.name}: {category.id}")
|
|
125
|
+
|
|
126
|
+
# Search for categories
|
|
127
|
+
results = client.categories.search(query="name=Laptops*")
|
|
128
|
+
for category in results.categories:
|
|
129
|
+
print(category.name)
|
|
130
|
+
|
|
131
|
+
# List all categories
|
|
132
|
+
results = client.categories.list(page=1, page_size=10)
|
|
133
|
+
|
|
134
|
+
# Paginate through categories
|
|
135
|
+
for category in client.categories.search_pages().items():
|
|
136
|
+
print(category.name)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Stores
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# Get a single store by ID
|
|
143
|
+
store = client.stores.get(store_id=281)
|
|
144
|
+
print(f"{store.name}: {store.city}, {store.region}")
|
|
145
|
+
|
|
146
|
+
# Search for stores
|
|
147
|
+
results = client.stores.search(query="city=Minneapolis")
|
|
148
|
+
for store in results.stores:
|
|
149
|
+
print(f"{store.store_id}: {store.name}")
|
|
150
|
+
|
|
151
|
+
# List all stores
|
|
152
|
+
results = client.stores.list(page=1, page_size=10)
|
|
153
|
+
|
|
154
|
+
# Find stores near a ZIP code
|
|
155
|
+
results = client.stores.search_by_area(postal_code="55401", distance=25)
|
|
156
|
+
for store in results.stores:
|
|
157
|
+
print(f"{store.name} - {store.distance} miles")
|
|
158
|
+
|
|
159
|
+
# Find stores near coordinates
|
|
160
|
+
results = client.stores.search_by_area(lat=44.9778, lng=-93.2650, distance=10)
|
|
161
|
+
|
|
162
|
+
# Paginate through stores by area
|
|
163
|
+
for store in client.stores.search_by_area_pages(postal_code="55401").items():
|
|
164
|
+
print(store.name)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Recommendations
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# Get trending products
|
|
171
|
+
trending = client.recommendations.trending()
|
|
172
|
+
for product in trending.results:
|
|
173
|
+
print(f"Trending: {product.name}")
|
|
174
|
+
|
|
175
|
+
# Get trending products in a specific category
|
|
176
|
+
trending = client.recommendations.trending(category_id="abcat0502000")
|
|
177
|
+
|
|
178
|
+
# Get most viewed products
|
|
179
|
+
most_viewed = client.recommendations.most_viewed()
|
|
180
|
+
for product in most_viewed.results:
|
|
181
|
+
print(f"Most viewed: {product.name}")
|
|
182
|
+
|
|
183
|
+
# Get products also viewed with a specific product
|
|
184
|
+
also_viewed = client.recommendations.also_viewed(sku=6487435)
|
|
185
|
+
for product in also_viewed.results:
|
|
186
|
+
print(f"Also viewed: {product.name}")
|
|
187
|
+
|
|
188
|
+
# Get products also bought with a specific product
|
|
189
|
+
also_bought = client.recommendations.also_bought(sku=6487435)
|
|
190
|
+
|
|
191
|
+
# Get products ultimately bought after viewing a product
|
|
192
|
+
ultimately_bought = client.recommendations.viewed_ultimately_bought(sku=6487435)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### Async Catalog Client
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
import asyncio
|
|
199
|
+
from bestbuy.clients.catalog import AsyncCatalogClient
|
|
200
|
+
|
|
201
|
+
async def main():
|
|
202
|
+
client = AsyncCatalogClient(api_key="your-api-key")
|
|
203
|
+
|
|
204
|
+
# All operations are async
|
|
205
|
+
product = await client.products.get(sku=6487435)
|
|
206
|
+
print(product.name)
|
|
207
|
+
|
|
208
|
+
# Async pagination
|
|
209
|
+
async for product in client.products.search_pages(query="onSale=true").items():
|
|
210
|
+
print(product.name)
|
|
211
|
+
|
|
212
|
+
asyncio.run(main())
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Commerce API
|
|
216
|
+
|
|
217
|
+
The Commerce API provides access to Best Buy's order management, fulfillment options, pricing, and payment services.
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from bestbuy.clients.commerce import CommerceClient, AsyncCommerceClient
|
|
221
|
+
|
|
222
|
+
# Initialize the client (sandbox mode by default)
|
|
223
|
+
client = CommerceClient(
|
|
224
|
+
api_key="your-api-key",
|
|
225
|
+
username="your-username", # For registered orders
|
|
226
|
+
password="your-password",
|
|
227
|
+
sandbox=True # Use sandbox environment
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Production mode
|
|
231
|
+
client = CommerceClient(
|
|
232
|
+
api_key="your-api-key",
|
|
233
|
+
sandbox=False
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Authentication
|
|
238
|
+
|
|
239
|
+
Authentication is required for registered orders (orders charged to your company credit card).
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
# Using context manager (recommended)
|
|
243
|
+
with client.auth:
|
|
244
|
+
# Session is automatically managed
|
|
245
|
+
response = client.orders.submit_registered(order)
|
|
246
|
+
# Session is automatically logged out
|
|
247
|
+
|
|
248
|
+
# With explicit credentials
|
|
249
|
+
with client.auth(username="user", password="pass"):
|
|
250
|
+
response = client.orders.submit_registered(order)
|
|
251
|
+
|
|
252
|
+
# Manual login/logout
|
|
253
|
+
client.auth.login()
|
|
254
|
+
try:
|
|
255
|
+
response = client.orders.submit_registered(order)
|
|
256
|
+
finally:
|
|
257
|
+
client.auth.logout()
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Fulfillment Operations
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# Check product availability
|
|
264
|
+
availability = client.fulfillment.check_availability(sku="5628900")
|
|
265
|
+
print(f"Shipping available: {availability.available_for_shipping}")
|
|
266
|
+
print(f"Pickup available: {availability.available_for_pickup}")
|
|
267
|
+
print(f"Delivery available: {availability.available_for_delivery}")
|
|
268
|
+
print(f"Max quantity: {availability.max_quantity}")
|
|
269
|
+
|
|
270
|
+
# Get shipping options
|
|
271
|
+
shipping = client.fulfillment.get_shipping_options(
|
|
272
|
+
sku="5628900",
|
|
273
|
+
address1="123 Main St",
|
|
274
|
+
city="Minneapolis",
|
|
275
|
+
state="MN",
|
|
276
|
+
postalcode="55401"
|
|
277
|
+
)
|
|
278
|
+
for option in shipping.options:
|
|
279
|
+
print(f"{option.name}: ${option.price} - Delivery by {option.expected_delivery_date}")
|
|
280
|
+
|
|
281
|
+
# Find stores with product availability
|
|
282
|
+
stores = client.fulfillment.find_stores(
|
|
283
|
+
sku="5628900",
|
|
284
|
+
zip_code="55401",
|
|
285
|
+
store_count=5
|
|
286
|
+
)
|
|
287
|
+
for store in stores.stores:
|
|
288
|
+
print(f"{store.name}: {store.availability_msg}")
|
|
289
|
+
|
|
290
|
+
# Get home delivery options (for large items)
|
|
291
|
+
delivery_options = client.fulfillment.get_delivery_options(
|
|
292
|
+
sku="5628900",
|
|
293
|
+
zip_code="55401"
|
|
294
|
+
)
|
|
295
|
+
for option in delivery_options.options:
|
|
296
|
+
print(f"{option.delivery_date}: {option.start_time} - {option.end_time}")
|
|
297
|
+
|
|
298
|
+
# Get delivery services (installation, haul away, etc.)
|
|
299
|
+
services = client.fulfillment.get_delivery_services(
|
|
300
|
+
sku="5628900",
|
|
301
|
+
zip_code="55401"
|
|
302
|
+
)
|
|
303
|
+
for service in services.delivery_services:
|
|
304
|
+
print(f"{service.service_display_name}: ${service.price}")
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### Pricing Operations
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
# Get unit price for a SKU
|
|
311
|
+
price = client.pricing.get_unit_price(sku="5628900")
|
|
312
|
+
print(f"Price: ${price.unit_price.value}")
|
|
313
|
+
|
|
314
|
+
# Get combined product service info (availability, price, shipping, stores)
|
|
315
|
+
product_info = client.pricing.get_product_service(
|
|
316
|
+
sku="5628900",
|
|
317
|
+
zip_code="55401"
|
|
318
|
+
)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### Order Operations
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
from bestbuy.models.commerce import (
|
|
325
|
+
OrderSubmitRegisteredRequest,
|
|
326
|
+
OrderSubmitGuestRequest,
|
|
327
|
+
OrderList,
|
|
328
|
+
OrderItem,
|
|
329
|
+
Fulfillment,
|
|
330
|
+
AddressFulfillment,
|
|
331
|
+
ShippingAddress,
|
|
332
|
+
Tender,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Create a registered order (requires authentication)
|
|
336
|
+
order = OrderSubmitRegisteredRequest(
|
|
337
|
+
id="my-order-123",
|
|
338
|
+
order_list=OrderList(
|
|
339
|
+
id="list-1",
|
|
340
|
+
items=[
|
|
341
|
+
OrderItem(
|
|
342
|
+
id="item-1",
|
|
343
|
+
quantity=1,
|
|
344
|
+
sku="5628900"
|
|
345
|
+
)
|
|
346
|
+
]
|
|
347
|
+
),
|
|
348
|
+
fulfillment=Fulfillment(
|
|
349
|
+
address_fulfillment=AddressFulfillment(
|
|
350
|
+
address=ShippingAddress(
|
|
351
|
+
address1="123 Main St",
|
|
352
|
+
city="Minneapolis",
|
|
353
|
+
state="MN",
|
|
354
|
+
postalcode="55401"
|
|
355
|
+
),
|
|
356
|
+
shipping_option_key="1" # From get_shipping_options
|
|
357
|
+
)
|
|
358
|
+
),
|
|
359
|
+
tender=Tender()
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
# Review order (get accurate tax calculation)
|
|
363
|
+
with client.auth:
|
|
364
|
+
review_response = client.orders.review(order)
|
|
365
|
+
print(f"Total: ${review_response.total}")
|
|
366
|
+
|
|
367
|
+
# Submit the order
|
|
368
|
+
submit_response = client.orders.submit_registered(order)
|
|
369
|
+
print(f"Order ID: {submit_response.id_map}")
|
|
370
|
+
|
|
371
|
+
# Query an existing order (no auth required)
|
|
372
|
+
order_details = client.orders.query(
|
|
373
|
+
order_id="BBY01-123456789",
|
|
374
|
+
last_name="Smith",
|
|
375
|
+
phone_number="6125551234"
|
|
376
|
+
)
|
|
377
|
+
print(f"Status: {order_details.status}")
|
|
378
|
+
|
|
379
|
+
# Lookup order by ID (requires auth)
|
|
380
|
+
with client.auth:
|
|
381
|
+
order_details = client.orders.lookup(order_id="BBY01-123456789")
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### Guest Orders
|
|
385
|
+
|
|
386
|
+
Guest orders are charged to the customer's credit card and require payment encryption.
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
from bestbuy.utils.encryption import create_encrypted_payment_token
|
|
390
|
+
|
|
391
|
+
# Get encryption key
|
|
392
|
+
encryption_key = client.encryption.get_encryption_key()
|
|
393
|
+
|
|
394
|
+
# Create encrypted payment token
|
|
395
|
+
payment_token = create_encrypted_payment_token(
|
|
396
|
+
card_number="5424180279791773",
|
|
397
|
+
base64_encoded_public_key=encryption_key.base64_encoded_public_key_bytes,
|
|
398
|
+
terminal_id=encryption_key.terminal_id,
|
|
399
|
+
track_id=encryption_key.track_id,
|
|
400
|
+
key_id=encryption_key.key_id
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# Create guest order with encrypted payment
|
|
404
|
+
guest_order = OrderSubmitGuestRequest(
|
|
405
|
+
id="guest-order-123",
|
|
406
|
+
order_list=OrderList(
|
|
407
|
+
id="list-1",
|
|
408
|
+
items=[
|
|
409
|
+
OrderItem(id="item-1", quantity=1, sku="5628900")
|
|
410
|
+
]
|
|
411
|
+
),
|
|
412
|
+
fulfillment=Fulfillment(
|
|
413
|
+
address_fulfillment=AddressFulfillment(
|
|
414
|
+
address=ShippingAddress(
|
|
415
|
+
address1="123 Main St",
|
|
416
|
+
city="Minneapolis",
|
|
417
|
+
state="MN",
|
|
418
|
+
postalcode="55401"
|
|
419
|
+
),
|
|
420
|
+
shipping_option_key="1"
|
|
421
|
+
)
|
|
422
|
+
),
|
|
423
|
+
tender=Tender(
|
|
424
|
+
# Include encrypted payment token in tender
|
|
425
|
+
)
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Submit guest order (no auth required)
|
|
429
|
+
response = client.orders.submit_guest(guest_order)
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Encryption Operations
|
|
433
|
+
|
|
434
|
+
```python
|
|
435
|
+
# Get the public encryption key for guest orders
|
|
436
|
+
key_response = client.encryption.get_encryption_key()
|
|
437
|
+
print(f"Terminal ID: {key_response.terminal_id}")
|
|
438
|
+
print(f"Track ID: {key_response.track_id}")
|
|
439
|
+
print(f"Key ID: {key_response.key_id}")
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
#### Async Commerce Client
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
import asyncio
|
|
446
|
+
from bestbuy.clients.commerce import AsyncCommerceClient
|
|
447
|
+
|
|
448
|
+
async def main():
|
|
449
|
+
client = AsyncCommerceClient(
|
|
450
|
+
api_key="your-api-key",
|
|
451
|
+
username="your-username",
|
|
452
|
+
password="your-password"
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Check availability
|
|
456
|
+
availability = await client.fulfillment.check_availability(sku="5628900")
|
|
457
|
+
print(availability.available_for_shipping)
|
|
458
|
+
|
|
459
|
+
# Async authentication context
|
|
460
|
+
async with client.auth:
|
|
461
|
+
response = await client.orders.submit_registered(order)
|
|
462
|
+
|
|
463
|
+
asyncio.run(main())
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Configuration Options
|
|
467
|
+
|
|
468
|
+
#### Catalog Client Options
|
|
469
|
+
|
|
470
|
+
| Option | Type | Default | Description |
|
|
471
|
+
|--------|------|---------|-------------|
|
|
472
|
+
| `api_key` | str | Required | Best Buy API key |
|
|
473
|
+
| `base_url` | str | `https://api.bestbuy.com` | API base URL |
|
|
474
|
+
| `timeout_ms` | int | `30000` | Request timeout in milliseconds |
|
|
475
|
+
| `log_level` | int | `logging.WARNING` | Logging level |
|
|
476
|
+
|
|
477
|
+
#### Commerce Client Options
|
|
478
|
+
|
|
479
|
+
| Option | Type | Default | Description |
|
|
480
|
+
|--------|------|---------|-------------|
|
|
481
|
+
| `api_key` | str | Required | Best Buy API key |
|
|
482
|
+
| `username` | str | None | Billing account username |
|
|
483
|
+
| `password` | str | None | Billing account password |
|
|
484
|
+
| `partner_id` | str | None | Partner ID for orders |
|
|
485
|
+
| `sandbox` | bool | `True` | Use sandbox environment |
|
|
486
|
+
| `auto_logout` | bool | `True` | Auto-logout on session end |
|
|
487
|
+
| `base_url` | str | Auto | API base URL (auto-set based on sandbox) |
|
|
488
|
+
| `timeout_ms` | int | `30000` | Request timeout in milliseconds |
|
|
489
|
+
| `log_level` | int | `logging.WARNING` | Logging level |
|
|
490
|
+
|
|
491
|
+
### Error Handling
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
from bestbuy.exceptions import APIError, ConfigError, SessionRequiredError
|
|
495
|
+
|
|
496
|
+
try:
|
|
497
|
+
product = client.products.get(sku=999999999)
|
|
498
|
+
except APIError as e:
|
|
499
|
+
print(f"API Error: {e.message}")
|
|
500
|
+
print(f"Error Code: {e.code}")
|
|
501
|
+
print(f"Response: {e.response_text}")
|
|
502
|
+
except ConfigError as e:
|
|
503
|
+
print(f"Configuration Error: {e}")
|
|
504
|
+
|
|
505
|
+
# Commerce API specific
|
|
506
|
+
try:
|
|
507
|
+
client.orders.submit_registered(order) # Without authentication
|
|
508
|
+
except SessionRequiredError as e:
|
|
509
|
+
print("Must be logged in to submit orders")
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Sandbox Testing
|
|
513
|
+
|
|
514
|
+
For the Commerce API, use the sandbox environment for testing:
|
|
515
|
+
|
|
516
|
+
```python
|
|
517
|
+
client = CommerceClient(
|
|
518
|
+
api_key="your-sandbox-api-key",
|
|
519
|
+
sandbox=True # Default
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
# Test credit card for sandbox
|
|
523
|
+
# Card Number: 5424180279791773
|
|
524
|
+
# Expiration: 12/2025
|
|
525
|
+
# CVV: 999
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Development
|
|
529
|
+
|
|
530
|
+
### Running Tests
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
uv run pytest
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Running Type Checks
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
uv run mypy
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## License
|
|
543
|
+
|
|
544
|
+
MIT
|