├── .coveragerc
├── .gitignore
├── .travis.yml
├── AUTHORS
├── CONTRIBUTING.rst
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── authorizenet
├── __init__.py
├── admin.py
├── cim.py
├── conf.py
├── creditcard.py
├── exceptions.py
├── fields.py
├── forms.py
├── helpers.py
├── managers.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto__add_cimresponse.py
│ ├── 0003_missing_response_fields.py
│ ├── 0004_auto__add_field_response_created__add_field_cimresponse_created__chg_f.py
│ ├── 0005_auto__add_customerpaymentprofile__add_customerprofile__chg_field_cimre.py
│ └── __init__.py
├── models.py
├── signals.py
├── templates
│ └── admin
│ │ └── authorizenet
│ │ ├── change_form.html
│ │ └── customerprofile
│ │ └── change_form.html
├── urls.py
├── utils.py
└── views.py
├── docs.rst
├── docs
├── Makefile
├── cim.rst
├── conf.py
├── index.rst
├── make.bat
└── usage.rst
├── runtests.py
├── sample_project
├── __init__.py
├── local_settings.py.example
├── manage.py
├── media
│ └── contentx
│ │ ├── IframeCommunicator.html
│ │ ├── closeButton1.png
│ │ ├── closeButton1a.png
│ │ ├── closeButton1h.png
│ │ ├── empty.html
│ │ ├── manage.css
│ │ ├── manageIELTE6.css
│ │ ├── popup.js
│ │ ├── powered_simple.png
│ │ ├── shadow1-bottom.png
│ │ ├── shadow1-bottomLeft.png
│ │ ├── shadow1-bottomRight.png
│ │ ├── shadow1-left.png
│ │ ├── shadow1-right.png
│ │ ├── shadow1-top.png
│ │ ├── shadow1-topLeft.png
│ │ └── shadow1-topRight.png
├── requirements.txt
├── samplestore
│ ├── __init__.py
│ ├── admin.py
│ ├── fixtures
│ │ └── initial_data.json
│ ├── models.py
│ ├── urls.py
│ └── views.py
├── settings.py
├── templates
│ ├── authorizenet
│ │ ├── aim_payment.html
│ │ ├── aim_success.html
│ │ └── sim_payment.html
│ ├── base.html
│ ├── registration
│ │ └── login.html
│ └── samplestore
│ │ ├── capture.html
│ │ ├── capture_index.html
│ │ ├── commit_to_buy.html
│ │ ├── edit_cim_profile.html
│ │ ├── items.html
│ │ └── make_payment.html
└── urls.py
├── setup.cfg
├── setup.py
├── tests
├── __init__.py
├── models.py
├── templates
│ └── authorizenet
│ │ ├── create_payment_profile.html
│ │ └── update_payment_profile.html
├── tests
│ ├── __init__.py
│ ├── cim.py
│ ├── mocks.py
│ ├── models.py
│ ├── test_data.py
│ ├── utils.py
│ └── views.py
├── urls.py
└── views.py
└── tox.ini
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | include = authorizenet/*
3 | omit = tests/*
4 | branch = 1
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | sample_project/local_settings.py
3 | sample_project/dev.db
4 | *.egg-info
5 | *.egg
6 | build/
7 | dist/
8 | .coverage
9 | .tox
10 | htmlcov
11 | docs/_build/
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 |
3 | python:
4 | - 2.6
5 | - 2.7
6 |
7 | env:
8 | - DJANGO=Django==1.4.2
9 | - DJANGO=Django==1.5.0
10 | - DJANGO=https://github.com/django/django/tarball/stable/1.6.x
11 | - DJANGO=https://github.com/django/django/tarball/master
12 |
13 | install:
14 | - pip install --use-mirrors $DJANGO
15 | - pip install --use-mirrors coverage coveralls
16 |
17 | script:
18 | - coverage run -a setup.py test
19 | - coverage report
20 |
21 | matrix:
22 | exclude:
23 | - python: 2.6
24 | env: DJANGO=https://github.com/django/django/tarball/master
25 |
26 | after_success: coveralls
27 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | https://github.com/zen4ever/django-authorizenet/contributors
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.rst:
--------------------------------------------------------------------------------
1 | Contributing
2 | ============
3 |
4 | Below is a list of tips for submitting issues and pull requests. These are
5 | suggestions and not requirements.
6 |
7 | Submitting Issues
8 | -----------------
9 |
10 | Issues are often easier to reproduce/resolve when they have:
11 |
12 | - A pull request with a failing test demonstrating the issue
13 | - A code example that produces the issue consistently
14 | - A traceback (when applicable)
15 |
16 | Pull Requests
17 | -------------
18 |
19 | When creating a pull request, try to:
20 |
21 | - Write tests if applicable
22 | - Update the `README`_ file if needed
23 | - Update the documentation if needed
24 |
25 | .. _README: README.rst
26 |
27 | Testing
28 | -------
29 |
30 | Please add tests for your code and ensure existing tests don't break. To run
31 | the tests against your code::
32 |
33 | python setup.py test
34 |
35 | Please use tox to test the code against supported Python and Django versions.
36 | First install tox::
37 |
38 | pip install tox
39 |
40 | To run tox and generate a coverage report (in ``htmlcov`` directory)::
41 |
42 | make test
43 |
44 | Generating documentation
45 | ------------------------
46 |
47 | To regenerate the documentation use::
48 |
49 | make docs
50 |
51 | The generated documentation HTML files can be found in ``docs/_build/html``.
52 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Andrii Kurinnyi
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include setup.py README.rst MANIFEST.in LICENSE
2 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: init test sphinx
2 |
3 | init:
4 | easy_install tox coverage Sphinx
5 |
6 | test:
7 | coverage erase
8 | tox
9 | coverage html
10 |
11 | docs: sphinx
12 |
13 | sphinx:
14 | python setup.py build_sphinx
15 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ===================
2 | django-authorizenet
3 | ===================
4 |
5 | .. image:: https://secure.travis-ci.org/zen4ever/django-authorizenet.png?branch=master
6 | :target: http://travis-ci.org/zen4ever/django-authorizenet
7 | :alt: Test Status
8 | .. image:: https://coveralls.io/repos/zen4ever/django-authorizenet/badge.png?branch=master
9 | :target: https://coveralls.io/r/zen4ever/django-authorizenet
10 | :alt: Coverage Status
11 |
12 | Django integration with Authorize.NET payment gateway.
13 | Includes SIM and AIM implementations.
14 |
15 | Usage example:
16 | https://github.com/zen4ever/django-authorizenet/tree/master/sample_project
17 |
18 | Authors: https://github.com/zen4ever/django-authorizenet/contributors
19 |
--------------------------------------------------------------------------------
/authorizenet/__init__.py:
--------------------------------------------------------------------------------
1 | AUTHNET_POST_URL = "https://secure.authorize.net/gateway/transact.dll"
2 | AUTHNET_TEST_POST_URL = "https://test.authorize.net/gateway/transact.dll"
3 | AUTHNET_TEST_CIM_URL = "https://apitest.authorize.net/xml/v1/request.api"
4 | AUTHNET_CIM_URL = "https://api.authorize.net/xml/v1/request.api"
5 |
--------------------------------------------------------------------------------
/authorizenet/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.core.urlresolvers import reverse
3 | from django.utils.safestring import mark_safe
4 | from authorizenet.models import (Response, CIMResponse, CustomerProfile,
5 | CustomerPaymentProfile)
6 | from authorizenet.forms import CustomerPaymentForm, CustomerPaymentAdminForm
7 | from relatives.utils import object_edit_link
8 |
9 |
10 | class ResponseAdmin(admin.ModelAdmin):
11 | list_display = ['response_code',
12 | 'response_reason_text',
13 | 'auth_code',
14 | 'trans_id']
15 |
16 | readonly_fields = ['response_code',
17 | 'response_subcode',
18 | 'response_reason_code',
19 | 'response_reason_text',
20 | 'auth_code',
21 | 'avs_code',
22 | 'trans_id',
23 | 'invoice_num',
24 | 'description',
25 | 'amount',
26 | 'method',
27 | 'type',
28 | 'cust_id',
29 | 'first_name',
30 | 'last_name',
31 | 'company',
32 | 'address',
33 | 'city',
34 | 'state',
35 | 'zip',
36 | 'country',
37 | 'phone',
38 | 'fax',
39 | 'email',
40 | 'ship_to_first_name',
41 | 'ship_to_last_name',
42 | 'ship_to_company',
43 | 'ship_to_address',
44 | 'ship_to_city',
45 | 'ship_to_state',
46 | 'ship_to_zip',
47 | 'ship_to_country',
48 | 'tax',
49 | 'duty',
50 | 'freight',
51 | 'tax_exempt',
52 | 'po_num',
53 | 'MD5_Hash',
54 | 'cvv2_resp_code',
55 | 'cavv_response',
56 | 'test_request',
57 | 'card_type',
58 | 'account_number',
59 | 'created']
60 |
61 | admin.site.register(Response, ResponseAdmin)
62 |
63 |
64 | class CIMResponseAdmin(admin.ModelAdmin):
65 | list_display = ['result_code',
66 | 'result']
67 |
68 | readonly_fields = ['result',
69 | 'result_code',
70 | 'result_text',
71 | 'response_link',
72 | 'created']
73 |
74 | exclude = ['transaction_response']
75 |
76 | def response_link(self, obj):
77 | change_url = reverse('admin:authorizenet_response_change',
78 | args=(obj.transaction_response.id,))
79 | return mark_safe('%s' % (change_url,
80 | obj.transaction_response))
81 | response_link.short_description = 'transaction response'
82 |
83 | admin.site.register(CIMResponse, CIMResponseAdmin)
84 |
85 |
86 | class CustomerPaymentProfileInline(admin.TabularInline):
87 | model = CustomerPaymentProfile
88 | form = CustomerPaymentForm
89 | fields = [object_edit_link("Edit"), 'first_name', 'last_name',
90 | 'card_number', 'expiration_date']
91 | readonly_fields = fields
92 | extra = 0
93 | max_num = 0
94 | can_delete = False
95 |
96 |
97 | class CustomerProfileAdmin(admin.ModelAdmin):
98 | list_display = ['profile_id', 'customer']
99 | readonly_fields = ['profile_id', 'customer']
100 | inlines = [CustomerPaymentProfileInline]
101 |
102 | def get_readonly_fields(self, request, obj=None):
103 | return self.readonly_fields if obj is not None else ['profile_id']
104 |
105 | admin.site.register(CustomerProfile, CustomerProfileAdmin)
106 |
107 |
108 | class CustomerPaymentProfileAdmin(admin.ModelAdmin):
109 | list_display = ['payment_profile_id', 'customer_profile', 'customer']
110 | readonly_fields = ['payment_profile_id', 'customer', 'customer_profile']
111 | form = CustomerPaymentAdminForm
112 |
113 | def get_readonly_fields(self, request, obj=None):
114 | return self.readonly_fields if obj is not None else []
115 |
116 | admin.site.register(CustomerPaymentProfile, CustomerPaymentProfileAdmin)
117 |
--------------------------------------------------------------------------------
/authorizenet/conf.py:
--------------------------------------------------------------------------------
1 | """
2 | Application-specific settings for django-authorizenet
3 |
4 | Available settings:
5 |
6 | - AUTHNET_DEBUG: Set to ``True`` if using Authorize.NET test account
7 | - AUTHNET_LOGIN_ID: Set to value of Authorize.NET login ID
8 | - AUTHNET_TRANSACTION_KEY: Set to value of Authorize.NET transaction key
9 | - AUTHNET_CUSTOMER_MODEL: Used to set customer model used for CIM customers
10 | (defaults to Django user)
11 | - AUTHNET_DELIM_CHAR: Used to set delimiter character for CIM requests
12 | (defaults to "|")
13 | - AUTHNET_FORCE_TEST_REQUEST
14 | - AUTHNET_EMAIL_CUSTOMER
15 | - AUTHNET_MD5_HASH
16 |
17 | """
18 |
19 | from django.conf import settings as django_settings
20 |
21 |
22 | class Settings(object):
23 |
24 | """
25 | Retrieves django.conf settings, using defaults from Default subclass
26 |
27 | All usable settings are specified in settings attribute. Use an
28 | ``AUTHNET_`` prefix when specifying settings in django.conf.
29 | """
30 |
31 | prefix = 'AUTHNET_'
32 | settings = set(('DEBUG', 'LOGIN_ID', 'TRANSACTION_KEY', 'CUSTOMER_MODEL',
33 | 'DELIM_CHAR', 'FORCE_TEST_REQUEST', 'EMAIL_CUSTOMER',
34 | 'MD5_HASH'))
35 |
36 | class Default:
37 | CUSTOMER_MODEL = getattr(
38 | django_settings, 'AUTH_USER_MODEL', "auth.User")
39 | DELIM_CHAR = "|"
40 | FORCE_TEST_REQUEST = False
41 | EMAIL_CUSTOMER = None
42 | MD5_HASH = ""
43 |
44 | def __init__(self):
45 | self.defaults = Settings.Default()
46 |
47 | def __getattr__(self, name):
48 | if name not in self.settings:
49 | raise AttributeError("Setting %s not understood" % name)
50 | try:
51 | return getattr(django_settings, self.prefix + name)
52 | except AttributeError:
53 | return getattr(self.defaults, name)
54 |
55 | settings = Settings()
56 |
--------------------------------------------------------------------------------
/authorizenet/creditcard.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # from http://github.com/johnboxall/django-paypal
4 | import re
5 | from string import digits, split as L
6 |
7 | # Adapted from:
8 | # http://www.djangosnippets.org/snippets/764/
9 | # http://www.satchmoproject.com/
10 | # http://tinyurl.com/shoppify-credit-cards
11 |
12 | # Well known card regular expressions.
13 | CARDS = {
14 | 'Visa': re.compile(r"^4\d{12}(\d{3})?$"),
15 | 'Mastercard': re.compile(r"(5[1-5]\d{4}|677189)\d{10}$"),
16 | 'Dinersclub': re.compile(r"^3(0[0-5]|[68]\d)\d{11}"),
17 | 'Amex': re.compile("^3[47]\d{13}$"),
18 | 'Discover': re.compile("^(6011|65\d{2})\d{12}$"),
19 | }
20 |
21 | # Well known test numbers
22 | TEST_NUMBERS = L("378282246310005 371449635398431 378734493671000"
23 | "30569309025904 38520000023237 6011111111111117"
24 | "6011000990139424 555555555554444 5105105105105100"
25 | "4111111111111111 4012888888881881 4222222222222")
26 |
27 |
28 | def verify_credit_card(number, allow_test=False):
29 | """Returns the card type for given card number or None if invalid."""
30 | return CreditCard(number).verify(allow_test)
31 |
32 |
33 | class CreditCard(object):
34 | def __init__(self, number):
35 | self.number = number
36 |
37 | def is_number(self):
38 | """Returns True if there is at least one digit in number."""
39 | if isinstance(self.number, basestring):
40 | self.number = "".join([c for c in self.number if c in digits])
41 | return self.number.isdigit()
42 | return False
43 |
44 | def is_mod10(self):
45 | """Returns True if number is valid according to mod10."""
46 | double = 0
47 | total = 0
48 | for i in range(len(self.number) - 1, -1, -1):
49 | for c in str((double + 1) * int(self.number[i])):
50 | total = total + int(c)
51 | double = (double + 1) % 2
52 | return (total % 10) == 0
53 |
54 | def is_test(self):
55 | """Returns True if number is a test card number."""
56 | return self.number in TEST_NUMBERS
57 |
58 | def get_type(self):
59 | """Return the type if it matches one of the cards."""
60 | for card, pattern in CARDS.iteritems():
61 | if pattern.match(self.number):
62 | return card
63 | return None
64 |
65 | def verify(self, allow_test):
66 | """Returns the card type if valid else None."""
67 | if self.is_number() and \
68 | (not self.is_test() or allow_test) \
69 | and self.is_mod10():
70 | return self.get_type()
71 | return None
72 |
--------------------------------------------------------------------------------
/authorizenet/exceptions.py:
--------------------------------------------------------------------------------
1 | class BillingError(Exception):
2 | """Error due to Authorize.NET request"""
3 |
--------------------------------------------------------------------------------
/authorizenet/fields.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from datetime import date
4 | from calendar import monthrange
5 |
6 | from django import forms
7 | from django.utils.translation import ugettext as _
8 |
9 | from authorizenet.conf import settings
10 | from authorizenet.creditcard import verify_credit_card
11 |
12 |
13 | class CreditCardField(forms.CharField):
14 | """
15 | Form field for checking out a credit card.
16 | """
17 | def __init__(self, *args, **kwargs):
18 | kwargs.setdefault('max_length', 20)
19 | super(CreditCardField, self).__init__(*args, **kwargs)
20 |
21 | def clean(self, value):
22 | """
23 | Raises a ValidationError if the card is not valid
24 | and stashes card type.
25 | """
26 | self.card_type = verify_credit_card(value, allow_test=settings.DEBUG)
27 | if self.card_type is None:
28 | raise forms.ValidationError("Invalid credit card number.")
29 | return value
30 |
31 |
32 | # Credit Card Expiry Fields from:
33 | # http://www.djangosnippets.org/snippets/907/
34 | class CreditCardExpiryWidget(forms.MultiWidget):
35 | """MultiWidget for representing credit card expiry date."""
36 | def decompress(self, value):
37 | if value:
38 | return [value.month, value.year]
39 | else:
40 | return [None, None]
41 |
42 | def format_output(self, rendered_widgets):
43 | html = u' / '.join(rendered_widgets)
44 | return u'%s' % html
45 |
46 |
47 | class CreditCardExpiryField(forms.MultiValueField):
48 | EXP_MONTH = [(x, "%02d" % x) for x in xrange(1, 13)]
49 | EXP_YEAR = [(x, x) for x in xrange(date.today().year,
50 | date.today().year + 15)]
51 |
52 | default_error_messages = {
53 | 'invalid_month': u'Enter a valid month.',
54 | 'invalid_year': u'Enter a valid year.',
55 | }
56 |
57 | def __init__(self, *args, **kwargs):
58 | errors = self.default_error_messages.copy()
59 | if 'error_messages' in kwargs:
60 | errors.update(kwargs['error_messages'])
61 |
62 | fields = (
63 | forms.ChoiceField(
64 | choices=self.EXP_MONTH,
65 | error_messages={'invalid': errors['invalid_month']}),
66 | forms.ChoiceField(
67 | choices=self.EXP_YEAR,
68 | error_messages={'invalid': errors['invalid_year']}),
69 | )
70 |
71 | super(CreditCardExpiryField, self).__init__(fields, *args, **kwargs)
72 | self.widget = CreditCardExpiryWidget(widgets=[fields[0].widget,
73 | fields[1].widget])
74 |
75 | def clean(self, value):
76 | exp = super(CreditCardExpiryField, self).clean(value)
77 | if date.today() > exp:
78 | raise forms.ValidationError(
79 | "The expiration date you entered is in the past.")
80 | return exp
81 |
82 | def compress(self, data_list):
83 | if data_list:
84 | if data_list[1] in forms.fields.EMPTY_VALUES:
85 | error = self.error_messages['invalid_year']
86 | raise forms.ValidationError(error)
87 | if data_list[0] in forms.fields.EMPTY_VALUES:
88 | error = self.error_messages['invalid_month']
89 | raise forms.ValidationError(error)
90 | year = int(data_list[1])
91 | month = int(data_list[0])
92 | # find last day of the month
93 | day = monthrange(year, month)[1]
94 | return date(year, month, day)
95 | return None
96 |
97 |
98 | class CreditCardCVV2Field(forms.CharField):
99 | def __init__(self, *args, **kwargs):
100 | kwargs.setdefault('min_length', 3)
101 | kwargs.setdefault('max_length', 4)
102 | super(CreditCardCVV2Field, self).__init__(*args, **kwargs)
103 |
104 |
105 | # Country Field from:
106 | # http://www.djangosnippets.org/snippets/494/
107 | # http://xml.coverpages.org/country3166.html
108 | COUNTRIES = (
109 | ('US', _('United States of America')),
110 | ('CA', _('Canada')),
111 | ('AD', _('Andorra')),
112 | ('AE', _('United Arab Emirates')),
113 | ('AF', _('Afghanistan')),
114 | ('AG', _('Antigua & Barbuda')),
115 | ('AI', _('Anguilla')),
116 | ('AL', _('Albania')),
117 | ('AM', _('Armenia')),
118 | ('AN', _('Netherlands Antilles')),
119 | ('AO', _('Angola')),
120 | ('AQ', _('Antarctica')),
121 | ('AR', _('Argentina')),
122 | ('AS', _('American Samoa')),
123 | ('AT', _('Austria')),
124 | ('AU', _('Australia')),
125 | ('AW', _('Aruba')),
126 | ('AZ', _('Azerbaijan')),
127 | ('BA', _('Bosnia and Herzegovina')),
128 | ('BB', _('Barbados')),
129 | ('BD', _('Bangladesh')),
130 | ('BE', _('Belgium')),
131 | ('BF', _('Burkina Faso')),
132 | ('BG', _('Bulgaria')),
133 | ('BH', _('Bahrain')),
134 | ('BI', _('Burundi')),
135 | ('BJ', _('Benin')),
136 | ('BM', _('Bermuda')),
137 | ('BN', _('Brunei Darussalam')),
138 | ('BO', _('Bolivia')),
139 | ('BR', _('Brazil')),
140 | ('BS', _('Bahama')),
141 | ('BT', _('Bhutan')),
142 | ('BV', _('Bouvet Island')),
143 | ('BW', _('Botswana')),
144 | ('BY', _('Belarus')),
145 | ('BZ', _('Belize')),
146 | ('CC', _('Cocos (Keeling) Islands')),
147 | ('CF', _('Central African Republic')),
148 | ('CG', _('Congo')),
149 | ('CH', _('Switzerland')),
150 | ('CI', _('Ivory Coast')),
151 | ('CK', _('Cook Iislands')),
152 | ('CL', _('Chile')),
153 | ('CM', _('Cameroon')),
154 | ('CN', _('China')),
155 | ('CO', _('Colombia')),
156 | ('CR', _('Costa Rica')),
157 | ('CU', _('Cuba')),
158 | ('CV', _('Cape Verde')),
159 | ('CX', _('Christmas Island')),
160 | ('CY', _('Cyprus')),
161 | ('CZ', _('Czech Republic')),
162 | ('DE', _('Germany')),
163 | ('DJ', _('Djibouti')),
164 | ('DK', _('Denmark')),
165 | ('DM', _('Dominica')),
166 | ('DO', _('Dominican Republic')),
167 | ('DZ', _('Algeria')),
168 | ('EC', _('Ecuador')),
169 | ('EE', _('Estonia')),
170 | ('EG', _('Egypt')),
171 | ('EH', _('Western Sahara')),
172 | ('ER', _('Eritrea')),
173 | ('ES', _('Spain')),
174 | ('ET', _('Ethiopia')),
175 | ('FI', _('Finland')),
176 | ('FJ', _('Fiji')),
177 | ('FK', _('Falkland Islands (Malvinas)')),
178 | ('FM', _('Micronesia')),
179 | ('FO', _('Faroe Islands')),
180 | ('FR', _('France')),
181 | ('FX', _('France, Metropolitan')),
182 | ('GA', _('Gabon')),
183 | ('GB', _('United Kingdom (Great Britain)')),
184 | ('GD', _('Grenada')),
185 | ('GE', _('Georgia')),
186 | ('GF', _('French Guiana')),
187 | ('GH', _('Ghana')),
188 | ('GI', _('Gibraltar')),
189 | ('GL', _('Greenland')),
190 | ('GM', _('Gambia')),
191 | ('GN', _('Guinea')),
192 | ('GP', _('Guadeloupe')),
193 | ('GQ', _('Equatorial Guinea')),
194 | ('GR', _('Greece')),
195 | ('GS', _('South Georgia and the South Sandwich Islands')),
196 | ('GT', _('Guatemala')),
197 | ('GU', _('Guam')),
198 | ('GW', _('Guinea-Bissau')),
199 | ('GY', _('Guyana')),
200 | ('HK', _('Hong Kong')),
201 | ('HM', _('Heard & McDonald Islands')),
202 | ('HN', _('Honduras')),
203 | ('HR', _('Croatia')),
204 | ('HT', _('Haiti')),
205 | ('HU', _('Hungary')),
206 | ('ID', _('Indonesia')),
207 | ('IE', _('Ireland')),
208 | ('IL', _('Israel')),
209 | ('IN', _('India')),
210 | ('IO', _('British Indian Ocean Territory')),
211 | ('IQ', _('Iraq')),
212 | ('IR', _('Islamic Republic of Iran')),
213 | ('IS', _('Iceland')),
214 | ('IT', _('Italy')),
215 | ('JM', _('Jamaica')),
216 | ('JO', _('Jordan')),
217 | ('JP', _('Japan')),
218 | ('KE', _('Kenya')),
219 | ('KG', _('Kyrgyzstan')),
220 | ('KH', _('Cambodia')),
221 | ('KI', _('Kiribati')),
222 | ('KM', _('Comoros')),
223 | ('KN', _('St. Kitts and Nevis')),
224 | ('KP', _('Korea, Democratic People\'s Republic of')),
225 | ('KR', _('Korea, Republic of')),
226 | ('KW', _('Kuwait')),
227 | ('KY', _('Cayman Islands')),
228 | ('KZ', _('Kazakhstan')),
229 | ('LA', _('Lao People\'s Democratic Republic')),
230 | ('LB', _('Lebanon')),
231 | ('LC', _('Saint Lucia')),
232 | ('LI', _('Liechtenstein')),
233 | ('LK', _('Sri Lanka')),
234 | ('LR', _('Liberia')),
235 | ('LS', _('Lesotho')),
236 | ('LT', _('Lithuania')),
237 | ('LU', _('Luxembourg')),
238 | ('LV', _('Latvia')),
239 | ('LY', _('Libyan Arab Jamahiriya')),
240 | ('MA', _('Morocco')),
241 | ('MC', _('Monaco')),
242 | ('MD', _('Moldova, Republic of')),
243 | ('MG', _('Madagascar')),
244 | ('MH', _('Marshall Islands')),
245 | ('ML', _('Mali')),
246 | ('MN', _('Mongolia')),
247 | ('MM', _('Myanmar')),
248 | ('MO', _('Macau')),
249 | ('MP', _('Northern Mariana Islands')),
250 | ('MQ', _('Martinique')),
251 | ('MR', _('Mauritania')),
252 | ('MS', _('Monserrat')),
253 | ('MT', _('Malta')),
254 | ('MU', _('Mauritius')),
255 | ('MV', _('Maldives')),
256 | ('MW', _('Malawi')),
257 | ('MX', _('Mexico')),
258 | ('MY', _('Malaysia')),
259 | ('MZ', _('Mozambique')),
260 | ('NA', _('Namibia')),
261 | ('NC', _('New Caledonia')),
262 | ('NE', _('Niger')),
263 | ('NF', _('Norfolk Island')),
264 | ('NG', _('Nigeria')),
265 | ('NI', _('Nicaragua')),
266 | ('NL', _('Netherlands')),
267 | ('NO', _('Norway')),
268 | ('NP', _('Nepal')),
269 | ('NR', _('Nauru')),
270 | ('NU', _('Niue')),
271 | ('NZ', _('New Zealand')),
272 | ('OM', _('Oman')),
273 | ('PA', _('Panama')),
274 | ('PE', _('Peru')),
275 | ('PF', _('French Polynesia')),
276 | ('PG', _('Papua New Guinea')),
277 | ('PH', _('Philippines')),
278 | ('PK', _('Pakistan')),
279 | ('PL', _('Poland')),
280 | ('PM', _('St. Pierre & Miquelon')),
281 | ('PN', _('Pitcairn')),
282 | ('PR', _('Puerto Rico')),
283 | ('PT', _('Portugal')),
284 | ('PW', _('Palau')),
285 | ('PY', _('Paraguay')),
286 | ('QA', _('Qatar')),
287 | ('RE', _('Reunion')),
288 | ('RO', _('Romania')),
289 | ('RU', _('Russian Federation')),
290 | ('RW', _('Rwanda')),
291 | ('SA', _('Saudi Arabia')),
292 | ('SB', _('Solomon Islands')),
293 | ('SC', _('Seychelles')),
294 | ('SD', _('Sudan')),
295 | ('SE', _('Sweden')),
296 | ('SG', _('Singapore')),
297 | ('SH', _('St. Helena')),
298 | ('SI', _('Slovenia')),
299 | ('SJ', _('Svalbard & Jan Mayen Islands')),
300 | ('SK', _('Slovakia')),
301 | ('SL', _('Sierra Leone')),
302 | ('SM', _('San Marino')),
303 | ('SN', _('Senegal')),
304 | ('SO', _('Somalia')),
305 | ('SR', _('Suriname')),
306 | ('ST', _('Sao Tome & Principe')),
307 | ('SV', _('El Salvador')),
308 | ('SY', _('Syrian Arab Republic')),
309 | ('SZ', _('Swaziland')),
310 | ('TC', _('Turks & Caicos Islands')),
311 | ('TD', _('Chad')),
312 | ('TF', _('French Southern Territories')),
313 | ('TG', _('Togo')),
314 | ('TH', _('Thailand')),
315 | ('TJ', _('Tajikistan')),
316 | ('TK', _('Tokelau')),
317 | ('TM', _('Turkmenistan')),
318 | ('TN', _('Tunisia')),
319 | ('TO', _('Tonga')),
320 | ('TP', _('East Timor')),
321 | ('TR', _('Turkey')),
322 | ('TT', _('Trinidad & Tobago')),
323 | ('TV', _('Tuvalu')),
324 | ('TW', _('Taiwan, Province of China')),
325 | ('TZ', _('Tanzania, United Republic of')),
326 | ('UA', _('Ukraine')),
327 | ('UG', _('Uganda')),
328 | ('UM', _('United States Minor Outlying Islands')),
329 | ('UY', _('Uruguay')),
330 | ('UZ', _('Uzbekistan')),
331 | ('VA', _('Vatican City State (Holy See)')),
332 | ('VC', _('St. Vincent & the Grenadines')),
333 | ('VE', _('Venezuela')),
334 | ('VG', _('British Virgin Islands')),
335 | ('VI', _('United States Virgin Islands')),
336 | ('VN', _('Viet Nam')),
337 | ('VU', _('Vanuatu')),
338 | ('WF', _('Wallis & Futuna Islands')),
339 | ('WS', _('Samoa')),
340 | ('YE', _('Yemen')),
341 | ('YT', _('Mayotte')),
342 | ('YU', _('Yugoslavia')),
343 | ('ZA', _('South Africa')),
344 | ('ZM', _('Zambia')),
345 | ('ZR', _('Zaire')),
346 | ('ZW', _('Zimbabwe')),
347 | ('ZZ', _('Unknown or unspecified country')),
348 | )
349 |
350 |
351 | class CountryField(forms.ChoiceField):
352 | def __init__(self, *args, **kwargs):
353 | kwargs.setdefault('choices', COUNTRIES)
354 | super(CountryField, self).__init__(*args, **kwargs)
355 |
--------------------------------------------------------------------------------
/authorizenet/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from authorizenet.conf import settings
3 | from authorizenet.fields import CreditCardField, CreditCardExpiryField, \
4 | CreditCardCVV2Field, CountryField
5 | from authorizenet.models import CustomerPaymentProfile
6 |
7 |
8 | class SIMPaymentForm(forms.Form):
9 | x_login = forms.CharField(max_length=20,
10 | required=True,
11 | widget=forms.HiddenInput,
12 | initial=settings.LOGIN_ID)
13 | x_type = forms.CharField(max_length=20,
14 | widget=forms.HiddenInput,
15 | initial="AUTH_CAPTURE")
16 | x_amount = forms.DecimalField(max_digits=15,
17 | decimal_places=2,
18 | widget=forms.HiddenInput)
19 | x_show_form = forms.CharField(max_length=20,
20 | widget=forms.HiddenInput,
21 | initial="PAYMENT_FORM")
22 | x_method = forms.CharField(max_length=10,
23 | widget=forms.HiddenInput,
24 | initial="CC")
25 | x_fp_sequence = forms.CharField(max_length=10,
26 | widget=forms.HiddenInput,
27 | initial="CC")
28 | x_version = forms.CharField(max_length=10,
29 | widget=forms.HiddenInput,
30 | initial="3.1")
31 | x_relay_response = forms.CharField(max_length=8,
32 | widget=forms.HiddenInput,
33 | initial="TRUE")
34 | x_fp_timestamp = forms.CharField(max_length=55,
35 | widget=forms.HiddenInput)
36 | x_relay_url = forms.CharField(max_length=55,
37 | widget=forms.HiddenInput)
38 | x_fp_hash = forms.CharField(max_length=55,
39 | widget=forms.HiddenInput)
40 | x_invoice_num = forms.CharField(max_length=55,
41 | required=False,
42 | widget=forms.HiddenInput)
43 | x_description = forms.CharField(max_length=255,
44 | required=False,
45 | widget=forms.HiddenInput)
46 |
47 |
48 | class SIMBillingForm(forms.Form):
49 | x_first_name = forms.CharField(max_length=50, widget=forms.HiddenInput)
50 | x_last_name = forms.CharField(max_length=50, widget=forms.HiddenInput)
51 | x_company = forms.CharField(max_length=50, widget=forms.HiddenInput)
52 | x_address = forms.CharField(max_length=60, widget=forms.HiddenInput)
53 | x_city = forms.CharField(max_length=40, widget=forms.HiddenInput)
54 | x_state = forms.CharField(max_length=40, widget=forms.HiddenInput)
55 | x_zip = forms.CharField(max_length=20, widget=forms.HiddenInput)
56 | x_country = forms.CharField(max_length=60, widget=forms.HiddenInput)
57 | x_phone = forms.CharField(max_length=25, widget=forms.HiddenInput)
58 | x_fax = forms.CharField(max_length=25, widget=forms.HiddenInput)
59 | x_email = forms.CharField(max_length=255, widget=forms.HiddenInput)
60 | x_cust_id = forms.CharField(max_length=20, widget=forms.HiddenInput)
61 |
62 |
63 | class BillingAddressForm(forms.Form):
64 | first_name = forms.CharField(50, label="First Name")
65 | last_name = forms.CharField(50, label="Last Name")
66 | company = forms.CharField(50, label="Company", required=False)
67 | address = forms.CharField(60, label="Street Address")
68 | city = forms.CharField(40, label="City")
69 | state = forms.CharField(40, label="State")
70 | country = CountryField(label="Country", initial="US")
71 | zip = forms.CharField(20, label="Postal / Zip Code")
72 |
73 | class ShippingAddressForm(forms.Form):
74 | ship_to_first_name = forms.CharField(50, label="First Name")
75 | ship_to_last_name = forms.CharField(50, label="Last Name")
76 | ship_to_company = forms.CharField(50, label="Company", required=False)
77 | ship_to_address = forms.CharField(60, label="Street Address")
78 | ship_to_city = forms.CharField(40, label="City")
79 | ship_to_state = forms.CharField(label="State")
80 | ship_to_zip = forms.CharField(20, label="Postal / Zip Code")
81 | ship_to_country = CountryField(label="Country", initial="US")
82 |
83 | class AIMPaymentForm(forms.Form):
84 | card_num = CreditCardField(label="Credit Card Number")
85 | exp_date = CreditCardExpiryField(label="Expiration Date")
86 | card_code = CreditCardCVV2Field(label="Card Security Code")
87 |
88 |
89 | class CIMPaymentForm(forms.Form):
90 | card_number = CreditCardField(label="Credit Card Number")
91 | expiration_date = CreditCardExpiryField(label="Expiration Date")
92 | card_code = CreditCardCVV2Field(label="Card Security Code")
93 |
94 |
95 | class CustomerPaymentForm(forms.ModelForm):
96 |
97 | """Base customer payment form without shipping address"""
98 |
99 | country = CountryField(label="Country", initial="US")
100 | card_number = CreditCardField(label="Credit Card Number")
101 | expiration_date = CreditCardExpiryField(label="Expiration Date")
102 | card_code = CreditCardCVV2Field(label="Card Security Code")
103 |
104 | def __init__(self, *args, **kwargs):
105 | self.customer = kwargs.pop('customer', None)
106 | return super(CustomerPaymentForm, self).__init__(*args, **kwargs)
107 |
108 | def save(self, commit=True):
109 | instance = super(CustomerPaymentForm, self).save(commit=False)
110 | if self.customer:
111 | instance.customer = self.customer
112 | instance.card_code = self.cleaned_data.get('card_code')
113 | if commit:
114 | instance.save()
115 | return instance
116 |
117 | class Meta:
118 | model = CustomerPaymentProfile
119 | fields = ('first_name', 'last_name', 'company', 'address', 'city',
120 | 'state', 'country', 'zip', 'card_number',
121 | 'expiration_date', 'card_code')
122 |
123 |
124 | class CustomerPaymentAdminForm(CustomerPaymentForm):
125 | class Meta(CustomerPaymentForm.Meta):
126 | fields = ('customer',) + CustomerPaymentForm.Meta.fields
127 |
128 |
129 | class HostedCIMProfileForm(forms.Form):
130 | token = forms.CharField(widget=forms.HiddenInput)
131 | def __init__(self, token, *args, **kwargs):
132 | super(HostedCIMProfileForm, self).__init__(*args, **kwargs)
133 | self.fields['token'].initial = token
134 | if settings.DEBUG:
135 | self.action = "https://test.authorize.net/profile/manage"
136 | else:
137 | self.action = "https://secure.authorize.net/profile/manage"
138 |
139 |
140 |
141 | def get_test_exp_date():
142 | from datetime import date, timedelta
143 | test_date = date.today() + timedelta(days=365)
144 | return test_date.strftime('%m%y')
145 |
--------------------------------------------------------------------------------
/authorizenet/helpers.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | import requests
4 |
5 | from authorizenet.conf import settings
6 | from authorizenet import AUTHNET_POST_URL, AUTHNET_TEST_POST_URL
7 |
8 |
9 | class AIMPaymentHelper(object):
10 | def __init__(self, defaults):
11 | self.defaults = defaults
12 | if settings.DEBUG:
13 | self.endpoint = AUTHNET_TEST_POST_URL
14 | else:
15 | self.endpoint = AUTHNET_POST_URL
16 |
17 | def get_response(self, data):
18 | final_data = dict(self.defaults)
19 | final_data.update(data)
20 | c = final_data['x_delim_char']
21 | # Escape delimiter characters in request fields
22 | for k, v in final_data.items():
23 | if k != 'x_delim_char':
24 | final_data[k] = unicode(v).replace(c, "\\%s" % c)
25 | response = requests.post(self.endpoint, data=final_data)
26 | # Split response by delimiter,
27 | # unescaping delimiter characters in fields
28 | response_list = re.split("(?
11 | Add payment profile
12 | {% endif %}
13 | {% endblock %}
14 |
--------------------------------------------------------------------------------
/authorizenet/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import patterns, url
2 |
3 | urlpatterns = patterns('authorizenet.views',
4 | url(r'^sim/payment/$', 'sim_payment', name="authnet_sim_payment"),
5 | )
6 |
--------------------------------------------------------------------------------
/authorizenet/utils.py:
--------------------------------------------------------------------------------
1 | import hmac
2 |
3 | from django.core.exceptions import ImproperlyConfigured
4 |
5 | from authorizenet.conf import settings
6 | from authorizenet.helpers import AIMPaymentHelper
7 | from authorizenet.models import Response
8 | from authorizenet.signals import payment_was_successful, payment_was_flagged
9 |
10 |
11 | def get_fingerprint(x_fp_sequence, x_fp_timestamp, x_amount):
12 | msg = '^'.join([settings.LOGIN_ID,
13 | x_fp_sequence,
14 | x_fp_timestamp,
15 | x_amount
16 | ]) + '^'
17 |
18 | return hmac.new(settings.TRANSACTION_KEY, msg).hexdigest()
19 |
20 |
21 | def extract_form_data(form_data):
22 | return dict(map(lambda x: ('x_' + x[0], x[1]),
23 | form_data.items()))
24 |
25 | AIM_DEFAULT_DICT = {
26 | 'x_login': settings.LOGIN_ID,
27 | 'x_tran_key': settings.TRANSACTION_KEY,
28 | 'x_delim_data': "TRUE",
29 | 'x_delim_char': settings.DELIM_CHAR,
30 | 'x_relay_response': "FALSE",
31 | 'x_type': "AUTH_CAPTURE",
32 | 'x_method': "CC"
33 | }
34 |
35 |
36 | def create_response(data):
37 | helper = AIMPaymentHelper(defaults=AIM_DEFAULT_DICT)
38 | response_list = helper.get_response(data)
39 | response = Response.objects.create_from_list(response_list)
40 | if response.is_approved:
41 | payment_was_successful.send(sender=response)
42 | else:
43 | payment_was_flagged.send(sender=response)
44 | return response
45 |
46 |
47 | def process_payment(form_data, extra_data):
48 | data = extract_form_data(form_data)
49 | data.update(extract_form_data(extra_data))
50 | data['x_exp_date'] = data['x_exp_date'].strftime('%m%y')
51 | if settings.FORCE_TEST_REQUEST:
52 | data['x_test_request'] = 'TRUE'
53 | if settings.EMAIL_CUSTOMER is not None:
54 | data['x_email_customer'] = settings.EMAIL_CUSTOMER
55 | return create_response(data)
56 |
57 |
58 | def combine_form_data(*args):
59 | data = {}
60 | for form in args:
61 | data.update(form.cleaned_data)
62 | return data
63 |
64 |
65 | def capture_transaction(response, extra_data=None):
66 | if response.type.lower() != 'auth_only':
67 | raise ImproperlyConfigured(
68 | "You can capture only transactions with AUTH_ONLY type")
69 | if extra_data is None:
70 | extra_data = {}
71 | data = dict(extra_data)
72 | data['x_trans_id'] = response.trans_id
73 | #if user already specified x_amount, don't override it with response value
74 | if not data.get('x_amount', None):
75 | data['x_amount'] = response.amount
76 | data['x_type'] = 'PRIOR_AUTH_CAPTURE'
77 | if settings.FORCE_TEST_REQUEST:
78 | data['x_test_request'] = 'TRUE'
79 | return create_response(data)
80 |
--------------------------------------------------------------------------------
/authorizenet/views.py:
--------------------------------------------------------------------------------
1 | try:
2 | import hashlib
3 | except ImportError:
4 | import md5 as hashlib
5 |
6 | from authorizenet.conf import settings
7 | from django.shortcuts import render
8 | from django.views.decorators.csrf import csrf_exempt
9 | from django.views.generic.edit import CreateView, UpdateView
10 |
11 | from authorizenet.forms import AIMPaymentForm, BillingAddressForm, CustomerPaymentForm
12 | from authorizenet.models import CustomerProfile, CustomerPaymentProfile
13 | from authorizenet.models import Response
14 | from authorizenet.signals import payment_was_successful, payment_was_flagged
15 | from authorizenet.utils import process_payment, combine_form_data
16 |
17 |
18 | @csrf_exempt
19 | def sim_payment(request):
20 | response = Response.objects.create_from_dict(request.POST)
21 | MD5_HASH = settings.MD5_HASH
22 | hash_is_valid = True
23 |
24 | #if MD5-Hash value is provided, use it to validate response
25 | if MD5_HASH:
26 | hash_is_valid = False
27 | hash_value = hashlib.md5(''.join([MD5_HASH,
28 | settings.LOGIN_ID,
29 | response.trans_id,
30 | response.amount])).hexdigest()
31 |
32 | hash_is_valid = hash_value.upper() == response.MD5_Hash
33 |
34 | if response.is_approved and hash_is_valid:
35 | payment_was_successful.send(sender=response)
36 | else:
37 | payment_was_flagged.send(sender=response)
38 |
39 | return render(request, 'authorizenet/sim_payment.html')
40 |
41 |
42 | class AIMPayment(object):
43 | """
44 | Class to handle credit card payments to Authorize.NET
45 | """
46 |
47 | processing_error = ("There was an error processing your payment. "
48 | "Check your information and try again.")
49 | form_error = "Please correct the errors below and try again."
50 |
51 | def __init__(self,
52 | extra_data={},
53 | payment_form_class=AIMPaymentForm,
54 | context={},
55 | billing_form_class=BillingAddressForm,
56 | shipping_form_class=None,
57 | payment_template="authorizenet/aim_payment.html",
58 | success_template='authorizenet/aim_success.html',
59 | initial_data={}):
60 | self.extra_data = extra_data
61 | self.payment_form_class = payment_form_class
62 | self.payment_template = payment_template
63 | self.success_template = success_template
64 | self.context = context
65 | self.initial_data = initial_data
66 | self.billing_form_class = billing_form_class
67 | self.shipping_form_class = shipping_form_class
68 |
69 | def __call__(self, request):
70 | self.request = request
71 | if request.method == "GET":
72 | return self.render_payment_form()
73 | else:
74 | return self.validate_payment_form()
75 |
76 | def render_payment_form(self):
77 | self.context['payment_form'] = self.payment_form_class(
78 | initial=self.initial_data)
79 | self.context['billing_form'] = self.billing_form_class(
80 | initial=self.initial_data)
81 | if self.shipping_form_class:
82 | self.context['shipping_form'] = self.shipping_form_class(
83 | initial=self.initial_data)
84 | return render(
85 | self.request,
86 | self.payment_template,
87 | self.context
88 | )
89 |
90 | def validate_payment_form(self):
91 | payment_form = self.payment_form_class(self.request.POST)
92 | billing_form = self.billing_form_class(self.request.POST)
93 |
94 | if self.shipping_form_class:
95 | shipping_form = self.shipping_form_class(self.request.POST)
96 |
97 | #if shipping for exists also validate it
98 | if payment_form.is_valid() and billing_form.is_valid() and (not self.shipping_form_class or shipping_form.is_valid()):
99 |
100 | if not self.shipping_form_class:
101 | args = payment_form, billing_form
102 | else:
103 | args = payment_form, billing_form, shipping_form
104 |
105 | form_data = combine_form_data(*args)
106 | response = process_payment(form_data, self.extra_data)
107 | self.context['response'] = response
108 | if response.is_approved:
109 | return render(
110 | self.request,
111 | self.success_template,
112 | self.context
113 | )
114 | else:
115 | self.context['errors'] = self.processing_error
116 | self.context['payment_form'] = payment_form
117 | self.context['billing_form'] = billing_form
118 | if self.shipping_form_class:
119 | self.context['shipping_form'] = shipping_form
120 | self.context.setdefault('errors', self.form_error)
121 | return render(
122 | self.request,
123 | self.payment_template,
124 | self.context
125 | )
126 |
127 |
128 | class PaymentProfileCreateView(CreateView):
129 | """
130 | View for creating a CustomerPaymentProfile instance
131 |
132 | CustomerProfile instance will be created automatically if needed.
133 | """
134 |
135 | template_name = 'authorizenet/create_payment_profile.html'
136 | form_class = CustomerPaymentForm
137 |
138 | def get_form_kwargs(self):
139 | kwargs = super(PaymentProfileCreateView, self).get_form_kwargs()
140 | kwargs['customer'] = self.request.user
141 | return kwargs
142 |
143 |
144 | class PaymentProfileUpdateView(UpdateView):
145 | """
146 | View for modifying an existing CustomerPaymentProfile instance
147 | """
148 |
149 | template_name = 'authorizenet/update_payment_profile.html'
150 | form_class = CustomerPaymentForm
151 |
152 | def get_form_kwargs(self):
153 | kwargs = super(PaymentProfileUpdateView, self).get_form_kwargs()
154 | kwargs['customer'] = self.request.user
155 | return kwargs
156 |
--------------------------------------------------------------------------------
/docs.rst:
--------------------------------------------------------------------------------
1 | Usage
2 | =====
3 |
4 | Installation
5 | ------------
6 |
7 | Install from `PyPI`_:
8 |
9 | .. code-block:: bash
10 |
11 | $ pip install django-authorizenet
12 |
13 | .. _PyPI: https://pypi.python.org/pypi/django-authorizenet/
14 |
15 |
16 | Quickstart
17 | ----------
18 |
19 | Add ``authorizenet`` to ``INSTALLED_APPS`` in your settings file:
20 |
21 | .. code-block:: python
22 |
23 | INSTALLED_APPS = (
24 | ...
25 | 'authorizenet',
26 | )
27 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-authorizenet.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-authorizenet.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-authorizenet"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-authorizenet"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/docs/cim.rst:
--------------------------------------------------------------------------------
1 | CIM Usage
2 | =========
3 |
4 | The easiest way to use the CIM support is to use the ``CustomerProfile`` and
5 | ``CustomerPaymentProfile`` models provided by the ``authorizenet`` app. These
6 | models map the ORM CRUD operations to Authorize.NET calls, making it easy to
7 | keep your local and remote data in sync.
8 |
9 | Customer profiles contain a one-to-one field ``customer`` which links to the
10 | Django user model by default. This foreign key target may be customized in the
11 | ``CUSTOMER_MODEL`` setting in your settings module.
12 |
13 | Using built-in models
14 | ---------------------
15 |
16 | CustomerPaymentProfile Model
17 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 |
19 | When the ``save()`` method is called on a ``CustomerPaymentProfile`` instance,
20 | the payment profile is created or update on Authorize.NET and saved to the
21 | database. A ``CustomerProfile`` will also be created if the specified
22 | ``customer`` doesn't have one yet.
23 |
24 | When the ``delete()`` method is called on a ``CustomerPaymentProfile``
25 | instance, the payment profile is deleted on Authorize.NET and deleted from the
26 | database.
27 |
28 | Payment Profile Form
29 | ~~~~~~~~~~~~~~~~~~~~
30 |
31 | The ``CustomerPaymentForm`` available in ``authorizenet.forms`` allows a
32 | ``CustomerPaymentProfile`` to be easily created or updated for a given
33 | ``customer``. This form is just a model form for the
34 | ``CustomerPaymentProfile`` model.
35 |
36 | Generic Views
37 | ~~~~~~~~~~~~~
38 |
39 | The ``PaymentProfileCreateView`` and ``PaymentProfileUpdateView`` allow
40 | ``CustomerPaymentProfile`` instances can be created and updated with ease.
41 | The ``customer`` argument sent to ``CustomerPaymentForm`` defaults to the
42 | currently authenticated user.
43 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # django-authorizenet documentation build configuration file, created by
4 | # sphinx-quickstart on Mon Jun 10 13:41:57 2013.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = ['sphinx.ext.autodoc']
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8-sig'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'django-authorizenet'
44 | copyright = u'2013, Andrii Kurinnyi'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '2.0'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '2.0'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | exclude_patterns = ['_build']
68 |
69 | # The reST default role (used for this markup: `text`) to use for all documents.
70 | #default_role = None
71 |
72 | # If true, '()' will be appended to :func: etc. cross-reference text.
73 | #add_function_parentheses = True
74 |
75 | # If true, the current module name will be prepended to all description
76 | # unit titles (such as .. function::).
77 | #add_module_names = True
78 |
79 | # If true, sectionauthor and moduleauthor directives will be shown in the
80 | # output. They are ignored by default.
81 | #show_authors = False
82 |
83 | # The name of the Pygments (syntax highlighting) style to use.
84 | pygments_style = 'sphinx'
85 |
86 | # A list of ignored prefixes for module index sorting.
87 | #modindex_common_prefix = []
88 |
89 | # If true, keep warnings as "system message" paragraphs in the built documents.
90 | #keep_warnings = False
91 |
92 |
93 | # -- Options for HTML output ---------------------------------------------------
94 |
95 | # The theme to use for HTML and HTML Help pages. See the documentation for
96 | # a list of builtin themes.
97 | html_theme = 'default'
98 |
99 | # Theme options are theme-specific and customize the look and feel of a theme
100 | # further. For a list of options available for each theme, see the
101 | # documentation.
102 | #html_theme_options = {}
103 |
104 | # Add any paths that contain custom themes here, relative to this directory.
105 | #html_theme_path = []
106 |
107 | # The name for this set of Sphinx documents. If None, it defaults to
108 | # " v documentation".
109 | #html_title = None
110 |
111 | # A shorter title for the navigation bar. Default is the same as html_title.
112 | #html_short_title = None
113 |
114 | # The name of an image file (relative to this directory) to place at the top
115 | # of the sidebar.
116 | #html_logo = None
117 |
118 | # The name of an image file (within the static path) to use as favicon of the
119 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
120 | # pixels large.
121 | #html_favicon = None
122 |
123 | # Add any paths that contain custom static files (such as style sheets) here,
124 | # relative to this directory. They are copied after the builtin static files,
125 | # so a file named "default.css" will overwrite the builtin "default.css".
126 | html_static_path = ['_static']
127 |
128 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
129 | # using the given strftime format.
130 | #html_last_updated_fmt = '%b %d, %Y'
131 |
132 | # If true, SmartyPants will be used to convert quotes and dashes to
133 | # typographically correct entities.
134 | #html_use_smartypants = True
135 |
136 | # Custom sidebar templates, maps document names to template names.
137 | #html_sidebars = {}
138 |
139 | # Additional templates that should be rendered to pages, maps page names to
140 | # template names.
141 | #html_additional_pages = {}
142 |
143 | # If false, no module index is generated.
144 | #html_domain_indices = True
145 |
146 | # If false, no index is generated.
147 | #html_use_index = True
148 |
149 | # If true, the index is split into individual pages for each letter.
150 | #html_split_index = False
151 |
152 | # If true, links to the reST sources are added to the pages.
153 | #html_show_sourcelink = True
154 |
155 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
156 | #html_show_sphinx = True
157 |
158 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
159 | #html_show_copyright = True
160 |
161 | # If true, an OpenSearch description file will be output, and all pages will
162 | # contain a tag referring to it. The value of this option must be the
163 | # base URL from which the finished HTML is served.
164 | #html_use_opensearch = ''
165 |
166 | # This is the file name suffix for HTML files (e.g. ".xhtml").
167 | #html_file_suffix = None
168 |
169 | # Output file base name for HTML help builder.
170 | htmlhelp_basename = 'django-authorizenetdoc'
171 |
172 |
173 | # -- Options for LaTeX output --------------------------------------------------
174 |
175 | latex_elements = {
176 | # The paper size ('letterpaper' or 'a4paper').
177 | #'papersize': 'letterpaper',
178 |
179 | # The font size ('10pt', '11pt' or '12pt').
180 | #'pointsize': '10pt',
181 |
182 | # Additional stuff for the LaTeX preamble.
183 | #'preamble': '',
184 | }
185 |
186 | # Grouping the document tree into LaTeX files. List of tuples
187 | # (source start file, target name, title, author, documentclass [howto/manual]).
188 | latex_documents = [
189 | ('index', 'django-authorizenet.tex', u'django-authorizenet Documentation',
190 | u'Andrii Kurinnyi', 'manual'),
191 | ]
192 |
193 | # The name of an image file (relative to this directory) to place at the top of
194 | # the title page.
195 | #latex_logo = None
196 |
197 | # For "manual" documents, if this is true, then toplevel headings are parts,
198 | # not chapters.
199 | #latex_use_parts = False
200 |
201 | # If true, show page references after internal links.
202 | #latex_show_pagerefs = False
203 |
204 | # If true, show URL addresses after external links.
205 | #latex_show_urls = False
206 |
207 | # Documents to append as an appendix to all manuals.
208 | #latex_appendices = []
209 |
210 | # If false, no module index is generated.
211 | #latex_domain_indices = True
212 |
213 |
214 | # -- Options for manual page output --------------------------------------------
215 |
216 | # One entry per manual page. List of tuples
217 | # (source start file, name, description, authors, manual section).
218 | man_pages = [
219 | ('index', 'django-authorizenet', u'django-authorizenet Documentation',
220 | [u'Andrii Kurinnyi'], 1)
221 | ]
222 |
223 | # If true, show URL addresses after external links.
224 | #man_show_urls = False
225 |
226 |
227 | # -- Options for Texinfo output ------------------------------------------------
228 |
229 | # Grouping the document tree into Texinfo files. List of tuples
230 | # (source start file, target name, title, author,
231 | # dir menu entry, description, category)
232 | texinfo_documents = [
233 | ('index', 'django-authorizenet', u'django-authorizenet Documentation',
234 | u'Andrii Kurinnyi', 'django-authorizenet', 'One line description of project.',
235 | 'Miscellaneous'),
236 | ]
237 |
238 | # Documents to append as an appendix to all manuals.
239 | #texinfo_appendices = []
240 |
241 | # If false, no module index is generated.
242 | #texinfo_domain_indices = True
243 |
244 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
245 | #texinfo_show_urls = 'footnote'
246 |
247 | # If true, do not generate a @detailmenu in the "Top" node's menu.
248 | #texinfo_no_detailmenu = False
249 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. django-authorizenet documentation master file, created by
2 | sphinx-quickstart on Mon Jun 10 13:41:57 2013.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to django-authorizenet's documentation!
7 | ===============================================
8 |
9 | Contents:
10 |
11 | .. toctree::
12 | :maxdepth: 2
13 |
14 | usage
15 | cim
16 |
17 |
18 |
19 | Indices and tables
20 | ==================
21 |
22 | * :ref:`genindex`
23 | * :ref:`modindex`
24 | * :ref:`search`
25 |
26 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | goto end
41 | )
42 |
43 | if "%1" == "clean" (
44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
45 | del /q /s %BUILDDIR%\*
46 | goto end
47 | )
48 |
49 |
50 | %SPHINXBUILD% 2> nul
51 | if errorlevel 9009 (
52 | echo.
53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
54 | echo.installed, then set the SPHINXBUILD environment variable to point
55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
56 | echo.may add the Sphinx directory to PATH.
57 | echo.
58 | echo.If you don't have Sphinx installed, grab it from
59 | echo.http://sphinx-doc.org/
60 | exit /b 1
61 | )
62 |
63 | if "%1" == "html" (
64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
68 | goto end
69 | )
70 |
71 | if "%1" == "dirhtml" (
72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
76 | goto end
77 | )
78 |
79 | if "%1" == "singlehtml" (
80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
84 | goto end
85 | )
86 |
87 | if "%1" == "pickle" (
88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can process the pickle files.
92 | goto end
93 | )
94 |
95 | if "%1" == "json" (
96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
97 | if errorlevel 1 exit /b 1
98 | echo.
99 | echo.Build finished; now you can process the JSON files.
100 | goto end
101 | )
102 |
103 | if "%1" == "htmlhelp" (
104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | if errorlevel 1 exit /b 1
106 | echo.
107 | echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | goto end
110 | )
111 |
112 | if "%1" == "qthelp" (
113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | if errorlevel 1 exit /b 1
115 | echo.
116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-authorizenet.qhcp
119 | echo.To view the help file:
120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-authorizenet.ghc
121 | goto end
122 | )
123 |
124 | if "%1" == "devhelp" (
125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished.
129 | goto end
130 | )
131 |
132 | if "%1" == "epub" (
133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | goto end
138 | )
139 |
140 | if "%1" == "latex" (
141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | goto end
146 | )
147 |
148 | if "%1" == "latexpdf" (
149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | cd %BUILDDIR%/latex
151 | make all-pdf
152 | cd %BUILDDIR%/..
153 | echo.
154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | goto end
156 | )
157 |
158 | if "%1" == "latexpdfja" (
159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | cd %BUILDDIR%/latex
161 | make all-pdf-ja
162 | cd %BUILDDIR%/..
163 | echo.
164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | goto end
166 | )
167 |
168 | if "%1" == "text" (
169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | if errorlevel 1 exit /b 1
171 | echo.
172 | echo.Build finished. The text files are in %BUILDDIR%/text.
173 | goto end
174 | )
175 |
176 | if "%1" == "man" (
177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | if errorlevel 1 exit /b 1
179 | echo.
180 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | goto end
182 | )
183 |
184 | if "%1" == "texinfo" (
185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | if errorlevel 1 exit /b 1
187 | echo.
188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | goto end
190 | )
191 |
192 | if "%1" == "gettext" (
193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | if errorlevel 1 exit /b 1
195 | echo.
196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | goto end
198 | )
199 |
200 | if "%1" == "changes" (
201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | if errorlevel 1 exit /b 1
203 | echo.
204 | echo.The overview file is in %BUILDDIR%/changes.
205 | goto end
206 | )
207 |
208 | if "%1" == "linkcheck" (
209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | if errorlevel 1 exit /b 1
211 | echo.
212 | echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | goto end
215 | )
216 |
217 | if "%1" == "doctest" (
218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | if errorlevel 1 exit /b 1
220 | echo.
221 | echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | goto end
224 | )
225 |
226 | if "%1" == "xml" (
227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | if errorlevel 1 exit /b 1
229 | echo.
230 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | goto end
232 | )
233 |
234 | if "%1" == "pseudoxml" (
235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | if errorlevel 1 exit /b 1
237 | echo.
238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | goto end
240 | )
241 |
242 | :end
243 |
--------------------------------------------------------------------------------
/docs/usage.rst:
--------------------------------------------------------------------------------
1 | Usage
2 | =====
3 |
4 | Installation
5 | ------------
6 |
7 | Install from `PyPI`_:
8 |
9 | .. code-block:: bash
10 |
11 | $ pip install django-authorizenet
12 |
13 | .. _PyPI: https://pypi.python.org/pypi/django-authorizenet/
14 |
15 |
16 | Quickstart
17 | ----------
18 |
19 | Add ``authorizenet`` to ``INSTALLED_APPS`` in your settings file:
20 |
21 | .. code-block:: python
22 |
23 | INSTALLED_APPS = (
24 | ...
25 | 'authorizenet',
26 | )
27 |
28 | The following settings are required:
29 |
30 | .. code-block:: python
31 |
32 | AUTHNET_DEBUG = True
33 |
34 | AUTHNET_LOGIN_ID = "yOuRl0g1nID"
35 |
36 | AUTHNET_TRANSACTION_KEY = "Tr4n5aCti0nK3y"
37 |
--------------------------------------------------------------------------------
/runtests.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from os.path import abspath, dirname
3 |
4 | from django.conf import settings
5 |
6 | sys.path.insert(0, abspath(dirname(__file__)))
7 |
8 | if not settings.configured:
9 | settings.configure(
10 | AUTHNET_DEBUG=False,
11 | AUTHNET_LOGIN_ID="loginid",
12 | AUTHNET_TRANSACTION_KEY="key",
13 | INSTALLED_APPS=(
14 | 'django.contrib.contenttypes',
15 | 'django.contrib.auth',
16 | 'django.contrib.sessions',
17 | 'tests',
18 | 'authorizenet',
19 | ),
20 | ROOT_URLCONF='tests.urls',
21 | STATIC_URL='/static/',
22 | DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3'}},
23 | )
24 |
25 |
26 | def runtests():
27 | from django.test.simple import DjangoTestSuiteRunner
28 | failures = DjangoTestSuiteRunner(failfast=False).run_tests(['tests'])
29 | sys.exit(failures)
30 |
--------------------------------------------------------------------------------
/sample_project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/__init__.py
--------------------------------------------------------------------------------
/sample_project/local_settings.py.example:
--------------------------------------------------------------------------------
1 | DEBUG = True
2 |
3 | AUTHNET_DEBUG = DEBUG
4 |
5 | if AUTHNET_DEBUG:
6 | AUTHNET_LOGIN_ID = "your login id"
7 | AUTHNET_TRANSACTION_KEY = "your transaction key"
8 |
9 | """
10 | For your reference, you can use the following test credit card numbers when testing your connection.
11 | The expiration date must be set to the present date or later:
12 | - American Express Test Card: 370000000000002
13 | - Discover Test Card: 6011000000000012
14 | - Visa Test Card: 4007000000027
15 | - Second Visa Test Card: 4012888818888
16 | - JCB: 3088000000000017
17 | - Diners Club/ Carte Blanche: 38000000000006
18 | """
--------------------------------------------------------------------------------
/sample_project/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from django.core.management import execute_manager
3 | try:
4 | import settings # Assumed to be in the same directory.
5 | except ImportError:
6 | import sys
7 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
8 | sys.exit(1)
9 |
10 | if __name__ == "__main__":
11 | execute_manager(settings)
12 |
--------------------------------------------------------------------------------
/sample_project/media/contentx/IframeCommunicator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | IFrame Communicator
5 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sample_project/media/contentx/closeButton1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/closeButton1.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/closeButton1a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/closeButton1a.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/closeButton1h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/closeButton1h.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/empty.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | empty
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample_project/media/contentx/manage.css:
--------------------------------------------------------------------------------
1 |
2 | #divAuthorizeNetPopupScreen { position:fixed; left:0px; top:0px; width:100%; height:100%; z-index:1; background-color:#808080; opacity:0.5; -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; filter:alpha(opacity=50); }
3 | #divAuthorizeNetPopup { position:absolute; left:50%; top:50%; margin-left:-200px; margin-top: -200px; z-index:2; overflow:visible; }
4 | .AuthorizeNetShadow {
5 | height: 16px;
6 | width: 16px;
7 | position: absolute;
8 | }
9 | .AuthorizeNetShadowT { top: -16px; left: 0px; background-image: url('shadow1-top.png'); background-repeat: repeat-x; width: 100%; }
10 | .AuthorizeNetShadowR { top: 0px; right: -16px; background-image: url('shadow1-right.png'); background-repeat: repeat-y; height: 100%; }
11 | .AuthorizeNetShadowB { bottom: -16px; left: 0px; background-image: url('shadow1-bottom.png'); background-repeat: repeat-x; width: 100%; }
12 | .AuthorizeNetShadowL { top: 0px; left: -16px; background-image: url('shadow1-left.png'); background-repeat: repeat-y; height: 100%; }
13 | .AuthorizeNetShadowTR { top: -16px; right: -16px; background-image: url('shadow1-topRight.png'); background-repeat: no-repeat; }
14 | .AuthorizeNetShadowBR { bottom: -16px; right: -16px; background-image: url('shadow1-bottomRight.png'); background-repeat: no-repeat; }
15 | .AuthorizeNetShadowBL { bottom: -16px; left: -16px; background-image: url('shadow1-bottomLeft.png'); background-repeat: no-repeat; }
16 | .AuthorizeNetShadowTL { top: -16px; left: -16px; background-image: url('shadow1-topLeft.png'); background-repeat: no-repeat; }
17 |
18 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupOuter { background-color:#dddddd; border-width:1px; border-style:solid; border-color: #a0a0a0 #909090 #909090 #a0a0a0; padding:4px; }
19 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupTop { height:23px; }
20 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupClose { position:absolute; right:7px; top:7px; }
21 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupClose a {
22 | background-image: url('closeButton1.png');
23 | background-repeat: no-repeat;
24 | height: 16px;
25 | width: 16px;
26 | display: inline-block;
27 | }
28 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupClose a:hover {
29 | background-image: url('closeButton1h.png');
30 | }
31 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupClose a:active {
32 | background-image: url('closeButton1a.png');
33 | }
34 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupInner { background-color:#ffffff; border-width:2px; border-style:solid; border-color: #cfcfcf #ebebeb #ebebeb #cfcfcf; }
35 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupBottom { height:30px; }
36 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupLogo { position:absolute; right:9px; bottom:4px; width:200px; height:25px; background-image:url('powered_simple.png'); }
37 |
--------------------------------------------------------------------------------
/sample_project/media/contentx/manageIELTE6.css:
--------------------------------------------------------------------------------
1 | body { height:100%; }
2 | #divAuthorizeNetPopupScreen { position:absolute; }
3 | .AuthorizeNetShadow { display:none; }
4 | .AuthorizeNetPopupGrayFrameTheme .AuthorizeNetPopupLogo { background-image:none; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='powered_simple.png', sizingMethod='scale'); }
5 |
--------------------------------------------------------------------------------
/sample_project/media/contentx/popup.js:
--------------------------------------------------------------------------------
1 | /* Usage:
2 |
3 |
4 |
5 |
26 | */
27 |
28 | (function () {
29 | if (!window.AuthorizeNetPopup) window.AuthorizeNetPopup = {};
30 | if (!AuthorizeNetPopup.options) AuthorizeNetPopup.options = {
31 | onPopupClosed: null
32 | ,eCheckEnabled: false
33 | ,skipZIndexCheck: false
34 | ,useTestEnvironment: false
35 | };
36 | AuthorizeNetPopup.closePopup = function() {
37 | document.getElementById("divAuthorizeNetPopupScreen").style.display = "none";
38 | document.getElementById("divAuthorizeNetPopup").style.display = "none";
39 | document.getElementById("iframeAuthorizeNet").src = "contentx/empty.html";
40 | if (AuthorizeNetPopup.options.onPopupClosed) AuthorizeNetPopup.options.onPopupClosed();
41 | };
42 | AuthorizeNetPopup.openManagePopup = function() {
43 | openSpecificPopup({action:"manage"});
44 | };
45 | AuthorizeNetPopup.openAddPaymentPopup = function() {
46 | openSpecificPopup({action:"addPayment", paymentProfileId:"new"});
47 | };
48 | AuthorizeNetPopup.openEditPaymentPopup = function(paymentProfileId) {
49 | openSpecificPopup({action:"editPayment", paymentProfileId:paymentProfileId});
50 | };
51 | AuthorizeNetPopup.openAddShippingPopup = function() {
52 | openSpecificPopup({action:"addShipping", shippingAddressId:"new"});
53 | };
54 | AuthorizeNetPopup.openEditShippingPopup = function(shippingAddressId) {
55 | openSpecificPopup({action:"editShipping", shippingAddressId:shippingAddressId});
56 | };
57 | AuthorizeNetPopup.onReceiveCommunication = function (querystr) {
58 | var params = parseQueryString(querystr);
59 | switch(params["action"]) {
60 | case "successfulSave":
61 | AuthorizeNetPopup.closePopup();
62 | break;
63 | case "cancel":
64 | AuthorizeNetPopup.closePopup();
65 | break;
66 | case "resizeWindow":
67 | var w = parseInt(params["width"]);
68 | var h = parseInt(params["height"]);
69 | var ifrm = document.getElementById("iframeAuthorizeNet");
70 | ifrm.style.width = w.toString() + "px";
71 | ifrm.style.height = h.toString() + "px";
72 | centerPopup();
73 | adjustPopupScreen();
74 | break;
75 | }
76 | };
77 | function openSpecificPopup(opt) {
78 | var popup = document.getElementById("divAuthorizeNetPopup");
79 | var popupScreen = document.getElementById("divAuthorizeNetPopupScreen");
80 | var ifrm = document.getElementById("iframeAuthorizeNet");
81 | var form = document.forms["formAuthorizeNetPopup"];
82 |
83 | switch (opt.action) {
84 | case "addPayment":
85 | ifrm.style.width = "435px";
86 | ifrm.style.height = AuthorizeNetPopup.options.eCheckEnabled ? "508px" : "479px";
87 | break;
88 | case "editPayment":
89 | ifrm.style.width = "435px";
90 | ifrm.style.height = "479px";
91 | break;
92 | case "addShipping":
93 | ifrm.style.width = "385px";
94 | ifrm.style.height = "359px";
95 | break;
96 | case "editShipping":
97 | ifrm.style.width = "385px";
98 | ifrm.style.height = "359px";
99 | break;
100 | case "manage":
101 | ifrm.style.width = "442px";
102 | ifrm.style.height = "578px";
103 | break;
104 | }
105 |
106 | if (!AuthorizeNetPopup.options.skipZIndexCheck) {
107 | var zIndexHightest = getHightestZIndex();
108 | popup.style.zIndex = zIndexHightest + 2;
109 | popupScreen.style.zIndex = zIndexHightest + 1;
110 | }
111 |
112 | if (AuthorizeNetPopup.options.useTestEnvironment) {
113 | form.action = "https://test.authorize.net/profile/" + opt.action;
114 | } else {
115 | form.action = "https://secure.authorize.net/profile/" + opt.action;
116 | }
117 | if (form.elements["PaymentProfileId"]) form.elements["PaymentProfileId"].value = opt.paymentProfileId ? opt.paymentProfileId : "";
118 | if (form.elements["ShippingAddressId"]) form.elements["ShippingAddressId"].value = opt.shippingAddressId ? opt.shippingAddressId : "";
119 | form.submit();
120 |
121 | popup.style.display = "";
122 | popupScreen.style.display = "";
123 | centerPopup();
124 | adjustPopupScreen();
125 | };
126 | function centerPopup() {
127 | var d = document.getElementById("divAuthorizeNetPopup");
128 | d.style.left = "50%";
129 | d.style.top = "50%";
130 | var left = -Math.floor(d.clientWidth / 2);
131 | var top = -Math.floor(d.clientHeight / 2);
132 | d.style.marginLeft = left.toString() + "px";
133 | d.style.marginTop = top.toString() + "px";
134 | if (d.offsetLeft < 16) {
135 | d.style.left = "16px";
136 | d.style.marginLeft = "0px";
137 | }
138 | if (d.offsetTop < 16) {
139 | d.style.top = "16px";
140 | d.style.marginTop = "0px";
141 | }
142 | }
143 | function adjustPopupScreen() { // IE6 fix
144 | var popupScreen = document.getElementById("divAuthorizeNetPopupScreen");
145 | if (popupScreen.currentStyle && popupScreen.currentStyle.position == "absolute") {
146 | if (popupScreen.clientHeight < document.documentElement.scrollHeight) {
147 | popupScreen.style.height = document.documentElement.scrollHeight.toString() + "px";
148 | }
149 | if (popupScreen.clientWidth < document.documentElement.scrollWidth) {
150 | popupScreen.style.width = document.documentElement.scrollWidth.toString() + "px";
151 | }
152 | }
153 | }
154 | function getHightestZIndex() {
155 | var max = 0;
156 | var z = 0;
157 | var a = document.getElementsByTagName('*');
158 | for (var i = 0; i < a.length; i++) {
159 | z = 0;
160 | if (a[i].currentStyle) {
161 | var style = a[i].currentStyle;
162 | if (style.display != "none") {
163 | z = parseFloat(style.zIndex);
164 | }
165 | } else if (window.getComputedStyle) {
166 | var style = window.getComputedStyle(a[i], null);
167 | if (style.getPropertyValue("display") != "none") {
168 | z = parseFloat(style.getPropertyValue("z-index"));
169 | }
170 | }
171 | if (!isNaN(z) && z > max) max = z;
172 | }
173 | return Math.ceil(max);
174 | }
175 | function parseQueryString(str) {
176 | var vars = [];
177 | var arr = str.split('&');
178 | var pair;
179 | for (var i = 0; i < arr.length; i++) {
180 | pair = arr[i].split('=');
181 | vars.push(pair[0]);
182 | vars[pair[0]] = unescape(pair[1]);
183 | }
184 | return vars;
185 | }
186 | } ());
187 |
--------------------------------------------------------------------------------
/sample_project/media/contentx/powered_simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/powered_simple.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-bottom.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-bottomLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-bottomLeft.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-bottomRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-bottomRight.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-left.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-right.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-top.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-topLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-topLeft.png
--------------------------------------------------------------------------------
/sample_project/media/contentx/shadow1-topRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/media/contentx/shadow1-topRight.png
--------------------------------------------------------------------------------
/sample_project/requirements.txt:
--------------------------------------------------------------------------------
1 | django>=1.5
2 | ../
3 |
--------------------------------------------------------------------------------
/sample_project/samplestore/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/sample_project/samplestore/__init__.py
--------------------------------------------------------------------------------
/sample_project/samplestore/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from samplestore.models import Invoice, Item, Customer, Address
3 |
4 | admin.site.register(Invoice)
5 | admin.site.register(Item)
6 | admin.site.register(Customer)
7 | admin.site.register(Address)
8 |
--------------------------------------------------------------------------------
/sample_project/samplestore/fixtures/initial_data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pk": 1,
4 | "model": "samplestore.customer",
5 | "fields": {
6 | "user": 1,
7 | "shipping_same_as_billing": true
8 | }
9 | },
10 | {
11 | "pk": 1,
12 | "model": "samplestore.item",
13 | "fields": {
14 | "price": "300",
15 | "title": "TV"
16 | }
17 | },
18 | {
19 | "pk": 2,
20 | "model": "samplestore.item",
21 | "fields": {
22 | "price": "5",
23 | "title": "T-Shirt"
24 | }
25 | },
26 | {
27 | "pk": 1,
28 | "model": "samplestore.invoice",
29 | "fields": {
30 | "customer": 1,
31 | "item": 1
32 | }
33 | },
34 | {
35 | "pk": 2,
36 | "model": "samplestore.invoice",
37 | "fields": {
38 | "customer": 1,
39 | "item": 1
40 | }
41 | }
42 | ]
--------------------------------------------------------------------------------
/sample_project/samplestore/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.db.models.signals import post_save
3 |
4 | from django.contrib.auth.models import User
5 | from django.contrib.localflavor.us.models import PhoneNumberField, USStateField
6 |
7 | from authorizenet.signals import payment_was_successful, payment_was_flagged
8 |
9 |
10 | ADDRESS_CHOICES = (
11 | ('billing', 'Billing'),
12 | ('shipping', 'Shipping'),
13 | )
14 |
15 |
16 | class Customer(models.Model):
17 | user = models.ForeignKey(User)
18 | shipping_same_as_billing = models.BooleanField(default=True)
19 | cim_profile_id = models.CharField(max_length=10)
20 |
21 | def __unicode__(self):
22 | return self.user.username
23 |
24 |
25 | class Address(models.Model):
26 | type = models.CharField(max_length=10, choices=ADDRESS_CHOICES)
27 | customer = models.ForeignKey(Customer)
28 | first_name = models.CharField(max_length=50)
29 | last_name = models.CharField(max_length=50)
30 | company = models.CharField(max_length=50, blank=True)
31 | address = models.CharField(max_length=60)
32 | city = models.CharField(max_length=40)
33 | state = USStateField()
34 | zip_code = models.CharField(max_length=20)
35 | phone = PhoneNumberField(blank=True)
36 | fax = PhoneNumberField(blank=True)
37 |
38 | def __unicode__(self):
39 | return self.customer.user.username
40 |
41 |
42 | class Item(models.Model):
43 | title = models.CharField(max_length=55)
44 | price = models.DecimalField(max_digits=8, decimal_places=2)
45 |
46 | def __unicode__(self):
47 | return self.title
48 |
49 |
50 | class Invoice(models.Model):
51 | customer = models.ForeignKey(Customer)
52 | item = models.ForeignKey(Item)
53 |
54 | def __unicode__(self):
55 | return u"" % (self.id, self.customer.user.username)
56 |
57 |
58 | def create_customer_profile(sender, instance=None, **kwargs):
59 | if instance is None:
60 | return
61 | profile, created = Customer.objects.get_or_create(user=instance)
62 |
63 |
64 | post_save.connect(create_customer_profile, sender=User)
65 |
66 |
67 | def successfull_payment(sender, **kwargs):
68 | response = sender
69 | # do something with the response
70 |
71 |
72 | def flagged_payment(sender, **kwargs):
73 | response = sender
74 | # do something with the response
75 |
76 |
77 | payment_was_successful.connect(successfull_payment)
78 | payment_was_flagged.connect(flagged_payment)
79 |
--------------------------------------------------------------------------------
/sample_project/samplestore/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import patterns, url
2 |
3 |
4 | urlpatterns = patterns('samplestore.views',
5 | url(r'^commit/(\d+)/$', 'commit_to_buy', name="samplestore_commit_to_buy"),
6 | url(r'^make_payment/(\d+)/$', 'make_payment',
7 | name="samplestore_make_payment"),
8 | url(r'^$', 'items', name="samplestore_items"),
9 | url(r'^capture/(\d+)/$', 'capture', name="samplestore_capture"),
10 | url(r'^capture/index/$', 'capture_index',
11 | name="samplestore_capture_index"),
12 | url(r'^create_invoice/(\d+)/$', 'create_invoice',
13 | name="samplestore_create_invoice"),
14 | url(r'^create_invoice/(\d+)/auth/$', 'create_invoice',
15 | {'auth_only': True}, name="samplestore_create_invoice_auth"),
16 | url(r'^make_direct_payment/(\d+)/$', 'make_direct_payment',
17 | name="samplestore_make_direct_payment"),
18 | url(r'^make_direct_payment/(\d+)/auth/$', 'make_direct_payment',
19 | {'auth_only': True}, name="samplestore_make_direct_payment_auth"),
20 |
21 | url(r'^edit_cim_profile/$', 'edit_cim_profile',
22 | name='edit_cim_profile'),
23 | )
24 |
--------------------------------------------------------------------------------
/sample_project/samplestore/views.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | from django.conf import settings
4 | from django.core.urlresolvers import reverse
5 | from django.http import HttpResponseRedirect, Http404
6 | from django.shortcuts import render_to_response, get_object_or_404
7 | from django.template import RequestContext
8 |
9 | from django.contrib.sites.models import Site
10 | from django.contrib.auth.decorators import login_required
11 |
12 | from authorizenet import AUTHNET_POST_URL, AUTHNET_TEST_POST_URL
13 | from authorizenet.forms import SIMPaymentForm, SIMBillingForm, ShippingAddressForm
14 | from authorizenet.models import Response
15 | from authorizenet.views import AIMPayment
16 | from authorizenet.utils import get_fingerprint, capture_transaction
17 |
18 | from samplestore.models import Invoice, Item, Address
19 |
20 |
21 | def items(request):
22 | return render_to_response('samplestore/items.html',
23 | {'items': Item.objects.all()},
24 | context_instance=RequestContext(request))
25 |
26 |
27 | @login_required
28 | def commit_to_buy(request, item_id):
29 | item = get_object_or_404(Item, id=item_id)
30 | if request.POST:
31 | if "yes" in request.POST:
32 | invoice = Invoice.objects.create(
33 | customer=request.user.get_profile(),
34 | item=item)
35 | return HttpResponseRedirect(reverse('samplestore_make_payment',
36 | args=[invoice.id]))
37 | else:
38 | return HttpResponseRedirect(reverse('samplestore_items'))
39 | return render_to_response('samplestore/commit_to_buy.html',
40 | {'item': item},
41 | context_instance=RequestContext(request))
42 |
43 |
44 | @login_required
45 | def make_payment(request, invoice_id):
46 | domain = Site.objects.get_current().domain
47 | invoice = get_object_or_404(Invoice, id=invoice_id)
48 | if invoice.customer.user != request.user:
49 | raise Http404
50 | params = {
51 | 'x_amount': "%.2f" % invoice.item.price,
52 | 'x_fp_sequence': invoice_id,
53 | 'x_invoice_num': invoice_id,
54 | 'x_description': invoice.item.title,
55 | 'x_fp_timestamp': str(int(time.time())),
56 | 'x_relay_url': "http://" + domain + reverse("authnet_sim_payment"),
57 | }
58 |
59 | try:
60 | ba = invoice.customer.address_set.get(type='billing')
61 | billing_params = {'x_first_name': ba.first_name,
62 | 'x_last_name': ba.last_name,
63 | 'x_company': ba.company,
64 | 'x_address': ba.address,
65 | 'x_city': ba.city,
66 | 'x_state': ba.state,
67 | 'x_zip': ba.zip_code,
68 | 'x_country': "United States",
69 | 'x_phone': ba.phone,
70 | 'x_fax': ba.fax,
71 | 'x_email': request.user.email,
72 | 'x_cust_id': invoice.customer.id}
73 | billing_form = SIMBillingForm(initial=billing_params)
74 | except Address.DoesNotExist:
75 | billing_form = None
76 |
77 | params['x_fp_hash'] = get_fingerprint(invoice_id,
78 | params['x_fp_timestamp'],
79 | params['x_amount'])
80 | form = SIMPaymentForm(initial=params)
81 | if settings.DEBUG:
82 | post_url = AUTHNET_TEST_POST_URL
83 | else:
84 | post_url = AUTHNET_POST_URL
85 | return render_to_response('samplestore/make_payment.html',
86 | {'form': form,
87 | 'billing_form': billing_form,
88 | 'post_url': post_url},
89 | context_instance=RequestContext(request))
90 |
91 |
92 | @login_required
93 | def create_invoice(request, item_id, auth_only=False):
94 | item = get_object_or_404(Item, id=item_id)
95 | invoice = Invoice.objects.create(item=item,
96 | customer=request.user.get_profile())
97 | if auth_only:
98 | final_url = reverse('samplestore_make_direct_payment_auth',
99 | args=[invoice.id])
100 | else:
101 | final_url = reverse('samplestore_make_direct_payment',
102 | args=[invoice.id])
103 | return HttpResponseRedirect(final_url)
104 |
105 |
106 | @login_required
107 | def make_direct_payment(request, invoice_id, auth_only=False):
108 | invoice = get_object_or_404(Invoice, id=invoice_id)
109 | if invoice.customer.user != request.user:
110 | raise Http404
111 | try:
112 | ba = invoice.customer.address_set.get(type='billing')
113 | initial_data = {'first_name': ba.first_name,
114 | 'last_name': ba.last_name,
115 | 'company': ba.company,
116 | 'address': ba.address,
117 | 'city': ba.city,
118 | 'state': ba.state,
119 | 'zip': ba.zip_code}
120 | extra_data = {'phone': ba.phone,
121 | 'fax': ba.fax,
122 | 'email': request.user.email,
123 | 'cust_id': invoice.customer.id}
124 | except Address.DoesNotExist:
125 | initial_data = {}
126 | extra_data = {}
127 | if auth_only:
128 | extra_data['type'] = 'AUTH_ONLY'
129 | extra_data['amount'] = "%.2f" % invoice.item.price
130 | extra_data['invoice_num'] = invoice.id
131 | extra_data['description'] = invoice.item.title
132 | pp = AIMPayment(
133 | extra_data=extra_data,
134 | context={'item': invoice.item},
135 | initial_data=initial_data,
136 | shipping_form_class=ShippingAddressForm
137 | )
138 | return pp(request)
139 |
140 |
141 | @login_required
142 | def capture_index(request):
143 | responses = Response.objects.filter(type='auth_only')
144 | if request.user.is_staff:
145 | return render_to_response('samplestore/capture_index.html',
146 | {'responses': responses},
147 | context_instance=RequestContext(request))
148 | raise Http404
149 |
150 |
151 | @login_required
152 | def capture(request, id):
153 | response = get_object_or_404(Response, id=id, type='auth_only')
154 | if Response.objects.filter(trans_id=response.trans_id,
155 | type='prior_auth_capture').count() > 0:
156 | raise Http404
157 | if request.user.is_staff:
158 | new_response = capture_transaction(response)
159 | return render_to_response('samplestore/capture.html',
160 | {'response': response,
161 | 'new_response': new_response},
162 | context_instance=RequestContext(request))
163 | raise Http404
164 |
165 |
166 | from authorizenet.cim import GetHostedProfilePageRequest, CreateProfileRequest, \
167 | get_profile
168 | from authorizenet.forms import HostedCIMProfileForm
169 |
170 | @login_required
171 | def edit_cim_profile(request):
172 | customer = request.user.get_profile()
173 | if not customer.cim_profile_id:
174 | # Create a new empty profile
175 | helper = CreateProfileRequest(request.user.id)
176 | resp = helper.get_response()
177 | if resp.success:
178 | customer.cim_profile_id = helper.profile_id
179 | customer.save()
180 | else:
181 | # since this is a sample app, we'll just raise an exception
182 | raise Exception("Error making Authorize.NET request: %s" % resp.result_text)
183 |
184 | # Get the token for displaying the hosted CIM form
185 | settings = {
186 | # Pass these when integrating the form as a redirect:
187 | #'hostedProfileReturnUrl': 'http://localhost:8000/edit_cim_profile',
188 | #'hostedProfileReturnUrlText': 'Back to the django-authorizenet sample app',
189 |
190 | # Pass 'false' for iframes, and 'true' for redirects
191 | #'hostedProfilePageBorderVisible',
192 |
193 | # Pass this for iframes for automatic resizing
194 | #'hostedProfileIFrameCommunicatorUrl'
195 |
196 | # Optional:
197 | 'hostedProfileHeadingBgColor': '#092E20'
198 | }
199 | helper = GetHostedProfilePageRequest(customer.cim_profile_id, **settings)
200 | resp = helper.get_response()
201 | if not resp.success:
202 | raise Exception("Error making Authorize.NET request: %s" % resp.result_text)
203 |
204 | form = HostedCIMProfileForm(helper.token)
205 |
206 | # Optional - retrieve the current payment profile information for display
207 | response, payment_profiles, shipping_profiles = get_profile(customer.cim_profile_id)
208 |
209 | return render_to_response('samplestore/edit_cim_profile.html',
210 | {'form': form,
211 | 'customer': customer,
212 | 'payment_profiles': payment_profiles,
213 | 'shipping_profiles': shipping_profiles},
214 | context_instance=RequestContext(request))
215 |
216 |
--------------------------------------------------------------------------------
/sample_project/settings.py:
--------------------------------------------------------------------------------
1 | # Django settings for authnetsite project.
2 |
3 | import os.path
4 |
5 | PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
6 |
7 | try:
8 | from local_settings import DEBUG
9 | except ImportError:
10 | DEBUG = True
11 | TEMPLATE_DEBUG = DEBUG
12 |
13 | ADMINS = (
14 | # ('Your name', 'email@example.com'),
15 | )
16 |
17 | AUTH_PROFILE_MODULE = 'samplestore.Customer'
18 |
19 | MANAGERS = ADMINS
20 |
21 | DATABASES = {
22 | 'default': {
23 | 'ENGINE':'django.db.backends.sqlite3', # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
24 | 'NAME':'dev.db', # Or path to database file if using sqlite3.
25 | 'USER':'', # Not used with sqlite3.
26 | 'PASSWORD':'', # Not used with sqlite3.
27 | 'HOST':'', # Set to empty string for localhost. Not used with sqlite3.
28 | 'PORT':'' # Set to empty string for default. Not used with sqlite3.
29 | }
30 | }
31 |
32 | # Local time zone for this installation. Choices can be found here:
33 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
34 | # although not all choices may be available on all operating systems.
35 | # If running in a Windows environment this must be set to the same as your
36 | # system time zone.
37 | TIME_ZONE = 'America/Los_Angeles'
38 |
39 | LOGIN_REDIRECT_URL = '/store/'
40 |
41 | # Language code for this installation. All choices can be found here:
42 | # http://www.i18nguy.com/unicode/language-identifiers.html
43 | LANGUAGE_CODE = 'en-us'
44 |
45 | SITE_ID = 1
46 |
47 | # If you set this to False, Django will make some optimizations so as not
48 | # to load the internationalization machinery.
49 | USE_I18N = False
50 |
51 | # Absolute path to the directory that holds media.
52 | # Example: "/home/media/media.lawrence.com/"
53 | MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media')
54 |
55 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a
56 | # trailing slash if there is a path component (optional in other cases).
57 | # Examples: "http://media.lawrence.com", "http://example.com/media/"
58 | MEDIA_URL = '/media/'
59 |
60 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
61 | # trailing slash.
62 | # Examples: "http://foo.com/media/", "/media/".
63 | STATIC_URL = '/static/'
64 |
65 | # Make this unique, and don't share it with anybody.
66 | SECRET_KEY = '=21(@m#)-$5r(cc110zpy$v4od_45r!k1nz!uq@v$w17&!i8=%'
67 |
68 | # List of callables that know how to import templates from various sources.
69 | TEMPLATE_LOADERS = (
70 | 'django.template.loaders.filesystem.Loader',
71 | 'django.template.loaders.app_directories.Loader',
72 | # 'django.template.loaders.eggs.load_template_source',
73 | )
74 |
75 | TEMPLATE_CONTEXT_PROCESSORS = (
76 | "django.contrib.auth.context_processors.auth",
77 | "django.core.context_processors.debug",
78 | "django.core.context_processors.request",
79 | "django.core.context_processors.media",
80 | "django.core.context_processors.csrf"
81 | )
82 |
83 | MIDDLEWARE_CLASSES = (
84 | 'django.middleware.common.CommonMiddleware',
85 | 'django.contrib.sessions.middleware.SessionMiddleware',
86 | 'django.contrib.messages.middleware.MessageMiddleware',
87 | 'django.middleware.csrf.CsrfViewMiddleware',
88 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
89 | 'django.contrib.redirects.middleware.RedirectFallbackMiddleware',
90 | )
91 |
92 | ROOT_URLCONF = 'urls'
93 |
94 | TEMPLATE_DIRS = (
95 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
96 | # Always use forward slashes, even on Windows.
97 | # Don't forget to use absolute paths, not relative paths.
98 | os.path.join(PROJECT_ROOT, 'templates'),
99 | )
100 |
101 | INSTALLED_APPS = (
102 | 'django.contrib.auth',
103 | 'django.contrib.contenttypes',
104 | 'django.contrib.sessions',
105 | 'django.contrib.sites',
106 | 'django.contrib.admin',
107 | 'django.contrib.redirects',
108 | 'django.contrib.staticfiles',
109 | 'authorizenet',
110 | 'samplestore',
111 | )
112 |
113 | try:
114 | from local_settings import *
115 | except ImportError:
116 | pass
117 |
--------------------------------------------------------------------------------
/sample_project/templates/authorizenet/aim_payment.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {{ item.title }}
5 | {{ item.price }}
6 | {{ errors }}
7 |
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/sample_project/templates/authorizenet/aim_success.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | Response: {{ response.response_reason_text }}
5 | Transaction ID: {{ response.trans_id }}
6 | Description: {{ response.description }}
7 | Amount: {{ response.amount }}
8 | Continue Shopping
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/sample_project/templates/authorizenet/sim_payment.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {{ request.POST }}
5 |
6 | - Response: {{ request.POST.x_response_reason_text }}
7 | - Response code: {{ request.POST.x_response_code }}
8 | - Response reason code: {{ request.POST.x_response_reason_code }}
9 | - Authorization code: {{ request.POST.x_auth_code }}
10 | - Response subcode: {{ request.POST.x_response_subcode }}
11 | - AVS code: {{ request.POST.x_avs_code }}
12 | - Invoice Number: {{ request.POST.x_invoice_num }}
13 | - Transaction ID: {{ request.POST.x_trans_id }}
14 | - Amount: {{ request.POST.x_amount }}
15 | - Description: {{ request.POST.x_description }}
16 |
17 | {% endblock %}
18 |
--------------------------------------------------------------------------------
/sample_project/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% if site_name %}{{ site_name }} : {% endif %}{% block head_title %}{% endblock %}
6 | {% block extra_head_base %}
7 | {% block extra_head %}{% endblock %}
8 | {% endblock %}
9 |
10 |
11 | {% block body_outer %}
12 |
13 |
Sample store
14 |
15 | {% if user.is_authenticated %}
16 | Logged in as {{ user.username }} Logout
17 | {% else %}
18 | Login
19 | {% endif %}
20 |
21 | {% if messages %}
22 |
23 | {% for message in messages %}
24 | - clear {{ message }}
25 | {% endfor %}
26 |
27 | {% endif %}
28 |
29 | {% block body %}
30 | {% endblock %}
31 |
32 |
33 | {% endblock %}
34 |
35 |
36 |
37 |
38 | {% block extra_body_base %}
39 | {% block extra_body %}{% endblock %}
40 | {% endblock %}
41 |
42 |
43 |
--------------------------------------------------------------------------------
/sample_project/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% if form.errors %}
5 | Your username and password didn't match. Please try again.
6 | {% endif %}
7 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/sample_project/templates/samplestore/capture.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% if new_response.is_approved %}
5 | Your transaction {{ response.trans_id }} has been sucessfully captured
6 | {% else %}
7 | There was an error capturing your transaction {{ response.trans_id }}
8 | {% endif %}
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/sample_project/templates/samplestore/capture_index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 |
5 | {% for response in responses %}
6 | - {{ response }}
7 | {% endfor %}
8 |
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/sample_project/templates/samplestore/commit_to_buy.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | Are you sure you want to buy the following item: {{ item.title }} - {{ item.price }}?
5 |
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/sample_project/templates/samplestore/edit_cim_profile.html:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 | Example Page
23 |
24 |
25 |
29 |
30 |
31 |
32 |
36 |
39 |
40 |
41 |
45 |
46 |
47 |
48 |
52 |
71 |
72 |
73 |
74 |
75 | CIM Profile Data
76 |
77 | {% for pp in payment_profiles %}
78 | Payment Profile ID: {{ pp.payment_profile_id }}
79 |
80 | - Billing:
81 |
82 | {% for k, v in pp.billing.iteritems %}
83 | - {{ k }}: {{ v}}
84 | {% endfor %}
85 |
86 |
87 | - Credit Card:
88 |
89 | - Number: {{ pp.credit_card.card_number }}
90 | - Expires: {{ pp.credit_card.expiration_date }}
91 |
92 |
93 |
94 | {% endfor %}
95 |
99 |
102 |
103 |
104 |
108 |
109 |
110 |
111 |
116 |
139 |
140 |
141 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/sample_project/templates/samplestore/items.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% if items %}
5 |
10 | {% endif %}
11 | {% endblock %}
12 |
--------------------------------------------------------------------------------
/sample_project/templates/samplestore/make_payment.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 |
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/sample_project/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url, include, patterns
2 | from django.conf import settings
3 | from django.views.generic.base import RedirectView
4 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns
5 |
6 | # Uncomment the next two lines to enable the admin:
7 | from django.contrib import admin
8 | admin.autodiscover()
9 |
10 | urlpatterns = patterns('',
11 | # Example:
12 | url(r'^$', RedirectView.as_view(url='/store/')),
13 | url(r'^accounts/login/$', 'django.contrib.auth.views.login', name="auth_login"),
14 | url(r'^accounts/logout/$', 'django.contrib.auth.views.logout_then_login', name="auth_logout"),
15 | url(r'^authnet/', include('authorizenet.urls')),
16 | url(r'^store/', include('samplestore.urls')),
17 |
18 | url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
19 | url(r'^admin/', include(admin.site.urls)),
20 |
21 | url(r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
22 | )
23 |
24 | urlpatterns += staticfiles_urlpatterns()
25 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [build_sphinx]
2 | source-dir = docs/
3 | build-dir = docs/_build
4 | all_files = 1
5 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from setuptools import setup, find_packages
4 | import os
5 |
6 | setup(name='django-authorizenet',
7 | version='2.1',
8 | description='Django and Authorize.NET payment gateway integration',
9 | author='Andrii Kurinnyi',
10 | author_email='andrew@zen4ever.com',
11 | url='http://github.com/zen4ever/django-authorizenet/tree/master',
12 | packages=['authorizenet', 'authorizenet.migrations'],
13 | keywords=['django', 'Authorize.NET', 'payment'],
14 | classifiers=[
15 | 'Development Status :: 4 - Beta',
16 | 'Programming Language :: Python',
17 | 'Intended Audience :: Developers',
18 | 'License :: OSI Approved :: MIT License',
19 | 'Operating System :: OS Independent',
20 | 'Framework :: Django',
21 | 'Topic :: Office/Business :: Financial',
22 | ],
23 | long_description=open(
24 | os.path.join(os.path.dirname(__file__), 'README.rst'),
25 | ).read().strip(),
26 | test_suite='runtests.runtests',
27 | tests_require=['httmock'],
28 | install_requires=['requests', 'django>=1.4', 'django-relatives>=0.2.0'])
29 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/tests/__init__.py
--------------------------------------------------------------------------------
/tests/models.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zen4ever/django-authorizenet/0342e79eabffc49a85a40b0937b5e89663c334ca/tests/models.py
--------------------------------------------------------------------------------
/tests/templates/authorizenet/create_payment_profile.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/tests/templates/authorizenet/update_payment_profile.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/tests/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from .cim import *
2 | from .views import *
3 | from .models import *
4 |
--------------------------------------------------------------------------------
/tests/tests/cim.py:
--------------------------------------------------------------------------------
1 | from copy import deepcopy
2 | from datetime import datetime
3 | from django.test import TestCase
4 | from xml.dom.minidom import parseString
5 | from httmock import HTTMock
6 |
7 | from authorizenet.cim import extract_form_data, extract_payment_form_data, \
8 | add_profile
9 |
10 | from .utils import xml_to_dict
11 | from .mocks import cim_url_match, customer_profile_success
12 | from .test_data import create_profile_success
13 |
14 |
15 | class ExtractFormDataTests(TestCase):
16 |
17 | """Tests for utility functions converting form data to CIM data"""
18 |
19 | def test_extract_form_data(self):
20 | new_data = extract_form_data({'word': "1", 'multi_word_str': "2"})
21 | self.assertEqual(new_data, {'word': "1", 'multiWordStr': "2"})
22 |
23 | def test_extract_payment_form_data(self):
24 | data = extract_payment_form_data({
25 | 'card_number': "1111",
26 | 'expiration_date': datetime(2020, 5, 1),
27 | 'card_code': "123",
28 | })
29 | self.assertEqual(data, {
30 | 'cardNumber': "1111",
31 | 'expirationDate': "2020-05",
32 | 'cardCode': "123",
33 | })
34 |
35 |
36 | class AddProfileTests(TestCase):
37 |
38 | """Tests for add_profile utility function"""
39 |
40 | def setUp(self):
41 | self.payment_form_data = {
42 | 'card_number': "5586086832001747",
43 | 'expiration_date': datetime(2020, 5, 1),
44 | 'card_code': "123",
45 | }
46 | self.billing_form_data = {
47 | 'first_name': "Danielle",
48 | 'last_name': "Thompson",
49 | 'company': "",
50 | 'address': "101 Broadway Avenue",
51 | 'city': "San Diego",
52 | 'state': "CA",
53 | 'country': "US",
54 | 'zip': "92101",
55 | }
56 | self.request_data = deepcopy(create_profile_success)
57 | profile = self.request_data['createCustomerProfileRequest']['profile']
58 | del profile['paymentProfiles']['billTo']['phoneNumber']
59 | del profile['paymentProfiles']['billTo']['faxNumber']
60 |
61 | def test_add_profile_minimal(self):
62 | """Success test with minimal complexity"""
63 | @cim_url_match
64 | def request_handler(url, request):
65 | request_xml = parseString(request.body)
66 | self.assertEqual(xml_to_dict(request_xml), self.request_data)
67 | return customer_profile_success.format('createCustomerProfileResponse')
68 | with HTTMock(request_handler):
69 | result = add_profile(42, self.payment_form_data,
70 | self.billing_form_data)
71 | response = result.pop('response')
72 | self.assertEqual(result, {
73 | 'profile_id': '6666',
74 | 'payment_profile_ids': ['7777'],
75 | 'shipping_profile_ids': [],
76 | })
77 | self.assertEqual(response.result, 'Ok')
78 | self.assertEqual(response.result_code, 'I00001')
79 | self.assertEqual(response.result_text, 'Successful.')
80 | self.assertIsNone(response.transaction_response)
81 |
--------------------------------------------------------------------------------
/tests/tests/mocks.py:
--------------------------------------------------------------------------------
1 | from httmock import urlmatch
2 |
3 |
4 | cim_url_match = urlmatch(scheme='https', netloc=r'^api\.authorize\.net$',
5 | path=r'^/xml/v1/request\.api$')
6 |
7 |
8 | delete_success = (
9 | ''
10 | '<{0}>'
11 | ''
12 | 'Ok'
13 | 'I00001
Successful.'
14 | ''
15 | '{0}>'
16 | )
17 |
18 |
19 | customer_profile_success = (
20 | ''
21 | '<{0}>'
22 | ''
23 | 'Ok'
24 | 'I00001
Successful.'
25 | ''
26 | '6666'
27 | ''
28 | '7777'
29 | ''
30 | ''
31 | ''
32 | '{0}>'
33 | )
34 |
35 |
36 | payment_profile_success = (
37 | ''
38 | '<{0}>'
39 | ''
40 | 'Ok'
41 | 'I00001
Successful.'
42 | ''
43 | '6666'
44 | '7777'
45 | '{0}>'
46 | )
47 |
--------------------------------------------------------------------------------
/tests/tests/models.py:
--------------------------------------------------------------------------------
1 | from httmock import HTTMock, with_httmock
2 | from xml.dom.minidom import parseString
3 | from django.test import TestCase
4 | from authorizenet.models import CustomerProfile
5 |
6 | from .utils import create_user, xml_to_dict
7 | from .mocks import cim_url_match, customer_profile_success, delete_success
8 | from .test_data import create_empty_profile_success, delete_profile_success
9 |
10 |
11 | class RequestError(Exception):
12 | pass
13 |
14 |
15 | def error_on_request(url, request):
16 | raise RequestError("CIM Request")
17 |
18 |
19 | class CustomerProfileModelTests(TestCase):
20 |
21 | """Tests for CustomerProfile model"""
22 |
23 | def setUp(self):
24 | self.user = create_user(id=42, username='billy', password='password')
25 |
26 | def create_profile(self):
27 | return CustomerProfile.objects.create(
28 | customer=self.user, profile_id='6666', sync=False)
29 |
30 | def test_create_sync_no_data(self):
31 | @cim_url_match
32 | def request_handler(url, request):
33 | request_xml = parseString(request.body)
34 | self.assertEqual(xml_to_dict(request_xml),
35 | create_empty_profile_success)
36 | return customer_profile_success.format(
37 | 'createCustomerProfileResponse')
38 | profile = CustomerProfile(customer=self.user)
39 | with HTTMock(error_on_request):
40 | self.assertRaises(RequestError, profile.save)
41 | self.assertEqual(profile.profile_id, '')
42 | with HTTMock(request_handler):
43 | profile.save(sync=True)
44 | self.assertEqual(profile.profile_id, '6666')
45 |
46 | @with_httmock(error_on_request)
47 | def test_create_no_sync(self):
48 | profile = CustomerProfile(customer=self.user)
49 | profile.save(sync=False)
50 | self.assertEqual(profile.profile_id, '')
51 |
52 | @with_httmock(error_on_request)
53 | def test_edit(self):
54 | profile = self.create_profile()
55 | self.assertEqual(profile.profile_id, '6666')
56 | profile.profile_id = '7777'
57 | profile.save()
58 | self.assertEqual(profile.profile_id, '7777')
59 | profile.profile_id = '8888'
60 | profile.save(sync=True)
61 | self.assertEqual(profile.profile_id, '8888')
62 | profile.profile_id = '9999'
63 | profile.save(sync=False)
64 | self.assertEqual(profile.profile_id, '9999')
65 |
66 | def test_delete(self):
67 | @cim_url_match
68 | def request_handler(url, request):
69 | request_xml = parseString(request.body)
70 | self.assertEqual(xml_to_dict(request_xml),
71 | delete_profile_success)
72 | return delete_success.format(
73 | 'deleteCustomerProfileResponse')
74 | profile = self.create_profile()
75 | with HTTMock(request_handler):
76 | profile.delete()
77 | self.assertEqual(profile.__class__.objects.count(), 0)
78 |
--------------------------------------------------------------------------------
/tests/tests/test_data.py:
--------------------------------------------------------------------------------
1 | create_empty_profile_success = {
2 | 'createCustomerProfileRequest': {
3 | 'xmlns': 'AnetApi/xml/v1/schema/AnetApiSchema.xsd',
4 | 'profile': {
5 | 'merchantCustomerId': '42',
6 | },
7 | 'merchantAuthentication': {
8 | 'transactionKey': 'key',
9 | 'name': 'loginid',
10 | },
11 | }
12 | }
13 |
14 |
15 | create_profile_success = {
16 | 'createCustomerProfileRequest': {
17 | 'xmlns': 'AnetApi/xml/v1/schema/AnetApiSchema.xsd',
18 | 'profile': {
19 | 'merchantCustomerId': '42',
20 | 'paymentProfiles': {
21 | 'billTo': {
22 | 'firstName': 'Danielle',
23 | 'lastName': 'Thompson',
24 | 'company': '',
25 | 'phoneNumber': '',
26 | 'faxNumber': '',
27 | 'address': '101 Broadway Avenue',
28 | 'city': 'San Diego',
29 | 'state': 'CA',
30 | 'zip': '92101',
31 | 'country': 'US'
32 | },
33 | 'payment': {
34 | 'creditCard': {
35 | 'cardCode': '123',
36 | 'cardNumber': "5586086832001747",
37 | 'expirationDate': '2020-05'
38 | }
39 | }
40 | }
41 | },
42 | 'merchantAuthentication': {
43 | 'transactionKey': 'key',
44 | 'name': 'loginid',
45 | },
46 | }
47 | }
48 |
49 |
50 | update_profile_success = {
51 | 'updateCustomerPaymentProfileRequest': {
52 | 'xmlns': 'AnetApi/xml/v1/schema/AnetApiSchema.xsd',
53 | 'customerProfileId': '6666',
54 | 'paymentProfile': {
55 | 'customerPaymentProfileId': '7777',
56 | 'billTo': {
57 | 'firstName': 'Danielle',
58 | 'lastName': 'Thompson',
59 | 'company': '',
60 | 'phoneNumber': '',
61 | 'faxNumber': '',
62 | 'address': '101 Broadway Avenue',
63 | 'city': 'San Diego',
64 | 'state': 'CA',
65 | 'zip': '92101',
66 | 'country': 'US'
67 | },
68 | 'payment': {
69 | 'creditCard': {
70 | 'cardCode': '123',
71 | 'cardNumber': "5586086832001747",
72 | 'expirationDate': '2020-05'
73 | }
74 | }
75 | },
76 | 'merchantAuthentication': {
77 | 'transactionKey': 'key',
78 | 'name': 'loginid',
79 | },
80 | }
81 | }
82 |
83 |
84 | create_payment_profile_success = {
85 | 'createCustomerPaymentProfileRequest': {
86 | 'xmlns': 'AnetApi/xml/v1/schema/AnetApiSchema.xsd',
87 | 'customerProfileId': '6666',
88 | 'paymentProfile': {
89 | 'billTo': {
90 | 'firstName': 'Danielle',
91 | 'lastName': 'Thompson',
92 | 'phoneNumber': '',
93 | 'faxNumber': '',
94 | 'company': '',
95 | 'address': '101 Broadway Avenue',
96 | 'city': 'San Diego',
97 | 'state': 'CA',
98 | 'zip': '92101',
99 | 'country': 'US'
100 | },
101 | 'payment': {
102 | 'creditCard': {
103 | 'cardCode': '123',
104 | 'cardNumber': "5586086832001747",
105 | 'expirationDate': '2020-05'
106 | }
107 | }
108 | },
109 | 'merchantAuthentication': {
110 | 'transactionKey': 'key',
111 | 'name': 'loginid',
112 | },
113 | }
114 | }
115 |
116 |
117 | delete_profile_success = {
118 | 'deleteCustomerProfileRequest': {
119 | 'xmlns': u'AnetApi/xml/v1/schema/AnetApiSchema.xsd',
120 | 'customerProfileId': '6666',
121 | 'merchantAuthentication': {
122 | 'transactionKey': 'key',
123 | 'name': 'loginid'
124 | },
125 | },
126 | }
127 |
--------------------------------------------------------------------------------
/tests/tests/utils.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 |
3 |
4 | def create_user(id=None, username='', password=''):
5 | user = User(username=username)
6 | user.id = id
7 | user.set_password(password)
8 | user.save()
9 | return user
10 |
11 |
12 | def xml_to_dict(node):
13 | """Recursively convert minidom XML node to dictionary"""
14 | node_data = {}
15 | if node.nodeType == node.TEXT_NODE:
16 | node_data = node.data
17 | elif node.nodeType not in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE):
18 | node_data.update(node.attributes.items())
19 | if node.nodeType not in (node.TEXT_NODE, node.DOCUMENT_TYPE_NODE):
20 | for child in node.childNodes:
21 | child_name, child_data = xml_to_dict(child)
22 | if not child_data:
23 | child_data = ''
24 | if child_name not in node_data:
25 | node_data[child_name] = child_data
26 | else:
27 | if not isinstance(node_data[child_name], list):
28 | node_data[child_name] = [node_data[child_name]]
29 | node_data[child_name].append(child_data)
30 | if node_data.keys() == ['#text']:
31 | node_data = node_data['#text']
32 | if node.nodeType == node.DOCUMENT_NODE:
33 | return node_data
34 | else:
35 | return node.nodeName, node_data
36 |
--------------------------------------------------------------------------------
/tests/tests/views.py:
--------------------------------------------------------------------------------
1 | from datetime import date
2 | from django.test import LiveServerTestCase
3 | from xml.dom.minidom import parseString
4 | from httmock import HTTMock
5 |
6 | from authorizenet.models import CustomerProfile, CustomerPaymentProfile
7 |
8 | from .utils import create_user, xml_to_dict
9 | from .mocks import cim_url_match, customer_profile_success, \
10 | payment_profile_success
11 | from .test_data import create_profile_success, update_profile_success, \
12 | create_payment_profile_success
13 |
14 |
15 | class PaymentProfileCreationTests(LiveServerTestCase):
16 |
17 | def setUp(self):
18 | self.user = create_user(id=42, username='billy', password='password')
19 | self.client.login(username='billy', password='password')
20 |
21 | def test_create_new_customer_get(self):
22 | response = self.client.get('/customers/create')
23 | self.assertNotIn("This field is required", response.content)
24 | self.assertIn("Credit Card Number", response.content)
25 | self.assertIn("City", response.content)
26 |
27 | def test_create_new_customer_post_error(self):
28 | response = self.client.post('/customers/create')
29 | self.assertIn("This field is required", response.content)
30 | self.assertIn("Credit Card Number", response.content)
31 | self.assertIn("City", response.content)
32 |
33 | def test_create_new_customer_post_success(self):
34 | @cim_url_match
35 | def create_customer_success(url, request):
36 | request_xml = parseString(request.body)
37 | self.assertEqual(xml_to_dict(request_xml), create_profile_success)
38 | return customer_profile_success.format('createCustomerProfileResponse')
39 | with HTTMock(create_customer_success):
40 | response = self.client.post('/customers/create', {
41 | 'card_number': "5586086832001747",
42 | 'expiration_date_0': "5",
43 | 'expiration_date_1': "2020",
44 | 'card_code': "123",
45 | 'first_name': "Danielle",
46 | 'last_name': "Thompson",
47 | 'address': "101 Broadway Avenue",
48 | 'city': "San Diego",
49 | 'state': "CA",
50 | 'country': "US",
51 | 'zip': "92101",
52 | }, follow=True)
53 | self.assertIn("success", response.content)
54 |
55 | def test_create_new_payment_profile_post_success(self):
56 | @cim_url_match
57 | def request_handler(url, request):
58 | request_xml = parseString(request.body)
59 | self.assertEqual(xml_to_dict(request_xml),
60 | create_payment_profile_success)
61 | return payment_profile_success.format('createCustomerPaymentProfileResponse')
62 | CustomerProfile.objects.create(customer=self.user, profile_id='6666', sync=False)
63 | with HTTMock(request_handler):
64 | response = self.client.post('/customers/create', {
65 | 'card_number': "5586086832001747",
66 | 'expiration_date_0': "5",
67 | 'expiration_date_1': "2020",
68 | 'card_code': "123",
69 | 'first_name': "Danielle",
70 | 'last_name': "Thompson",
71 | 'address': "101 Broadway Avenue",
72 | 'city': "San Diego",
73 | 'state': "CA",
74 | 'country': "US",
75 | 'zip': "92101",
76 | }, follow=True)
77 | self.assertIn("success", response.content)
78 |
79 |
80 | class PaymentProfileUpdateTests(LiveServerTestCase):
81 |
82 | def setUp(self):
83 | self.user = create_user(id=42, username='billy', password='password')
84 | profile = CustomerProfile(customer=self.user, profile_id='6666')
85 | profile.save(sync=False)
86 | self.payment_profile = CustomerPaymentProfile(
87 | customer=self.user,
88 | customer_profile=profile,
89 | payment_profile_id='7777',
90 | )
91 | self.payment_profile.save(sync=False)
92 | self.client.login(username='billy', password='password')
93 |
94 | def test_update_profile_get(self):
95 | response = self.client.get('/customers/update')
96 | self.assertNotIn("This field is required", response.content)
97 | self.assertIn("Credit Card Number", response.content)
98 | self.assertIn("City", response.content)
99 |
100 | def test_update_profile_post_error(self):
101 | response = self.client.post('/customers/update')
102 | self.assertIn("This field is required", response.content)
103 | self.assertIn("Credit Card Number", response.content)
104 | self.assertIn("City", response.content)
105 |
106 | def test_update_profile_post_success(self):
107 | @cim_url_match
108 | def create_customer_success(url, request):
109 | request_xml = parseString(request.body)
110 | self.assertEqual(xml_to_dict(request_xml),
111 | update_profile_success)
112 | return customer_profile_success.format('updateCustomerProfileResponse')
113 | with HTTMock(create_customer_success):
114 | response = self.client.post('/customers/update', {
115 | 'card_number': "5586086832001747",
116 | 'expiration_date_0': "5",
117 | 'expiration_date_1': "2020",
118 | 'card_code': "123",
119 | 'first_name': "Danielle",
120 | 'last_name': "Thompson",
121 | 'address': "101 Broadway Avenue",
122 | 'city': "San Diego",
123 | 'state': "CA",
124 | 'country': "US",
125 | 'zip': "92101",
126 | }, follow=True)
127 | self.assertIn("success", response.content)
128 | payment_profile = self.user.customer_profile.payment_profiles.get()
129 | self.assertEqual(payment_profile.raw_data, {
130 | 'id': payment_profile.id,
131 | 'customer_profile': self.user.customer_profile.id,
132 | 'customer': self.user.id,
133 | 'payment_profile_id': '7777',
134 | 'card_number': 'XXXX1747',
135 | 'expiration_date': date(2020, 5, 31),
136 | 'card_code': None,
137 | 'first_name': 'Danielle',
138 | 'last_name': 'Thompson',
139 | 'company': '',
140 | 'fax_number': '',
141 | 'phone_number': '',
142 | 'address': '101 Broadway Avenue',
143 | 'city': 'San Diego',
144 | 'state': 'CA',
145 | 'country': 'US',
146 | 'zip': '92101',
147 | })
148 |
149 |
--------------------------------------------------------------------------------
/tests/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url, patterns
2 | from .views import CreateCustomerView, UpdateCustomerView, success_view
3 |
4 | urlpatterns = patterns(
5 | '',
6 | url(r"^customers/create$", CreateCustomerView.as_view()),
7 | url(r"^customers/update$", UpdateCustomerView.as_view()),
8 | url(r"^success$", success_view),
9 | )
10 |
--------------------------------------------------------------------------------
/tests/views.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponse
2 | from authorizenet.views import PaymentProfileCreateView, PaymentProfileUpdateView
3 |
4 |
5 | class CreateCustomerView(PaymentProfileCreateView):
6 | def get_success_url(self):
7 | return '/success'
8 |
9 |
10 | class UpdateCustomerView(PaymentProfileUpdateView):
11 |
12 | def get_object(self):
13 | return self.request.user.customer_profile.payment_profiles.get()
14 |
15 | def get_success_url(self):
16 | return '/success'
17 |
18 |
19 | def success_view(request):
20 | return HttpResponse("success")
21 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist =
3 | py26-1.4, py26-1.5, py26-1.6,
4 | py27-1.4, py27-1.5, py26-1.6, py27-trunk,
5 | docs
6 |
7 |
8 | [testenv]
9 | commands = coverage run -a setup.py test
10 |
11 |
12 | [testenv:docs]
13 | changedir = docs
14 | deps =
15 | Sphinx
16 | commands =
17 | sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
18 |
19 |
20 | [testenv:py26-1.4]
21 | basepython = python2.6
22 | deps =
23 | django == 1.4.2
24 | coverage == 3.6
25 |
26 | [testenv:py26-1.5]
27 | basepython = python2.6
28 | deps =
29 | django == 1.5
30 | coverage == 3.6
31 |
32 | [testenv:py26-1.6]
33 | basepython = python2.6
34 | deps =
35 | https://github.com/django/django/tarball/stable/1.6.x
36 | coverage == 3.6
37 |
38 |
39 | [testenv:py27-1.4]
40 | basepython = python2.7
41 | deps =
42 | django == 1.4.2
43 | coverage == 3.6
44 |
45 | [testenv:py27-1.5]
46 | basepython = python2.7
47 | deps =
48 | django == 1.5
49 | coverage == 3.6
50 |
51 | [testenv:py27-1.6]
52 | basepython = python2.7
53 | deps =
54 | https://github.com/django/django/tarball/stable/1.6.x
55 | coverage == 3.6
56 |
57 | [testenv:py27-trunk]
58 | basepython = python2.7
59 | deps =
60 | https://github.com/django/django/tarball/master
61 | coverage == 3.6
62 |
--------------------------------------------------------------------------------