changes
This commit is contained in:
177
fusion_clover/utils.py
Normal file
177
fusion_clover/utils.py
Normal file
@@ -0,0 +1,177 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import uuid
|
||||
|
||||
from odoo.addons.fusion_clover import const
|
||||
|
||||
|
||||
def generate_idempotency_key():
|
||||
"""Generate a unique idempotency key for Clover API requests."""
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
def build_ecom_url(endpoint, is_test=False):
|
||||
"""Build a full Clover Ecommerce API URL.
|
||||
|
||||
:param str endpoint: The API endpoint path (e.g., 'v1/charges').
|
||||
:param bool is_test: Whether to use the sandbox environment.
|
||||
:return: The full API URL.
|
||||
:rtype: str
|
||||
"""
|
||||
base = const.ECOM_BASE_URL_TEST if is_test else const.ECOM_BASE_URL
|
||||
return f"{base}/{endpoint}"
|
||||
|
||||
|
||||
def build_platform_url(endpoint, merchant_id=None, is_test=False):
|
||||
"""Build a full Clover Platform API URL.
|
||||
|
||||
:param str endpoint: The API endpoint path.
|
||||
:param str merchant_id: The merchant ID (optional).
|
||||
:param bool is_test: Whether to use the sandbox environment.
|
||||
:return: The full API URL.
|
||||
:rtype: str
|
||||
"""
|
||||
base = const.API_BASE_URL_TEST if is_test else const.API_BASE_URL
|
||||
if merchant_id:
|
||||
return f"{base}/v3/merchants/{merchant_id}/{endpoint}"
|
||||
return f"{base}/{endpoint}"
|
||||
|
||||
|
||||
def build_ecom_headers(api_key, idempotency_key=None):
|
||||
"""Build the standard HTTP headers for a Clover Ecommerce API request.
|
||||
|
||||
:param str api_key: The Clover API key (Bearer token).
|
||||
:param str idempotency_key: Optional unique key for idempotency.
|
||||
:return: The request headers dict.
|
||||
:rtype: dict
|
||||
"""
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Authorization': f'Bearer {api_key}',
|
||||
}
|
||||
if idempotency_key:
|
||||
headers['idempotency-key'] = idempotency_key
|
||||
return headers
|
||||
|
||||
|
||||
def format_clover_amount(amount, currency):
|
||||
"""Convert a major currency amount to Clover's minor units (cents).
|
||||
|
||||
:param float amount: The amount in major currency units.
|
||||
:param recordset currency: The currency record.
|
||||
:return: The amount in minor currency units (integer).
|
||||
:rtype: int
|
||||
"""
|
||||
decimals = const.CURRENCY_DECIMALS.get(currency.name, 2)
|
||||
return int(round(amount * (10 ** decimals)))
|
||||
|
||||
|
||||
def parse_clover_amount(minor_amount, currency):
|
||||
"""Convert Clover's minor currency units back to major units.
|
||||
|
||||
:param int minor_amount: The amount in minor currency units.
|
||||
:param recordset currency: The currency record.
|
||||
:return: The amount in major currency units.
|
||||
:rtype: float
|
||||
"""
|
||||
decimals = const.CURRENCY_DECIMALS.get(currency.name, 2)
|
||||
return minor_amount / (10 ** decimals)
|
||||
|
||||
|
||||
def extract_card_details(source):
|
||||
"""Extract card details from a Clover charge source object.
|
||||
|
||||
:param dict source: The Clover source object from a charge response.
|
||||
:return: Dict with card brand, last4, expiration.
|
||||
:rtype: dict
|
||||
"""
|
||||
if not source:
|
||||
return {}
|
||||
|
||||
brand_raw = source.get('brand', '')
|
||||
brand_code = const.CARD_BRAND_MAPPING.get(brand_raw, 'card')
|
||||
|
||||
return {
|
||||
'brand': brand_code,
|
||||
'last4': str(source.get('last4', '')),
|
||||
'exp_month': source.get('exp_month'),
|
||||
'exp_year': source.get('exp_year'),
|
||||
'first6': str(source.get('first6', '')),
|
||||
}
|
||||
|
||||
|
||||
def get_clover_status(status_str):
|
||||
"""Map a Clover charge status string to an Odoo transaction state.
|
||||
|
||||
:param str status_str: The Clover charge status.
|
||||
:return: The corresponding Odoo payment state.
|
||||
:rtype: str
|
||||
"""
|
||||
for odoo_state, clover_statuses in const.STATUS_MAPPING.items():
|
||||
if status_str in clover_statuses:
|
||||
return odoo_state
|
||||
return 'error'
|
||||
|
||||
|
||||
def build_charge_payload(amount, currency, source_token, capture=True,
|
||||
description='', ecomind='ecom',
|
||||
external_reference_id='', receipt_email='',
|
||||
metadata=None):
|
||||
"""Build a Clover charge creation payload.
|
||||
|
||||
:param float amount: The charge amount in major currency units.
|
||||
:param recordset currency: The currency record.
|
||||
:param str source_token: The Clover card token.
|
||||
:param bool capture: Whether to capture immediately (True) or pre-auth (False).
|
||||
:param str description: Optional charge description.
|
||||
:param str ecomind: 'ecom' for customer-initiated, 'moto' for merchant-initiated.
|
||||
:param str external_reference_id: External reference (max 12 chars).
|
||||
:param str receipt_email: Email to send receipt to.
|
||||
:param dict metadata: Optional key-value metadata.
|
||||
:return: The Clover-formatted charge payload.
|
||||
:rtype: dict
|
||||
"""
|
||||
minor_amount = format_clover_amount(amount, currency)
|
||||
|
||||
payload = {
|
||||
'amount': minor_amount,
|
||||
'currency': currency.name.lower(),
|
||||
'source': source_token,
|
||||
'capture': capture,
|
||||
'ecomind': ecomind,
|
||||
}
|
||||
|
||||
if description:
|
||||
payload['description'] = description
|
||||
if external_reference_id:
|
||||
payload['external_reference_id'] = external_reference_id[:12]
|
||||
if receipt_email:
|
||||
payload['receipt_email'] = receipt_email
|
||||
if metadata:
|
||||
payload['metadata'] = metadata
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
def build_refund_payload(charge_id, amount=None, currency=None, reason=''):
|
||||
"""Build a Clover refund payload.
|
||||
|
||||
:param str charge_id: The Clover charge ID to refund.
|
||||
:param float amount: Optional partial refund amount in major currency units.
|
||||
:param recordset currency: Optional currency record (needed for partial refunds).
|
||||
:param str reason: Optional reason for the refund.
|
||||
:return: The Clover-formatted refund payload.
|
||||
:rtype: dict
|
||||
"""
|
||||
payload = {
|
||||
'charge': charge_id,
|
||||
}
|
||||
|
||||
if amount is not None and currency:
|
||||
payload['amount'] = format_clover_amount(amount, currency)
|
||||
|
||||
if reason:
|
||||
payload['reason'] = reason
|
||||
|
||||
return payload
|
||||
Reference in New Issue
Block a user