Changeset - 67b047e7b30e
[Not reviewed]
0 1 0
Christopher Neugebauer - 8 years ago 2016-04-24 21:20:41
chrisjrn@gmail.com
Simplifies invoice-getting documentation.
1 file changed with 1 insertions and 4 deletions:
0 comments (0 inline, 0 general)
docs/payments.rst
Show inline comments
 
.. automodule:: registrasion.models.commerce
 

	
 
Payments and Refunds
 
====================
 

	
 
Registrasion aims to support whatever payment platform you have available to use. Therefore, Registrasion uses a bare minimum payments model to track money within the system. It's the role of you, as a deployer of Registrasion, to implement a payment application that communicates with your own payment platform.
 

	
 
Invoices may have multiple ``PaymentBase`` objects attached to them; each of these represent a payment against the invoice. Payments can be negative (and this represents a refund against the Invoice), however, this approach is not recommended for use by implementers.
 

	
 
Registrasion also keeps track of money that is not currently attached to invoices through `credit notes`_. Credit notes may be applied to unpaid invoices *in full*, if there is an amount left over from the credit note, a new credit note will be created from that amount. Credit notes may also be released, at which point they're the responsibility of the payment application to create a refund.
 

	
 
Finally, Registrasion provides a `manual payments`_ feature, which allows for staff members to manually report payments into the system. This is the only payment facility built into Registrasion, but it's not intended as a reference implementation.
 

	
 

	
 
Making payments
 
---------------
 

	
 
Making payments is a three-step process:
 

	
 
#. Validate that the invoice is ready to be paid,
 
#. Create a payment object for the amount that you are paying towards an invoice,
 
#. Ask the invoice to calculate its status now that the payment has been made.
 

	
 
Pre-validation
 
~~~~~~~~~~~~~~
 
Registrasion's ``InvoiceController`` has a ``validate_allowed_to_pay`` method, which performs all of the pre-payment checks (is the invoice still unpaid and non-void? has the registration been amended?).
 

	
 
If the pre-payment check fails, ``InvoiceController`` will raise a Django ``ValidationError``.
 

	
 
Our the ``demopay`` view from the ``registrasion-demo`` project implements pre-validation like so::
 

	
 
    from registrasion.controllers.invoice import InvoiceController
 
    from django.core.exceptions import ValidationError
 

	
 
    # Get the Registrasion Invoice model
 
    inv = get_object_or_404(rego.Invoice.objects, pk=invoice_id)
 

	
 
    invoice = InvoiceController(inv)
 
    invoice = InvoiceController.for_id_or_404(invoice.id)
 

	
 
    try:
 
        invoice.validate_allowed_to_pay()  # Verify that we're allowed to do this.
 
    except ValidationError as ve:
 
        messages.error(request, ve.message)
 
        return REDIRECT_TO_INVOICE  # And display the validation message.
 

	
 
In most cases, you don't engage your actual payment application until after pre-validation is finished, as this gives you an opportunity to bail out if the invoice isn't able to have funds applied to it.
 

	
 
Applying payments
 
~~~~~~~~~~~~~~~~~
 
Payments in Registrasion are represented as subclasses of the ``PaymentBase`` model. ``PaymentBase`` looks like this:
 

	
 
.. autoclass :: PaymentBase
 

	
 
When you implement your own payment application, you'll need to subclass ``PaymentBase``, as this will allow you to add metadata that lets you link the Registrasion payment object with your payment platform's object.
 

	
 
Generally, the ``reference`` field should be something that lets your end-users identify the payment on their credit card statement, and to provide to your team's tech support in case something goes wrong.
 

	
 
Once you've subclassed ``PaymentBase``, applying a payment is really quite simple. In the ``demopay`` view, we have a subclass called ``DemoPayment``::
 

	
 
    invoice = InvoiceController(some_invoice_model)
 

	
 
    # Create the payment object
 
    models.DemoPayment.objects.create(
 
        invoice=invoice.invoice,
 
        reference="Demo payment by user: " + request.user.username,
 
        amount=invoice.invoice.value,
 
    )
 

	
 
Note that multiple payments can be provided against an ``Invoice``, however, payments that exceed the total value of the invoice will have credit notes generated.
 

	
 
Updating an invoice's status
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
``InvoiceController`` has a method called ``update_status``. You should call ``update_status`` immediately after you create a ``PaymentBase`` object, as this keeps invoice and its payments synchronised::
 

	
 
    invoice = InvoiceController(some_invoice_model)
 
    invoice.update_status()
 

	
 
Calling ``update_status`` collects the ``PaymentBase`` objects that are attached to the ``Invoice``, and will update the status of the invoice:
 

	
 
* If an invoice is ``VOID``, it will remain void.
 
* If an invoice is ``UNPAID`` and it now has ``PaymentBase`` objects whose value meets or exceed's the invoice's value, the invoice becomes ``PAID``.
 
* If an invoice is ``UNPAID`` and it now has ``PaymentBase`` objects whose values sum to zero, the invoice becomes ``VOID``.
 
* If an invoice is ``PAID`` and it now has ``PaymentBase`` objects of less than the invoice's value, the invoice becomes ``REFUNDED``.
 

	
 
When your invoice becomes ``PAID`` for the first time, if there's a cart of inventory items attached to it, that cart becomes permanently reserved -- that is, all of the items within it are no longer available for other users to purchase. If an invoice becomes ``REFUNDED``, the items in the cart are released, which means that they are available for anyone to purchase again.
 

	
0 comments (0 inline, 0 general)