Writing a payment provider plugin
In this document, we will walk through the creation of a payment provider plugin. This is very similar to creating an export output.
Please read Creating a plugin first, if you haven’t already.
Warning
We changed our payment provider API a lot in eventyay 2.x. Our documentation page on Porting a payment provider from eventyay 1.x to eventyay 2.x might be insightful even if you do not have a payment provider to port, as it outlines the rationale behind the current design.
Provider registration
The payment provider API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available payment providers. Your plugin
should listen for this signal and return the subclass of eventyay.base.payment.BasePaymentProvider
that the plugin will provide:
1from django.dispatch import receiver
2
3from eventyay.base.signals import register_payment_providers
4
5
6@receiver(register_payment_providers, dispatch_uid="payment_paypal")
7def register_payment_provider(sender, **kwargs):
8 from .payment import Paypal
9 return Paypal
The provider class
- class eventyay.base.payment.BasePaymentProvider
The central object of each payment provider is the subclass of
BasePaymentProvider.- BasePaymentProvider.event
The default constructor sets this property to the event we are currently working for.
- BasePaymentProvider.settings
The default constructor sets this property to a
SettingsSandboxobject. You can use this object to store settings using itsgetandsetmethods. All settings you store are transparently prefixed, so you get your very own settings namespace.
- identifier
A short and unique identifier for this payment provider.
This is an abstract attribute, you must override this!
- verbose_name
A human-readable name for this payment provider.
This is an abstract attribute, you must override this!
- public_name
The public name of this payment provider as shown to customers.
- is_enabled
Whether this payment provider is enabled.
- priority
The priority of this payment provider. Lower values are shown first.
- settings_form_fields
A dictionary of form fields for the provider settings.
- settings_form_clean(cleaned_data)
Validate the settings form data.
- settings_content_render(request)
Render additional content for the settings page.
- is_allowed(request, total=None)
Check if this payment provider is allowed for the given request.
- payment_form_render(request)
Render the payment form shown during checkout.
- payment_form(request)
Return a form instance for payment details.
- payment_form_fields
A dictionary of form fields for payment details.
- payment_is_valid_session(request)
Check if the payment session is valid.
- checkout_prepare(request, cart)
Prepare the checkout process.
- checkout_confirm_render(request)
Render content for the checkout confirmation page.
This is an abstract method, you must override this!
- execute_payment(request, payment)
Execute the payment.
- calculate_fee(price)
Calculate the fee for this payment method.
- order_pending_mail_render(order)
Render content for the pending order email.
- payment_pending_render(request, payment)
Render content for pending payment status.
- abort_pending_allowed
Whether aborting pending payments is allowed.
- render_invoice_text(order, payment)
Render text to be shown on the invoice.
- order_change_allowed(order)
Check if order changes are allowed for this payment provider.
- payment_prepare(request, payment)
Prepare a payment.
- payment_control_render(request, payment)
Render control interface for a payment.
- payment_control_render_short(payment)
Render short control interface for a payment.
- payment_refund_supported(payment)
Check if refunds are supported for this payment.
- payment_partial_refund_supported(payment)
Check if partial refunds are supported for this payment.
- payment_presale_render(payment)
Render payment information in the presale interface.
- execute_refund(refund)
Execute a refund.
- refund_control_render(request, refund)
Render control interface for a refund.
- new_refund_control_form_render(request, payment)
Render form for creating a new refund.
- new_refund_control_form_process(request, payment, form_data)
Process the new refund form.
- api_payment_details(payment)
Return payment details for API responses.
- matching_id(payment)
Return an identifier for matching payments.
- shred_payment_info(payment)
Shred sensitive payment information.
- cancel_payment(payment)
Cancel a payment.
- is_implicit
Whether this is an implicit payment provider.
- is_meta
Whether this is a meta payment provider.
- test_mode_message
Message to display when in test mode.
- requires_invoice_immediately
Whether this provider requires an invoice to be generated immediately.
Additional views
See also: Creating custom views.
For most simple payment providers it is more than sufficient to implement
some of the BasePaymentProvider methods. However, in some cases
it is necessary to introduce additional views. One example is the PayPal
provider. It redirects the user to a PayPal website in the
BasePaymentProvider.checkout_prepare() step of the checkout process
and provides PayPal with a URL to redirect back to. This URL points to a
view which looks roughly like this:
1@login_required
2def success(request):
3 pid = request.GET.get('paymentId')
4 payer = request.GET.get('PayerID')
5 # We stored some information in the session in checkout_prepare(),
6 # let's compare the new information to double-check that this is about
7 # the same payment
8 if pid == request.session['payment_paypal_id']:
9 # Save the new information to the user's session
10 request.session['payment_paypal_payer'] = payer
11 try:
12 # Redirect back to the confirm page. We chose to save the
13 # event ID in the user's session. We could also put this
14 # information into a URL parameter.
15 event = Event.objects.current.get(identity=request.session['payment_paypal_event'])
16 return redirect(reverse('presale:event.checkout.confirm', kwargs={
17 'event': event.slug,
18 'organizer': event.organizer.slug,
19 }))
20 except Event.DoesNotExist:
21 pass # TODO: Display error message
22 else:
23 pass # TODO: Display error message
If you do not want to provide a view of your own, you could even let PayPal
redirect directly back to the confirm page and handle the query parameters
inside BasePaymentProvider.checkout_is_valid_session(). However,
because some external providers (not PayPal) force you to have a constant
redirect URL, it might be necessary to define custom views.