feat: add WooCommerce REST API v3 client wrapper

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-03-31 20:01:13 -04:00
parent 45775fa295
commit 2cc146b253
2 changed files with 161 additions and 0 deletions

View File

@@ -1 +1,2 @@
# Library helpers will be imported here
from .woo_api_client import WooApiClient

View File

@@ -0,0 +1,160 @@
import base64
import hashlib
import hmac
import logging
import time
import requests
_logger = logging.getLogger(__name__)
class WooApiClient:
"""WooCommerce REST API v3 client wrapper."""
def __init__(self, url, consumer_key, consumer_secret, api_version='wc/v3', timeout=30):
self.base_url = url.rstrip('/')
self.api_version = api_version
self.timeout = timeout
self.session = requests.Session()
self.session.auth = (consumer_key, consumer_secret)
self.session.headers.update({
'Content-Type': 'application/json',
'User-Agent': 'FusionWooCommerce/1.0',
})
def _url(self, endpoint):
return f"{self.base_url}/wp-json/{self.api_version}/{endpoint}"
def _request(self, method, endpoint, data=None, params=None, retries=3):
url = self._url(endpoint)
last_exc = None
for attempt in range(retries):
try:
response = self.session.request(
method,
url,
json=data,
params=params,
timeout=self.timeout,
)
response.raise_for_status()
return response.json()
except Exception as exc:
last_exc = exc
wait = 2 ** attempt
_logger.warning(
"WooCommerce API %s %s failed (attempt %d/%d): %s — retrying in %ds",
method, endpoint, attempt + 1, retries, exc, wait,
)
if attempt < retries - 1:
time.sleep(wait)
raise last_exc
# Convenience methods
def get(self, endpoint, params=None):
return self._request('GET', endpoint, params=params)
def post(self, endpoint, data):
return self._request('POST', endpoint, data=data)
def put(self, endpoint, data):
return self._request('PUT', endpoint, data=data)
def delete(self, endpoint):
return self._request('DELETE', endpoint)
# Product endpoints
def get_products(self, page=1, per_page=100, **kwargs):
params = {'page': page, 'per_page': per_page, **kwargs}
return self.get('products', params=params)
def get_product(self, product_id):
return self.get(f'products/{product_id}')
def get_product_variations(self, product_id, page=1, per_page=100):
params = {'page': page, 'per_page': per_page}
return self.get(f'products/{product_id}/variations', params=params)
def update_product(self, product_id, data):
return self.put(f'products/{product_id}', data)
def create_product(self, data):
return self.post('products', data)
# Order endpoints
def get_orders(self, page=1, per_page=100, **kwargs):
params = {'page': page, 'per_page': per_page, **kwargs}
return self.get('orders', params=params)
def get_order(self, order_id):
return self.get(f'orders/{order_id}')
def update_order(self, order_id, data):
return self.put(f'orders/{order_id}', data)
# Customer endpoints
def get_customers(self, page=1, per_page=100, **kwargs):
params = {'page': page, 'per_page': per_page, **kwargs}
return self.get('customers', params=params)
def get_customer(self, customer_id):
return self.get(f'customers/{customer_id}')
def create_customer(self, data):
return self.post('customers', data)
def update_customer(self, customer_id, data):
return self.put(f'customers/{customer_id}', data)
# Webhook endpoints
def create_webhook(self, data):
return self.post('webhooks', data)
def get_webhooks(self):
return self.get('webhooks', params={'per_page': 100})
def delete_webhook(self, webhook_id):
return self.delete(f'webhooks/{webhook_id}')
# Tax endpoints
def get_tax_classes(self):
return self.get('taxes/classes')
# Utility
def test_connection(self):
try:
result = self.get('system_status')
wc_version = result.get('environment', {}).get('version', 'unknown')
return True, wc_version
except Exception as exc:
return False, str(exc)
@staticmethod
def verify_webhook_signature(payload, signature, secret):
"""Verify a WooCommerce webhook HMAC-SHA256 signature.
Args:
payload (bytes): Raw request body bytes.
signature (str): Value of the X-WC-Webhook-Signature header.
secret (str): The webhook secret configured in WooCommerce.
Returns:
bool: True if the signature matches, False otherwise.
"""
if isinstance(payload, str):
payload = payload.encode('utf-8')
if isinstance(secret, str):
secret = secret.encode('utf-8')
computed = base64.b64encode(
hmac.new(secret, payload, hashlib.sha256).digest()
).decode('utf-8')
return hmac.compare_digest(computed, signature)