Skip to content

Commit

Permalink
Transfrom Payment's and Transaction's MoneyFields into immutable Deci…
Browse files Browse the repository at this point in the history
…mal fields
  • Loading branch information
Pacu2 committed Oct 30, 2018
1 parent 8f245fb commit c629988
Show file tree
Hide file tree
Showing 31 changed files with 325 additions and 248 deletions.
102 changes: 53 additions & 49 deletions docs/guides/payments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Payments
========

Integrating a new Payment Gateway into Saleor
-------------------------------------------
---------------------------------------------

We are using a universal flow, that each provider should fulfill, there are
several methods that should be implemented.
Expand Down Expand Up @@ -33,8 +33,8 @@ Example
.. code-block:: python
def get_transaction_token(**connection_params: Dict) -> str:
gateway - get_gateway(**connection_params)
transaction_token - gateway.transaction_token.generate()
gateway = get_gateway(**connection_params)
transaction_token = gateway.transaction_token.generate()
return transaction_token
authorize(payment, transaction_token, **connection_params)
Expand All @@ -54,15 +54,16 @@ Example
**connection_params: Dict) -> Tuple[Transaction, str]:
# Handle connecting to the gateway and sending the auth request here
response - gateway.auth(token-transaction_token)
txn - Transaction.objects.create(
payment-payment,
transaction_type-TransactionType.AUTH,
amount-payment.total.amount,
gateway_response-get_gateway_response(response),
token-response.transaction.id,
is_success-response.is_success)
response = gateway.auth(token=transaction_token)
txn = Transaction.objects.create(
payment=payment,
transaction_type=TransactionType.AUTH,
amount=payment.total,
currency=payment.currency,
gateway_response=get_gateway_response(response),
token=response.transaction.id,
is_success=response.is_success)
return txn, response['error']
Expand All @@ -82,20 +83,21 @@ Example
**connection_params: Dict) -> Tuple[Transaction, str]:
# Please note that token from the last AUTH transaction should be used
capture_txn - payment.transactions.filter(
transaction_type-TransactionType.CAPTURE).first()
transaction_token - capture_txn.token
capture_txn = payment.transactions.filter(
transaction_type=TransactionType.CAPTURE).first()
transaction_token = capture_txn.token
# Handle connecting to the gateway and sending the refund request here
response - gateway.refund(token-transaction_token)
txn - create_transaction(
payment-payment,
transaction_type-TransactionType.REFUND,
amount-amount,
token-response.transaction.id,
is_success-response.is_success,
gateway_response-get_gateway_response(response))
response = gateway.refund(token=transaction_token)
txn = create_transaction(
payment=payment,
transaction_type=TransactionType.REFUND,
amount=amount,
currency=payment.currency,
token=response.transaction.id,
is_success=response.is_success,
gateway_response=get_gateway_response(response))
return txn, response['error']
capture(payment, amount, **connection_params)
Expand All @@ -114,20 +116,21 @@ Example
**connection_params: Dict) -> Tuple[Transaction, str]:
# Please note that token from the last AUTH transaction should be used
auth_transaction - payment.transactions.filter(
transaction_type-TransactionType.AUTH).first()
transaction_token - auth_transaction.token
auth_transaction = payment.transactions.filter(
transaction_type=TransactionType.AUTH).first()
transaction_token = auth_transaction.token
# Handle connecting to the gateway and sending the capture request here
response - gateway.capture(token-transaction_token)
txn - create_transaction(
payment-payment,
transaction_type-TransactionType.CAPTURE,
amount-amount,
token-response.transaction.id,
is_success-response.is_success,
gateway_response-get_gateway_response(response))
response = gateway.capture(token=transaction_token)
txn = create_transaction(
payment=payment,
transaction_type=TransactionType.CAPTURE,
amount=amount,
currency=payment.currency,
token=response.transaction.id,
is_success=response.is_success,
gateway_response=get_gateway_response(response))
return txn, response['error']
void(payment, **connection_params)
Expand All @@ -145,20 +148,21 @@ Example
**connection_params: Dict) -> Tuple[Transaction, str]:
# Please note that token from the last AUTH transaction should be used
auth_transaction - payment.transactions.filter(
transaction_type-TransactionType.AUTH).first()
transaction_token - auth_transaction.token
auth_transaction = payment.transactions.filter(
transaction_type=TransactionType.AUTH).first()
transaction_token = auth_transaction.token
# Handle connecting to the gateway and sending the void request here
response - gateway.void(token-transaction_token)
txn - create_transaction(
payment-payment,
transaction_type-TransactionType.VOID,
amount-payment.total.amount,
gateway_response-get_gateway_response(response),
token-response.transaction.id,
is_success-response.is_success)
response = gateway.void(token=transaction_token)
txn = create_transaction(
payment=payment,
transaction_type=TransactionType.VOID,
amount=payment.total,
currency=payment.currency,
gateway_response=get_gateway_response(response),
token=response.transaction.id,
is_success=response.is_success)
return txn, response['error']
Parameters
Expand Down Expand Up @@ -192,7 +196,7 @@ Adding new payment provider in the settings

.. code-block:: python
PAYMENT_PROVIDERS - {
PAYMENT_PROVIDERS = {
'braintree': {
'module': 'saleor.payment.providers.braintree',
'connection_params': {
Expand Down
2 changes: 1 addition & 1 deletion saleor/checkout/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ def create_order(cart, tracking_code, discounts, taxes):
def is_fully_paid(cart: Cart):
payments = cart.payments.filter(is_active=True)
total_paid = sum(
[p.total.amount for p in payments])
[p.total for p in payments])
return total_paid >= cart.get_total().gross.amount


Expand Down
34 changes: 20 additions & 14 deletions saleor/core/utils/random_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,8 @@
from ...order.utils import update_order_status
from ...page.models import Page
from ...payment import ChargeStatus, TransactionType
<<<<<<< HEAD
<<<<<<< HEAD
from ...payment.models import PaymentMethod
=======
from ...payment.utils import get_billing_data
from ...payment.models import Payment
>>>>>>> Rename PaymentMethod to Payment
=======
from ...payment.models import Payment
from ...payment.utils import get_billing_data
>>>>>>> Add documentation on adding new payment gateway, fix style and inconsistencies in naming
from ...product.models import (
Attribute, AttributeValue, Category, Collection, Product, ProductImage,
ProductType, ProductVariant)
Expand Down Expand Up @@ -423,12 +414,27 @@ def create_payment(order):
customer_ip_address=fake.ipv4(),
is_active=True,
order=order,
total=order.total.gross,
total=order.total.gross.amount,
currency=order.total.gross.currency,
**get_billing_data(order))
if status == ChargeStatus.CHARGED:
payment.captured_amount = payment.total
payment.save()
create_transactions(payment)

provider, provider_params = get_provider(payment.variant)
transaction_token = provider.get_transaction_token(**provider_params)

# Create authorization transaction
gateway_authorize(payment, transaction_token, **provider_params)
# 20% chance to void the transaction at this stage
if random.choice([0, 0, 0, 0, 1]):
gateway_void(payment, **provider_params)
return payment
# 25% to end the payment at the authorization stage
if not random.choice([1, 1, 1, 0]):
return payment
# Create capture transaction
gateway_capture(payment, payment.total, **provider_params)
# 25% to refund the payment
if random.choice([0, 0, 0, 1]):
gateway_refund(payment, payment.total, **provider_params)
return payment


Expand Down
14 changes: 7 additions & 7 deletions saleor/dashboard/order/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,11 @@ class OrderNoteForm(forms.Form):


class ManagePaymentForm(forms.Form):
amount = MoneyField(
amount = forms.DecimalField(
label=pgettext_lazy(
'Payment management form (capture, refund, void)', 'Amount'),
max_digits=settings.DEFAULT_MAX_DIGITS,
decimal_places=settings.DEFAULT_DECIMAL_PLACES,
currency=settings.DEFAULT_CURRENCY)
decimal_places=settings.DEFAULT_DECIMAL_PLACES)

def __init__(self, *args, **kwargs):
self.payment = kwargs.pop('payment')
Expand All @@ -273,9 +272,9 @@ def payment_error(self, message):
'Payment form error', 'Payment gateway error: %s') % message)

def try_payment_action(self, action):
money = self.cleaned_data['amount']
amount = self.cleaned_data['amount']
try:
action(money.amount)
action(amount)
except (PaymentError, ValueError) as e:
self.payment_error(str(e))
return False
Expand Down Expand Up @@ -355,8 +354,9 @@ def clean(self):
def save(self):
# FIXME add more fields to the payment method
defaults = {
'total': self.order.total.gross,
'captured_amount': self.order.total.gross,
'total': self.order.total.gross.amount,
'captured_amount': self.order.total.gross.amount,
'currency': self.order.total.gross.currency,
**get_billing_data(self.order)}
Payment.objects.get_or_create(
variant=CustomPaymentChoices.MANUAL,
Expand Down
16 changes: 8 additions & 8 deletions saleor/dashboard/order/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@ def capture_payment(request, order_pk, payment_pk):
orders = Order.objects.confirmed().prefetch_related('payments')
order = get_object_or_404(orders, pk=order_pk)
payment = get_object_or_404(order.payments, pk=payment_pk)
amount = order.total.quantize('0.01').gross
amount = order.total.gross
form = CapturePaymentForm(
request.POST or None, payment=payment, initial={'amount': amount})
request.POST or None, payment=payment,
initial={'amount': amount.amount})
if form.is_valid() and form.capture():
amount = form.cleaned_data['amount']
msg = pgettext_lazy(
'Dashboard message related to a payment',
'Captured %(amount)s') % {'amount': prices_i18n.amount(amount)}
Expand All @@ -174,7 +174,7 @@ def capture_payment(request, order_pk, payment_pk):
return redirect('dashboard:order-details', order_pk=order.pk)
status = 400 if form.errors else 200
ctx = {
'captured': payment.captured_amount,
'captured': amount,
'form': form,
'order': order,
'payment': payment}
Expand All @@ -195,7 +195,8 @@ def refund_payment(request, order_pk, payment_pk):
amount = form.cleaned_data['amount']
msg = pgettext_lazy(
'Dashboard message related to a payment',
'Refunded %(amount)s') % {'amount': prices_i18n.amount(amount)}
'Refunded %(amount)s') % {
'amount': prices_i18n.amount(payment.get_captured_amount())}
order.events.create(
parameters={'amount': amount},
user=request.user,
Expand All @@ -204,7 +205,7 @@ def refund_payment(request, order_pk, payment_pk):
return redirect('dashboard:order-details', order_pk=order.pk)
status = 400 if form.errors else 200
ctx = {
'captured': payment.captured_amount,
'captured': payment.get_captured_amount(),
'form': form,
'order': order,
'payment': payment}
Expand All @@ -228,8 +229,7 @@ def void_payment(request, order_pk, payment_pk):
return redirect('dashboard:order-details', order_pk=order.pk)
status = 400 if form.errors else 200
ctx = {
'captured': payment.captured_amount, 'form': form, 'order': order,
'payment': payment}
'form': form, 'order': order, 'payment': payment}
return TemplateResponse(request, 'dashboard/order/modal/void.html', ctx,
status=status)

Expand Down
2 changes: 1 addition & 1 deletion saleor/graphql/checkout/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ def mutate(cls, root, info, checkout_id):
return CheckoutComplete(order=order, errors=errors)
# capture payment
try:
gateway_capture(payment, payment.total.amount)
gateway_capture(payment, payment.total)
except PaymentError as exc:
msg = str(exc)
cls.add_error(field=None, message=msg, errors=errors)
Expand Down
8 changes: 5 additions & 3 deletions saleor/graphql/order/mutations/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ def clean_order_update_shipping(order, method, errors):
return errors


def try_payment_action(action, money, errors):
def try_payment_action(action, amount, errors):
try:
action(money)
action(amount)
except (PaymentError, ValueError) as e:
errors.append(Error(field='payment', message=str(e)))

Expand Down Expand Up @@ -292,7 +292,9 @@ def mutate(cls, root, info, id):
return OrderMarkAsPaid(errors=errors)
# FIXME add more fields to the payment method
defaults = {
'total': order.total.gross, 'captured_amount': order.total.gross,
'total': order.total.gross.amount,
'captured_amount': order.total.gross.amount,
'currency': order.total.gross.currency,
**get_billing_data(order)}
Payment.objects.get_or_create(
variant=CustomPaymentChoices.MANUAL,
Expand Down

0 comments on commit c629988

Please sign in to comment.