kopia lustrzana https://github.com/longclawshop/longclaw
174 wiersze
6.4 KiB
Python
174 wiersze
6.4 KiB
Python
'''
|
|
Shipping logic and payment capture API
|
|
'''
|
|
from django.utils.module_loading import import_string
|
|
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework import permissions, status
|
|
from rest_framework.response import Response
|
|
from basket.utils import get_basket_items, destroy_basket
|
|
from orders.models import Order, OrderItem, Address
|
|
from checkout.models import ShippingCountry
|
|
from checkout import app_settings, serializers
|
|
from checkout.utils import PaymentError
|
|
|
|
gateway = import_string(app_settings.PAYMENT_GATEWAY)()
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([permissions.AllowAny])
|
|
def create_token(request):
|
|
''' Generic function for creating a payment token from the
|
|
payment backend. Some payment backends (e.g. braintree) support creating a payment
|
|
token, which should be imported from the backend as 'get_token'
|
|
'''
|
|
token = gateway.get_token(request)
|
|
return Response({'token': token}, status=status.HTTP_200_OK)
|
|
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([permissions.AllowAny])
|
|
def capture_payment(request):
|
|
'''
|
|
Capture the payment for a basket and create an order
|
|
|
|
request.data should contain:
|
|
|
|
'address': Dict with the following fields:
|
|
shipping_name
|
|
shipping_address_line1
|
|
shipping_address_city
|
|
shipping_address_zip
|
|
shipping_address_country
|
|
billing_name
|
|
billing_address_line1
|
|
billing_address_city
|
|
billing_address_zip
|
|
billing_address_country
|
|
|
|
'email': Email address of the customer
|
|
'ip': IP address of the customer
|
|
'shipping': The shipping rate (standard or premium)
|
|
'''
|
|
|
|
# Get the contents of the basket
|
|
items, _ = get_basket_items(request)
|
|
|
|
# Compute basket total
|
|
total = 0
|
|
for item in items:
|
|
total += item.total()
|
|
|
|
# Create the address for the order
|
|
address = request.data['address']
|
|
shipping_address, _ = Address.objects.get_or_create(name=address['shipping_name'],
|
|
line_1=address['shipping_address_line1'],
|
|
city=address['shipping_address_city'],
|
|
postcode=address['shipping_address_zip'],
|
|
country=address['shipping_address_country'])
|
|
shipping_address.save()
|
|
|
|
address = request.data['address']
|
|
billing_address, _ = Address.objects.get_or_create(name=address['billing_name'],
|
|
line_1=address['billing_address_line1'],
|
|
city=address['billing_address_city'],
|
|
postcode=address['billing_address_zip'],
|
|
country=address['billing_address_country'])
|
|
billing_address.save()
|
|
# Create the order
|
|
order = Order(
|
|
email=request.data['email'],
|
|
ip_address=request.data.get('ip', '0.0.0.0'),
|
|
shipping_address=shipping_address,
|
|
billing_address=billing_address
|
|
)
|
|
order.save()
|
|
|
|
# Create the order items
|
|
for item in items:
|
|
order_item = OrderItem(
|
|
product=item.product,
|
|
quantity=item.quantity,
|
|
order=order
|
|
)
|
|
order_item.save()
|
|
|
|
postage = float(request.data['shipping_rate'])
|
|
try:
|
|
gateway.create_payment(request, float(total)+postage)
|
|
# Once the order has been successfully taken, we can empty the basket
|
|
destroy_basket(request)
|
|
response = Response(data={"order_id": order.id}, status=status.HTTP_201_CREATED)
|
|
except PaymentError as err:
|
|
order.status = Order.CANCELLED
|
|
order.note = "Payment failed"
|
|
order.save()
|
|
response = Response(data={"message": err.message, "order_id": order.id},
|
|
status=status.HTTP_400_BAD_REQUEST)
|
|
return response
|
|
|
|
class InvalidShippingRate(Exception):
|
|
pass
|
|
|
|
class InvalidShippingCountry(Exception):
|
|
pass
|
|
|
|
def get_shipping_cost(country_code, option):
|
|
try:
|
|
obj = ShippingCountry.objects.get(country_code=country_code)
|
|
if option == 'standard':
|
|
return {"rate": obj.standard_rate,
|
|
"description": obj.standard_rate_description,
|
|
"carrier": obj.standard_rate_carrier}
|
|
elif option == 'premium':
|
|
return {"rate": obj.premium_rate,
|
|
"description": obj.premium_rate_description,
|
|
"carrier": obj.premium_rate_carrier}
|
|
else:
|
|
raise InvalidShippingRate
|
|
|
|
except ShippingCountry.DoesNotExist:
|
|
if app_settings.DEFAULT_SHIPPING_ENABLED:
|
|
return {"rate": app_settings.DEFAULT_SHIPPING_RATE,
|
|
"description": "Standard shipping to rest of world",
|
|
"carrier": app_settings.DEFAULT_SHIPPING_CARRIER}
|
|
else:
|
|
raise InvalidShippingCountry
|
|
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes({permissions.AllowAny})
|
|
def shipping_cost(request):
|
|
''' Returns the shipping cost for a given country
|
|
If the shipping cost for the given country has not been set, it will
|
|
fallback to the default shipping cost if it has been enabled in the app
|
|
settings
|
|
'''
|
|
try:
|
|
code = request.query_params.get('country_code')
|
|
except AttributeError:
|
|
return Response(data={"message": "No country code supplied"},
|
|
status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
option = request.query_params.get('shipping_option', 'standard')
|
|
try:
|
|
data = get_shipping_cost(code, option)
|
|
response = Response(data=data, status=status.HTTP_200_OK)
|
|
except InvalidShippingRate:
|
|
response = Response(data={"message": "Shipping option {} is invalid".format(option)},
|
|
status=status.HTTP_400_BAD_REQUEST)
|
|
except InvalidShippingCountry:
|
|
response = Response(data={"message": "Shipping to {} is not available".format(code)},
|
|
status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
return response
|
|
|
|
|
|
@api_view(["GET"])
|
|
@permission_classes([permissions.AllowAny])
|
|
def get_shipping_countries(request):
|
|
''' Get all shipping countries
|
|
'''
|
|
queryset = ShippingCountry.objects.all()
|
|
serializer = serializers.ShippingCountrySerializer(queryset, many=True)
|
|
return Response(data=serializer.data, status=status.HTTP_200_OK)
|