summaryrefslogtreecommitdiff
path: root/payments/stripe/forms.py
blob: 0c83657beb70aa1c846139393607f0ef89003286 (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
from __future__ import unicode_literals

import stripe
from django import forms
from django.utils.translation import ugettext as _

from .. import FraudStatus, PaymentStatus, RedirectNeeded
from ..forms import PaymentForm as BasePaymentForm, CreditCardPaymentFormWithName
from ..utils import get_month_choices, get_year_choices
from ..widgets import (
    SensitiveTextInput, SensitiveSelect, CreditCardExpiryWidget)
from .widgets import StripeCheckoutWidget, StripeWidget


class StripeFormMixin(object):

    charge = None

    def _handle_potentially_fraudulent_charge(self, charge, commit=True):
        fraud_details = charge['fraud_details']
        if fraud_details.get('stripe_report', None) == 'fraudulent':
            self.payment.change_fraud_status(FraudStatus.REJECT, commit=commit)
        else:
            self.payment.change_fraud_status(FraudStatus.ACCEPT, commit=commit)

    def clean(self):
        data = self.cleaned_data

        if not self.errors:
            if not self.payment.transaction_id:
                stripe.api_key = self.provider.secret_key
                try:
                    self.charge = stripe.Charge.create(
                        capture=False,
                        amount=int(self.payment.total * 100),
                        currency=self.payment.currency,
                        card=data['stripeToken'],
                        description='%s %s' % (
                            self.payment.billing_last_name,
                            self.payment.billing_first_name))
                except stripe.CardError as e:
                    # Making sure we retrieve the charge
                    charge_id = e.json_body['error']['charge']
                    self.charge = stripe.Charge.retrieve(charge_id)
                    # Checking if the charge was fraudulent
                    self._handle_potentially_fraudulent_charge(
                        self.charge, commit=False)
                    # The card has been declined
                    self._errors['__all__'] = self.error_class([str(e)])
                    self.payment.change_status(PaymentStatus.ERROR, str(e))
            else:
                msg = _('This payment has already been processed.')
                self._errors['__all__'] = self.error_class([msg])

        return data

    def save(self):
        self.payment.transaction_id = self.charge.id
        self.payment.attrs.charge = stripe.util.json.dumps(self.charge)
        self.payment.change_status(PaymentStatus.PREAUTH)
        if self.provider._capture:
            self.payment.capture()
        # Make sure we store the info of the charge being marked as fraudulent
        self._handle_potentially_fraudulent_charge(self.charge)


class ModalPaymentForm(StripeFormMixin, BasePaymentForm):

    def __init__(self, *args, **kwargs):
        super(StripeFormMixin, self).__init__(hidden_inputs=False, *args, **kwargs)
        widget = StripeCheckoutWidget(provider=self.provider, payment=self.payment)
        self.fields['stripeToken'] = forms.CharField(widget=widget)
        if self.is_bound and not self.data.get('stripeToken'):
            self.payment.change_status(PaymentStatus.REJECTED)
            raise RedirectNeeded(self.payment.get_failure_url())


class PaymentForm(StripeFormMixin, CreditCardPaymentFormWithName):

    stripeToken = forms.CharField(widget=StripeWidget())

    def __init__(self, *args, **kwargs):
        super(PaymentForm, self).__init__(*args, **kwargs)
        stripe_attrs = self.fields['stripeToken'].widget.attrs
        stripe_attrs['data-publishable-key'] = self.provider.public_key
        stripe_attrs['data-address-line1'] = self.payment.billing_address_1
        stripe_attrs['data-address-line2'] = self.payment.billing_address_2
        stripe_attrs['data-address-city'] = self.payment.billing_city
        stripe_attrs['data-address-state'] = self.payment.billing_country_area
        stripe_attrs['data-address-zip'] = self.payment.billing_postcode
        stripe_attrs['data-address-country'] = self.payment.billing_country_code
        widget_map = {
            'name': SensitiveTextInput(
                attrs={'autocomplete': 'cc-name', 'required': 'required'}),
            'cvv2': SensitiveTextInput(attrs={'autocomplete': 'cc-csc'}),
            'number': SensitiveTextInput(
                attrs={'autocomplete': 'cc-number', 'required': 'required'}),
            'expiration': CreditCardExpiryWidget(
                widgets=[
                    SensitiveSelect(
                        attrs={'autocomplete': 'cc-exp-month',
                               'required': 'required'},
                        choices=get_month_choices()),
                    SensitiveSelect(
                        attrs={'autocomplete': 'cc-exp-year',
                               'required': 'required'},
                        choices=get_year_choices())])}
        for field_name, widget in widget_map.items():
            self.fields[field_name].widget = widget
            self.fields[field_name].required = False