diff --git a/commerce_coordinator/apps/commercetools/catalog_info/constants.py b/commerce_coordinator/apps/commercetools/catalog_info/constants.py index 62b86e56..6f2ed259 100644 --- a/commerce_coordinator/apps/commercetools/catalog_info/constants.py +++ b/commerce_coordinator/apps/commercetools/catalog_info/constants.py @@ -79,5 +79,7 @@ class Languages: ) STRIPE_PAYMENT_STATUS_INTERFACE_CODE_SUCCEEDED = "succeeded" +PAYPAL_PAYMENT_STATUS_INTERFACE_CODE_SUCCEEDED = "COMPLETED" EDX_STRIPE_PAYMENT_INTERFACE_NAME = "stripe_edx" +EDX_PAYPAL_PAYMENT_INTERFACE_NAME = "paypal_edx" diff --git a/commerce_coordinator/apps/commercetools/catalog_info/edx_utils.py b/commerce_coordinator/apps/commercetools/catalog_info/edx_utils.py index 78a32931..601f1366 100644 --- a/commerce_coordinator/apps/commercetools/catalog_info/edx_utils.py +++ b/commerce_coordinator/apps/commercetools/catalog_info/edx_utils.py @@ -7,13 +7,15 @@ from commercetools.platform.models import Payment as CTPayment from commercetools.platform.models import Product as CTProduct from commercetools.platform.models import ProductVariant as CTProductVariant -from commercetools.platform.models import TransactionType +from commercetools.platform.models import TransactionType, TransactionState from commerce_coordinator.apps.commercetools.catalog_info.constants import ( EDX_STRIPE_PAYMENT_INTERFACE_NAME, STRIPE_PAYMENT_STATUS_INTERFACE_CODE_SUCCEEDED, EdXFieldNames, - TwoUKeys + TwoUKeys, + PAYPAL_PAYMENT_STATUS_INTERFACE_CODE_SUCCEEDED, + EDX_PAYPAL_PAYMENT_INTERFACE_NAME ) from commerce_coordinator.apps.commercetools.catalog_info.utils import typed_money_to_string @@ -48,23 +50,38 @@ def get_edx_lms_user_name(customer: CTCustomer): return customer.custom.fields[EdXFieldNames.LMS_USER_NAME] -def get_edx_successful_stripe_payment(order: CTOrder) -> Union[CTPayment, None]: +def get_edx_successful_payment(order: CTOrder) -> Union[CTPayment, None]: for pr in order.payment_info.payments: pmt = pr.obj + print('\n\n\n\n\n get_edx_successful_payment pmt', pmt.payment_status, pmt.payment_method_info.payment_interface) if pmt.payment_status.interface_code == STRIPE_PAYMENT_STATUS_INTERFACE_CODE_SUCCEEDED \ and pmt.payment_method_info.payment_interface == EDX_STRIPE_PAYMENT_INTERFACE_NAME and \ pmt.interface_id: - return pmt - return None - + print('\n\n\n\n\n get_edx_successful_payment returning stripe', pmt, EDX_STRIPE_PAYMENT_INTERFACE_NAME) + return pmt, EDX_STRIPE_PAYMENT_INTERFACE_NAME + elif pmt.payment_status.interface_code == PAYPAL_PAYMENT_STATUS_INTERFACE_CODE_SUCCEEDED \ + and pmt.payment_method_info.payment_interface == EDX_PAYPAL_PAYMENT_INTERFACE_NAME and \ + pmt.interface_id: + print('\n\n\n\n\n get_edx_successful_payment returning paypal', pmt, EDX_PAYPAL_PAYMENT_INTERFACE_NAME) + return pmt, EDX_PAYPAL_PAYMENT_INTERFACE_NAME + return None, None -def get_edx_payment_intent_id(order: CTOrder) -> Union[str, None]: - pmt = get_edx_successful_stripe_payment(order) +def get_edx_payment_interface_id(order: CTOrder) -> Union[str, None]: + pmt, psp = get_edx_successful_payment(order) if pmt: - return pmt.interface_id + return pmt.interface_id, psp + return None, None + +def get_edx_paypal_payment_transaction_id(order: CTOrder) -> Union[str, None]: + pmt, psp = get_edx_successful_payment(order) + if pmt and psp == EDX_PAYPAL_PAYMENT_INTERFACE_NAME: + print('\n\n\n\n\n pmt.transactions', pmt.transactions) + for transaction in pmt.transactions: + if transaction.type == TransactionType.CHARGE and transaction.state == TransactionState.SUCCESS: + print('\n\n\n\n\n transaction.interaction_id', transaction.interaction_id) + return transaction.interaction_id return None - def get_edx_order_workflow_state_key(order: CTOrder) -> Optional[str]: order_workflow_state = None if order.state and order.state.obj: # it should never be that we have one and not the other. # pragma no cover @@ -78,7 +95,7 @@ def get_edx_is_sanctioned(order: CTOrder) -> bool: def get_edx_refund_amount(order: CTOrder) -> decimal: refund_amount = decimal.Decimal(0.00) - pmt = get_edx_successful_stripe_payment(order) + pmt, psp = get_edx_successful_payment(order) for transaction in pmt.transactions: if transaction.type == TransactionType.CHARGE: # pragma no cover refund_amount += decimal.Decimal(typed_money_to_string(transaction.amount, money_as_decimal_string=True)) diff --git a/commerce_coordinator/apps/commercetools/clients.py b/commerce_coordinator/apps/commercetools/clients.py index 0df503e9..556108db 100644 --- a/commerce_coordinator/apps/commercetools/clients.py +++ b/commerce_coordinator/apps/commercetools/clients.py @@ -47,7 +47,7 @@ from commerce_coordinator.apps.commercetools.utils import ( find_refund_transaction, handle_commercetools_error, - translate_stripe_refund_status_to_transaction_status + translate_stripe_refund_status_to_transaction_status, translate_paypal_refund_status_to_transaction_status ) from commerce_coordinator.apps.core.constants import ORDER_HISTORY_PER_SYSTEM_REQ_LIMIT @@ -484,6 +484,48 @@ def create_return_payment_transaction( handle_commercetools_error(err, context) raise err + def create_paypal_return_payment_transaction( + self, payment, paypal_refund) -> CTPayment: + try: + logger.info(f"[CommercetoolsAPIClient] - Creating refund transaction for payment with ID {payment.id} " + f"following successful Paypal refund {paypal_refund.get('id')}") + + amount_as_money = CTMoney( + cent_amount=payment.amount_planned.cent_amount, + currency_code=payment.amount_planned.currency_code.upper() + ) + print('\n\n\n\n amount_as_money', amount_as_money) + transaction_draft = TransactionDraft( + type=TransactionType.REFUND, + amount=amount_as_money, + timestamp=datetime.datetime.utcfromtimestamp( + getattr(paypal_refund, 'create_time', datetime.datetime.now().timestamp())), + state=translate_paypal_refund_status_to_transaction_status(paypal_refund.get('status')), + interaction_id=paypal_refund.get('id') + ) + print('\n\n\n\n transaction_draft', transaction_draft) + print('\n\n\n\n datetime', datetime.datetime.utcfromtimestamp( + getattr(paypal_refund, 'create_time', datetime.datetime.now().timestamp()))) + + add_transaction_action = PaymentAddTransactionAction( + transaction=transaction_draft + ) + + returned_payment = self.base_client.payments.update_by_id( + id=payment.id, + version=payment.version, + actions=[add_transaction_action] + ) + print('\n\n\n\n returned_payment', returned_payment) + + + return returned_payment + except CommercetoolsError as err: + context = f"Unable to create refund payment transaction for "\ + f"payment {payment.id} and paypal refund {paypal_refund.get('id')}" + handle_commercetools_error(err, context) + raise err + def update_line_item_transition_state_on_fulfillment(self, order_id: str, order_version: int, line_item_id: str, item_quantity: int, from_state_id: str, new_state_key: str) -> CTOrder: @@ -584,3 +626,39 @@ def retire_customer_anonymize_fields(self, customer_id: str, customer_version: i f"with ID: {customer_id}, after LMS retirement with " f"error correlation id {err.correlation_id} and error/s: {err.errors}") raise err + + +from paypalserversdk.http.auth.o_auth_2 import ClientCredentialsAuthCredentials +from paypalserversdk.logging.configuration.api_logging_configuration import ( + LoggingConfiguration, + RequestLoggingConfiguration, + ResponseLoggingConfiguration, +) +from paypalserversdk.paypalserversdk_client import PaypalserversdkClient +from paypalserversdk.controllers.orders_controller import OrdersController +from paypalserversdk.controllers.payments_controller import PaymentsController +from paypalserversdk.models.amount_with_breakdown import AmountWithBreakdown +from paypalserversdk.models.checkout_payment_intent import CheckoutPaymentIntent +from paypalserversdk.models.order_request import OrderRequest +from paypalserversdk.models.purchase_unit_request import PurchaseUnitRequest +from paypalserversdk.api_helper import ApiHelper + +paypal_client: PaypalserversdkClient = PaypalserversdkClient( + client_credentials_auth_credentials=ClientCredentialsAuthCredentials( + o_auth_client_id=settings.PAYPAL_CLIENT_ID, + o_auth_client_secret=settings.PAYPAL_CLIENT_SECRET, + ), + logging_configuration=LoggingConfiguration( + log_level=logging.INFO, + # Disable masking of sensitive headers for Sandbox testing. + # This should be set to True (the default if unset)in production. + mask_sensitive_headers=False, + request_logging_config=RequestLoggingConfiguration( + log_headers=True, log_body=True + ), + response_logging_config=ResponseLoggingConfiguration( + log_headers=True, log_body=True + ), + ), +) +payments_controller: PaymentsController = paypal_client.payments diff --git a/commerce_coordinator/apps/commercetools/pipeline.py b/commerce_coordinator/apps/commercetools/pipeline.py index f2a938fa..9ae207e1 100644 --- a/commerce_coordinator/apps/commercetools/pipeline.py +++ b/commerce_coordinator/apps/commercetools/pipeline.py @@ -11,9 +11,11 @@ from openedx_filters.exceptions import OpenEdxFilterException from requests import HTTPError +from commerce_coordinator.apps.commercetools.catalog_info.constants import EDX_STRIPE_PAYMENT_INTERFACE_NAME, \ + EDX_PAYPAL_PAYMENT_INTERFACE_NAME from commerce_coordinator.apps.commercetools.catalog_info.edx_utils import ( - get_edx_payment_intent_id, - get_edx_refund_amount + get_edx_payment_interface_id, + get_edx_refund_amount, ) from commerce_coordinator.apps.commercetools.clients import CommercetoolsAPIClient from commerce_coordinator.apps.commercetools.constants import COMMERCETOOLS_ORDER_MANAGEMENT_SYSTEM @@ -106,19 +108,22 @@ def run_filter(self, active_order_management_system, order_number, **kwargs): # "order_data": ct_order, } - intent_id = get_edx_payment_intent_id(ct_order) + interface_id, payment_service_provider = get_edx_payment_interface_id(ct_order) + print('\n\n\n\n\n FetchOrderDetailsByOrderNumber interface_id, payment_service_provider', interface_id, payment_service_provider) - if intent_id: - ct_payment = ct_api_client.get_payment_by_key(intent_id) - ret_val['payment_intent_id'] = intent_id + if interface_id: + ct_payment = ct_api_client.get_payment_by_key(interface_id) + ret_val['payment_intent_id'] = interface_id ret_val['amount_in_cents'] = get_edx_refund_amount(ct_order) ret_val['has_been_refunded'] = has_refund_transaction(ct_payment) ret_val['payment_data'] = ct_payment + ret_val['payment_service_provider'] = payment_service_provider else: ret_val['payment_intent_id'] = None ret_val['amount_in_cents'] = decimal.Decimal(0.00) ret_val['has_been_refunded'] = False ret_val['payment_data'] = None + ret_val['payment_service_provider'] = None return ret_val except CommercetoolsError as err: # pragma no cover @@ -160,19 +165,21 @@ def run_filter(self, active_order_management_system, order_id, **kwargs): # pyl "order_id": ct_order.id } - intent_id = get_edx_payment_intent_id(ct_order) + interface_id, payment_service_provider = get_edx_payment_interface_id(ct_order) - if intent_id: - ct_payment = ct_api_client.get_payment_by_key(intent_id) - ret_val['payment_intent_id'] = intent_id + if interface_id and payment_service_provider: + ct_payment = ct_api_client.get_payment_by_key(interface_id) + ret_val['payment_intent_id'] = interface_id ret_val['amount_in_cents'] = get_edx_refund_amount(ct_order) ret_val['has_been_refunded'] = has_refund_transaction(ct_payment) ret_val['payment_data'] = ct_payment + ret_val['payment_service_provider'] = payment_service_provider else: ret_val['payment_intent_id'] = None ret_val['amount_in_cents'] = decimal.Decimal(0.00) ret_val['has_been_refunded'] = False ret_val['payment_data'] = None + ret_val['payment_service_provider'] = None return ret_val except CommercetoolsError as err: # pragma no cover @@ -296,8 +303,14 @@ def run_filter( active_order_management_system, payment_data, has_been_refunded, + payment_service_provider, **kwargs ): # pylint: disable=arguments-differ + print('\n\n\n\n CreateReturnPaymentTransaction refund_response', refund_response) + print('\n\n\n\n CreateReturnPaymentTransaction payment_data', payment_data) + print('\n\n\n\n CreateReturnPaymentTransaction has_been_refunded', has_been_refunded) + print('\n\n\n\n CreateReturnPaymentTransaction payment_service_provider', payment_service_provider) + print('\n\n\n\n CreateReturnPaymentTransaction **kwargs', kwargs) """ Execute a filter with the signature specified. Arguments: @@ -328,11 +341,17 @@ def run_filter( payment_key = refund_response['payment_intent'] payment_on_order = ct_api_client.get_payment_by_key(payment_key) - updated_payment = ct_api_client.create_return_payment_transaction( - payment_id=payment_on_order.id, - payment_version=payment_on_order.version, - stripe_refund=refund_response - ) + if payment_service_provider == EDX_STRIPE_PAYMENT_INTERFACE_NAME: + updated_payment = ct_api_client.create_return_payment_transaction( + payment_id=payment_on_order.id, + payment_version=payment_on_order.version, + stripe_refund=refund_response + ) + elif payment_service_provider == EDX_PAYPAL_PAYMENT_INTERFACE_NAME: + updated_payment = ct_api_client.create_paypal_return_payment_transaction( + payment=payment_on_order, + paypal_refund=refund_response + ) return { 'returned_payment': updated_payment diff --git a/commerce_coordinator/apps/commercetools/sub_messages/signals_delayed.py b/commerce_coordinator/apps/commercetools/sub_messages/signals_delayed.py index bb953af4..a353c0a0 100644 --- a/commerce_coordinator/apps/commercetools/sub_messages/signals_delayed.py +++ b/commerce_coordinator/apps/commercetools/sub_messages/signals_delayed.py @@ -17,6 +17,8 @@ @log_receiver(logger) def fulfill_order_placed_message_signal(**kwargs): """ CoordinatorSignal receiver to invoke Celery Task fulfill_order_placed_message_signal_task""" + print('\n\n\n\nfulfill_order_placed_message_signal starting celery') + async_result = fulfill_order_placed_message_signal_task.delay( order_id=kwargs['order_id'], line_item_state_id=kwargs['line_item_state_id'], diff --git a/commerce_coordinator/apps/commercetools/sub_messages/tasks.py b/commerce_coordinator/apps/commercetools/sub_messages/tasks.py index 3ec13378..f847e121 100644 --- a/commerce_coordinator/apps/commercetools/sub_messages/tasks.py +++ b/commerce_coordinator/apps/commercetools/sub_messages/tasks.py @@ -14,9 +14,8 @@ get_edx_lms_user_id, get_edx_lms_user_name, get_edx_order_workflow_state_key, - get_edx_payment_intent_id, get_edx_product_course_run_key, - is_edx_lms_order + is_edx_lms_order, get_edx_payment_interface_id ) from commerce_coordinator.apps.commercetools.clients import CommercetoolsAPIClient from commerce_coordinator.apps.commercetools.constants import EMAIL_NOTIFICATION_CACHE_TTL_SECS @@ -49,6 +48,9 @@ def fulfill_order_placed_message_signal_task( tag = "fulfill_order_placed_message_signal_task" + print('\n\n\n\ninside fulfill_order_placed_message_signal_task = ', f'[CT-{tag}] Processing order {order_id}, ' + f'line item {line_item_state_id}, source system {source_system}, message id: {message_id}') + logger.info(f'[CT-{tag}] Processing order {order_id}, ' f'line item {line_item_state_id}, source system {source_system}, message id: {message_id}') @@ -61,6 +63,8 @@ def fulfill_order_placed_message_signal_task( f'message id: {message_id}') return False + print('\n\n\n\ninside fulfill_order_placed_message_signal_task order = ', order) + try: customer = client.get_customer_by_id(order.customer_id) except CommercetoolsError as err: # pragma no cover @@ -68,6 +72,8 @@ def fulfill_order_placed_message_signal_task( f'CT error {err}, {err.errors}, message id: {message_id}') return False + print('\n\n\n\ninside fulfill_order_placed_message_signal_task customer = ', customer) + if not (customer and order and is_edx_lms_order(order)): logger.debug(f'[CT-{tag}] order {order_id} is not an edX order, message id: {message_id}') @@ -121,6 +127,8 @@ def fulfill_order_placed_message_signal_task( serializer.is_valid(raise_exception=True) # pragma no cover payload = serializer.validated_data + print('\n\n\n\nsending fulfill_order_placed_signal payload = ', payload) + fulfill_order_placed_signal.send_robust( sender=fulfill_order_placed_message_signal_task, **payload @@ -265,7 +273,10 @@ def _prepare_segment_event_properties(in_order, return_line_item_return_id): logger.debug(f'[CT-{tag}] order {order_id} is not an edX order, message id: {message_id}') return True - payment_intent_id = get_edx_payment_intent_id(order) + print('\n\n\n\ninside fulfill_order_returned_signal_task order = ', order) + payment_intent_id, psp = get_edx_payment_interface_id(order) + print('\n\n\n\ninside fulfill_order_returned_signal_task payment_intent_id = ', payment_intent_id, psp) + lms_user_name = get_edx_lms_user_name(customer) lms_user_id = get_edx_lms_user_id(customer) diff --git a/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py b/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py index b7d88260..d32d0530 100644 --- a/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py +++ b/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py @@ -317,7 +317,7 @@ def test_correct_arguments_passed_valid_stripe_refund( mock_values.customer_mock.assert_called_once_with(mock_values.customer_id) _stripe_api_mock.return_value.refund_payment_intent.assert_called_once() - @patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.get_edx_payment_intent_id') + @patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.get_edx_payment_interface_id') @patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.OrderRefundRequested.run_filter') def test_refund_already_charged( self, diff --git a/commerce_coordinator/apps/commercetools/utils.py b/commerce_coordinator/apps/commercetools/utils.py index 01116d09..c0852b29 100644 --- a/commerce_coordinator/apps/commercetools/utils.py +++ b/commerce_coordinator/apps/commercetools/utils.py @@ -184,6 +184,18 @@ def translate_stripe_refund_status_to_transaction_status(stripe_refund_status: s } return translations.get(stripe_refund_status.lower(), stripe_refund_status) +def translate_paypal_refund_status_to_transaction_status(paypal_refund_status: str): + """ + Utility to translate stripe's refund object's status attribute to a valid CT transaction state + """ + translations = { + 'completed': TransactionState.SUCCESS, + } + print('\n\n\n\n\n\n translate_paypal_refund_status_to_transaction_status paypal_refund_status ', paypal_refund_status, type(paypal_refund_status)) + print('\n\n\n\n\n\n translate_paypal_refund_status_to_transaction_status return ', translations.get(paypal_refund_status.lower(), paypal_refund_status)) + print('\n\n\n\n\n\n translate_paypal_refund_status_to_transaction_status TransactionState.SUCCESS ', TransactionState.SUCCESS) + return translations.get(paypal_refund_status.lower(), paypal_refund_status) + def _create_retired_hash_withsalt(value_to_retire, salt): """ diff --git a/commerce_coordinator/apps/commercetools/views.py b/commerce_coordinator/apps/commercetools/views.py index f159248a..b105bb4a 100644 --- a/commerce_coordinator/apps/commercetools/views.py +++ b/commerce_coordinator/apps/commercetools/views.py @@ -46,6 +46,7 @@ def post(self, request): message_details = OrderLineItemMessageInputSerializer(data=input_data) message_details.is_valid(raise_exception=True) + print('\n\n\n\nmessage_details.data', message_details.data) order_id = message_details.data['order_id'] line_item_state_id = message_details.data['to_state']['id'] message_id = message_details.data['message_id'] @@ -56,6 +57,8 @@ def post(self, request): else: self.mark_running(tag, order_id) + print('\n\n\n\nOrderFulfilview sending signal', order_id, line_item_state_id, message_id) + fulfill_order_placed_message_signal.send_robust( sender=self, order_id=order_id, diff --git a/commerce_coordinator/apps/frontend_app_ecommerce/views.py b/commerce_coordinator/apps/frontend_app_ecommerce/views.py index a46a98f9..39218274 100644 --- a/commerce_coordinator/apps/frontend_app_ecommerce/views.py +++ b/commerce_coordinator/apps/frontend_app_ecommerce/views.py @@ -58,6 +58,8 @@ def get(self, request): order_number=params['order_number'] ) + print('\n\n\n\n\nredirect_url:', redirect_url, '\n\n\n\n\n') + if redirect_url: redirect = HttpResponseRedirect(redirect_url, status=HTTP_303_SEE_OTHER) redirect.headers[HttpHeadersNames.CACHE_CONTROL.value] = "max-age=2591000" # 16ish mins short of 30 days diff --git a/commerce_coordinator/apps/rollout/pipeline.py b/commerce_coordinator/apps/rollout/pipeline.py index f05728ff..de4605b9 100644 --- a/commerce_coordinator/apps/rollout/pipeline.py +++ b/commerce_coordinator/apps/rollout/pipeline.py @@ -43,7 +43,9 @@ def run_filter(self, request): # pylint: disable=arguments-differ commercetools_available_course = None if course_run and is_redirect_to_commercetools_enabled_for_user(request): + print('\n\n\n\n\ncourse_run, is_redirect_to_commercetools_enabled_for_user', course_run, is_redirect_to_commercetools_enabled_for_user(request)) try: + course_run = 'course-v1:MichiganX+InjuryPreventionX+1T2021' ct_api_client = CommercetoolsAPIClient() commercetools_available_course = ct_api_client.get_product_variant_by_course_run(course_run) except HTTPError as exc: # pragma no cover @@ -53,6 +55,7 @@ def run_filter(self, request): # pylint: disable=arguments-differ f'for course_run: {course_run} with exception: {exc}' ) + print('\n\n\n\n\ncommercetools_available_course', commercetools_available_course) if commercetools_available_course and not is_user_enterprise_learner(request): active_order_management_system = COMMERCETOOLS_FRONTEND elif sku: diff --git a/commerce_coordinator/apps/rollout/waffle.py b/commerce_coordinator/apps/rollout/waffle.py index 87144a98..0f88918e 100644 --- a/commerce_coordinator/apps/rollout/waffle.py +++ b/commerce_coordinator/apps/rollout/waffle.py @@ -13,4 +13,5 @@ def is_redirect_to_commercetools_enabled_for_user(request): """ Check if REDIRECT_TO_COMMERCETOOLS_CHECKOUT flag is enabled. """ + return True return waffle.flag_is_active(request, REDIRECT_TO_COMMERCETOOLS_CHECKOUT) diff --git a/commerce_coordinator/apps/stripe/pipeline.py b/commerce_coordinator/apps/stripe/pipeline.py index 740acbc9..d40089cc 100644 --- a/commerce_coordinator/apps/stripe/pipeline.py +++ b/commerce_coordinator/apps/stripe/pipeline.py @@ -7,6 +7,10 @@ from openedx_filters import PipelineStep from stripe.error import StripeError +from commerce_coordinator.apps.commercetools.catalog_info.constants import EDX_STRIPE_PAYMENT_INTERFACE_NAME, \ + EDX_PAYPAL_PAYMENT_INTERFACE_NAME +from commerce_coordinator.apps.commercetools.catalog_info.edx_utils import get_edx_paypal_payment_transaction_id +from commerce_coordinator.apps.commercetools.clients import CommercetoolsAPIClient, payments_controller from commerce_coordinator.apps.core.constants import PaymentMethod, PipelineCommand from commerce_coordinator.apps.stripe.clients import StripeAPIClient from commerce_coordinator.apps.stripe.constants import Currency @@ -214,21 +218,26 @@ class GetPaymentIntentReceipt(PipelineStep): """ Pull the receipt if the payment_intent is set """ # pylint: disable=unused-argument - def run_filter(self, payment_intent_id=None, **params): + def run_filter(self, payment_service_provider, payment_intent_id=None, **params): tag = type(self).__name__ - if payment_intent_id is None: - logger.debug(f'[{tag}] payment_intent_id not set, skipping.') - return PipelineCommand.CONTINUE.value + if payment_service_provider == EDX_STRIPE_PAYMENT_INTERFACE_NAME: + if payment_intent_id is None: + logger.debug(f'[{tag}] payment_intent_id not set, skipping.') + return PipelineCommand.CONTINUE.value - stripe_api_client = StripeAPIClient() + stripe_api_client = StripeAPIClient() - payment_intent = stripe_api_client.retrieve_payment_intent( - payment_intent_id, - ["latest_charge"] - ) + payment_intent = stripe_api_client.retrieve_payment_intent( + payment_intent_id, + ["latest_charge"] + ) - receipt_url = payment_intent.latest_charge.receipt_url + receipt_url = payment_intent.latest_charge.receipt_url + elif payment_service_provider == EDX_PAYPAL_PAYMENT_INTERFACE_NAME: + # this can be achieved by introducing a new step in the filter. doing here for poc + receipt_url = 'https://sandbox.paypal.com/myaccount/activities' + payment_intent = None return { 'payment_intent': payment_intent, @@ -245,6 +254,7 @@ def run_filter( self, order_id, payment_intent_id, + payment_service_provider, amount_in_cents, has_been_refunded, **kwargs @@ -271,19 +281,43 @@ def run_filter( 'refund_response': "charge_already_refunded" } - stripe_api_client = StripeAPIClient() + print('\n\n\n\n\n\n Inside RefundPaymentIntent \n\n\n\n\n\n') + + if payment_service_provider == EDX_STRIPE_PAYMENT_INTERFACE_NAME: + stripe_api_client = StripeAPIClient() + + try: + ret_val = stripe_api_client.refund_payment_intent( + payment_intent_id=payment_intent_id, + amount=amount_in_cents, + order_uuid=order_id + ) + return { + 'refund_response': ret_val + } + except StripeError as ex: # pragma: no cover + logger.info(f'[CT-{tag}] Unsuccessful Stripe refund with details:' + f'[order_id: {order_id}, payment_intent_id: {payment_intent_id}' + f'message_id: {kwargs["message_id"]}') + raise StripeIntentRefundAPIError from ex + elif payment_service_provider == EDX_PAYPAL_PAYMENT_INTERFACE_NAME: + # this can be achieved by introducing a new step in the filter. doing here for poc + from paypalserversdk.api_helper import ApiHelper + import json + + print('\n\n\n\n\n\n Refunding the Paypal Order here \n\n\n\n\n\n') + ct_api_client = CommercetoolsAPIClient() + ct_order = ct_api_client.get_order_by_id(order_id=order_id) + capture_id = get_edx_paypal_payment_transaction_id(ct_order) + collect = {"capture_id": capture_id, "prefer": "return=minimal"} + refund_response = payments_controller.captures_refund(collect) + + print("\n\n\n\n\n PAYPAL REFUND result:", refund_response) + print("\n\n\n\n\n PAYPAL REFUND result.body:", refund_response.body) + print("\n\n\n\n\n PAYPAL REFUND ApiHelper.json_serialize result.body:", ApiHelper.json_serialize(refund_response.body)) + print("\n\n\n\n\n PAYPAL REFUND json.loads(ApiHelper.json_serialize(refund_response.body)):", json.loads(ApiHelper.json_serialize(refund_response.body))) + print("\n\n\n\n\n PAYPAL REFUND json.loads(ApiHelper.json_serialize(refund_response.body)).id:", json.loads(ApiHelper.json_serialize(refund_response.body)).get('id')) - try: - ret_val = stripe_api_client.refund_payment_intent( - payment_intent_id=payment_intent_id, - amount=amount_in_cents, - order_uuid=order_id - ) return { - 'refund_response': ret_val + 'refund_response': json.loads(ApiHelper.json_serialize(refund_response.body)) } - except StripeError as ex: # pragma: no cover - logger.info(f'[CT-{tag}] Unsuccessful Stripe refund with details:' - f'[order_id: {order_id}, payment_intent_id: {payment_intent_id}' - f'message_id: {kwargs["message_id"]}') - raise StripeIntentRefundAPIError from ex diff --git a/commerce_coordinator/settings/local.py b/commerce_coordinator/settings/local.py index 17bd5c97..bfbb5a86 100644 --- a/commerce_coordinator/settings/local.py +++ b/commerce_coordinator/settings/local.py @@ -7,6 +7,7 @@ 'host.docker.internal', 'localhost', '.ngrok-free.app', + '.share.zrok.io' ) INSTALLED_APPS += ( @@ -17,6 +18,7 @@ CORS_ORIGIN_WHITELIST = [ 'http://localhost:1996', # frontend-app-ecommerce 'http://localhost:1998', # frontend-app-payment + 'https://bo1qumbnpc79.share.zrok.io', ] # END CORS CONFIGURATION @@ -138,8 +140,8 @@ 'log_level': 'debug', 'max_network_retries': 0, 'proxy': None, - 'publishable_key': 'SET-ME-PLEASE', - 'secret_key': 'SET-ME-PLEASE', + 'publishable_key': 'pk_test_51Ls7QSH4caH7G0X1prLj26IWylx2AP5vGA3nd4GMGPRXjVQlA9HATsF2aC5QhbeGNnTr2xijDLQPQzqefrMvHvke00L5eGLK4N', + 'secret_key': 'sk_test_51Ls7QSH4caH7G0X1EYdtyB8nd9mZxOkpm8qDg4cv2GPVYZkP0tGttGn7DAJgBZMWyxme3Gjro8u6ClqbnDwxcAH9001GSoURRk', 'source_system_identifier': 'edx/commerce_coordinator?v=1', 'webhook_endpoint_secret': 'SET-ME-PLEASE', },