Skip to content

Commit

Permalink
Merge dc70127 into 60bdc6a
Browse files Browse the repository at this point in the history
  • Loading branch information
treyhunner committed Jun 27, 2013
2 parents 60bdc6a + dc70127 commit 320484e
Show file tree
Hide file tree
Showing 34 changed files with 1,185 additions and 187 deletions.
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[run]
include = authorizenet/*
omit = authorizenet/tests/*
omit = tests/*
branch = 1
38 changes: 37 additions & 1 deletion authorizenet/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from django.contrib import admin
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from authorizenet.models import Response, CIMResponse
from authorizenet.models import (Response, CIMResponse, CustomerProfile,
CustomerPaymentProfile)
from authorizenet.forms import CustomerPaymentForm, CustomerPaymentAdminForm
from relatives.utils import object_edit_link


class ResponseAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -78,3 +81,36 @@ def response_link(self, obj):
response_link.short_description = 'transaction response'

admin.site.register(CIMResponse, CIMResponseAdmin)


class CustomerPaymentProfileInline(admin.TabularInline):
model = CustomerPaymentProfile
form = CustomerPaymentForm
fields = [object_edit_link("Edit"), 'first_name', 'last_name',
'card_number', 'expiration_date']
readonly_fields = fields
extra = 0
max_num = 0
can_delete = False


class CustomerProfileAdmin(admin.ModelAdmin):
list_display = ['profile_id', 'customer']
readonly_fields = ['profile_id', 'customer']
inlines = [CustomerPaymentProfileInline]

def get_readonly_fields(self, request, obj=None):
return self.readonly_fields if obj is not None else ['profile_id']

admin.site.register(CustomerProfile, CustomerProfileAdmin)


class CustomerPaymentProfileAdmin(admin.ModelAdmin):
list_display = ['payment_profile_id', 'customer_profile', 'customer']
readonly_fields = ['payment_profile_id', 'customer', 'customer_profile']
form = CustomerPaymentAdminForm

def get_readonly_fields(self, request, obj=None):
return self.readonly_fields if obj is not None else []

admin.site.register(CustomerPaymentProfile, CustomerPaymentProfileAdmin)
33 changes: 24 additions & 9 deletions authorizenet/cim.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
import xml.dom.minidom

from django.utils.datastructures import SortedDict
from django.conf import settings
from authorizenet.conf import settings
import requests

from authorizenet import AUTHNET_CIM_URL, AUTHNET_TEST_CIM_URL
from authorizenet.models import CIMResponse, Response
from authorizenet.signals import customer_was_created, customer_was_flagged, \
payment_was_successful, payment_was_flagged

Expand Down Expand Up @@ -48,8 +47,9 @@ def extract_form_data(data):

def extract_payment_form_data(data):
payment_data = extract_form_data(data)
payment_data['expirationDate'] = \
payment_data['expirationDate'].strftime('%Y-%m')
if payment_data.get('expirationDate') is not None:
payment_data['expirationDate'] = \
payment_data['expirationDate'].strftime('%Y-%m')
return payment_data


Expand Down Expand Up @@ -113,6 +113,18 @@ def add_profile(customer_id, payment_form_data, billing_form_data,
'shipping_profile_ids': shipping_profile_ids}


def delete_profile(profile_id):
"""
Delete a customer profile and return the CIMResponse.
Arguments:
profile_id -- unique gateway-assigned profile identifier
"""
helper = DeleteProfileRequest(profile_id)
response = helper.get_response()
return response


def update_payment_profile(profile_id,
payment_profile_id,
payment_form_data,
Expand Down Expand Up @@ -265,7 +277,7 @@ class BaseRequest(object):

def __init__(self, action):
self.create_base_document(action)
if settings.AUTHNET_DEBUG:
if settings.DEBUG:
self.endpoint = AUTHNET_TEST_CIM_URL
else:
self.endpoint = AUTHNET_CIM_URL
Expand All @@ -285,9 +297,9 @@ def create_base_document(self, action):

self.document = doc
authentication = doc.createElement("merchantAuthentication")
name = self.get_text_node("name", settings.AUTHNET_LOGIN_ID)
name = self.get_text_node("name", settings.LOGIN_ID)
key = self.get_text_node("transactionKey",
settings.AUTHNET_TRANSACTION_KEY)
settings.TRANSACTION_KEY)
authentication.appendChild(name)
authentication.appendChild(key)
root.appendChild(authentication)
Expand All @@ -303,7 +315,8 @@ def get_response(self):
self.endpoint,
data=self.document.toxml().encode('utf-8'),
headers={'Content-Type': 'text/xml'})
response_xml = xml.dom.minidom.parseString(response.text)
text = response.text.encode('utf-8')
response_xml = xml.dom.minidom.parseString(text)
self.process_response(response_xml)
return self.create_response_object()

Expand All @@ -317,6 +330,7 @@ def get_text_node(self, node_name, text):
return node

def create_response_object(self):
from authorizenet.models import CIMResponse
return CIMResponse.objects.create(result=self.result,
result_code=self.resultCode,
result_text=self.resultText)
Expand Down Expand Up @@ -694,7 +708,7 @@ def __init__(self,
if delimiter:
self.delimiter = delimiter
else:
self.delimiter = getattr(settings, 'AUTHNET_DELIM_CHAR', "|")
self.delimiter = settings.DELIM_CHAR
self.add_transaction_node()
self.add_extra_options()
if order_info:
Expand Down Expand Up @@ -755,6 +769,7 @@ def add_extra_options(self):
self.root.appendChild(extra_options_node)

def create_response_object(self):
from authorizenet.models import CIMResponse, Response
try:
response = Response.objects.create_from_list(
self.transaction_result)
Expand Down
39 changes: 39 additions & 0 deletions authorizenet/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Application-specific settings for django-authorizenet"""

from django.conf import settings as django_settings


class Settings(object):

"""
Retrieves django.conf settings, using defaults from Default subclass
All usable settings are specified in settings attribute. Use an
``AUTHNET_`` prefix when specifying settings in django.conf.
"""

prefix = 'AUTHNET_'
settings = set(('DEBUG', 'LOGIN_ID', 'TRANSACTION_KEY', 'CUSTOMER_MODEL',
'DELIM_CHAR', 'FORCE_TEST_REQUEST', 'EMAIL_CUSTOMER',
'MD5_HASH'))

class Default:
CUSTOMER_MODEL = getattr(
django_settings, 'AUTH_USER_MODEL', "auth.User")
DELIM_CHAR = "|"
FORCE_TEST_REQUEST = False
EMAIL_CUSTOMER = None
MD5_HASH = ""

def __init__(self):
self.defaults = Settings.Default()

def __getattr__(self, name):
if name not in self.settings:
raise AttributeError("Setting %s not understood" % name)
try:
return getattr(django_settings, self.prefix + name)
except AttributeError:
return getattr(self.defaults, name)

settings = Settings()
2 changes: 2 additions & 0 deletions authorizenet/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class BillingError(Exception):
"""Error due to Authorize.NET request"""
6 changes: 3 additions & 3 deletions authorizenet/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from calendar import monthrange

from django import forms
from django.conf import settings
from django.utils.translation import ugettext as _

from authorizenet.conf import settings
from authorizenet.creditcard import verify_credit_card


Expand All @@ -23,8 +23,7 @@ def clean(self, value):
Raises a ValidationError if the card is not valid
and stashes card type.
"""
self.card_type = verify_credit_card(value,
allow_test=settings.AUTHNET_DEBUG)
self.card_type = verify_credit_card(value, allow_test=settings.DEBUG)
if self.card_type is None:
raise forms.ValidationError("Invalid credit card number.")
return value
Expand Down Expand Up @@ -98,6 +97,7 @@ def compress(self, data_list):

class CreditCardCVV2Field(forms.CharField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('min_length', 3)
kwargs.setdefault('max_length', 4)
super(CreditCardCVV2Field, self).__init__(*args, **kwargs)

Expand Down
41 changes: 38 additions & 3 deletions authorizenet/forms.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from django import forms
from django.conf import settings
from authorizenet.conf import settings
from authorizenet.fields import CreditCardField, CreditCardExpiryField, \
CreditCardCVV2Field, CountryField
from authorizenet.models import CustomerPaymentProfile


class SIMPaymentForm(forms.Form):
x_login = forms.CharField(max_length=20,
required=True,
widget=forms.HiddenInput,
initial=settings.AUTHNET_LOGIN_ID)
initial=settings.LOGIN_ID)
x_type = forms.CharField(max_length=20,
widget=forms.HiddenInput,
initial="AUTH_CAPTURE")
Expand Down Expand Up @@ -91,12 +92,46 @@ class CIMPaymentForm(forms.Form):
card_code = CreditCardCVV2Field(label="Card Security Code")


class CustomerPaymentForm(forms.ModelForm):

"""Base customer payment form without shipping address"""

country = CountryField(label="Country", initial="US")
card_number = CreditCardField(label="Credit Card Number")
expiration_date = CreditCardExpiryField(label="Expiration Date")
card_code = CreditCardCVV2Field(label="Card Security Code")

def __init__(self, *args, **kwargs):
self.customer = kwargs.pop('customer', None)
return super(CustomerPaymentForm, self).__init__(*args, **kwargs)

def save(self, commit=True):
instance = super(CustomerPaymentForm, self).save(commit=False)
if self.customer:
instance.customer = self.customer
instance.card_code = self.cleaned_data.get('card_code')
if commit:
instance.save()
return instance

class Meta:
model = CustomerPaymentProfile
fields = ('first_name', 'last_name', 'company', 'address', 'city',
'state', 'country', 'zip', 'card_number',
'expiration_date', 'card_code')


class CustomerPaymentAdminForm(CustomerPaymentForm):
class Meta(CustomerPaymentForm.Meta):
fields = ('customer',) + CustomerPaymentForm.Meta.fields


class HostedCIMProfileForm(forms.Form):
token = forms.CharField(widget=forms.HiddenInput)
def __init__(self, token, *args, **kwargs):
super(HostedCIMProfileForm, self).__init__(*args, **kwargs)
self.fields['token'].initial = token
if settings.AUTHNET_DEBUG:
if settings.DEBUG:
self.action = "https://test.authorize.net/profile/manage"
else:
self.action = "https://secure.authorize.net/profile/manage"
Expand Down
4 changes: 2 additions & 2 deletions authorizenet/helpers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import re

from django.conf import settings
import requests

from authorizenet.conf import settings
from authorizenet import AUTHNET_POST_URL, AUTHNET_TEST_POST_URL


class AIMPaymentHelper(object):
def __init__(self, defaults):
self.defaults = defaults
if settings.AUTHNET_DEBUG:
if settings.DEBUG:
self.endpoint = AUTHNET_TEST_POST_URL
else:
self.endpoint = AUTHNET_POST_URL
Expand Down
34 changes: 34 additions & 0 deletions authorizenet/managers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from django.db import models


class CustomerProfileManager(models.Manager):

def create(self, **data):

"""Create new Authorize.NET customer profile"""

from .models import CustomerPaymentProfile

kwargs = data
sync = kwargs.pop('sync', True)
kwargs = {
'customer': kwargs.get('customer', None),
'profile_id': kwargs.pop('profile_id', None),
}

# Create customer profile
obj = self.model(**kwargs)
self._for_write = True
obj.save(force_insert=True, using=self.db, sync=sync, data=data)

if sync:
# Store customer payment profile data locally
for payment_profile_id in obj.payment_profile_ids:
CustomerPaymentProfile.objects.create(
customer_profile=obj,
payment_profile_id=payment_profile_id,
sync=False,
**data
)

return obj
Loading

0 comments on commit 320484e

Please sign in to comment.