django-athm
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_syncmanagement 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:
- Initiate: User clicks button, backend creates payment via ATH Móvil API
- Confirm: User confirms payment in ATH Móvil app
- Authorize: Backend authorizes the confirmed payment
- 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):
- Navigate to ATH Móvil Webhook Events in the admin
- Click Install Webhooks button
- Verify the auto-detected URL (edit if needed)
- 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
- athm-python - ATH Móvil API client
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)
| File | Size | Uploaded | |
|---|---|---|---|
| django_athm-1.0.2.tar.gz | 45.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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