Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

temp: POC on PayPal paritial refund with multiple items #316

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions commerce_coordinator/apps/commercetools/catalog_info/edx_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,30 @@ def get_edx_is_sanctioned(order: CTOrder) -> bool:
return get_edx_order_workflow_state_key(order) == TwoUKeys.SDN_SANCTIONED_ORDER_STATE


def get_edx_refund_info(payment: CTPayment) -> decimal:
refund_amount = decimal.Decimal(0.00)
def _cents_to_dollars(in_amount):
return in_amount.cent_amount / pow(
10, in_amount.fraction_digits
if hasattr(in_amount, 'fraction_digits')
else 2
)

def get_line_item_discounted_price(order: CTOrder, return_line_item_id: str):
if len(order.line_items) > 1:
for line_item in get_edx_items(order):
if line_item.id == return_line_item_id:
return _cents_to_dollars(line_item.total_price)
elif len(order.line_items) == 1:
return _cents_to_dollars(order.total_price)
return decimal.Decimal(0.00)

def get_edx_refund_info(payment: CTPayment, order: CTOrder, return_line_item_id: str) -> (decimal.Decimal, str):
interaction_id = None

for transaction in payment.transactions:
if transaction.type == TransactionType.CHARGE: # pragma no cover
refund_amount += decimal.Decimal(typed_money_to_string(transaction.amount, money_as_decimal_string=True))
interaction_id = transaction.interaction_id
return refund_amount, interaction_id

refund_amount = get_line_item_discounted_price(order, return_line_item_id)
print('\n\n\n\n\n line item price = ',refund_amount, '\n\n\n\n\n')

return refund_amount, interaction_id
9 changes: 8 additions & 1 deletion commerce_coordinator/apps/commercetools/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ class OrderRefundRequested(OpenEdxPublicFilter):
filter_type = "org.edx.coordinator.commercetools.order.refund.requested.v1"

@classmethod
def run_filter(cls, order_id, return_line_item_return_id, message_id):
def run_filter(
cls,
order_id,
return_line_item_return_id,
return_line_item_id,
message_id
):
"""
Call the PipelineStep(s) defined for this filter.
Arguments:
Expand All @@ -21,4 +27,5 @@ def run_filter(cls, order_id, return_line_item_return_id, message_id):
"""
return super().run_pipeline(order_id=order_id,
return_line_item_return_id=return_line_item_return_id,
return_line_item_id=return_line_item_id,
message_id=message_id)
16 changes: 11 additions & 5 deletions commerce_coordinator/apps/commercetools/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
)
from commerce_coordinator.apps.commercetools.catalog_info.edx_utils import (
get_edx_refund_info,
get_edx_successful_payment_info
get_edx_successful_payment_info, get_edx_items
)
from commerce_coordinator.apps.commercetools.clients import CommercetoolsAPIClient
from commerce_coordinator.apps.commercetools.constants import COMMERCETOOLS_ORDER_MANAGEMENT_SYSTEM
from commerce_coordinator.apps.commercetools.data import order_from_commercetools
from commerce_coordinator.apps.commercetools.utils import create_retired_fields, has_refund_transaction
from commerce_coordinator.apps.commercetools.utils import create_retired_fields, has_refund_transaction, \
has_full_refund_transaction
from commerce_coordinator.apps.core.constants import PipelineCommand
from commerce_coordinator.apps.core.exceptions import InvalidFilterType
from commerce_coordinator.apps.rollout.utils import (
Expand Down Expand Up @@ -132,7 +133,12 @@ def run_filter(self, active_order_management_system, order_number, **kwargs): #
class FetchOrderDetailsByOrderID(PipelineStep):
""" Fetch the order details and if we can, set the PaymentIntent """

def run_filter(self, active_order_management_system, order_id, **kwargs): # pylint: disable=arguments-differ
def run_filter(self,
active_order_management_system,
order_id,
return_line_item_id,
**kwargs
): # pylint: disable=arguments-differ
"""
Execute a filter with the signature specified.
Arguments:
Expand Down Expand Up @@ -169,10 +175,10 @@ def run_filter(self, active_order_management_system, order_id, **kwargs): # pyl

if payment:
ct_payment = ct_api_client.get_payment_by_key(payment.interface_id)
refund_amount, ct_transaction_interaction_id = get_edx_refund_info(ct_payment)
refund_amount, ct_transaction_interaction_id = get_edx_refund_info(ct_payment, ct_order, return_line_item_id)
ret_val['amount_in_cents'] = refund_amount
ret_val['ct_transaction_interaction_id'] = ct_transaction_interaction_id
ret_val['has_been_refunded'] = has_refund_transaction(ct_payment)
ret_val['has_been_refunded'] = is_commercetools_line_item_already_refunded(ct_order, return_line_item_id) or has_full_refund_transaction(ct_payment)
ret_val['payment_data'] = ct_payment
else:
ret_val['amount_in_cents'] = decimal.Decimal(0.00)
Expand Down
3 changes: 3 additions & 0 deletions commerce_coordinator/apps/commercetools/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,6 @@ def get_return_info(self):

def get_return_line_item_return_id(self):
return self.get_return_info().get('id', None)

def get_return_line_item_id(self):
return self.get_return_info().get('lineItemId', None)
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def fulfill_order_returned_signal(**kwargs):
async_result = fulfill_order_returned_signal_task.delay(
order_id=kwargs['order_id'],
return_line_item_return_id=kwargs['return_line_item_return_id'],
return_line_item_id=kwargs['return_line_item_id'],
message_id=kwargs['message_id']
)
return async_result.id
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ def fulfill_order_sanctioned_message_signal_task(
def fulfill_order_returned_signal_task(
order_id,
return_line_item_return_id,
return_line_item_id,
message_id
):
"""Celery task for an order return (and refunded) message."""
Expand Down Expand Up @@ -278,7 +279,10 @@ def _prepare_segment_event_properties(in_order, return_line_item_return_id):
# Return payment if payment id is set
if payment_intent_id is not None:
result = OrderRefundRequested.run_filter(
order_id=order_id, return_line_item_return_id=return_line_item_return_id, message_id=message_id
order_id=order_id,
return_line_item_return_id=return_line_item_return_id,
return_line_item_id=return_line_item_id,
message_id=message_id
)

if 'refund_response' in result and result['refund_response']:
Expand Down
6 changes: 4 additions & 2 deletions commerce_coordinator/apps/commercetools/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,20 @@ def post(self, request):
message_details.is_valid(raise_exception=True)
order_id = message_details.data['order_id']
return_line_item_return_id = message_details.get_return_line_item_return_id()
return_line_item_id = message_details.get_return_line_item_id()
message_id = message_details.data['message_id']

if self._is_running(tag, order_id): # pragma no cover
if self._is_running(tag, return_line_item_return_id): # pragma no cover
self.meta_should_mark_not_running = False
return Response(status=status.HTTP_200_OK)
else:
self.mark_running(tag, order_id)
self.mark_running(tag, return_line_item_return_id)

fulfill_order_returned_signal.send_robust(
sender=self,
order_id=order_id,
return_line_item_return_id=return_line_item_return_id,
return_line_item_id=return_line_item_id,
message_id=message_id
)

Expand Down
13 changes: 11 additions & 2 deletions commerce_coordinator/apps/paypal/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self):
),
)

def refund_order(self, capture_id):
def refund_order(self, capture_id, amount):
"""
Capture PayPal refund.

Expand All @@ -34,7 +34,16 @@ def refund_order(self, capture_id):
paypal_client = self.paypal_client
payments_controller: PaymentsController = paypal_client.payments

collect = {"capture_id": capture_id, "prefer": "return=representation"}
collect = {
"capture_id": capture_id,
"prefer": "return=representation",
"body": {
"amount": {
"value": amount,
"currency_code": "USD"
}
}
}
refund_response = payments_controller.captures_refund(collect)

if refund_response.body:
Expand Down
2 changes: 1 addition & 1 deletion commerce_coordinator/apps/paypal/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def run_filter(

try:
paypal_client = PayPalClient()
paypal_refund_response = paypal_client.refund_order(capture_id=ct_transaction_interaction_id)
paypal_refund_response = paypal_client.refund_order(capture_id=ct_transaction_interaction_id, amount=amount_in_cents)

return {
'refund_response': paypal_refund_response,
Expand Down
8 changes: 6 additions & 2 deletions commerce_coordinator/apps/rollout/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

from commercetools.platform.models import Order as CTOrder
from commercetools.platform.models import ReturnItem as CTReturnItem
from commercetools.platform.models import ReturnPaymentState



def is_legacy_order(order_number: str) -> bool:
Expand Down Expand Up @@ -38,8 +40,10 @@ def is_commercetools_line_item_already_refunded(order: CTOrder, order_line_item_

return_info_return_items = get_order_return_info_return_items(order)

return len(list(filter(lambda item: item.line_item_id == order_line_item_id, return_info_return_items))) >= 1

return len(list(filter(
lambda item: item.line_item_id == order_line_item_id and item.payment_state == ReturnPaymentState.REFUNDED,
return_info_return_items
))) >= 1

def is_commercetools_stripe_refund(source_system: str) -> bool:
"""
Expand Down
Loading