-
Notifications
You must be signed in to change notification settings - Fork 1
Crypto Exchange
📕 Table of Contents
Web Pages (incl. static files)
- The application should be registered in settings.p ⚙️
# core/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'payments.apps.PaymentsConfig'
]
- A function view called
home_view
🏠 was is added to views.py
# payments/views.py
from django.shortcuts import render
def home_view(request):
return render(request, 'home.html', {})
- Create a file inside the payments app named urls.py
# payments/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home_view, name='payments-home'),
]
- The project-level URLs file with the payments app should also be updated
# core/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('payments.urls')),
path('admin/', admin.site.urls)
]
- A web front should be created. This can be achieved by a HTML homepage in the templates folder
(ecrypto)$ mkdir templates
(ecrypto)$ touch templates/home.html
Below is the example HTML that is used to achieve the web front end homepage:
<!-- templates/home.html -->
{% load static %}
<html lang="en">
<head>
<title>Django + Coinbase Pay</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>
</head>
<body>
<div class="container mt-5">
<div class="card w-25">
<div class="card-body">
<h3 class="card-title">{{ charge.name }}</h3>
<img src="static/img/whiskey_1.jpeg" alt="the finest" width="250" height="200">
<p class="card-text">
<span>{{ charge.description }}</span>
<br>
<span>€{{ charge.pricing.local.amount }} {{ charge.pricing.local.currency }}</span>
</p>
<div>
<a class="btn btn-primary w-100" href="{{ charge.hosted_url }}">Purchase<a>
</div>
</div>
</div>
</div>
</body>
</html>
- At this point Django will not know about this newly created templates folder. As such, this needs to be updated via the settings.py
# core/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['templates'],
......
- For more completeness, two additional pages were created to support web page redirects. This is to support
redirect_url
andcancel_url
which can be noted in the views.py
# payments/views.py
def success_view(request):
return render(request, 'success.html', {})
def cancel_view(request):
return render(request, 'cancel.html', {})
- Each view has to be registered inside urls.py
# payments/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.home_view, name="payments-home"),
path("success/", views.success_view, name="payments-success"),
path("cancel/", views.cancel_view, name="payments-cancel"),
path("webhook/", views.coinbase_webhook),
path('ping/', views.ping, name="ping"),
]
Below is the code snippet used for the success page:
{% load static %}
<html lang="en">
<head>
<title>E-Crypto: Purchase</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400" rel="stylesheet">
<!-- <link rel="stylesheet" type="text/html" href="{% static 'css/base.css' %}" crossorigin="anonymous"> -->
<link rel="stylesheet" type="text/css" href='../static/css/base.css'>
</head>
<body>
<div class="box">
<div class="success alert">
<div class="alert-body">
<p>Your payment has been successful</p>
<a href="/">Continue Shopping</a>
</div>
</div>
</div>
</body>
</html>
Below is the code snippet used in the cancel page:
{% load static %}
<html lang="en">
<head>
<title>E-Crypto: Cancel</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400" rel="stylesheet">
<!-- <link rel="stylesheet" type="text/html" href="{% static 'css/base.css' %}" crossorigin="anonymous"> -->
<link rel="stylesheet" type="text/css" href='../static/css/base.css'>
</head>
<body>
<div class="box">
<div class="error alert">
<div class="alert-body">
<p>Your payment has been cancelled!</p>
<a href="/">Continue Shopping</a>
</div>
</div>
</div>
</body>
</html>
- Noting that custom static files are used in both
success
andcancel
web pages. As such, serving these file was done using Django specific logic via{% load static %}
. This is referenced in settings.py - For further details on managing static files in Django please review: Django: Static How to documentation
- The python package WhiteNoise was used to manage static assets and files (images and custom css)
Please note the code section below, detailing where this is initialised:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
- And again in settings.py
STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
- Coinbase should be added via pip
(ecrypto)$ pip install coinbase-commerce==1.0.1
- The API key that is generated via the Coinbase Commerce account should be added to a local file named
config.py
For local development, when using Docker compose, an env file was created in the project root
touch /app/env.dev
Below is an example of the env.dev
file and it contents.
SECRET_KEY='foo'
DEBUG=False
COINBASE_COMMERCE_API_KEY = 'eeb11339-****-****-****-************'
Note, this is a minimum requirement for the app to function correctly
- This key 🔐 is then referenced in settings.py
from core import config
COINBASE_COMMERCE_API_KEY = os.environ.get('COINBASE_API_KEY')
home_view
is updated in views.py
# payments/views.py
from coinbase_commerce.client import Client
from django.shortcuts import render
from core import settings
def home_view(request):
logger = logging.getLogger(__name__)
client = Client(api_key=settings.COINBASE_COMMERCE_API_KEY)
domain_url = "http://localhost:8000/"
hostname = request.get_host().split(":", 1)[0]
if hostname in ("testserver", "localhost", "127.0.0.1"):
domain_url = "http://localhost:8000/"
else:
domain_url = f"https://{hostname}/"
product = {
"name": "₿ig (Ξ)'s Whiskey",
"description": "Sweet sweet nectar",
"local_price": {"amount": "5.00", "currency": "EUR"},
"pricing_type": "fixed_price",
"redirect_url": domain_url + "success/",
"cancel_url": domain_url + "cancel/",
"metadata": {
"customer_id": request.user.id if request.user.is_authenticated else None,
"customer_username": request.user.username
if request.user.is_authenticated
else None,
},
}
charge = client.charge.create(**product)
return render(request, "home.html", {"charge": charge,})
- The client is first initialised by passing
COINBASE_COMMERCE_API_KEY
to it. - A JSON object is created which represents the product (we provided the
name
,description
, etc.). Includingredirect_url
andcancel_url
to the charge. - The JSON object is unpacked and then used the client to create a charge.
- Finally, it is passed to the home template as context.
This application was published to Heroku for proof of concept purposes. As such, if someone wants to make a fork of this repository for their own purposes. Please note the below Heroku & Docker commands that might be of assistance for deployment:
- Create an app (i.e. dyno)
$ heroku create
Creating app... done, ⬢ fierce-aholl-45578
https://fierce-aholl-45578.herokuapp.com/ | https://git.heroku.com/fierce-aholl-45578.git
- Add environmental variable(s)
$ heroku config:set SECRET_KEY=SOME_SECRET_VALUE -a fierce-aholl-45578
- Deployment can be done directly from a local development environment. Below examples of how to achieve this:
$ docker build -t registry.heroku.com/fierce-aholl-45578/web .
$ docker push registry.heroku.com/fierce-aholl-45578/web
$ heroku container:release -a fierce-aholl-45578 web