Esempio n. 1
0
    def end_redirection_payment(self, token, payer_id):
        payment = paypalrestsdk.Payment.find(token)

        if not payment.execute({"payer_id": payer_id}):
            raise PaymentError("The payment cannot be executed: " + payment.error)

        sales_ids = []
        response = payment.to_dict()
        for t in response['transactions']:
            for r in t['related_resources']:
                sales_ids.append(r['sale']['id'])

        return sales_ids
Esempio n. 2
0
    def start_redirection_payment(self, transactions):

        # Build URL
        url = Context.objects.all()[0].site.domain
        if url[-1] != '/':
            url += '/'

        # Build payment object
        payment = paypalrestsdk.Payment({
            'intent': 'sale',
            'payer': {
                'payment_method': 'paypal'
            },
            'redirect_urls': {
                'return_url': url + 'payment?action=accept&ref=' + self._order.pk,
                'cancel_url': url + 'payment?action=cancel&ref=' + self._order.pk
            },
            'transactions': [{
                'amount': {
                    'total': unicode(t['price']),
                    'currency': t['currency']
                },
                'description': t['description']
            } for t in transactions]
        })

        # Create Payment
        if not payment.create():

            # Check if the error is due to a problem supporting multiple transactions
            details = payment.error['details']
            if len(transactions) > 1 and len(details) == 1 and \
                    details[0]['issue'] == 'Only single payment transaction currently supported':

                # Aggregate transactions in a single payment if possible
                current_curr = transactions[0]['currency']
                total = Decimal('0')
                items = ''

                for t in transactions:
                    # Only if all the transactions have the same currency they can be aggregated
                    if t['currency'] != current_curr:
                        break

                    total += Decimal(t['price'])
                    items += t['item'] + ':' + t['price'] + '<' + t['description'] + '>'
                else:
                    msg = 'All your order items have been aggregated, since PayPal is not able '
                    msg += 'to process multiple transactions in this moment.                   '
                    msg += 'Order composed of the following items ' + items

                    self.start_redirection_payment([{
                        'price': unicode(total),
                        'currency': current_curr,
                        'description': msg
                    }])
                    return

            raise PaymentError("The payment cannot be created: " + details[0]["issue"])

        # Extract URL where redirecting the customer
        response = payment.to_dict()
        for l in response['links']:
            if l['rel'] == 'approval_url':
                self._checkout_url = l['href']
                break
Esempio n. 3
0
    def refund(self, sale_id):
        sale = paypalrestsdk.Sale.find(sale_id)

        if not sale.refund({}):
            raise PaymentError("The refund cannot be completed: " + sale.error)
    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')