Villa Backend SDK — User Manual

Python SDK and CLI for the Villa Market backend: Cognito login, order building, validation, and card payment.


1. Install

pip install villa-market-backend-sdk

Development (from git):

git clone https://github.com/villa-market/villa-backend-sdk-1.git
cd villa-backend-sdk-1
pip install -e ".[dev]"

Verify:

villa --version
python -c "from villa_backend_sdk import VillaClient; print('OK')"

2. Configure environment

Copy the example file and set your Cognito client secret:

cp .env.example .env
# Edit .env — paste value from Cognito → Show client secret
set -a && source .env && set +a
VariableRequiredDescription
VILLA_COGNITO_CLIENT_IDYesCognito app client ID
VILLA_COGNITO_USER_POOL_IDYesCognito user pool ID
VILLA_COGNITO_CLIENT_SECRETYes*App client secret (*confidential clients)
VILLA_COGNITO_REGIONNoDefault ap-southeast-1
VILLA_BASE_URLNoDefault https://shop.villamarket.com
VILLA_ENVNodev or prod (default dev)
VILLA_CUSTOMER_IDNoDefault customer / owner ID
VILLA_MIDNoKBank merchant ID

**Dev app client (villa-backend-sdk-dev):**

SettingValue
User poolvillaMembers2 (ap-southeast-1_bul3MgmNE)
Client ID6k932iah7v1hgnd33a53c3v1mj
Regionap-southeast-1

Cognito app client must have **Username and password** (ALLOW_USER_PASSWORD_AUTH) enabled.


3. Authenticate (Cognito)

All Villa backend API calls require a Cognito **ID token** as Authorization: Bearer <token>.

Python — login

from villa_backend_sdk import VillaClient

client = VillaClient.from_env()
tokens = client.login("user@example.com", "your-password")
# ID token is now attached to client HTTP calls automatically
print(tokens.id_token[:40], "...")

Python — verify ID token

Use this before trusting a stored JWT or when your backend receives a token from a client:

from villa_backend_sdk import VillaClient

client = VillaClient.from_env()

# Verify a token string
verified = client.verify_id_token(tokens.id_token)
print(verified.sub, verified.email, verified.exp)

# Or after login, verify the token currently on the client
client.login("user@example.com", "your-password")
assert client.is_id_token_valid() is True
verified = client.verify_id_token()  # uses the logged-in token

CLI:

villa auth verify-token --token "$ID_TOKEN"

Verification checks JWT signature against Cognito JWKS, plus exp, iss, aud, and token_use=id.

Python — register new user (SignUp)

No AWS admin IAM needed — same API your chat app uses:

client = VillaClient.from_env()
result = client.register(
    "newuser@example.com",
    "SecurePass123!",
    email="newuser@example.com",
)
print(result.user_confirmed)  # False until email confirmed

# After user enters email code:
client.auth.confirm_sign_up("newuser@example.com", confirmation_code="123456")
tokens = client.login("newuser@example.com", "SecurePass123!")

CLI — login

villa auth login --username user@example.com --password 'your-password'

Smoke test (shared dev account)

python3 scripts/smoke_test_cognito_login.py

Shared test credentials (safe to commit): see COGNITO_TEST.md.


4. Build an order

Orders follow the villaMasterSchema contract.

from villa_backend_sdk import OrderBuilder

order = (
    OrderBuilder()
    .with_ids(order_id="order-1", owner_id="owner-1", basket_id="basket-1")
    .with_branch("1000")
    .add_product(cprcode=25281, quantity=1)
    .with_delivery_shipping(
        shipping_postcode="10330",
        shipping_phone="0812345678",
        shipping_address="123 Sukhumvit Rd",
    )
    .with_payment_totals(grand_total=174.0)
    .build()
)

Generate an order ID from the backend:

generated = client.orders.generate_order_id("1000")
created = client.orders.create(order)
fetched = client.orders.get(created.order_id)

5. Validate before payment

Always validate locally and remotely before charging a card.

# 1. Local JSON Schema check (bundled order.yaml)
client.schema_validator.validate_order(order.to_api_payload())

# 2. Remote pricing APIs
result = client.pre_payment.validate(
    order,
    expected_grand_total=174.0,
)
print(result.valid, result.errors, result.calculated_grand_total)

if not result.valid:
    raise SystemExit("Fix order before payment")
StepAPI pathPurpose
Local schemabundled order.yamlStructure / required fields
Grand total/api/webPayment/calculateGrandtotalVerify totals
Cost/api/calculateCost/getCostVerify cart pricing

6. Pay with card

token = client.payments.create_card_token(
    client.payments.build_card_token_request(
        order_id=order.order_id,
        basket_id=order.basket_id,
        grand_total=str(result.calculated_grand_total or 174),
    )
)

payment = client.payments.initiate_card_payment(
    client.payments.build_card_payment_request(
        order_id=order.order_id,
        basket_id=order.basket_id,
        grand_total=str(result.calculated_grand_total or 174),
        token=token.card_token,
    )
)
print(payment.redirect_url)

Dev environment uses /api/payment3devapi/* paths automatically when VILLA_ENV=dev.


7. Full checkout example

from villa_backend_sdk import VillaClient, OrderBuilder

client = VillaClient.from_env()
client.login("user@example.com", "your-password")

order = (
    OrderBuilder()
    .with_ids(order_id="order-1", owner_id="owner-1", basket_id="basket-1")
    .with_branch("1000")
    .add_product(cprcode=25281, quantity=1)
    .with_payment_totals(grand_total=174.0)
    .build()
)

check = client.pre_payment.validate(order, expected_grand_total=174.0)
if check.valid:
    token = client.payments.create_card_token(
        client.payments.build_card_token_request(
            order_id=order.order_id,
            basket_id=order.basket_id,
            grand_total="174",
        )
    )
    print("Token:", token.card_token)

8. CLI reference

Global options (also read from env vars):

villa --env dev --customer-id OWNER_ID --merchant-id MERCHANT_ID <command>
CommandDescription
villa healthCheck backend reachability
villa config showPrint active SDK config
villa auth loginCognito login
villa order generate-id --branch-id 1000Generate order ID
villa order get ORDER_IDFetch order
villa order validate-schema --file order.jsonLocal schema validation
villa validate pre-payment --file order.json --expected-grand-total 500Pre-payment checks
villa payment charge --order-id ... --basket-id ... --amount 174Card payment flow

Examples:

villa health
villa config show
villa auth login --username user@example.com --password 'secret'
villa order validate-schema --file tests/fixtures/goodSample.json
villa validate pre-payment --file tests/fixtures/goodSample.json --expected-grand-total 500
villa --env dev payment charge \
  --order-id order-1 \
  --basket-id basket-1 \
  --amount 174 \
  --validate \
  --order-file tests/fixtures/goodSample.json

9. Run tests

pytest tests/unit -m unit              # fast, mocked
pytest tests/integration -m integration  # uses real Cognito login + JWT
pytest                                   # full suite (72 tests)

Integration tests log in with the shared test account and attach JWT to all API calls.


10. Troubleshooting

SymptomFix
Unable to verify secret hashUse **Show client secret** value, not the Client Secret ID
User is not confirmedConfirm via email code or Cognito console → Users → Confirm
USER_PASSWORD_AUTH flow not enabledEnable **Username and password** on app client
401 on API callsCall client.login() first; check token expiry (60 min)
Integration tests fail on loginSet Cognito env vars; confirm test user

11. Related docs

  • DESIGN.md — architecture and data flow
  • TESTING.md — test strategy
  • COGNITO_TEST.md — shared test account
  • CI_CD.md — CI/CD pipeline
  • Repository: github.com/villa-market/villa-backend-sdk-1