Skip to main content

django-athm

Build Status readthedocs codecov PyPI - Python Version PyPI - Django Version PyPI version Published on Django Packages Packaged with uv License badge

Django integration for ATH Móvil payments (Puerto Rico's mobile payment system).

See this README in spanish: README_ES.md

Main Purpose

Webhook-driven payment and refund synchronization. ATH Móvil sends definitive transaction data via webhooks, providing complete audit trails with fees, net amounts, and customer information.

Features

  • Webhook handling with idempotency
  • Transaction persistence with refunds and client records for complete audit trails
  • Read-only Django Admin with refund actions and webhook management
  • Django signals for payment lifecycle events (completed, cancelled, expired, refunded)
  • Transaction reconciliation via the athm_sync management command
  • Optional payment button template tag with zero-dependency JavaScript for quick integration

Requirements

  • Python 3.10 - 3.14
  • Django 5.1 - 6.0

Installation

pip install django-athm

Add to your INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    "django_athm",
]

Add your ATH Móvil Business API tokens (find these in the mobile app's settings):

DJANGO_ATHM_PUBLIC_TOKEN = "your-public-token"
DJANGO_ATHM_PRIVATE_TOKEN = "your-private-token"

Include the URLs:

# urls.py
from django.urls import include, path

urlpatterns = [
    # ...
    path("athm/", include("django_athm.urls")),
]

Run migrations:

python manage.py migrate django_athm

Quick Start (using bundled templatetag)

1. Create a view with payment configuration

# views.py
from django.shortcuts import render

def checkout(request):
    athm_config = {
        "total": 25.00,
        "subtotal": 23.36,
        "tax": 1.64,
        "metadata_1": "order-123",
        "items": [
            {"name": "Widget", "price": 23.36, "quantity": 1}
        ],
        "success_url": "/order/complete/",
        "failure_url": "/order/failed/",
    }
    return render(request, "checkout.html", {"ATHM_CONFIG": athm_config})

2. Add the payment button to your template

{% load django_athm %}

<h1>Checkout</h1>
{% athm_button ATHM_CONFIG %}

3. Handle payment completion with signals

# signals.py
from django.dispatch import receiver
from django_athm.signals import payment_completed

@receiver(payment_completed)
def handle_payment_completed(sender, payment, **kwargs):
    # Update your order status, send confirmation email, etc.
    print(f"Payment {payment.reference_number} completed for ${payment.total}")

Architecture

Webhook-Driven Synchronization

Webhooks provide definitive transaction data with idempotency guarantees:

ATH Móvil webhook -> Idempotent processing -> Payment/Refund sync -> Django signals

Optional Modal Payment Flow

For quick integration, use the included template tag:

  1. Initiate: User clicks button, backend creates payment via ATH Móvil API
  2. Confirm: User confirms payment in ATH Móvil app
  3. Authorize: Backend authorizes the confirmed payment
  4. Webhook: ATH Móvil sends completion event with final details
User clicks    ->  Backend creates  ->  User confirms   ->  Backend authorizes  ->  Webhook received
ATH Móvil         payment (OPEN)        in app (CONFIRM)    payment (COMPLETED)     (final details)
button

You can also build your own payment UI and use only the webhook synchronization features.

Webhooks

Installing Webhooks

Recommended: Use Django Admin (auto-detects your webhook URL):

  1. Navigate to ATH Móvil Webhook Events in the admin
  2. Click Install Webhooks button
  3. Verify the auto-detected URL (edit if needed)
  4. Click submit to register with ATH Móvil

Alternative: Management command

# Auto-detect from DJANGO_ATHM_WEBHOOK_URL setting
python manage.py install_webhook

# Or provide explicit URL
python manage.py install_webhook https://yourdomain.com/athm/webhook/

Transaction Reconciliation

Reconcile local records with ATH Movil's Transaction Report API to recover missed webhooks or backfill historical data:

# Sync transactions for a date range
python manage.py athm_sync --from-date 2025-01-01 --to-date 2025-01-31

# Preview changes without modifying database
python manage.py athm_sync --from-date 2025-01-01 --to-date 2025-01-31 --dry-run

Webhook Idempotency

All webhooks are processed idempotently using deterministic keys based on the event payload. Duplicate events are automatically detected and ignored.

Custom Webhook Views

If you need custom logic before/after webhook processing:

from django.views.decorators.csrf import csrf_exempt
from django_athm.views import process_webhook_request

@csrf_exempt
def my_custom_webhook(request):
    # Pre-processing (logging, rate limiting, etc.)
    log_webhook(request)

    # Call django-athm handler (maintains idempotency)
    response = process_webhook_request(request)

    # Post-processing (notifications, analytics, etc.)
    notify_team()

    return response

Django Admin

The package provides a read-only admin interface:

  • Payments: View all transactions, filter by status/date, bulk refund action
  • Refunds: View refund records
  • Clients: View customer records linked by phone number
  • Webhook Events: View webhook history, reprocess failed events, install webhooks

All models are read-only to preserve data integrity - payments can only be refunded, not edited.

Optional Template Tag Configuration

The athm_button template tag is optional - you can build your own payment UI and use only the webhook features.

athm_config = {
    # Required
    "total": 25.00,              # Payment amount (1.00 - 1500.00)

    # Optional
    "subtotal": 23.36,           # Subtotal for display
    "tax": 1.64,                 # Tax amount
    "metadata_1": "order-123",   # Custom field (max 40 chars)
    "metadata_2": "customer-456",# Custom field (max 40 chars)
    "items": [...],              # List of item objects
    "theme": "btn",              # Button theme: btn, btn-dark, btn-light
    "lang": "es",                # Language: es, en
    "success_url": "/thanks/",   # Redirect on success (adds ?reference_number=...&ecommerce_id=...)
    "failure_url": "/failed/",   # Redirect on failure
}

Signals

Subscribe to payment lifecycle events:

from django_athm.signals import (
    payment_completed,  # Payment successful (webhook)
    payment_cancelled,  # Payment cancelled (webhook)
    payment_expired,    # Payment expired (webhook)
    refund_sent,        # Refund processed (webhook)
)

Development Setup

This project uses uv for package management.

# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install dependencies
uv sync

# Run tests
DJANGO_SETTINGS_MODULE=tests.settings pytest --cov django_athm

# Run full test matrix
tox

# Run linting
tox -e lint

Legal

This project is not affiliated with or endorsed by Evertec, Inc. or ATH Móvil.

Dependencies

References

Feedback

Have suggestions or ideas for improving django-athm? We'd love to hear from you! Submit feedback.

License

MIT License - see LICENSE for details.

Release files for django-athm 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-athm 1.0.2
File Size Uploaded
django_athm-1.0.2.tar.gz 45.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-athm 1.0.2
File Interpreter ABI Platform
django_athm-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 102.2 kB

Release files / django_athm-1.0.2.tar.gz

Download URL django_athm-1.0.2.tar.gz
Size 45.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2191ba2282329dbe6221b21cf2d8474ceaba6a3cfd96ba2d271d76c7df8ff510
BLAKE2b-256 checksum
How to use checksums
762c94a1ce34224eaa8a20b50149d8178931c97a998bff0feeff1e0c4751e51e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 12, 2026.

Transparency log

Release files / django_athm-1.0.2-py3-none-any.whl

Download URL django_athm-1.0.2-py3-none-any.whl
Size 56.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dc69072dfc4dc15dd5c82d656f92b18d2e4e4708c0d6dc1e3623a7cc76d96033
BLAKE2b-256 checksum
How to use checksums
0e93cea5bacd8c551028b9d951afe6a3890b69f3a82809c0ef3df60c05d64d46
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 12, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page