addons-server/lib/pay_server/__init__.py

174 строки
5.8 KiB
Python

import hashlib
import urllib
import uuid
from django.conf import settings
from django.db.models import Model
from base import Client, Encoder, SolitudeError
from errors import pre_approval_codes
from amo.helpers import absolutify, urlparams
from amo.urlresolvers import reverse
client = None
def model_to_uid(model):
return ':'.join((settings.DOMAIN.replace('-', '.'),
model.__class__._meta.db_table,
str(model.pk))).lower()
class ZamboniEncoder(Encoder):
def default(self, v):
if isinstance(v, Model):
return model_to_uid(v)
return super(ZamboniEncoder, self).default(v)
def filter_encoder(query):
for k, v in query.items():
if isinstance(v, Model):
query[k] = model_to_uid(v)
return urllib.urlencode(query)
class ZamboniClient(Client):
Error = SolitudeError
def get(self, url):
url = '%s%s' % (self.config['server'], url)
return self.call(url, 'get')
def lookup_transaction(self, tx_uuid):
return self.api.generic.transaction.get_object_or_404(uuid=tx_uuid)
def lookup_buyer_paypal(self, buyer):
res = self.get_buyer(filters={'uuid': buyer})
count = res['meta']['total_count']
if count == 1:
return res['objects'][0]['paypal']
else:
raise ValueError('Get returned %s buyers.' % count)
def create_buyer_if_missing(self, buyer):
"""
Checks to see if the buyer exists in solitude. If not we'll create
it so that solitude can store the pre-approval data for that buyer.
"""
res = self.get_buyer(filters={'uuid': buyer})
if res['meta']['total_count'] == 0:
self.post_buyer(data={'uuid': buyer})
def get_seller_paypal_if_exists(self, seller):
"""
Will return the paypal details if the user and paypal data exists.
"""
res = self.get_seller(filters={'uuid': seller})
if res['meta']['total_count'] == 1:
return res['objects'][0]['paypal']
def create_seller_paypal(self, seller):
"""
Will see if the user exists. If it does, will see if paypal exists, if
it doesn't it will create a paypal record. It will then return the
paypal pk, so we can do calls to it.
"""
res = self.get_seller(filters={'uuid': seller})
count = res['meta']['total_count']
if count == 0:
# There's no seller data, so create the seller objects.
sel = self.post_seller(data={'uuid': seller})
return self.post_seller_paypal(data={'seller':
sel['resource_uri']})
elif count == 1:
sel = res['objects'][0]
paypal = sel['paypal']
if not paypal:
# There is no PayPal object. Create one.
return self.post_seller_paypal(data={'seller':
sel['resource_uri']})
# The resource_pk is there in the first results, just save it.
return paypal
else:
raise ValueError('Get returned %s sellers.' % count)
def create_seller_for_pay(self, addon):
"""
A temporary method to populate seller data, when a data migration
is completed, this can be removed.
"""
obj = client.create_seller_paypal(addon)
if not obj['paypal_id']:
client.patch_seller_paypal(pk=obj['resource_pk'],
data={'paypal_id': addon.paypal_id})
return obj['resource_pk']
def make_uuid(self):
return hashlib.md5(str(uuid.uuid4())).hexdigest()
def make_urls(self, data):
uuid = self.make_uuid()
seller = data['seller']
# If the pay call doesn't specify complete URLs, use some defaults.
if 'complete' not in data:
data['complete'] = urlparams(
seller.get_purchase_url(action='done', args=['complete']),
uuid=uuid)
if 'cancel' not in data:
data['cancel'] = urlparams(
seller.get_purchase_url(action='done', args=['cancel']),
uuid=uuid)
# Absolutify all URLs. Absolutify can safely be called multiple
# times with no ill effects.
return {
'cancel_url': absolutify(data['cancel']),
'return_url': absolutify(data['complete']),
'ipn_url': absolutify(reverse('amo.paypal')),
'uuid': uuid
}
def pay(self, payload, retry=True):
"""
Add in uuid and urls on the way. If retry is True, if the transaction
fails because of a pre-approval failure, we'll try it a second time
without it.
"""
data = payload.copy()
data.update(self.make_urls(payload))
data['use_preapproval'] = True
try:
return self.post_pay(data=data)
except self.Error, error:
if not (retry and error.code in pre_approval_codes):
raise
data.update(self.make_urls(payload))
data['use_preapproval'] = False
return self.post_pay(data=data)
def get_client():
# If you haven't specified a solitude host, we can't do anything.
if settings.SOLITUDE_HOSTS:
config = {
# TODO: when seclusion can cope with multiple hosts, we'll pass
# them all through and let seclusion do its magic.
'server': settings.SOLITUDE_HOSTS[0],
'key': settings.SOLITUDE_KEY,
'secret': settings.SOLITUDE_SECRET,
'timeout': settings.SOLITUDE_TIMEOUT
}
client = ZamboniClient(config)
client.encoder = ZamboniEncoder
client.filter_encoder = filter_encoder
return client
if not client:
client = get_client()