def post(self, request, *args, **kwargs):
        """
        Place an order.

        We fetch the txn details again and then proceed with oscar's standard
        payment details view for placing the order.
        """
        if buyer_pays_on_paypal():
            return HttpResponseBadRequest()  # we don't expect any user here if we let users buy on PayPal

        try:
            self.token = request.POST['token']
        except KeyError:
            # Probably suspicious manipulation if we get here
            messages.error(self.request, self.error_msg)
            return redirect('basket:summary')

        try:
            self.txn = fetch_transaction_details(self.token)
        except HttpError as e:
            logger.warning('Unable to fetch transaction details for token %s: %s', self.token, e.message)
            # Unable to fetch txn details from PayPal - we have to bail out
            messages.error(request, self.error_msg)
            return redirect('basket:summary')

        # Reload frozen basket which is specified in the URL
        basket = self.load_frozen_basket(kwargs['basket_id'])
        if not basket:
            messages.error(self.request, self.error_msg)
            return redirect('basket:summary')

        submission = self.build_submission(basket=basket)
        return self.submit(**submission)
    def get(self, request, *args, **kwargs):
        """
        Fetch details about the successful transaction from PayPal.
        We use these details to show a preview of the order with a 'submit' button to place it.
        The preview step can be skipped with `PAYPAL_BUYER_PAYS_ON_PAYPAL=True` inside settings.
        """
        try:
            self.payer_id = request.GET['PayerID']
            self.token = request.GET['token']
        except KeyError:
            # Manipulation - redirect to basket page with warning message
            logger.warning('Missing GET params on success response page')
            messages.error(self.request,
                           _('Unable to determine PayPal transaction details'))
            return redirect('basket:summary')

        try:
            self.txn = fetch_transaction_details(self.token)
        except HttpError as e:
            messages.error(self.request, e.message)
            logger.warning(
                'Unable to fetch transaction details for token %s: %s',
                self.token, e.message)
            message = _(
                'A problem occurred communicating with PayPal - please try again later'
            )
            messages.error(self.request, message)
            return redirect('basket:summary')

        # Reload frozen basket which is specified in the URL
        kwargs['basket'] = self.load_frozen_basket(kwargs['basket_id'])
        if not kwargs['basket']:
            logger.warning('Unable to load frozen basket with ID %s',
                           kwargs['basket_id'])
            message = _(
                'No basket was found that corresponds to your PayPal transaction'
            )
            messages.error(self.request, message)
            return redirect('basket:summary')

        if buyer_pays_on_paypal():
            return self.submit(**self.build_submission(
                basket=kwargs['basket']))

        logger.info(
            'Basket #%s - showing preview with payer ID %s and token %s',
            kwargs['basket'].id, self.payer_id, self.token)

        return super().get(request, *args, **kwargs)
def get_paypal_url(basket, user=None, shipping_address=None, shipping_method=None, host=None):
    """
    Return the URL for a PayPal Express transaction.

    This involves registering the txn with PayPal to get a one-time
    URL.  If a shipping method and shipping address are passed, then these are
    given to PayPal directly - this is used within when using PayPal as a
    payment method.
    """

    if basket.currency:
        currency = basket.currency
    else:
        currency = getattr(settings, 'PAYPAL_CURRENCY', 'GBP')
    if host is None:
        host = Site.objects.get_current().domain

    use_https = getattr(settings, 'PAYPAL_CALLBACK_HTTPS', True)
    scheme = 'https' if use_https else 'http'

    view_name = 'express-checkout-handle-order' if buyer_pays_on_paypal() else 'express-checkout-success-response'
    return_url_path = reverse(view_name, kwargs={'basket_id': basket.id})
    return_url = f'{scheme}://{host}{return_url_path}'

    cancel_url_path = reverse('express-checkout-cancel-response', kwargs={'basket_id': basket.id})
    cancel_url = f'{scheme}://{host}{cancel_url_path}'

    address = None
    if basket.is_shipping_required():
        if shipping_address is not None:
            address = shipping_address
        elif user is not None:
            addresses = user.addresses.all().order_by('-is_default_for_billing')
            if addresses.exists():
                address = addresses.first()

    shipping_charge = None
    order_total = basket.total_incl_tax
    if shipping_method:
        shipping_charge = shipping_method.calculate(basket).incl_tax
        order_total += shipping_charge

    intent = get_intent()

    result = PaymentProcessor().create_order(
        basket=basket,
        currency=currency,
        return_url=return_url,
        cancel_url=cancel_url,
        order_total=order_total,
        address=address,
        shipping_charge=shipping_charge,
        intent=intent,
    )

    Transaction.objects.create(
        order_id=result.id,
        amount=order_total,
        currency=currency,
        status=result.status,
        intent=intent,
    )

    for link in result.links:
        if link.rel == 'approve':
            return link.href