/satchmo/apps/payment/models.py
Python | 124 lines | 104 code | 7 blank | 13 comment | 3 complexity | 14167bc5030c7a2b49a273869c9488ff MD5 | raw file
1"""
2Stores details about the available payment options.
3Also stores credit card info in an encrypted format.
4"""
5
6from Crypto.Cipher import Blowfish
7from datetime import datetime
8from decimal import Decimal
9from django.conf import settings
10from django.db import models
11from django.utils.translation import ugettext_lazy as _
12from livesettings import config_value, config_choice_values, SettingNotSet
13from payment.fields import PaymentChoiceCharField, CreditChoiceCharField
14from satchmo_store.contact.models import Contact
15import base64
16import config
17import keyedcache
18import logging
19import satchmo_utils.sslurllib
20
21log = logging.getLogger('payment.models')
22
23class PaymentOption(models.Model):
24 """
25 If there are multiple options - CC, Cash, COD, etc this class allows
26 configuration.
27 """
28 description = models.CharField(_("Description"), max_length=20)
29 active = models.BooleanField(_("Active"),
30 help_text=_("Should this be displayed as an option for the user?"))
31 optionName = PaymentChoiceCharField(_("Option Name"), max_length=20,
32 unique=True,
33 help_text=_("The class name as defined in payment.py"))
34 sortOrder = models.IntegerField(_("Sort Order"))
35
36 class Meta:
37 verbose_name = _("Payment Option")
38 verbose_name_plural = _("Payment Options")
39
40class CreditCardDetail(models.Model):
41 """
42 Stores an encrypted CC number, its information, and its
43 displayable number.
44 """
45 orderpayment = models.ForeignKey('shop.OrderPayment', unique=True,
46 related_name="creditcards")
47 credit_type = CreditChoiceCharField(_("Credit Card Type"), max_length=16)
48 display_cc = models.CharField(_("CC Number (Last 4 digits)"),
49 max_length=4, )
50 encrypted_cc = models.CharField(_("Encrypted Credit Card"),
51 max_length=40, blank=True, null=True, editable=False)
52 expire_month = models.IntegerField(_("Expiration Month"))
53 expire_year = models.IntegerField(_("Expiration Year"))
54 card_holder = models.CharField(_("card_holder Name"), max_length=60, blank=True)
55 start_month = models.IntegerField(_("Start Month"), blank=True, null=True)
56 start_year = models.IntegerField(_("Start Year"), blank=True, null=True)
57 issue_num = models.CharField(blank=True, null=True, max_length=2)
58
59 def storeCC(self, ccnum):
60 """Take as input a valid cc, encrypt it and store the last 4 digits in a visible form"""
61 self.display_cc = ccnum[-4:]
62 encrypted_cc = _encrypt_code(ccnum)
63 if config_value('PAYMENT', 'STORE_CREDIT_NUMBERS'):
64 self.encrypted_cc = encrypted_cc
65 else:
66 standin = "%s%i%i%i" % (self.display_cc, self.expire_month, self.expire_year, self.orderpayment.id)
67 self.encrypted_cc = _encrypt_code(standin)
68 key = _encrypt_code(standin + '-card')
69 keyedcache.cache_set(key, skiplog=True, length=60*60, value=encrypted_cc)
70
71 def setCCV(self, ccv):
72 """Put the CCV in the cache, don't save it for security/legal reasons."""
73 if not self.encrypted_cc:
74 raise ValueError('CreditCardDetail expecting a credit card number to be stored before storing CCV')
75
76 keyedcache.cache_set(self.encrypted_cc, skiplog=True, length=60*60, value=ccv)
77
78 def getCCV(self):
79 try:
80 ccv = keyedcache.cache_get(self.encrypted_cc)
81 except keyedcache.NotCachedError:
82 ccv = ""
83
84 return ccv
85
86 ccv = property(fget=getCCV, fset=setCCV)
87
88 def _decryptCC(self):
89 ccnum = _decrypt_code(self.encrypted_cc)
90 if not config_value('PAYMENT', 'STORE_CREDIT_NUMBERS'):
91 try:
92 key = _encrypt_code(ccnum + '-card')
93 encrypted_ccnum = keyedcache.cache_get(key)
94 ccnum = _decrypt_code(encrypted_ccnum)
95 except keyedcache.NotCachedError:
96 ccnum = ""
97 return ccnum
98
99 decryptedCC = property(_decryptCC)
100
101 def _expireDate(self):
102 return(str(self.expire_month) + "/" + str(self.expire_year))
103 expirationDate = property(_expireDate)
104
105 class Meta:
106 verbose_name = _("Credit Card")
107 verbose_name_plural = _("Credit Cards")
108
109def _decrypt_code(code):
110 """Decrypt code encrypted by _encrypt_code"""
111 secret_key = settings.SECRET_KEY
112 encryption_object = Blowfish.new(secret_key)
113 # strip padding from decrypted credit card number
114 return encryption_object.decrypt(base64.b64decode(code)).rstrip('X')
115
116def _encrypt_code(code):
117 """Quick encrypter for CC codes or code fragments"""
118 secret_key = settings.SECRET_KEY
119 encryption_object = Blowfish.new(secret_key)
120 # block cipher length must be a multiple of 8
121 padding = ''
122 if (len(code) % 8) <> 0:
123 padding = 'X' * (8 - (len(code) % 8))
124 return base64.b64encode(encryption_object.encrypt(code + padding))