def _process_modify_items(self, items):
        if len(items) > 1:
            raise OrderingError(
                'Only a modify item is supported per order item')

        item = items[0]
        if 'product' not in item:
            raise OrderingError(
                'It is required to specify product information in modify order items'
            )

        product = item['product']

        if 'id' not in product:
            raise OrderingError(
                'It is required to provide product id in modify order items')

        client = InventoryClient()
        order, contract = self._get_existing_contract(client, product['id'])

        # Build the new contract
        new_contract = self._build_contract(item)
        if new_contract.pricing_model != {}:
            contract.pricing_model = new_contract.pricing_model
            contract.revenue_class = new_contract.revenue_class

        order.save()

        # The modified item is treated as an initial payment
        charging_engine = ChargingEngine(order)
        return charging_engine.resolve_charging(type_='initial',
                                                related_contracts=[contract])
    def _process_modify_items(self, items):
        if len(items) > 1:
            raise OrderingError('Only a modify item is supported per order item')

        item = items[0]
        if 'product' not in item:
            raise OrderingError('It is required to specify product information in modify order items')

        product = item['product']

        if 'id' not in product:
            raise OrderingError('It is required to provide product id in modify order items')

        client = InventoryClient()
        order, contract = self._get_existing_contract(client, product['id'])

        # Build the new contract
        new_contract = self._build_contract(item)
        if new_contract.pricing_model != {}:
            contract.pricing_model = new_contract.pricing_model
            contract.revenue_class = new_contract.revenue_class

        order.save()

        # The modified item is treated as an initial payment
        charging_engine = ChargingEngine(order)
        return charging_engine.resolve_charging(type_='initial', related_contracts=[contract])
Example #3
0
    def update(self, request, reference):

        purchase = Purchase.objects.get(ref=reference)

        data = json.loads(request.raw_post_data)

        try:
            if data['method'] == 'paypal':
                charging_engine = ChargingEngine(purchase, payment_method='paypal')
            elif data['method'] == 'credit_card':

                # Get the payment info
                if 'credit_card' in data:
                    credit_card = data['credit_card']
                else:
                    if purchase.organization_owned:
                        credit_card = purchase.owner_organization.payment_info
                    else:
                        credit_card = purchase.customer.userprofile.payment_info
                charging_engine = ChargingEngine(purchase, payment_method='credit_card', credit_card=credit_card)

            charging_engine.resolve_charging()
        except:
            # Refresh the purchase info
            purchase = Purchase.objects.get(ref=reference)
            rollback(purchase)
            return build_response(request, 400, 'Invalid JSON content')

        return build_response(request, 200, 'OK')
Example #4
0
    def _process_add_items(self, items, order_id, description, terms_accepted):

        new_contracts = [self._build_contract(item) for item in items]

        terms_found = False
        for c in new_contracts:
            off = Offering.objects.get(pk=ObjectId(c.offering))
            if off.asset is not None and off.asset.has_terms:
                terms_found = True

        if terms_found and not terms_accepted:
            raise OrderingError('You must accept the terms and conditions of the offering to acquire it')

        current_org = self._customer.userprofile.current_organization
        order = Order.objects.create(
            order_id=order_id,
            customer=self._customer,
            owner_organization=current_org,
            date=datetime.utcnow(),
            state='pending',
            tax_address=self._get_billing_address(items),
            contracts=new_contracts,
            description=description
        )

        self.rollback_logger['models'].append(order)

        charging_engine = ChargingEngine(order)
        return charging_engine.resolve_charging()
    def resolve_purchase_usage(self, purchase):
        # Get payment info
        if purchase.organization_owned:
            org = purchase.owner_organization
            payment_info = org.payment_info
        else:
            payment_info = purchase.customer.userprofile.payment_info

        charging = ChargingEngine(purchase, payment_method='credit_card', credit_card=payment_info)
        charging.resolve_charging(type_='use')
    def create(self, request):

        try:
            task = json.loads(request.body)
        except:
            return build_response(request, 400, 'The provided data is not a valid JSON object')

        # Check the products to be renovated
        if 'name' not in task or 'id' not in task or 'priceType' not in task:
            return build_response(request, 400, 'Missing required field, must contain name, id  and priceType fields')

        # Parse oid from product name
        parsed_name = task['name'].split('=')

        try:
            order = Order.objects.get(order_id=parsed_name[1])
        except:
            return build_response(request, 404, 'The oid specified in the product name is not valid')

        # Get contract to renovate
        if isinstance(task['id'], int):
            task['id'] = unicode(task['id'])

        try:
            contract = order.get_product_contract(task['id'])
        except:
            return build_response(request, 404, 'The specified product id is not valid')

        # Refresh accounting information
        on_usage_refreshed(order, contract)

        # Build charging engine
        charging_engine = ChargingEngine(order)

        if task['priceType'].lower() not in ['recurring', 'usage']:
            return build_response(request, 400, 'Invalid priceType only recurring and usage types can be renovated')

        try:
            redirect_url = charging_engine.resolve_charging(type_=task['priceType'].lower(), related_contracts=[contract])
        except ValueError as e:
            return build_response(request, 400, unicode(e))
        except OrderingError as e:
            return build_response(request, 422, unicode(e))
        except:
            return build_response(request, 500, 'An unexpected event prevented your payment to be created')

        response = build_response(request, 200, 'OK')

        # Include redirection header if needed
        if redirect_url is not None:
            response['X-Redirect-URL'] = redirect_url

        return response
Example #7
0
    def create(self, request, reference):
        try:
            # Extract SDR document from the HTTP request
            data = json.loads(request.raw_post_data)

            # Validate SDR structure
            if 'offering' not in data or 'customer' not in data or 'time_stamp' not in data \
                    or 'correlation_number' not in data or 'record_type' not in data or'unit' not in data \
                    or 'value' not in data or 'component_label' not in data:
                raise Exception('Invalid JSON content')

            # Get the purchase
            purchase = Purchase.objects.get(ref=reference)
            # Call the charging engine core with the SDR
            charging_engine = ChargingEngine(purchase)
            charging_engine.include_sdr(data)
        except Exception, e:
            return build_response(request, 400, e.message)
Example #8
0
    def create(self, request, reference):
        try:
            # Extract SDR document from the HTTP request
            data = json.loads(request.raw_post_data)

            # Validate SDR structure
            if 'offering' not in data or 'customer' not in data or 'time_stamp' not in data \
                    or 'correlation_number' not in data or 'record_type' not in data or'unit' not in data \
                    or 'value' not in data or 'component_label' not in data:
                raise Exception('Invalid JSON content')

            # Get the purchase
            purchase = Purchase.objects.get(ref=reference)
            # Call the charging engine core with the SDR
            charging_engine = ChargingEngine(purchase)
            charging_engine.include_sdr(data)
        except Exception, e:
            return build_response(request, 400, e.message)
    def _process_add_items(self, items, order_id, description):

        new_contracts = [self._build_contract(item) for item in items]

        current_org = self._customer.userprofile.current_organization
        order = Order.objects.create(
            order_id=order_id,
            customer=self._customer,
            owner_organization=current_org,
            date=datetime.utcnow(),
            state='pending',
            tax_address=self._get_billing_address(items),
            contracts=new_contracts,
            description=description)

        self.rollback_logger['models'].append(order)

        charging_engine = ChargingEngine(order)
        return charging_engine.resolve_charging()
    def _process_add_items(self, items, order_id, description):

        new_contracts = [self._build_contract(item) for item in items]

        current_org = self._customer.userprofile.current_organization
        order = Order.objects.create(
            order_id=order_id,
            customer=self._customer,
            owner_organization=current_org,
            date=datetime.utcnow(),
            state='pending',
            tax_address=self._get_billing_address(items),
            contracts=new_contracts,
            description=description
        )

        self.rollback_logger['models'].append(order)

        charging_engine = ChargingEngine(order)
        return charging_engine.resolve_charging()
Example #11
0
    def update(self, request, reference):

        purchase = Purchase.objects.get(ref=reference)

        data = json.loads(request.raw_post_data)

        try:
            if data['method'] == 'paypal':
                charging_engine = ChargingEngine(purchase, payment_method='paypal')
            elif data['method'] == 'credit_card':

                # Get the payment info
                if 'credit_card' in data:
                    credit_card = data['credit_card']
                else:
                    if purchase.organization_owned:
                        credit_card = purchase.owner_organization.payment_info
                    else:
                        credit_card = purchase.customer.userprofile.payment_info
                charging_engine = ChargingEngine(purchase, payment_method='credit_card', credit_card=credit_card)

            charging_engine.resolve_charging()
        except:
            # Refresh the purchase info
            purchase = Purchase.objects.get(ref=reference)
            rollback(purchase)
            return build_response(request, 400, 'Invalid JSON content')

        return build_response(request, 200, 'OK')
def process_product_payment(self, request, task, order, contract):
    # Refresh accounting information
    on_usage_refreshed(order, contract)

    # Build charging engine
    charging_engine = ChargingEngine(order)

    redirect_url = None
    try:
        redirect_url = charging_engine.resolve_charging(
            type_=task['priceType'].lower(), related_contracts=[contract])
    except ValueError as e:
        return None, build_response(request, 400, str(e))
    except OrderingError as e:
        # The error might be raised because renewing a suspended product not expired
        if str(
                e
        ) == 'OrderingError: There is not recurring payments to renovate' and contract.suspended:
            try:
                on_product_acquired(order, contract)

                # Change product state to active
                contract.suspended = False
                order.save()

                inventory_client = InventoryClient()
                inventory_client.activate_product(contract.product_id)
            except:
                return None, build_response(
                    request, 400, 'The asset has failed to be activated')

        else:
            return None, build_response(request, 422, str(e))
    except:
        return None, build_response(
            request, 500,
            'An unexpected event prevented your payment to be created')

    return redirect_url, None
    def handle(self, *args, **options):
        """
            This method is used to perform the charging process
            of the offerings that have pending SDR for more than
            a month
        """
        now = time.mktime(datetime.now().timetuple())

        if len(args) == 0:
            # Get contracts
            for contract in Contract.objects.all():

                pending_sdrs = contract.pending_sdrs

                # If there are subscriptions the renovations are used as triggers
                if len(pending_sdrs) > 0 and (not 'subscription' in contract.pricing_model):
                    time_stamp = time.mktime(pending_sdrs[0]['time_stamp'].timetuple())

                    if (time_stamp + 2592000) <= now:  # A month
                        # Get the related payment info
                        purchase = contract.purchase

                        if purchase.organization_owned:
                            org = purchase.owner_organization
                            payment_info = org.payment_info
                        else:
                            payment_info = purchase.customer.userprofile.payment_info

                        charging = ChargingEngine(purchase, payment_method='credit_card', credit_card=payment_info)
                        charging.resolve_charging(sdr=True)

        elif len(args) == 1:
            # Get the purchase
            try:
                purchase = Purchase.objects.get(ref=args[0])
            except:
                raise Exception('The provided purchase does not exists')

            # Get the contract
            contract = purchase.contract

            # Check if there are pending SDRs
            if (len(contract.pending_sdrs) > 0):

                # Get payment info
                if purchase.organization_owned:
                    org = purchase.owner_organization
                    payment_info = org.payment_info
                else:
                    payment_info = purchase.customer.userprofile.payment_info

                charging = ChargingEngine(purchase, payment_method='credit_card', credit_card=payment_info)
                charging.resolve_charging(sdr=True)

            else:
                raise Exception('No accounting info in the provided purchase')
        else:
            raise Exception('Invalid number of arguments')
def create_purchase(user, offering, org_owned=False, payment_info=None):

    if offering.state != 'published':
        raise PermissionDenied("This offering can't be purchased")

    if offering.open:
        raise PermissionDenied('Open offerings cannot be purchased')

    if accepted_needed(offering) and not payment_info['accepted']:
        raise PermissionDenied('You must accept the terms and conditions of the offering to acquire it')

    profile = UserProfile.objects.get(user=user)

    # Check if the offering is already purchased
    if (org_owned and offering.pk in profile.current_organization.offerings_purchased) \
            or (not org_owned and offering.pk in profile.offerings_purchased):
        raise PermissionDenied('The offering has been already purchased')

    organization = profile.current_organization

    plan = None
    # Check the selected plan
    if payment_info and 'plan' in payment_info:
        plan = payment_info['plan']

    # Get the effective tax address
    if 'tax_address' not in payment_info:
        if org_owned:
            tax = organization.tax_address
        else:
            tax = profile.tax_address

        # Check that the customer has a tax address
        if 'street' not in tax:
            raise ValueError('The customer does not have a tax address')
    else:
        tax = payment_info['tax_address']

        # Check tax_address fields
        if ('street' not in tax) or ('postal' not in tax)\
                or ('city' not in tax) or ('country' not in tax):
            raise ValueError('The tax address is not valid')

    # Check the payment method before purchase creation in order to avoid
    # an inconsistent state in the database
    credit_card_info = None
    if payment_info['payment_method'] == 'credit_card':
        if 'credit_card' in payment_info:
            # Check credit card info
            if (not ('number' in payment_info['credit_card'])) or (not ('type' in payment_info['credit_card']))\
                    or (not ('expire_year' in payment_info['credit_card'])) or (not ('expire_month' in payment_info['credit_card']))\
                    or (not ('cvv2' in payment_info['credit_card'])):
                raise ValueError('Invalid credit card info')

            credit_card_info = payment_info['credit_card']
        else:
            if org_owned:
                credit_card_info = organization.payment_info
            else:
                credit_card_info = profile.payment_info

            # Check the credit card info
            if 'number' not in credit_card_info:
                raise Exception('The customer does not have payment info')

    elif payment_info['payment_method'] != 'paypal':
        raise ValueError('Invalid payment method')

    # Check if a purchase object exists
    old_purchase = Purchase.objects.filter(owner_organization=organization, offering=offering)
    if len(old_purchase) > 0:
        for p in old_purchase:
            p.delete()

    # Create the purchase
    purchase = Purchase.objects.create(
        customer=user,
        date=datetime.now(),
        offering=offering,
        organization_owned=org_owned,
        state='pending',
        tax_address=tax,
        owner_organization=organization
    )

    # Load ref
    purchase.ref = purchase.pk
    purchase.save()

    if credit_card_info is not None:
        charging_engine = ChargingEngine(purchase, payment_method='credit_card', credit_card=credit_card_info, plan=plan)
    else:
        charging_engine = ChargingEngine(purchase, payment_method='paypal', plan=plan)

    redirect_url = charging_engine.resolve_charging(new_purchase=True)

    if redirect_url is None:
        result = purchase

        # If no redirect URL is provided the purchase has ended so the user profile
        # info is updated
        if org_owned:
            organization.offerings_purchased.append(offering.pk)
            organization.save()
        else:
            profile.offerings_purchased.append(offering.pk)
            profile.save()

        notify_provider(purchase)

    else:
        result = redirect_url

    # Update offering indexes
    index_path = os.path.join(settings.BASEDIR, 'wstore')
    index_path = os.path.join(index_path, 'search')
    index_path = os.path.join(index_path, 'indexes')

    se = SearchEngine(index_path)
    se.update_index(offering)

    return result
Example #15
0
    def test_purchase_offering_update_exception(self):

        # Test view exceptions
        # Create the request, The user has not purchased a previous version
        # of the offering, so she is not allowed to purchase the offering
        data = {
            'offering': {
                'organization': 'test_organization',
                'name': 'test_offering',
                'version': '1.1'
            },
            'plan_label': 'update',
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment': {
                'method': 'paypal'
            }
        }
        request = self.factory.post(
            '/api/contracting/',
            json.dumps(data),
            HTTP_ACCEPT='application/json; charset=utf-8',
            content_type='application/json; charset=utf-8')
        request.user = self._user
        purchase_collection = views.PurchaseCollection(
            permitted_methods=('POST', ))

        response = purchase_collection.create(request)

        # Check response
        body_response = json.loads(response.content)
        self.assertEquals(body_response['result'], 'error')
        self.assertEquals(body_response['message'], 'Forbidden')
        self.assertEquals(response.status_code, 403)

        # Test Create contract exceptions

        offering = Offering.objects.get(pk="71000aba8e05ac2115f022ff")
        offering.offering_description = {
            'pricing': {
                'price_plans': [{
                    'title': 'Plan 1',
                    'label': 'update',
                    'price_components': []
                }, {
                    'title': 'Plan 1',
                    'label': 'regular',
                    'price_components': []
                }]
            }
        }

        offering.save()

        from datetime import datetime
        purchase = Purchase.objects.create(
            customer=self._user,
            date=datetime.now(),
            offering=offering,
            organization_owned=False,
            state='paid',
            tax_address={
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            bill=['/media/bills/11111111111.pdf'])
        from wstore.charging_engine.charging_engine import ChargingEngine

        # Check exceptions that can occur with multiple price plans when
        # creating the related purchase contract
        errors = {
            'The price plan label is required to identify the plan': None,
            'The specified plan does not exist': 'unexisting'
        }
        for err in errors:

            error = False
            msg = None
            try:
                charging = ChargingEngine(purchase,
                                          payment_method='paypal',
                                          plan=errors[err])
                charging._create_purchase_contract()
            except Exception, e:
                error = True
                msg = e.message

                self.assertTrue(error)
                self.assertEquals(msg, err)
Example #16
0
    def test_purchase_offering_update_payment(self):

        current_org = Organization.objects.get(pk="91000aba8e06ac2199999999")
        current_org.offerings_purchased.append('61000aba8e05ac2115f022f9')
        self._user.userprofile.current_organization = current_org

        self._user.userprofile.get_current_roles = MagicMock()
        self._user.userprofile.get_current_roles.return_value = ['customer']
        self._user.userprofile.save()

        # Create the request
        data = {
            'offering': {
                'organization': 'test_organization',
                'name': 'test_offering',
                'version': '1.1'
            },
            'plan_label': 'update',
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment': {
                'method': 'paypal'
            }
        }
        request = self.factory.post(
            '/api/contracting/',
            json.dumps(data),
            HTTP_ACCEPT='application/json; charset=utf-8',
            content_type='application/json; charset=utf-8'
        )
        request.user = self._user
        
        # Test purchase view
        views.create_purchase = MagicMock(name='create_purchase')
        offering = Offering.objects.get(pk="71000aba8e05ac2115f022ff")

        from datetime import datetime
        purchase = Purchase.objects.create(
            customer=self._user,
            date=datetime.now(),
            offering=offering,
            organization_owned=True,
            state='paid',
            tax_address={
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            bill=['/media/bills/11111111111.pdf']
        )
        views.create_purchase.return_value = purchase

        views.get_current_site = MagicMock(name='get_current_site')
        views.get_current_site.return_value = Site.objects.get(name='antares')
        views.Context.objects.get = MagicMock(name='get')
        context = MagicMock()
        context.user_refs = []
        views.Context.objects.get.return_value = context

        purchase_collection = views.PurchaseCollection(permitted_methods=('POST',))

        response = purchase_collection.create(request)

        # Check response
        body_response = json.loads(response.content)
        self.assertEquals(len(body_response['bill']), 1)
        self.assertEquals(body_response['bill'][0], '/media/bills/11111111111.pdf')
        payment_info = {
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment_method': 'paypal',
            'plan': 'update'
        }
        views.create_purchase.assert_called_once_with(self._user, offering, org_owned=True, payment_info=payment_info)

        # Test Contract creation
        # Load usdl info
        f = open('wstore/test/test_usdl2.ttl', 'rb')
        g = rdflib.Graph()
        g.parse(data=f.read(), format='n3')
        f.close()

        offering.offering_description = json.loads(g.serialize(format='json-ld', auto_compact=True))
        offering.save()

        from wstore.charging_engine.charging_engine import ChargingEngine
        charging = ChargingEngine(purchase, payment_method='paypal', plan='update')
        charging._create_purchase_contract()

        # Refresh purchase
        purchase = Purchase.objects.get(pk=purchase.pk)
        contract = purchase.contract

        # Check contract pricing model
        self.assertTrue('single_payment' in contract.pricing_model)
        self.assertEquals(len(contract.pricing_model['single_payment']), 1)
        payment = contract.pricing_model['single_payment'][0]
        self.assertEquals(payment['title'], 'Price component update')
        self.assertEquals(payment['value'], '1.0')

        self.assertFalse('subscription' in contract.pricing_model)
        self.assertFalse('pay_per_use' in contract.pricing_model)
Example #17
0
    def test_purchase_offering_update_exception(self):

        # Test view exceptions
        # Create the request, The user has not purchased a previous version
        # of the offering, so she is not allowed to purchase the offering
        data = {
            'offering': {
                'organization': 'test_organization',
                'name': 'test_offering',
                'version': '1.1'
            },
            'plan_label': 'update',
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment': {
                'method': 'paypal'
            }
        }
        request = self.factory.post(
            '/api/contracting/',
            json.dumps(data),
            HTTP_ACCEPT='application/json; charset=utf-8',
            content_type='application/json; charset=utf-8'
        )
        request.user = self._user
        purchase_collection = views.PurchaseCollection(permitted_methods=('POST',))

        response = purchase_collection.create(request)

        # Check response
        body_response = json.loads(response.content)
        self.assertEquals(body_response['result'], 'error')
        self.assertEquals(body_response['message'], 'Forbidden')
        self.assertEquals(response.status_code, 403)

        # Test Create contract exceptions
        # Load usdl info
        f = open('wstore/test/test_usdl2.ttl', 'rb')
        g = rdflib.Graph()
        g.parse(data=f.read(), format='n3')
        f.close()

        offering = Offering.objects.get(pk="71000aba8e05ac2115f022ff")
        offering.offering_description = json.loads(g.serialize(format='json-ld', auto_compact=True))
        offering.save()

        from datetime import datetime
        purchase = Purchase.objects.create(
            customer=self._user,
            date=datetime.now(),
            offering=offering,
            organization_owned=False,
            state='paid',
            tax_address={
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            bill=['/media/bills/11111111111.pdf']
        )
        from wstore.charging_engine.charging_engine import ChargingEngine

        # Check exceptions that can occur with multiple price plans when
        # creating the related purchase contract
        errors = {
            'The price plan label is required to identify the plan': None,
            'The specified plan does not exist': 'unexisting'
        }
        for err in errors:

            error = False
            msg = None
            try:
                charging = ChargingEngine(purchase, payment_method='paypal', plan=errors[err])
                charging._create_purchase_contract()
            except Exception, e:
                error = True
                msg = e.message

                self.assertTrue(error)
                self.assertEquals(msg, err)
Example #18
0
    def test_basic_purchase_offering_update(self):
        # Get context
        cnt = Context.objects.all()[0]
        cnt.allowed_currencies = {
            "default": "EUR",
            "allowed": [{
                "currency": "EUR",
                "in_use": True
            }]
        }
        cnt.save()
        self._user.userprofile.offerings_purchased.append('61000aba8e05ac2115f022f9')
        # Create the request
        data = {
            'offering': {
                'organization': 'test_organization',
                'name': 'test_offering',
                'version': '1.1'
            },
            'plan_label': 'update',
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment': {
                'method': 'paypal'
            }
        }
        request = self.factory.post(
            '/api/contracting/',
            json.dumps(data),
            HTTP_ACCEPT='application/json; charset=utf-8',
            content_type='application/json; charset=utf-8'
        )
        request.user = self._user

        # Test purchase view
        views.create_purchase = MagicMock(name='create_purchase')
        offering = Offering.objects.get(pk="71000aba8e05ac2115f022ff")

        from datetime import datetime
        purchase = Purchase.objects.create(
            customer=self._user,
            date=datetime.now(),
            offering=offering,
            organization_owned=False,
            state='paid',
            tax_address={
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            bill=['/media/bills/11111111111.pdf']
        )
        views.create_purchase.return_value = purchase

        views.get_current_site = MagicMock(name='get_current_site')
        views.get_current_site.return_value = Site.objects.get(name='antares')
        views.Context.objects.get = MagicMock(name='get')
        context = MagicMock()
        context.user_refs = []
        views.Context.objects.get.return_value = context

        purchase_collection = views.PurchaseCollection(permitted_methods=('POST',))

        response = purchase_collection.create(request)

        # Check response
        body_response = json.loads(response.content)
        self.assertEquals(len(body_response['bill']), 1)
        self.assertEquals(body_response['bill'][0], '/media/bills/11111111111.pdf')
        payment_info = {
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment_method': 'paypal',
            'plan': 'update',
            'accepted': False
        }
        views.create_purchase.assert_called_once_with(self._user, offering, org_owned=False, payment_info=payment_info)

        offering.offering_description = {
            'pricing': {
                'price_plans': []
            }
        }

        offering.save()

        from wstore.charging_engine.charging_engine import ChargingEngine
        charging = ChargingEngine(purchase, payment_method='paypal', plan='update')
        charging._create_purchase_contract()

        # Refresh purchase
        purchase = Purchase.objects.get(pk=purchase.pk)
        contract = purchase.contract

        self.assertFalse('single_payment' in contract.pricing_model)
        self.assertFalse('subscription' in contract.pricing_model)
        self.assertFalse('pay_per_use' in contract.pricing_model)
    def create(self, request):

        order = None
        concept = None
        self.ordering_client = OrderingClient()
        try:
            # Extract payment information
            data = json.loads(request.body)

            if 'reference' not in data or 'paymentId' not in data or 'payerId' not in data:
                raise ValueError('Missing required field. It must contain reference, paymentId, and payerId')

            reference = data['reference']
            token = data['paymentId']
            payer_id = data['payerId']

            if not Order.objects.filter(pk=reference):
                raise ValueError('The provided reference does not identify a valid order')

            db = get_database_connection()

            # Uses an atomic operation to get and set the _lock value in the purchase
            # document
            pre_value = db.wstore_order.find_one_and_update(
                {'_id': ObjectId(reference)},
                {'$set': {'_lock': True}}
            )

            # If the value of _lock before setting it to true was true, means
            # that the time out function has acquired it previously so the
            # view ends
            if not pre_value or '_lock' in pre_value and pre_value['_lock']:
                raise PaymentError('The timeout set to process the payment has finished')

            order = Order.objects.get(pk=reference)
            raw_order = self.ordering_client.get_order(order.order_id)
            pending_info = order.pending_payment
            concept = pending_info.concept

            # If the order state value is different from pending means that
            # the timeout function has completely ended before acquiring the resource
            # so _lock is set to false and the view ends
            if pre_value['state'] != 'pending':
                db.wstore_order.find_one_and_update(
                    {'_id': ObjectId(reference)},
                    {'$set': {'_lock': False}}
                )
                raise PaymentError('The timeout set to process the payment has finished')

            # Check that the request user is authorized to end the payment
            if request.user.userprofile.current_organization != order.owner_organization or request.user != order.customer:
                raise PaymentError('You are not authorized to execute the payment')

            transactions = pending_info.transactions

            # Get the payment client
            # Load payment client
            cln_str = settings.PAYMENT_CLIENT
            client_package, client_class = cln_str.rsplit('.', 1)

            payment_client = getattr(importlib.import_module(client_package), client_class)

            # build the payment client
            client = payment_client(order)
            order.sales_ids = client.end_redirection_payment(token, payer_id)
            order.save()

            charging_engine = ChargingEngine(order)
            charging_engine.end_charging(transactions, pending_info.free_contracts, concept)

        except Exception as e:

            # Rollback the purchase if existing
            if order is not None and raw_order is not None:
                if concept == 'initial':
                    # Set the order to failed in the ordering API
                    # Set all items as Failed, mark the whole order as failed
                    self.ordering_client.update_items_state(raw_order, 'Failed')
                    order.delete()
                else:
                    order.state = 'paid'
                    order.pending_payment = None
                    order.save()

            expl = ' due to an unexpected error'
            err_code = 500
            if isinstance(e, PaymentError) or isinstance(e, ValueError):
                expl = ': ' + unicode(e)
                err_code = 403

            msg = 'The payment has been canceled' + expl
            return build_response(request, err_code, msg)

        # Change states of TMForum resources (orderItems, products, etc)
        # depending on the concept of the payment

        states_processors = {
            'initial': self._set_initial_states,
            'recurring': self._set_renovation_states,
            'usage': self._set_renovation_states
        }
        # Include the free contracts as transactions in order to activate them
        ext_transactions = deepcopy(transactions)
        ext_transactions.extend([{'item': contract.item_id} for contract in pending_info.free_contracts])

        states_processors[concept](ext_transactions, raw_order, order)

        # _lock is set to false
        db.wstore_order.find_one_and_update(
            {'_id': ObjectId(reference)},
            {'$set': {'_lock': False}}
        )

        return build_response(request, 200, 'Ok')
Example #20
0
    def handle(self, *args, **options):
        """
            This method is used to perform the charging process
            of the offerings that have pending SDR for more than
            a month
        """
        now = time.mktime(datetime.now().timetuple())

        if len(args) == 0:
            # Get contracts
            for contract in Contract.objects.all():

                pending_sdrs = contract.pending_sdrs

                # If there are subscriptions the renovations are used as triggers
                if len(pending_sdrs) > 0 and (not 'subscription'
                                              in contract.pricing_model):
                    time_stamp = time.mktime(
                        pending_sdrs[0]['time_stamp'].timetuple())

                    if (time_stamp + 2592000) <= now:  # A month
                        # Get the related payment info
                        purchase = contract.purchase

                        if purchase.organization_owned:
                            org = purchase.owner_organization
                            payment_info = org.payment_info
                        else:
                            payment_info = purchase.customer.userprofile.payment_info

                        charging = ChargingEngine(purchase,
                                                  payment_method='credit_card',
                                                  credit_card=payment_info)
                        charging.resolve_charging(sdr=True)

        elif len(args) == 1:
            # Get the purchase
            try:
                purchase = Purchase.objects.get(ref=args[0])
            except:
                raise Exception('The provided purchase does not exists')

            # Get the contract
            contract = purchase.contract

            # Check if there are pending SDRs
            if (len(contract.pending_sdrs) > 0):

                # Get payment info
                if purchase.organization_owned:
                    org = purchase.owner_organization
                    payment_info = org.payment_info
                else:
                    payment_info = purchase.customer.userprofile.payment_info

                charging = ChargingEngine(purchase,
                                          payment_method='credit_card',
                                          credit_card=payment_info)
                charging.resolve_charging(sdr=True)

            else:
                raise Exception('No accounting info in the provided purchase')
        else:
            raise Exception('Invalid number of arguments')
Example #21
0
    def create(self, request):

        order = None
        concept = None
        self.ordering_client = OrderingClient()
        try:
            # Extract payment information
            data = json.loads(request.body)

            if 'reference' not in data or 'paymentId' not in data or 'payerId' not in data:
                raise ValueError('Missing required field. It must contain reference, paymentId, and payerId')

            reference = data['reference']
            token = data['paymentId']
            payer_id = data['payerId']

            if not Order.objects.filter(pk=reference):
                raise ValueError('The provided reference does not identify a valid order')

            db = get_database_connection()

            # Uses an atomic operation to get and set the _lock value in the purchase
            # document
            pre_value = db.wstore_order.find_one_and_update(
                {'_id': ObjectId(reference)},
                {'$set': {'_lock': True}}
            )

            # If the value of _lock before setting it to true was true, means
            # that the time out function has acquired it previously so the
            # view ends
            if not pre_value or '_lock' in pre_value and pre_value['_lock']:
                raise PaymentError('The timeout set to process the payment has finished')

            order = Order.objects.get(pk=reference)
            raw_order = self.ordering_client.get_order(order.order_id)
            pending_info = order.pending_payment
            concept = pending_info['concept']

            # If the order state value is different from pending means that
            # the timeout function has completely ended before acquiring the resource
            # so _lock is set to false and the view ends
            if pre_value['state'] != 'pending':
                db.wstore_order.find_one_and_update(
                    {'_id': ObjectId(reference)},
                    {'$set': {'_lock': False}}
                )
                raise PaymentError('The timeout set to process the payment has finished')

            # Check that the request user is authorized to end the payment
            if request.user.userprofile.current_organization != order.owner_organization:
                raise PaymentError('You are not authorized to execute the payment')

            transactions = pending_info['transactions']

            # Get the payment client
            # Load payment client
            cln_str = settings.PAYMENT_CLIENT
            client_package, client_class = cln_str.rsplit('.', 1)

            payment_client = getattr(importlib.import_module(client_package), client_class)

            # build the payment client
            client = payment_client(order)
            order.sales_ids = client.end_redirection_payment(token, payer_id)
            order.save()

            charging_engine = ChargingEngine(order)
            charging_engine.end_charging(transactions, concept)

        except Exception as e:

            # Rollback the purchase if existing
            if order is not None and raw_order is not None:
                if concept == 'initial':
                    # Set the order to failed in the ordering API
                    # Set all items as Failed, mark the whole order as failed
                    self.ordering_client.update_items_state(raw_order, 'Failed')
                    order.delete()
                else:
                    order.state = 'paid'
                    order.pending_payment = {}
                    order.save()

            expl = ' due to an unexpected error'
            err_code = 500
            if isinstance(e, PaymentError) or isinstance(e, ValueError):
                expl = ': ' + unicode(e)
                err_code = 403

            msg = 'The payment has been canceled' + expl
            return build_response(request, err_code, msg)

        # Change states of TMForum resources (orderItems, products, etc)
        # depending on the concept of the payment

        states_processors = {
            'initial': self._set_initial_states,
            'renovation': self._set_renovation_states,
            'use': self._set_renovation_states
        }
        states_processors[concept](transactions, raw_order, order)

        # _lock is set to false
        db.wstore_order.find_one_and_update(
            {'_id': ObjectId(reference)},
            {'$set': {'_lock': False}}
        )

        return build_response(request, 200, 'Ok')
Example #22
0
    def read(self, request, reference):
        purchase = None
        try:
            token = request.GET.get('token')
            payer_id = request.GET.get('PayerID', '')

            db = get_database_connection()

            # Uses an atomic operation to get and set the _lock value in the purchase
            # document
            pre_value = db.wstore_purchase.find_and_modify(
                query={'_id': ObjectId(reference)},
                update={'$set': {'_lock': True}}
            )

            # If the value of _lock before setting it to true was true, means
            # that the time out function has acquired it previously so the
            # view ends
            if '_lock' in pre_value and pre_value['_lock']:
                raise Exception('')

            purchase = Purchase.objects.get(ref=reference)

            # Check that the request user is authorized to end the payment
            if request.user.userprofile.current_organization != purchase.owner_organization:
                raise Exception()

            # If the purchase state value is different from pending means that
            # the timeout function has completely ended before acquire the resource
            # so _lock is set to false and the view ends
            if purchase.state != 'pending':
                db.wstore_purchase.find_and_modify(
                    query={'_id': ObjectId(reference)},
                    update={'$set': {'_lock': False}}
                )
                raise Exception('')

            pending_info = purchase.contract.pending_payment

            # Get the payment client
            # Load payment client
            cln_str = settings.PAYMENT_CLIENT
            client_class = cln_str.split('.')[-1]
            client_package = cln_str.partition('.' + client_class)[0]

            payment_client = getattr(__import__(client_package, globals(), locals(), [client_class], -1), client_class)

            # build the payment client
            client = payment_client(purchase)
            client.end_redirection_payment(token, payer_id)

            charging_engine = ChargingEngine(purchase)
            accounting = None
            if 'accounting' in pending_info:
                accounting = pending_info['accounting']

            charging_engine.end_charging(pending_info['price'], pending_info['concept'], pending_info['related_model'], accounting)
        except:
            # Rollback the purchase if existing
            if purchase is not None:
                rollback(purchase)

            context = {
                'title': 'Payment Canceled',
                'message': 'Your payment has been canceled. An error occurs or the timeout has finished, if you want to acquire the offering purchase it again in WStore.'
            }
            return render(request, 'err_msg.html', context)

        # Check if is the first payment
        if len(purchase.contract.charges) == 1:

            if purchase.organization_owned:
                org = purchase.owner_organization
                org.offerings_purchased.append(purchase.offering.pk)
                org.save()
            else:
                # Add the offering to the user profile
                user_profile = UserProfile.objects.get(user=purchase.customer)
                user_profile.offerings_purchased.append(purchase.offering.pk)
                user_profile.save()

            notify_provider(purchase)

        # _lock is set to false
        db.wstore_purchase.find_and_modify(
            query={'_id': reference},
            update={'$set': {'_lock': False}}
        )

        # Return the confirmation web page
        context = {
            'title': 'Payment Confirmed',
            'message': 'Your payment has been received. To download the resources and the invoice go to the offering details page.'
        }
        return render(request, 'err_msg.html', context)
Example #23
0
    def test_purchase_offering_update_payment(self):

        current_org = Organization.objects.get(pk="91000aba8e06ac2199999999")
        current_org.offerings_purchased.append('61000aba8e05ac2115f022f9')
        self._user.userprofile.current_organization = current_org

        self._user.userprofile.get_current_roles = MagicMock()
        self._user.userprofile.get_current_roles.return_value = ['customer']
        self._user.userprofile.save()

        # Create the request
        data = {
            'offering': {
                'organization': 'test_organization',
                'name': 'test_offering',
                'version': '1.1'
            },
            'plan_label': 'update',
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment': {
                'method': 'paypal'
            }
        }
        request = self.factory.post(
            '/api/contracting/',
            json.dumps(data),
            HTTP_ACCEPT='application/json; charset=utf-8',
            content_type='application/json; charset=utf-8')
        request.user = self._user

        # Test purchase view
        views.create_purchase = MagicMock(name='create_purchase')
        offering = Offering.objects.get(pk="71000aba8e05ac2115f022ff")

        from datetime import datetime
        purchase = Purchase.objects.create(
            customer=self._user,
            date=datetime.now(),
            offering=offering,
            organization_owned=True,
            state='paid',
            tax_address={
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            bill=['/media/bills/11111111111.pdf'])
        views.create_purchase.return_value = purchase

        views.get_current_site = MagicMock(name='get_current_site')
        views.get_current_site.return_value = Site.objects.get(name='antares')
        views.Context.objects.get = MagicMock(name='get')
        context = MagicMock()
        context.user_refs = []
        views.Context.objects.get.return_value = context

        purchase_collection = views.PurchaseCollection(
            permitted_methods=('POST', ))

        response = purchase_collection.create(request)

        # Check response
        body_response = json.loads(response.content)
        self.assertEquals(len(body_response['bill']), 1)
        self.assertEquals(body_response['bill'][0],
                          '/media/bills/11111111111.pdf')
        payment_info = {
            'tax_address': {
                'street': 'test street',
                'postal': '28000',
                'city': 'test city',
                'country': 'test country'
            },
            'payment_method': 'paypal',
            'plan': 'update',
            'accepted': False
        }
        views.create_purchase.assert_called_once_with(
            self._user, offering, org_owned=True, payment_info=payment_info)

        # Test Contract creation
        offering.offering_description = {
            'pricing': {
                'price_plans': [{
                    'title':
                    'Price plan',
                    'currency':
                    'EUR',
                    'price_components': [{
                        'label': 'Price component update',
                        'unit': 'single payment',
                        'value': '1.0'
                    }]
                }]
            }
        }
        offering.save()

        from wstore.charging_engine.charging_engine import ChargingEngine
        charging = ChargingEngine(purchase,
                                  payment_method='paypal',
                                  plan='update')
        charging._create_purchase_contract()

        # Refresh purchase
        purchase = Purchase.objects.get(pk=purchase.pk)
        contract = purchase.contract

        # Check contract pricing model
        self.assertTrue('single_payment' in contract.pricing_model)
        self.assertEquals(len(contract.pricing_model['single_payment']), 1)
        payment = contract.pricing_model['single_payment'][0]
        self.assertEquals(payment['label'], 'Price component update')
        self.assertEquals(payment['value'], '1.0')

        self.assertFalse('subscription' in contract.pricing_model)
        self.assertFalse('pay_per_use' in contract.pricing_model)
Example #24
0
    def read(self, request, reference):
        purchase = None
        try:
            token = request.GET.get('token')
            payer_id = request.GET.get('PayerID', '')

            db = get_database_connection()

            # Uses an atomic operation to get and set the _lock value in the purchase
            # document
            pre_value = db.wstore_purchase.find_and_modify(
                query={'_id': ObjectId(reference)},
                update={'$set': {
                    '_lock': True
                }})

            # If the value of _lock before setting it to true was true, means
            # that the time out function has acquired it previously so the
            # view ends
            if '_lock' in pre_value and pre_value['_lock']:
                raise Exception('')

            purchase = Purchase.objects.get(ref=reference)

            # Check that the request user is authorized to end the payment
            if request.user.userprofile.current_organization != purchase.owner_organization:
                raise Exception()

            # If the purchase state value is different from pending means that
            # the timeout function has completely ended before acquire the resource
            # so _lock is set to false and the view ends
            if purchase.state != 'pending':
                db.wstore_purchase.find_and_modify(
                    query={'_id': ObjectId(reference)},
                    update={'$set': {
                        '_lock': False
                    }})
                raise Exception('')

            pending_info = purchase.contract.pending_payment

            # Get the payment client
            # Load payment client
            cln_str = settings.PAYMENT_CLIENT
            client_class = cln_str.split('.')[-1]
            client_package = cln_str.partition('.' + client_class)[0]

            payment_client = getattr(
                __import__(client_package, globals(), locals(), [client_class],
                           -1), client_class)

            # build the payment client
            client = payment_client(purchase)
            client.end_redirection_payment(token, payer_id)

            charging_engine = ChargingEngine(purchase)
            accounting = None
            if 'accounting' in pending_info:
                accounting = pending_info['accounting']

            charging_engine.end_charging(pending_info['price'],
                                         pending_info['concept'],
                                         pending_info['related_model'],
                                         accounting)
        except:
            # Rollback the purchase if existing
            if purchase is not None:
                rollback(purchase)

            context = {
                'title':
                'Payment Canceled',
                'message':
                'Your payment has been canceled. An error occurs or the timeout has finished, if you want to acquire the offering purchase it again in WStore.'
            }
            return render(request, 'err_msg.html', context)

        # Check if is the first payment
        if len(purchase.contract.charges) == 1:

            if purchase.organization_owned:
                org = purchase.owner_organization
                org.offerings_purchased.append(purchase.offering.pk)
                org.save()
            else:
                # Add the offering to the user profile
                user_profile = UserProfile.objects.get(user=purchase.customer)
                user_profile.offerings_purchased.append(purchase.offering.pk)
                user_profile.save()

            notify_provider(purchase)

        # _lock is set to false
        db.wstore_purchase.find_and_modify(query={'_id': reference},
                                           update={'$set': {
                                               '_lock': False
                                           }})

        # Return the confirmation web page
        context = {
            'title':
            'Payment Confirmed',
            'message':
            'Your payment has been received. To download the resources and the invoice go to the offering details page.'
        }
        return render(request, 'err_msg.html', context)