def handle_withdraw_notify(is_success, sn, vas_name, vas_sn, data): tx = get_tx_by_sn(sn) withdraw_record = tx.record logger.info('withdraw notify: {0}'.format(data)) if tx is None or withdraw_record is None: # 不存在 logger.warning('withdraw [{0}] not exits.'.format(sn)) return if is_duplicated_notify(tx, vas_name, vas_sn): logger.warning('withdraw notify duplicated: [{0}, {1}]'.format( vas_name, vas_sn)) return if tx.state != WithdrawTxState.PROCESSING: logger.warning('bad withdraw notify: [sn: {0}]'.format(sn)) return _record_withdraw_extra_info(withdraw_record, data) with require_transaction_context(): tx = update_transaction_info(tx.id, vas_sn, vas_name=vas_name) if is_success: _success_withdraw(tx, withdraw_record) else: _fail_withdraw(tx, withdraw_record) # notify client. tx = get_tx_by_id(tx.id) _try_notify_client(tx, withdraw_record)
def handle_prepaid_notify(is_success, sn, vas_name, vas_sn, amount, data): """ :param is_success: 是否成功 :param sn: 订单号 :param vas_name: 来源系统 :param vas_sn: 来源系统订单号 :param data: 数据 :return: """ tx = get_tx_by_sn(sn) prepaid_record = tx.record if is_duplicated_notify(tx, vas_name, vas_sn): return if _is_duplicated_prepaid(tx, vas_name, vas_sn): # 重复支付 logger.warning('duplicated prepaid: [{0}, {1}], [{2}, {3}]'.format( tx.vas_name, tx.vas_sn, vas_name, vas_sn)) if is_success: # 成功支付, 充值到余额 handle_duplicate_pay(vas_name, vas_sn, tx, prepaid_record) return with require_transaction_context(): tx = update_transaction_info(tx.id, vas_sn, vas_name=vas_name) if is_success: succeed_prepaid(vas_name, tx, prepaid_record) else: fail_prepaid(tx) # notify client. tx = get_tx_by_id(tx.id) _try_notify_client(tx, prepaid_record)
def handle_refund_notify(is_success, sn, vas_name, vas_sn, data): """ :param is_success: 是否成功 :param sn: 订单号 :param vas_name: 来源系统 :param vas_sn: 来源系统订单号 :param data: 数据 :return: """ import time tx = get_tx_by_sn(sn) if tx is None: for i in range(10): # 这是因为_create_and_request_refund在一个事务中,notify到这儿的时候,事务没有结束,导致tx获取不到。 # 这里的解决办法就是重试 tx = get_tx_by_sn(sn) if tx is None: logger.warn( 'apply refund [{0}] is not finish, sleep then retry.') time.sleep(0.5) continue break refund_record = tx.record logger.info('refund notify: {0}, {1}, {2}, {3}, {4}'.format( is_success, sn, vas_name, vas_sn, data)) if tx is None or refund_record is None: # 不存在 logger.warning('refund [{0}] not exits.'.format(sn)) return if is_duplicated_notify(tx, vas_name, vas_sn): logger.warning('refund notify duplicated: [{0}, {1}]'.format( vas_name, vas_sn)) return if tx.state not in [RefundTxState.PROCESSING, RefundTxState.REFUNDED_IN]: logger.warning('bad refund notify: [sn: {0}]'.format(sn)) return payment_tx = tx.super payment_record = payment_tx.record if payment_tx.state != PaymentTxState.REFUNDING: logger.warning( 'bad refund notify: [sn: {0}], [payment_sn: {1}]'.format( sn, payment_tx.sn)) return with require_transaction_context(): tx = update_transaction_info(refund_record.tx_id, vas_sn) if is_success: handle_succeed_refund(vas_name, payment_record, refund_record) else: handle_fail_refund(payment_record, refund_record) # notify client. tx = get_tx_by_id(tx.id) _try_notify_client(tx, refund_record)
def _create_refund(channel, payment_tx, amount, client_notify_url): cur_payment_state = payment_tx.state # start refunding. transit_transaction_state(payment_tx.id, payment_tx.state, PaymentTxState.REFUNDING) payment_tx = get_tx_by_id(payment_tx.id) payment_record = payment_tx.record if payment_tx.state != PaymentTxState.REFUNDING: raise PaymentNotRefundableError() if amount + payment_record.refunded_amount > payment_record.paid_amount: raise RefundAmountError(payment_record.paid_amount, payment_record.refunded_amount, amount) comments = "退款-{0}".format(payment_record.product_name) user_ids = [ user_roles.tx_to_user(payment_record.payer_id), user_roles.from_user(payment_record.payee_id) ] if payment_record.type == PaymentType.GUARANTEE: secure_user_id = get_system_account_user_id(SECURE_USER_NAME) user_ids.append(user_roles.guaranteed_by(secure_user_id)) elif payment_record.type == PaymentType.DIRECT: # check balance. # 直付退款时收款方余额必须足够 balance = get_user_cash_balance(payment_record.payee_id) if amount > balance.available: raise RefundBalanceError(amount, balance.available) tx = create_transaction(channel.name, TransactionType.REFUND, amount, comments, user_ids, super_id=payment_tx.id, vas_name=payment_tx.vas_name, order_id=payment_record.order_id) fields = { 'tx_id': tx.id, 'sn': tx.sn, 'payment_sn': payment_record.sn, 'payment_state': cur_payment_state, 'payer_id': payment_record.payer_id, 'payee_id': payment_record.payee_id, 'order_id': payment_record.order_id, 'amount': amount, 'client_notify_url': client_notify_url } refund_record = RefundRecord(**fields) db.session.add(refund_record) return payment_record, tx, refund_record
def get_cheque_record_from_cash_token(cash_token): try: from api_x.zyt.biz.transaction.dba import get_tx_by_id tx_id, hash_sign = cash_token.split('_') tx_id = ints.base36_to_int(tx_id) tx = get_tx_by_id(tx_id) if tx is None or tx.type != TransactionType.CHEQUE: return None cheque_record = tx.record if cheque_record.cash_token == cash_token: return cheque_record except Exception as _: pass return None
def handle_payment_notify(is_success, sn, vas_name, vas_sn, amount, data=None): """ :param is_success: 是否成功 :param sn: 订单号 :param vas_name: 来源系统 :param vas_sn: 来源系统订单号 :param data: 数据 :return: """ logger.info( 'receive payment notify: [{0}], [{1}], [{2}], [{3}], [{4}]'.format( is_success, sn, vas_name, vas_sn, amount)) tx = get_payment_tx_by_sn(sn) payment_record = tx.record if amount != payment_record.real_amount: # 单次支付的金额一定要完全一致 raise AmountValueMissMatchError(payment_record.real_amount, amount) if is_duplicated_notify(tx, vas_name, vas_sn): return with require_transaction_context(): if is_success: # update paid amount payment_record.add_paid(amount) if _is_duplicated_payment(tx, vas_name, vas_sn): # 重复支付 logger.warning( 'duplicated payment: [{0}, {1}], [{2}, {3}], [{4}]'.format( tx.vas_name, tx.vas_sn, vas_name, vas_sn, amount)) handle_duplicate_payment(vas_name, vas_sn, tx, payment_record) return # 直付和担保付的不同操作 if payment_record.type == PaymentType.DIRECT: succeed_payment(vas_name, tx, payment_record) elif payment_record.type == PaymentType.GUARANTEE: secure_payment(vas_name, tx, payment_record) else: fail_payment(payment_record) tx = update_transaction_info(tx.id, vas_sn, vas_name=vas_name) # notify client. tx = get_tx_by_id(tx.id) _try_notify_client(tx, payment_record)
def confirm_payment(channel, order_id): payment_record = get_payment_by_channel_order_id(channel.id, order_id) if payment_record is None or payment_record.type != PaymentType.GUARANTEE: raise TransactionNotFoundError( 'guarantee payment tx channel=[{0}], order_id=[{1}] not found.'. format(channel.name, order_id)) tx = get_tx_by_id(payment_record.tx_id) # 只有担保才能确认 # 另外,担保可能发生退款的情况,则需要退款完成之后再次确认。 if tx.state == PaymentTxState.SECURED: _confirm_payment(payment_record) elif tx.state == PaymentTxState.REFUNDING: msg = 'payment is REFUNDING, try again later.' logger.warn(msg) raise TransactionStateError(msg) else: logger.warn('payment state should be SECURED.')
def _cash_cheque(cheque_record, to_id): from api_x.zyt.vas import NAME tx = cheque_record.tx with require_transaction_context(): tx = update_transaction_info(tx.id, tx.sn, vas_name=NAME) if cheque_record.type == ChequeType.INSTANT: event_ids = transfer_frozen(tx.sn, cheque_record.from_id, to_id, cheque_record.amount) transit_transaction_state(tx.id, ChequeTxState.FROZEN, ChequeTxState.CASHED, event_ids) elif cheque_record.type == ChequeType.LAZY: event_ids = transfer(tx.sn, cheque_record.from_id, to_id, cheque_record.amount) transit_transaction_state(tx.id, ChequeTxState.CREATED, ChequeTxState.CASHED, event_ids) # update to id. add_tx_user(tx.id, user_roles.to_user(to_id)) cheque_record.to_id = to_id db.session.add(cheque_record) # notify client. tx = get_tx_by_id(tx.id) _try_notify_client(tx, cheque_record)