PageRenderTime 42ms CodeModel.GetById 25ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/satchmo/apps/payment/models.py

https://github.com/ringemup/satchmo
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))