# Python Examples

Complete Python integration examples for AuxCORE API.

> **Note:** AuxCORE does not publish a Python SDK package. These examples call the REST API directly with `requests`.

---

## Installation

```bash
pip install requests
```

---

## Setup

```python
import os
import requests

BASE_URL = os.getenv('AUXCORE_API_BASE', 'https://uat.auxcore.net/api/v1/public')
API_KEY = os.getenv('AUXCORE_API_KEY')

session = requests.Session()
session.headers.update({
    'x-api-key': API_KEY,
    'Content-Type': 'application/json',
})
```

Tenant-wide ERP keys (`lv_erp_…`) must also send `X-Merchant-ID: <merchant uuid>`.

---

## Process Payment

Raw card numbers are rejected on Sandbox and Production. Tokenize first (`POST /tokenize-card` with a server-side key, or the PCI SDK in the browser) and charge the `pci_tok_…` token.

```python
# 1. Tokenize (server-side key only)
tok = session.post(f'{BASE_URL}/tokenize-card', json={
    'cardNumber': '4111111111111111',
    'expiryMonth': '12',
    'expiryYear': '2027',
    'cvv': '123',
    'cardHolderName': 'John Doe',
}).json()
pci_token = tok['data']['pciToken']

# 2. Charge
res = session.post(f'{BASE_URL}/transaction', json={
    'Amount': 100.00,
    'pciToken': pci_token,
    'BillingCustomerName': 'John Doe',
    'BillingEmail': 'john@example.com',
    'BillingAddress': '123 Main St',
    'BillingCity': 'New York',
    'BillingState': 'NY',
    'BillingPostalCode': '10001',
    'BillingCountry': 'US',
    'OrderId': 'ORD-1001',
})
body = res.json()

if res.status_code == 200 and body.get('requires_action'):
    print(f"Redirect payer to: {body['data']['RedirectUrl']}")
elif res.status_code == 200 and body.get('success'):
    print(f"Transaction approved: {body['data']['TransactionId']}")
elif res.status_code == 402:
    print(f"Payment declined: {body.get('message')} (code {body['data'].get('ResponseCode')})")
else:
    print(f"Error {res.status_code}: {body}")
```

---

## Webhook Handler (Flask)

The `X-Webhook-Signature` header is `t=<unix>,v1=<hex>`, where `v1` is HMAC-SHA256 of `"<t>.<raw body>"` keyed with the endpoint secret.

```python
import hashlib
import hmac
import json
import time
from flask import Flask, request

app = Flask(__name__)
WEBHOOK_SECRET = os.getenv('AUXCORE_WEBHOOK_SECRET')

def verify_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split('=', 1) for p in (header or '').split(',') if '=' in p)
    try:
        t = int(parts['t'])
    except (KeyError, ValueError):
        return False
    if abs(time.time() - t) > tolerance or 'v1' not in parts:
        return False
    expected = hmac.new(secret.encode(), f'{t}.'.encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts['v1'])

@app.route('/webhooks/auxcore', methods=['POST'])
def handle_webhook():
    payload = request.get_data()
    signature = request.headers.get('X-Webhook-Signature')

    if not verify_signature(payload, signature, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    event = json.loads(payload)  # { id, type, created, data, metadata }

    if event['type'] == 'transaction.approved':
        handle_transaction_approved(event['data'])
    elif event['type'] == 'transaction.declined':
        handle_transaction_declined(event['data'])

    return '', 200

def handle_transaction_approved(data):
    print(f"Transaction approved: {data['transactionId']}")

def handle_transaction_declined(data):
    print(f"Transaction declined: {data['transactionId']} {data.get('responseText')}")

if __name__ == '__main__':
    app.run(port=3000)
```

---

**Next:** [Postman collection →](./postman.md)
