Last active
January 18, 2022 13:05
-
-
Save meetchandan/0945c034415bbaffbe23a954391b2823 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import requests | |
from decimal import Decimal | |
''' | |
Few points: | |
1. MP would only tell the total amount to refund, it would not do any | |
calculations of how much is already refunded etc etc | |
2. MP would not talk to temporal directly(?) | |
3. Didn't feel the need to use signals, | |
4. Can be done in temporal-python-sdk | |
''' | |
# the below table can be maintained in MP to show on A1 etc | |
# this can also be used to determine whether refund-workflow should be triggered or not (collect != collected) | |
class SalesPaymentInfo: | |
order_nr = sa.Column(sa.String(100), nullable=False, index=True) | |
order_wallet_amount = sa.Column(CCY, nullable=False, server_default='0') | |
order_wallet_captured = sa.Column(CCY, nullable=False, server_default='0') | |
payment_authorized = sa.Column(CCY, nullable=False, server_default='0') | |
payment_captured = sa.Column(CCY, nullable=False, server_default='0') | |
payment_refunded = sa.Column(CCY, nullable=False, server_default='0') | |
order_collect_from_customer = sa.Column(CCY, nullable=False) | |
order_collected_from_customer = sa.Column(CCY, nullable=False) | |
def get_order_refund_total(order_nr): | |
''' | |
this would just return how much should be refunded for the order | |
doesn't consider past refunded amount | |
''' | |
return 10.0 | |
def get_order_wallet_amount(order_nr): | |
''' | |
returns the amount originally paid from credits when the order was placed. | |
this value would never change in the lifetime of the order | |
''' | |
return 1.0 | |
# MP | |
@subscriber("trigger_rts") | |
def trigger_rts(order_nr): | |
# this would be an mp-payment endpoint: | |
if requests.get('/is-wf-running', {'exref_type': 'order', 'mp_code': 'noon', 'exref_value': order_nr}): | |
delay_pubsub_message(60 * 5) | |
total_to_refund = get_order_refund_total(order_nr) | |
order_wallet_amount = get_order_wallet_amount(order_nr) | |
# workflow_id should not be passed from here? | |
requests.post('/rts-worklow', payload={'workflow_id': order_nr, 'total_to_refund': total_to_refund, | |
'mp_code': 'noon', 'exref_type': 'ORDER', 'exref_nr': order_nr, | |
'order_wallet_amount': order_wallet_amount}) | |
# MP-payment | |
@app.get('/is-wf-running') | |
def is_wf_running(exref_type, exref_value): | |
return False | |
class RTSPayload: | |
exref_type: str | |
exref_value: str | |
mp_code: str | |
total_to_refund: Decimal | |
order_wallet_amouont: Decimal | |
payment_method_code: str | |
@app.post('/rts-workflow') | |
def trigger_rts(payload: RTSPayload): | |
workflow_id = f"{payload.mp_code}-{payload.exref_value}" | |
# below would be async | |
async_start_workflow('rts', 'RTSWorkflow', payload.__dict__, workflow_id=workflow_id) | |
async def async_start_workflow(temporal_namespace, workflow_name, *args, workflow_id=None, skip_on_dup=False, **kwargs): | |
temporal_client = get_client(temporal_namespace) | |
workflow = get_workflow(workflow_name) | |
wf: workflow = temporal_client.new_workflow_stub(workflow) | |
set_workflow_id(workflow, workflow_id) | |
wf_execution = None | |
try: | |
wf_execution = await WorkflowClient.start(wf.process, args, kwargs) | |
except grpclib.exceptions.GRPCError as e: | |
if e.status == Status.ALREADY_EXISTS: | |
if not skip_on_dup: | |
raise e | |
except Exception as e: | |
raise e | |
finally: | |
temporal_client.service.channel.close() | |
return wf_execution | |
@workflow(timeout=5, ...) | |
class RTSWorkflow(): | |
@activity(retry_options='', timeout=2) | |
@staticmethod | |
def get_payment_details(...): | |
return { | |
'authorized': 15, | |
'captured': 10, | |
'refunded': 5, | |
'reversed': 0 | |
} | |
@activity(retry_options='', timeout=2) | |
@staticmethod | |
def get_ref_balance(...): | |
return 2.0 | |
@activity() | |
@staticmethod | |
def wallet_capture(...): | |
pass | |
@activity() | |
@staticmethod | |
def pg_refund(...): | |
pass | |
''' | |
in case MP wants to update things at its end | |
''' | |
@activity() | |
@staticmethod | |
def publish_message(...): | |
pass | |
"wallet_amount would always be non-positive" | |
async def execute(self, exref_type, exref_value, target_refund, wallet_amount, payment_method_code): | |
if payment_method_code in {'prepaid', ...}: | |
payment_info = await self.get_payment_details() | |
wallet_captured = await self.get_ref_balance(exref_value, 'order') | |
already_refunded = payment_info['refunded'] + wallet_captured - wallet_amount | |
diff = target_refund - already_refunded | |
''' | |
first refund to card if prepaid payment | |
''' | |
if prepaid: | |
to_refund = max(diff, captured - refunded) | |
await self.pg_refund(ref, to_refund) | |
# get payment details again to see if previous refund was successful | |
# the below call can be avoided, if pg_refund() returns the updated payment info | |
payment_info = await self.get_payment_details() | |
already_refunded = payment_info['refunded'] + wallet_captured - wallet_amount | |
diff = target_refund - already_refunded | |
await self.wallet_capture(ref, wallet_captured + diff) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment