Permalink
Browse files

allow transactions to be created and then altered on billing (bug 802…

…198)
  • Loading branch information...
1 parent 4ec368b commit a58bed2379b9403647e8a8d3d4a1d6c97900ecbd @andymckay andymckay committed Dec 20, 2012
View
@@ -99,6 +99,7 @@ class CreateBillingConfigurationForm(SellerProductForm):
prices = ListField()
redirect_url_onerror = forms.URLField()
redirect_url_onsuccess = forms.URLField()
+ transaction_uuid = forms.CharField()
@property
def bango_data(self):
@@ -35,6 +35,7 @@ class TestBilling(TestCase):
def setUp(self):
self.billing = good_billing_request.copy()
+ self.billing['transaction_uuid'] = 'foo'
@kumar303

kumar303 Dec 20, 2012

Member

should this just go into the good_billing_request dict directly?

@andymckay

andymckay Dec 20, 2012

Owner

i don't want to put values into samples that will depend upon data, later on i test the transaction_uuid matches a real transaction

self.billing['seller_product_bango'] = '/blah/'
def test_form(self, clean):
@@ -3,10 +3,11 @@
from django.conf import settings
import mock
-from nose.tools import eq_
+from nose.tools import eq_, ok_
from lib.sellers.models import (Seller, SellerBango, SellerProduct,
SellerProductBango)
+from lib.transactions import constants
from lib.transactions.models import Transaction
from solitude.base import APITest
@@ -249,18 +250,44 @@ def setUp(self):
super(TestCreateBillingConfiguration, self).setUp()
self.list_url = self.get_list_url('billing')
+ def create(self):
+ super(TestCreateBillingConfiguration, self).create()
+ self.transaction = Transaction.objects.create(
+ provider=constants.SOURCE_BANGO,
+ seller_product=self.seller_product,
+ status=constants.STATUS_RECEIVED,
+ uuid=self.uuid)
+
def good(self):
self.create()
data = samples.good_billing_request.copy()
data['seller_product_bango'] = self.seller_product_bango_uri
+ data['transaction_uuid'] = self.transaction.uuid
return data
def test_good(self):
- data = self.good()
- res = self.client.post(self.list_url, data=data)
- eq_(res.status_code, 201, res)
+ res = self.client.post(self.list_url, data=self.good())
+ eq_(res.status_code, 201, res.content)
assert 'billingConfigurationId' in json.loads(res.content)
+ def test_not_found(self):
+ data = self.good()
+ self.transaction.provider = constants.SOURCE_PAYPAL
+ self.transaction.save()
+ with self.assertRaises(Transaction.DoesNotExist):
+ self.client.post(self.list_url, data=data)
+
+ def test_changed(self):
+ res = self.client.post(self.list_url, data=self.good())
+ eq_(res.status_code, 201)
+ transactions = Transaction.objects.all()
+ eq_(len(transactions), 1)
+ transaction = transactions[0]
+ eq_(transaction.status, constants.STATUS_PENDING)
+ eq_(transaction.type, constants.TYPE_PAYMENT)
+ ok_(transaction.uid_pay)
+ ok_(transaction.uid_support)
+
def test_missing(self):
data = samples.good_billing_request.copy()
del data['prices']
@@ -287,6 +314,13 @@ def test_transaction(self):
tran = Transaction.objects.get()
eq_(tran.provider, 1)
+ def test_no_transaction(self):
+ data = self.good()
+ del data['transaction_uuid']
+ res = self.client.post(self.list_url, data=data)
+ eq_(res.status_code, 400, res)
+ assert 'transaction_uuid' in json.loads(res.content)
+
@mock.patch.object(settings, 'BANGO_MOCK', True)
class TestCreateBankConfiguration(BangoAPI):
@@ -1,13 +1,17 @@
-STATUS_PENDING = 0
+STATUS_PENDING = 0 # When the payment has been started.
STATUS_COMPLETED = 1 # When the IPN says its ok.
STATUS_CHECKED = 2 # When someone calls pay-check on the transaction.
+# When we we've got a request for a payment, but more work needs to be done
+# before we can proceed to the next stage, pending.
+STATUS_RECEIVED = 3
STATUS_DEFAULT = STATUS_PENDING
STATUSES = {
- 'pending': STATUS_PENDING,
- 'completed': STATUS_COMPLETED,
'checked': STATUS_CHECKED,
+ 'completed': STATUS_COMPLETED,
+ 'pending': STATUS_PENDING,
+ 'received': STATUS_RECEIVED,
}
TYPE_PAYMENT = 0
View
@@ -19,7 +19,7 @@ class Transaction(Model):
null=True)
buyer = models.ForeignKey('buyers.Buyer', blank=True, null=True,
db_index=True)
- currency = models.CharField(max_length=3, default='USD')
+ currency = models.CharField(max_length=3, blank=True)
provider = models.PositiveIntegerField(
choices=constants.SOURCES_CHOICES)
related = models.ForeignKey('self', blank=True, null=True,
@@ -38,7 +38,8 @@ class Transaction(Model):
null=True)
# An ID from the provider that relates to this transaction.
uid_pay = models.CharField(max_length=255, db_index=True)
- # An ID we generate for this transaction.
+ # An ID we generate for this transaction, we'll generate one for you if
@kumar303

kumar303 Dec 20, 2012

Member

This comment is stale.

+ # you don't specify one.
uuid = models.CharField(max_length=255, db_index=True, unique=True)
class Meta(Model.Meta):
@@ -108,12 +109,15 @@ def create_bango_transaction(sender, **kwargs):
form = kwargs['form']
seller_product = form.cleaned_data['seller_product_bango'].seller_product
- transaction = Transaction.create(
- provider=constants.SOURCE_BANGO,
- seller_product=seller_product,
- source=data.get('source', ''),
- type=constants.TYPE_PAYMENT,
- uuid=data['externalTransactionId'],
- uid_pay=bundle['billingConfigurationId'])
+ transaction = Transaction.objects.get(uuid=data['transaction_uuid'],
+ status=constants.STATUS_RECEIVED,
+ provider=constants.SOURCE_BANGO)
+ transaction.source = data.get('source', '')
+ transaction.uid_support = data['externalTransactionId']
+ transaction.uid_pay = bundle['billingConfigurationId']
+ transaction.seller_product = seller_product
+ transaction.status = constants.STATUS_PENDING
+ transaction.type = constants.TYPE_PAYMENT
+ transaction.save()
log.info('Bango transaction: %s pending' % (transaction.pk,))
@@ -1,5 +1,6 @@
+import uuid
+
from tastypie import fields
-from tastypie.constants import ALL_WITH_RELATIONS
from lib.transactions.models import Transaction
from solitude.base import ModelResource
@@ -8,21 +9,24 @@
class TransactionResource(ModelResource):
seller_product = fields.ToOneField(
'lib.sellers.resources.SellerProductResource',
- 'seller_product', blank=True, full=False, null=True,
- readonly=True)
+ 'seller_product', blank=True, full=False, null=True)
related = fields.ToOneField(
'lib.transactions.resources.TransactionResource',
- 'related', blank=True, full=False, null=True, readonly=True)
+ 'related', blank=True, full=False, null=True)
class Meta(ModelResource.Meta):
queryset = Transaction.objects.filter()
fields = ['uuid', 'seller_product', 'amount', 'currency', 'provider',
'uid_support', 'type', 'status', 'related']
- list_allowed_methods = ['get']
+ list_allowed_methods = ['get', 'post']
allowed_methods = ['get']
resource_name = 'transaction'
filtering = {
'uuid': 'exact',
'seller': 'exact',
'provider': 'exact'
}
+
+ def hydrate_uuid(self, bundle):
+ bundle.data.setdefault('uuid', str(uuid.uuid4()))
+ return bundle
@@ -1,6 +1,6 @@
import json
-from nose.tools import eq_
+from nose.tools import eq_, ok_
from lib.sellers.tests.utils import make_seller_paypal
from lib.transactions import constants
@@ -23,7 +23,7 @@ def setUp(self):
self.detail_url = self.get_detail_url('transaction', self.trans.pk)
def test_list_allowed(self):
- self.allowed_verbs(self.list_url, ['get'])
+ self.allowed_verbs(self.list_url, ['get', 'post'])
self.allowed_verbs(self.detail_url, ['get'])
def test_list(self):
@@ -36,6 +36,13 @@ def test_get(self):
eq_(res.status_code, 200)
eq_(json.loads(res.content)['uuid'], self.uuid)
+ def test_post_uuid(self):
+ data = {'provider': constants.SOURCE_BANGO,
+ 'seller_product': '/generic/product/%s/' % self.product.pk}
+ res = self.client.post(self.list_url, data=data)
+ eq_(res.status_code, 201)
+ ok_(json.loads(res.content)['uuid'])
+
def test_provider(self):
res = self.client.get(self.list_url, data={'provider':
constants.SOURCE_BANGO})

0 comments on commit a58bed2

Please sign in to comment.