Welcome to django-getpaid’s documentation!¶
django-getpaid is a multi-broker payment processor for Django. Main features include:
support for multiple payment brokers at the same time
very flexible architecture
support for asynchronous status updates - both push and pull
support for modern REST-based broker APIs
support for multiple currencies (but one per payment)
support for global and per-plugin validators
easy customization with provided base abstract models and swappable mechanic (same as with Django’s User model)
We would like to provide a catalog of plugins for django-getpaid
- if you create a plugin please let us know.
Disclaimer: this project has nothing in common with getpaid plone project.
This project uses semantic versioning.
Contents:¶
Installation & Configuration¶
This document presents the minimal steps required to use django-getpaid
in your project.
Get it from PyPI¶
pip install django-getpaid
We do not recommend using development version as it may contain bugs.
Install at least one plugin¶
There should be several plugins available in our repo. Each follows this
schema: django-getpaid-<backend_name>
For example if you want to install PayU integration, run:
pip install django-getpaid-payu
Enable app and plugin¶
Next, add "getpaid"
and any plugin to INSTALLED_APPS
in your settings.py
.
Plugins have the format getpaid_<backend_name>
:
INSTALLED_APPS = [
# ...
"getpaid",
"getpaid_payu",
]
Create Order model¶
You need to create your own model for an order. It needs to inherit from
getpaid.models.AbstractOrder
and you need to implement some methods. It
could look like this example:
from django.conf import settings
from django.db import models
from getpaid.models import AbstractOrder
class CustomOrder(AbstractOrder):
buyer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
description = models.CharField(max_length=128, default='Order from mystore')
total = models.DecimalField()
currency = models.CharField(max_length=3, default=settings.DEFAULT_CURRENCY)
def get_buyer_info(self):
return {"email": self.buyer.email}
def get_total_amount(self):
return self.total
def get_description(self):
return self.description
def get_currency(self):
return self.currency
# either one of those two is required:
def get_redirect_url(self, *args, success=None, **kwargs):
# this method will be called to get the url that will be displayed
# after returning from the paywall page and you can use `success` param
# to differentiate the behavior in case the backend supports it.
# By default it returns this:
return self.get_absolute_url()
def get_absolute_url(self):
# This is a standard method recommended in Django documentation.
# It should return an URL with order details. Here's an example:
return reverse("order-detail", kwargs={"pk": self.pk})
# these are optional:
def is_ready_for_payment(self):
# Most of the validation will be handled by the form
# but if you need any extra logic beyond that, you can write it here.
# This is the default implementation:
return True
def get_items(self):
# Some backends expect you to provide the list of items.
# This is the default implementation:
return [{
"name": self.get_description(),
"quantity": 1,
"unit_price": self.get_total_amount(),
}]
Tell getpaid
what model handles orders¶
Put this inside your settings.py
:
GETPAID_ORDER_MODEL = "yourapp.CustomOrder"
(Optional) Provide custom Payment model¶
If you want, you can provide your own Payment model. Read more in Customization - Payment API.
Note
Payment model behaves like django.auth.User model - after you use the original, migration to a custom version is VERY hard.
Add getpaid to urls¶
urlpatterns = [
# ...
path("payments", include("getpaid.urls")),
]
Provide config for plugins¶
For each installed plugin you can configure it in settings.py
:
GETPAID = {
"BACKENDS":{
"getpaid_payu": { # dotted import path of the plugin
# refer to backend docs and take these from your merchant panel:
"pos_id": 12345,
"second_key": "91ae651578c5b5aa93f2d38a9be8ce11",
"client_id": 12345,
"client_secret": "12f071174cb7eb79d4aac5bc2f07563f",
},
# this plugin is meant only for testing purposes
"getpaid.backends.dummy": {
"confirmation_method": "push",
},
}
}
Prepare views and business logic¶
The logic for building an order is up to you. You can eg. use a cart application to gather all Items for your Order.
An example view and its hookup to urls.py can look like this:
# orders/views.py
class OrderView(DetailView):
model = Order
def get_context_data(self, **kwargs):
context = super(OrderView, self).get_context_data(**kwargs)
context["payment_form"] = PaymentMethodForm(
initial={"order": self.object, "currency": self.object.currency}
)
return context
# main urls.py
urlpatterns = [
# ...
path("order/<int:pk>/", OrderView.as_view(), name="order_detail"),
]
You’ll also need a template (order_detail.html
in this case) for this view.
Here’s the important part:
<h2>Choose payment broker:</h2>
<form action="{% url 'getpaid:create-payment' %}" method="post">
{% csrf_token %}
{{ payment_form.as_p }}
<input type="submit" value="Checkout">
</form>
And that’s pretty much it.
After you open order detail you should see a list of plugins supporting your currency and a “Checkout” button that will redirect you to selected paywall. After completing the payment, you will return to the same view.
Please see fully working example app.
Next steps¶
If you’re not satisfied with provided Payment model or the PaymentMethodForm, please see customization docs.
Plugin catalog¶
All plugins in alphabetical order.
Currently a lot of plugins is being developed or ported. Please stay tuned.
PayU¶
PyPI name:
django-getpaid-payu
Repository: https://github.com/django-getpaid/django-getpaid-payu
Original API Docs: https://www.payu.pl/en/developers
Sandbox panel: https://merch-prod.snd.payu.com/user/login
Currencies: BGN, CHF, CZK, DKK, EUR, GBP, HRK, HUF, NOK, PLN, RON, RUB, SEK, UAH, USD
Settings¶
Core settings¶
GETPAID_ORDER_MODEL
¶
No default, you must provide this setting.
The model to represent an Order. See Customization - Payment API.
Warning
You cannot change the GETPAID_ORDER_MODEL
setting during the lifetime of
a project (i.e. once you have made and migrated models that depend on it)
without serious effort. It is intended to be set at the project start,
and the model it refers to must be available in the first migration of
the app that it lives in.
GETPAID_PAYMENT_MODEL
¶
Default: 'getpaid.Payment'
The model to represent a Payment. See Customization - Payment API.
Warning
You cannot change the GETPAID_PAYMENT_MODEL
setting during the lifetime of
a project (i.e. once you have made and migrated models that depend on it)
without serious effort. It is intended to be set at the project start,
and the model it refers to must be available in the first migration of
the app that it lives in.
Backend settings¶
To provide configuration for payment backends, place them inside GETPAID_BACKEND_SETTINGS
dictionary. Use plugin’s dotted path - just as you put it in INSTALLED_APPS
- as a key for the config dict. See this example:
GETPAID_BACKEND_SETTINGS = {
"getpaid.backends.dummy": {
"confirmation_method": "push",
"gateway": reverse_lazy("paywall:gateway"),
},
"getpaid_paynow": {
"api_key": "9bcdead5-b194-4eb5-a1d5-c1654572e624",
"signature_key": "54d22fdb-2a8b-4711-a2e9-0e69a2a91189",
},
}
Each backend defines its own settings this way. Please check the backend’s documentation.
Optional settings¶
A place for optional settings is GETPAID
dictionary, empty by default.
It can contain these keys:
POST_TEMPLATE
¶
Default: None
This setting is used by processor’s default get_template_names()
method to override backend’s template_name
.
The template is used to render that backend’s POST form.
This setting can be used to provide a global default for such cases if you use more
plugins requiring such template. You can also use POST_TEMPLATE
key in
backend’s config to override the template just for one backend.
POST_FORM_CLASS
¶
Default: None
This setting is used by backends that use POST flow.
This setting can be used to provide a global default for such cases if you use more
plugins requiring such template. You can also use POST_FORM_CLASS
key in
backend’s config to override the template just for one backend.
Use full dotted path name.
SUCCESS_URL
¶
Default: "getpaid:payment-success"
Allows setting custom view name for successful returns from paywall. Again, this can also be set on a per-backend basis.
If the view requires kwargs to be resolved, you need to override
FAILURE_URL
¶
Default: "getpaid:payment-failure"
Allows setting custom view name for fail returns from paywall. Again, this can also be set on a per-backend basis.
HIDE_LONELY_PLUGIN
¶
Default: False
Allows you to hide plugin selection if only one plugin would be presented. The hidden plugin will be chosen as default.
Customization - Payment API¶
Django-getpaid was designed to be very customizable. In this document you’ll
read about the Payment API which lets you customize most of the mechanics
of django-getpaid
.
Since most Payment methods act as interface to PaymentProcessor, you can use this to add extra layer between the Payment and the PaymentProcessor.
Basic Order API¶
-
class
getpaid.models.
AbstractOrder
(*args, **kwargs)¶ Please consider setting either primary or secondary key of your Orders to UUIDField. This way you will hide your volume which is valuable business information that should be kept hidden. If you set it as secondary key, remember to use
dbindex=True
(primary keys are indexed by default). Read more: https://docs.djangoproject.com/en/3.0/ref/models/fields/#uuidfield-
get_return_url
(*args, success=None, **kwargs)¶ Method used to determine the final url the client should see after returning from gateway. Client will be redirected to this url after backend handled the original callback (i.e. updated payment status) and only if SUCCESS_URL or FAILURE_URL settings are NOT set. By default it returns the result of get_absolute_url
-
get_absolute_url
()¶ Standard method recommended in Django docs. It should return the URL to see details of particular Payment (or usually - Order).
-
is_ready_for_payment
()¶ Most of the validation is made in PaymentMethodForm but if you need any extra validation. For example you most probably want to disable making another payment for order that is already paid.
You can raise
ValidationError
if you want more verbose error message.
-
get_items
()¶ There are backends that require some sort of item list to be attached to the payment. But it’s up to you if the list is real or contains only one item called “Payment for stuff in {myshop}” ;)
- Returns
List of {“name”: str, “quantity”: Decimal, “unit_price”: Decimal} dicts.
-
get_total_amount
()¶ This method must return the total value of the Order.
- Returns
Decimal object
-
get_buyer_info
() → dict¶ This method should return dict with necessary user info. For most backends email should be sufficient. Expected field names: email, first_name, last_name, phone
-
get_description
() → str¶ - Returns
Description of the Order. Should return the value of appropriate field.
-
Basic Payment API¶
AbstractPayment
defines a minimal set of fields that are expected by
BaseProcessor
API. If you want to have it completely your own way, make sure
to provide properties linking your fieldnames to expected names.
-
class
getpaid.models.
AbstractPayment
(*args, **kwargs)¶ -
id
¶ UUID to not disclose your volume.
-
order
¶ ForeignKey to (swappable) Order model.
-
amount_required
¶ Decimal value with 4 decimal places. Total value of the Order that needs to be paid.
-
currency
¶ Currency code in ISO 4217 format.
-
status
¶ Status of the Payment - one of
PAYMENT_STATUS_CHOICES
. This field is managed using django-fsm.
-
backend
¶ Identifier of the backend processor used to handle this Payment.
-
created_on
¶ Datetime of Payment creation - automated.
-
last_payment_on
¶ Datetime the Payment has been completed. Defaults to NULL.
-
amount_paid
¶ Amount actually paid by the buyer. Should be equal amount_required if backend does not support partial payments. Will be smaller than that after partial refund is done.
-
amount_locked
¶ Amount that has been pre-authed by the buyer. Needs to be charged to finalize payment or released if the transaction cannot be fulfilled.
-
amount_refunded
¶ Amount that was refunded. Technically this should be equal to amount_required - amount_paid.
-
external_id
¶ ID of the payment on paywall’s system. Optional.
-
description
¶ Payment description (max 128 chars).
-
fraud_status
¶ Field representing the result of fraud check (only on supported backends).
-
fraud_message
¶ Message provided along with the fraud status.
-
get_processor
()¶ Returns the processor instance for the backend that was chosen for this Payment. By default it takes it from global backend registry and tries to import it when it’s not there. You most probably don’t want to mess with this.
-
get_unique_id
()¶ Return unique identifier for this payment. Most paywalls call this “external id”. Default: str(self.id) which is uuid4.
-
get_items
()¶ Some backends require the list of items to be added to Payment.
This method relays the call to Order. It is here simply because you can change the Order’s fieldname when customizing Payment model. In that case you need to overwrite this method so that it properly returns a list.
-
get_form
(*args, **kwargs)¶ Interfaces processor’s
get_form
.Returns a Form to be used on intermediate page if the method returned by
get_redirect_method
is ‘POST’.
-
get_template_names
(view=None)¶ Interfaces processor’s
get_template_names
.Used to get templates for intermediate page when
get_redirect_method
returns ‘POST’.
-
handle_paywall_callback
(request, **kwargs)¶ Interfaces processor’s
handle_paywall_callback
.Called when ‘PUSH’ flow is used for a backend. In this scenario paywall will send a request to our server with information about the state of Payment. Broker can send several such requests during Payment’s lifetime. Backend should analyze this request and return appropriate response that can be understood by paywall.
- Parameters
request – Request sent by paywall.
- Returns
HttpResponse instance
-
fetch_status
()¶ Interfaces processor’s
fetch_payment_status
.Used during ‘PULL’ flow. Fetches status from paywall and translates it to a value from
PAYMENT_STATUS_CHOICES
.
-
fetch_and_update_status
()¶ Used during ‘PULL’ flow to automatically fetch and update Payment’s status.
-
prepare_transaction
(request=None, amount=None, view=None, **kwargs)¶ Interfaces processor’s
prepare_transaction()
.
-
confirm_prepared
(**kwargs)¶ Used to confirm that paywall registered POSTed form.
-
confirm_lock
(amount=None, **kwargs)¶ Used to confirm that certain amount has been locked (pre-authed).
-
confirm_charge_sent
(**kwargs)¶ Used during async charge cycle - after you send charge request, the confirmation will be sent to callback endpoint.
-
confirm_payment
(amount=None, **kwargs)¶ Used when receiving callback confirmation.
-
mark_as_paid
(**kwargs)¶ Marks payment as fully paid if condition is met.
-
confirm_refund
(amount=None, **kwargs)¶ Used when receiving callback confirmation.
-
mark_as_refunded
(**kwargs)¶ Verify if refund was partial or full.
-
fail
(**kwargs)¶ Sets Payment as failed.
-
Creating payment plugins¶
In order to create a plugin for a payment broker, first you need to
write a subclass of BaseProcessor
named PaymentProcessor
and place it in processor.py
in your app.
The only method you have to provide is prepare_transaction()
that needs to return a HttpResponse
subclass (eg. HttpResponseRedirect or TemplateResponse).
The use of all other methods depends directly on how the paywall operates.
To make your plugin available for the rest of the framework, you need to register it.
The most convenient way to do so is apps.py
:
from django.apps import AppConfig
class MyPluginAppConfig(AppConfig):
name = "getpaid_myplugin"
verbose_name = "Some payment broker"
def ready(self):
from getpaid.registry import registry
registry.register(self.module)
This way your plugin will be automatically registered after adding it to INSTALLED_APPS
.
Detailed API¶
-
class
getpaid.processor.
BaseProcessor
(payment)¶ -
production_url
= None¶ Base URL of production environment.
-
sandbox_url
= None¶ Base URL of sandbox environment.
-
display_name
= None¶ The name of the provider for the
choices
.
-
accepted_currencies
= None¶ List of accepted currency codes (ISO 4217).
-
logo_url
= None¶ Logo URL - can be used in templates.
-
ok_statuses
= [200]¶ List of potentially successful HTTP status codes returned by paywall when creating payment
-
slug
= None¶ For friendly urls
-
static
get_our_baseurl
(request=None, **kwargs)¶ Little helper function to get base url for our site. Note that this way ‘https’ is enforced on production environment.
-
prepare_form_data
(post_data, **kwargs)¶ If backend support several modes of operation, POST should probably additionally calculate some sort of signature based on passed data.
-
get_form
(post_data, **kwargs)¶ (Optional) Used to get POST form for backends that use such flow.
-
abstract
prepare_transaction
(request, view=None, **kwargs)¶ Prepare Response for the view asking to prepare transaction.
- Returns
HttpResponse instance
-
handle_paywall_callback
(request, **kwargs)¶ This method handles the callback from paywall for the purpose of asynchronously updating the payment status in our system.
- Returns
HttpResponse instance that will be presented as answer to the callback.
-
fetch_payment_status
(**kwargs) → dict¶ Logic for checking payment status with paywall.
Should return dict with either “amount” or “status” keys. If “status” key is used, it should be one of getpaid.models.PAYMENT_STATUS_CHOICES If both keys are present, “status” takes precedence.
-
charge
(amount=None, **kwargs)¶ (Optional) Check if payment can be locked and call processor’s method. This method is used eg. in flows that pre-authorize payment during order placement and charge money later. Returns charged amount.
-
release_lock
(**kwargs)¶ (Optional) Release locked payment. This can happen if pre-authorized payment cannot be fullfilled (eg. the ordered product is no longer available for some reason). Returns released amount.
-
start_refund
(amount=None, **kwargs)¶ Refunds the given amount.
Returns the amount that was actually refunded.
-
cancel_refund
(**kwargs)¶ Cancels started refund.
Returns True/False if the cancel succeeded.
-
Plugin registry¶
Plugin registry is a convenient way to handle multiple brokers that can support different currencies and provide different flows.
Internal API¶
-
class
getpaid.registry.
PluginRegistry
¶ -
register
(module_or_proc)¶ Register module containing PaymentProcessor class or a PaymentProcessor directly.
-
get_choices
(currency)¶ Get plugins that support given currency, in CHOICES format.
-
property
urls
¶ Provide URL structure for all registered plugins that have urls defined.
-
get_all_supported_currency_choices
()¶ Get all currencies that are supported by at least one plugin, in CHOICES format.
-
Planned features¶
These features are planned for future versions (in no particular order):
translations
paywall communication log (all responses and callbacks)
Subscriptions handling
Complete type annotations
cookiecutter for plugins
django-rest-framework helpers
async/await support
admin actions to PULL payment statuses
History¶
Version 2.0.0 (2020-04-18)¶
BREAKING: Complete redesign of internal APIs.
Supports only Django 2.2+ and Python 3.6+
Payment and Order became swappable models - like Django’s User model
Payment acts as customizable interface to PaymentProcessor instances (but be careful).
Payment statuses guarded with django-fsm
Broker plugins separated from main repo - easier updates.
Version 1.8.0 (2018-07-24)¶
Updated project structure thanks to cookiecutter-djangopackage
New plugin: pay_rest - New PayU API
Updated following plugins: - payu - legacy API still works on new URL
Dropped support for following plugins: - epaydk (API no longer functional) - moip (will be moved to separate package) - transferuj.pl (API no longer functional) - przelewy24.pl (API needs update, but no sandbox available anymore)
Dropped support for Django <= 1.10
Provide support for Django 2.0
Version 1.7.5¶
Fixed przelewy24 params (py3 support)
Version 1.7.4¶
Added default apps config getpaid.apps.Config
Fixed and refactoring for utils.get_domain, build_absolute_uri, settings.GETPAID_SITE_DOMAIN
Refactored register_to_payment
Refactored build_absolute_uri
Refactored and fixes in transferuj backend - payment.paid_on uses local TIMEZONE now as opposed to UTC - changed params - add post method to SuccessView and FailureView
Added test models factories
Dropped support for Django <=1.6
Version 1.7.3¶
Refactored Dotpay
Moved all existing tests to test_project and added more/refactored
Fixed utils.import_module
Fixed Payu and tests (py3 support)
Updated docs
Version 1.7.2¶
Updated coveragerc and travis.yml
Added missing mgiration for Payment.status
Version 1.7.1¶
Added coveragerc
Updated README
Added settings.GETPAID_ORDER_MODEL
Added epay.dk support
Added initial django migration
Version 1.7.0¶
Refactoring to support for py3 (3.4)
Change imports to be relative - fixes #43
Add USD to supported currencies in Paymill backend (thanks lauris)
Fix a few typos
Version 1.6.0¶
Adding paymill backend
PEP 8 improvements
Adding support for django 1.5 in test project (+ tests)
Fixed issue on utils.import_name to allow packages without parents
Adding dependency to pytz for przelewy24 backend
Refactoring of PayU backend (xml->txt api, better logging) and adding support for non-auto payment accepting
Version 1.5.1¶
Fixing packaging that causes errors with package installation
Version 1.5.0¶
Adding new backend - Przelewy24.pl (thanks to IssueStand.com funding)
Fixing packaging package data (now using only MANIFEST.in)
Version 1.4.0¶
Cleaned version 1.3 from minor issues before implementing new backends
Brazilian backend moip
Updated PL translation
Added brazilian portuguese translation
Storing payment external id and description in the database (warning: database migration needed!)
Transferuj backend can now predefine interface language when redirecting
POST method supported on redirect to payment
Version 1.3.0¶
Logotypes support in new payment form
Fixing packaging
Version 1.2¶
Dotpay backend added
Hooks for backends to accept email and user name
Refactoring
Version 1.1¶
PayU backend added
Lots of documentation
Refactoring
Version 1.0¶
First stable version
Development team¶
Project leader:
Dominik Kozaczko <https://github.com/dekoza>
Original author:
Krzysztof Dorosz <https://github.com/cypreess>.
Contributors:
Paweł Bielecki <https://github.com/pawciobiel>
Bernardo Pires Carneiro <https://github.com/bcarneiro>
Sponsors:
You are welcome to contribute to this project via github fork & pull request.