summaryrefslogtreecommitdiff
path: root/postgresqleu/util/payment/braintree.py
blob: db2c273fb03ad1ab7f52f69685a6cf9c60df2972 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from django import forms

import re

from postgresqleu.invoices.models import Invoice
from postgresqleu.invoices.backendforms import BackendInvoicePaymentMethodForm
from postgresqleu.accounting.util import get_account_choices
from postgresqleu.braintreepayment.models import BraintreeTransaction

from . import BasePayment


class BackendBraintreeForm(BackendInvoicePaymentMethodForm):
    sandbox = forms.BooleanField(required=False, label="Use testing sandbox")
    merchantid = forms.CharField(required=True, label="Merchant ID",
                                 help_text="From Account -> Merchant Account Info")
    merchantacctid = forms.CharField(required=False, label="Merchant Account ID",
                                     help_text="If provided, will use specific Merchant Account instead of the default")
    notification_receiver = forms.EmailField(required=True)
    public_key = forms.CharField(required=True, label="Public key",
                                 help_text="From Settings -> API Keys")
    private_key = forms.CharField(required=True, label="Private key",
                                  help_text="From Settings -> API Keys",
                                  widget=forms.widgets.PasswordInput(render_value=True))
    accounting_authorized = forms.ChoiceField(required=True, choices=get_account_choices,
                                              label="Authorized payments account")
    accounting_payable = forms.ChoiceField(required=True, choices=get_account_choices,
                                           label="Payable balance account")
    accounting_fee = forms.ChoiceField(required=True, choices=get_account_choices,
                                       label="Fees account")
    accounting_payout = forms.ChoiceField(required=True, choices=get_account_choices,
                                          label="Payout account")

    config_fields = ['sandbox', 'merchantid', 'merchantacctid', 'notification_receiver', 'public_key', 'private_key',
                     'accounting_authorized', 'accounting_payable', 'accounting_fee', 'accounting_payout']

    config_fieldsets = [
        {
            'id': 'braintree',
            'legend': 'Braintree',
            'fields': ['merchantid', 'merchantacctid', 'notification_receiver', 'sandbox', 'public_key', 'private_key', ],
        },
        {
            'id': 'accounting',
            'legend': 'Accounting',
            'fields': ['accounting_authorized', 'accounting_payable', 'accounting_fee', 'accounting_payout'],
        }
    ]


class Braintree(BasePayment):
    backend_form_class = BackendBraintreeForm
    description = """
Using this payment method, you can pay using your credit card, including
Mastercard, VISA and American Express.
"""

    def __init__(self, *args, **kwargs):
        super(Braintree, self).__init__(*args, **kwargs)
        try:
            globals()["braintree"] = __import__("braintree")
            self.braintree_ok = True
            self.braintree_initialized = False
        except ImportError:
            self.braintree_ok = False

    def build_payment_url(self, invoicestr, invoiceamount, invoiceid, returnurl=None):
        i = Invoice.objects.get(pk=invoiceid)
        if i.recipient_secret:
            return "/invoices/braintree/{0}/{1}/{2}/".format(self.id, invoiceid, i.recipient_secret)
        else:
            return "/invoices/braintree/{0}/{1}/".format(self.id, invoiceid)

    _re_braintree = re.compile('^Braintree id ([a-z0-9]+)$')

    def _find_invoice_transaction(self, invoice):
        m = self._re_braintree.match(invoice.paymentdetails)
        if m:
            try:
                return (BraintreeTransaction.objects.get(transid=m.groups(1)[0]), None)
            except BraintreeTransaction.DoesNotExzist:
                return (None, "not found")
        else:
            return (None, "unknown format")

    def payment_fees(self, invoice):
        (trans, reason) = self._find_invoice_transaction(invoice)
        if not trans:
            return reason
        if trans.disbursedamount:
            return trans.amount - trans.disbursedamount
        else:
            return "not disbursed yet"

    def used_method_details(self, invoice):
        (trans, reason) = self._find_invoice_transaction(invoice)
        if not trans:
            raise Exception(reason)
        return "Credit Card ({0})".format(trans.method)

    def available(self, invoice):
        return self.braintree_ok

    def unavailable_reason(self, invoice):
        if not self.braintree_ok:
            return "Unable to load processing module"

    def initialize_braintree(self):
        if self.braintree_initialized:
            return

        braintree.Configuration.configure(
            self.config('sandbox') and braintree.Environment.Sandbox or braintree.Environment.Production,
            self.config('merchantid'),
            self.config('public_key'),
            self.config('private_key'),
        )
        self.braintree_initialized = True

    def generate_client_token(self):
        self.initialize_braintree()
        return braintree.ClientToken.generate({})

    def braintree_sale(self, options):
        self.initialize_braintree()
        return braintree.Transaction.sale(options)

    def braintree_find(self, transid):
        self.initialize_braintree()
        try:
            return (True, braintree.Transaction.find(transid))
        except braintree.exceptions.not_found_error.NotFoundError as ex:
            return (False, ex)