Skip to content

Instantly share code, notes, and snippets.

@meetchandan
Last active January 18, 2022 13:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save meetchandan/0945c034415bbaffbe23a954391b2823 to your computer and use it in GitHub Desktop.
Save meetchandan/0945c034415bbaffbe23a954391b2823 to your computer and use it in GitHub Desktop.
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