I'm developing a small webshop that should have some payment options. My platform is Google App Engine and python 2.7. There is a project called satchmo that is django / python and I wonder if I can use its code? Here is the Open Source for some of its models for payments:
class PaymentOption(models.Model):
"""
If there are multiple options - CC, Cash, COD, etc this class allows
configuration.
"""
description = models.CharField(_("Description"), max_length=20)
active = models.BooleanField(_("Active"),
help_text=_("Should this be displayed as an option for the user?"))
optionName = models.CharField(_("Option Name"), max_length=20, choices=iterchoices_db(payment.config.labelled_gateway_choices),
unique=True,
help_text=_("The class name as defined in payment.py"))
sortOrder = models.IntegerField(_("Sort Order"))
class Meta:
verbose_name = _("Payment Option")
verbose_name_plural = _("Payment Options")
class CreditCardDetail(models.Model):
"""
Stores an encrypted CC number, its information, and its
displayable number.
"""
orderpayment = models.ForeignKey('shop.OrderPayment', unique=True,
related_name="creditcards")
credit_type = models.CharField(_("Credit Card Type"), max_length=16, choices=iterchoices_db(payment.config.credit_choices))
display_cc = models.CharField(_("CC Number (Last 4 digits)"),
max_length=4, )
encrypted_cc = models.CharField(_("Encrypted Credit Card"),
max_length=40, blank=True, null=True, editable=False)
expire_month = models.IntegerField(_("Expiration Month"))
expire_year = models.IntegerField(_("Expiration Year"))
card_holder = models.CharField(_("card_holder Name"), max_length=60, blank=True)
start_month = models.IntegerField(_("Start Month"), blank=True, null=True)
start_year = models.IntegerField(_("Start Year"), blank=True, null=True)
issue_num = models.CharField(blank=True, null=True, max_length=2)
def storeCC(self, ccnum):
"""Take as input a valid cc, encrypt it and store the last 4 digits in a visible form"""
self.display_cc = ccnum[-4:]
encrypted_cc = _encrypt_code(ccnum)
if config_value('PAYMENT', 'STORE_CREDIT_NUMBERS'):
self.encrypted_cc = encrypted_cc
else:
standin = "%s%i%i%i" % (self.display_cc, self.expire_month, self.expire_year, self.orderpayment.id)
self.encrypted_cc = _encrypt_code(standin)
key = _encrypt_code(standin + '-card')
keyedcache.cache_set(key, skiplog=True, length=60*60, value=encrypted_cc)
def setCCV(self, ccv):
"""Put the CCV in the cache, don't save it for security/legal reasons."""
if not self.encrypted_cc:
raise ValueError('CreditCardDetail expecting a credit card number to be stored before storing CCV')
keyedcache.cache_set(self.encrypted_cc, skiplog=True, length=60*60, value=ccv)
def getCCV(self):
try:
ccv = keyedcache.cache_get(self.encrypted_cc)
except keyedcache.NotCachedError:
ccv = ""
return ccv
ccv = property(fget=getCCV, fset=setCCV)
def _decryptCC(self):
ccnum = _decrypt_code(self.encrypted_cc)
if not config_value('PAYMENT', 'STORE_CREDIT_NUMBERS'):
try:
key = _encrypt_code(ccnum + '-card')
encrypted_ccnum = keyedcache.cache_get(key)
ccnum = _decrypt_code(encrypted_ccnum)
except keyedcache.NotCachedError:
ccnum = ""
return ccnum
decryptedCC = property(_decryptCC)
def _expireDate(self):
return(str(self.expire_month) + "/" + str(self.expire_year))
expirationDate = property(_expireDate)
class Meta:
verbose_name = _("Credit Card")
verbose_name_plural = _("Credit Cards")
def _decrypt_code(code):
"""Decrypt code encrypted by _encrypt_code"""
# In some blowfish implementations, > 56 char keys can cause problems
secret_key = settings.SECRET_KEY[:56]
encryption_object = Blowfish.new(secret_key)
# strip padding from decrypted credit card number
return encryption_object.decrypt(base64.b64decode(code)).rstrip('X')
def _encrypt_code(code):
"""Quick encrypter for CC codes or code fragments"""
# In some blowfish implementations, > 56 char keys can cause problems
secret_key = settings.SECRET_KEY[:56]
encryption_object = Blowfish.new(secret_key)
# block cipher length must be a multiple of 8
padding = ''
if (len(code) % 8) <> 0:
padding = 'X' * (8 - (len(code) % 8))
return base64.b64encode(encryption_object.encrypt(code + padding))
The code looks portable to app engine and if I want could I or even should port this code, thinking that the satchmo project already solved many of the problems I will face when implementing my webshop? Or should I just get more "django-friendly hosting" as was suggested in a similar question about actually running satchmo on app engine?
Doing some research, looks like many people have tried this and gave up. There are several problems with running satchmo on app engine - most are related to models and dependencies, but there is also the question of PCI Compliance.
I'll address the PCI aspect first - In order to be able to store credit card data without fear of your merchant account provider pulling the plug on your account, you need to comply with PCI DSS. It's a document written by lawyers (ha!) to set the standards for security if you intend to interact with credit card data. It's mostly good best practices, such as never storing the CVV2 (3 digit code on the back of the card), and so on. However, one big aspect has to do with security of the storage used. Google doesn't advertise this, but I'm fairly confident that App Engine and their proprietary DB are not PCI compliant. This means anything you build on top will never be PCI compliant.
Now the tech aspect. From what I have read, your best bet to getting this working is django_nonrel. You will have to modify some models to not depend on foreign keys. There are also some other dependencies called out, such as reportlab. Here's the post talking about that: http://groups.google.com/group/satchmo-developers/browse_thread/thread/f14053df13232615
And last but not least, here's the older discussion on stackoverflow: How to make Satchmo work in Google App Engine
The general consensus is that satchmo is not a good fit for GAE. There don't seem to be good off the shelf solutions, but I'd encourage you to take a look at Paypal, here's a tutorial from them: https://www.x.com/devzone/articles/using-paypals-adaptive-payments-and-google-app-engine-build-online-market-python
Also, this code might be adapted for what you need. They are using google checkout: http://code.google.com/p/chippysshop/
Finally, there's Stripe, that handles all kinds of credit card transactions and does not require a merchant account: https://stripe.com/
Good luck!