def recover_lost_funds(db, exchange, lost_amount, repudiation_id): """Recover as much money as possible from a payin which has been reverted. """ original_owner = exchange.participant # Try (again) to swap the disputed bundles with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") disputed_bundles = [ NS(d._asdict()) for d in cursor.all( """ SELECT * FROM cash_bundles WHERE origin = %s AND disputed = true """, (exchange.id, )) ] bundles_sum = sum(b.amount for b in disputed_bundles) assert bundles_sum == lost_amount - exchange.fee for b in disputed_bundles: if b.owner == original_owner: continue try_to_swap_bundle(cursor, b, original_owner) # Move the funds back to the original wallet currency = exchange.amount.currency chargebacks_account, credit_wallet = Participant.get_chargebacks_account( currency) LiberapayOrg = Participant.from_username('LiberapayOrg') assert LiberapayOrg return_payin_bundles_to_origin(db, exchange, LiberapayOrg, create_debts=True) # Add a debt for the fee create_debt(db, original_owner, LiberapayOrg.id, exchange.fee, exchange.id) # Send the funds to the credit wallet # We have to do a SettlementTransfer instead of a normal Transfer. The amount # can't exceed the original payin amount, so we can't settle the fee debt. original_owner = Participant.from_id(original_owner) from_wallet = original_owner.get_current_wallet(currency).remote_id to_wallet = credit_wallet.remote_id t_id = prepare_transfer( db, original_owner.id, chargebacks_account.id, exchange.amount, 'chargeback', from_wallet, to_wallet, prefer_bundles_from=exchange.id, ) tr = SettlementTransfer() tr.AuthorId = original_owner.mangopay_user_id tr.CreditedUserId = chargebacks_account.mangopay_user_id tr.CreditedWalletId = to_wallet tr.DebitedFunds = exchange.amount.int() tr.DebitedWalletId = from_wallet tr.Fees = Money(0, currency) tr.RepudiationId = repudiation_id tr.Tag = str(t_id) return execute_transfer(db, t_id, tr)
def return_payin_bundles_to_origin(db, exchange, last_resort_payer, create_debts=True): """Transfer money linked to a specific payin back to the original owner. """ currency = exchange.amount.currency chargebacks_account = Participant.get_chargebacks_account(currency)[0] original_owner = exchange.participant origin_wallet = db.one("SELECT * FROM wallets WHERE remote_id = %s", (exchange.wallet_id, )) transfer_kw = dict( tippee_wallet_id=origin_wallet.remote_id, tippee_mango_id=origin_wallet.remote_owner_id, ) payin_bundles = [ NS(d._asdict()) for d in db.all( """ SELECT * FROM cash_bundles WHERE origin = %s AND disputed = true """, (exchange.id, )) ] grouped = group_by(payin_bundles, lambda b: (b.owner, b.withdrawal)) for (current_owner, withdrawal), bundles in grouped.items(): assert current_owner != chargebacks_account.id if current_owner == original_owner: continue amount = sum(b.amount for b in bundles) if current_owner is None: if not last_resort_payer or not create_debts: continue bundles = None withdrawer = db.one( "SELECT participant FROM exchanges WHERE id = %s", (withdrawal, )) payer = last_resort_payer.id create_debt(db, withdrawer, payer, amount, exchange.id) create_debt(db, original_owner, withdrawer, amount, exchange.id) else: bundles = [b.id for b in bundles] payer = current_owner if create_debts: create_debt(db, original_owner, payer, amount, exchange.id) transfer(db, payer, original_owner, amount, 'chargeback', bundles=bundles, **transfer_kw)
def recover_lost_funds(db, exchange, lost_amount, repudiation_id): """Recover as much money as possible from a payin which has been reverted. """ original_owner = exchange.participant # Try (again) to swap the disputed bundles with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") disputed_bundles = [NS(d._asdict()) for d in cursor.all(""" SELECT * FROM cash_bundles WHERE origin = %s AND disputed = true """, (exchange.id,))] bundles_sum = sum(b.amount for b in disputed_bundles) assert bundles_sum == lost_amount - exchange.fee for b in disputed_bundles: if b.owner == original_owner: continue try_to_swap_bundle(cursor, b, original_owner) # Move the funds back to the original wallet currency = exchange.amount.currency chargebacks_account, credit_wallet = Participant.get_chargebacks_account(currency) LiberapayOrg = Participant.from_username('LiberapayOrg') assert LiberapayOrg return_payin_bundles_to_origin(db, exchange, LiberapayOrg, create_debts=True) # Add a debt for the fee create_debt(db, original_owner, LiberapayOrg.id, exchange.fee, exchange.id) # Send the funds to the credit wallet # We have to do a SettlementTransfer instead of a normal Transfer. The amount # can't exceed the original payin amount, so we can't settle the fee debt. original_owner = Participant.from_id(original_owner) from_wallet = original_owner.get_current_wallet(currency).remote_id to_wallet = credit_wallet.remote_id t_id = prepare_transfer( db, original_owner.id, chargebacks_account.id, exchange.amount, 'chargeback', from_wallet, to_wallet, prefer_bundles_from=exchange.id, ) tr = SettlementTransfer() tr.AuthorId = original_owner.mangopay_user_id tr.CreditedUserId = chargebacks_account.mangopay_user_id tr.CreditedWalletId = to_wallet tr.DebitedFunds = Money_to_cents(exchange.amount) tr.DebitedWalletId = from_wallet tr.Fees = Money(0, currency) tr.RepudiationId = repudiation_id tr.Tag = str(t_id) return execute_transfer(db, t_id, tr)
def return_payin_bundles_to_origin(db, exchange, last_resort_payer, create_debts=True): """Transfer money linked to a specific payin back to the original owner. """ currency = exchange.amount.currency chargebacks_account = Participant.get_chargebacks_account(currency)[0] original_owner = exchange.participant origin_wallet = db.one("SELECT * FROM wallets WHERE remote_id = %s", (exchange.wallet_id,)) transfer_kw = dict( tippee_wallet_id=origin_wallet.remote_id, tippee_mango_id=origin_wallet.remote_owner_id, ) payin_bundles = [NS(d._asdict()) for d in db.all(""" SELECT * FROM cash_bundles WHERE origin = %s AND disputed = true """, (exchange.id,))] grouped = group_by(payin_bundles, lambda b: (b.owner, b.withdrawal)) for (current_owner, withdrawal), bundles in grouped.items(): assert current_owner != chargebacks_account.id if current_owner == original_owner: continue amount = sum(b.amount for b in bundles) if current_owner is None: if not last_resort_payer or not create_debts: continue bundles = None withdrawer = db.one("SELECT participant FROM exchanges WHERE id = %s", (withdrawal,)) payer = last_resort_payer.id create_debt(db, withdrawer, payer, amount, exchange.id) create_debt(db, original_owner, withdrawer, amount, exchange.id) else: bundles = [b.id for b in bundles] payer = current_owner if create_debts: create_debt(db, original_owner, payer, amount, exchange.id) transfer(db, payer, original_owner, amount, 'chargeback', bundles=bundles, **transfer_kw)
def recover_lost_funds(db, exchange, lost_amount, repudiation_id): """Recover as much money as possible from a payin which has been reverted. """ original_owner = exchange.participant # Try (again) to swap the disputed bundles with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") disputed_bundles = [ NS(d._asdict()) for d in cursor.all( """ SELECT * FROM cash_bundles WHERE origin = %s AND disputed = true """, (exchange.id, )) ] bundles_sum = sum(b.amount for b in disputed_bundles) assert bundles_sum == lost_amount - exchange.fee for b in disputed_bundles: if b.owner == original_owner: continue try_to_swap_bundle(cursor, b, original_owner) # Move the funds back to the original wallet chargebacks_account = Participant.get_chargebacks_account() LiberapayOrg = Participant.from_username('LiberapayOrg') assert LiberapayOrg grouped = group_by(disputed_bundles, lambda b: (b.owner, b.withdrawal)) for (owner, withdrawal), bundles in grouped.items(): assert owner != chargebacks_account.id if owner == original_owner: continue amount = sum(b.amount for b in bundles) if owner is None: bundles = None withdrawer = db.one( "SELECT participant FROM exchanges WHERE id = %s", (withdrawal, )) payer = LiberapayOrg.id create_debt(db, withdrawer, payer, amount, exchange.id) create_debt(db, original_owner, withdrawer, amount, exchange.id) else: payer = owner create_debt(db, original_owner, payer, amount, exchange.id) transfer(db, payer, original_owner, amount, 'chargeback', bundles=bundles) # Add a debt for the fee create_debt(db, original_owner, LiberapayOrg.id, exchange.fee, exchange.id) # Send the funds to the credit wallet # We have to do a SettlementTransfer instead of a normal Transfer. The amount # can't exceed the original payin amount, so we can't settle the fee debt. original_owner = Participant.from_id(original_owner) t_id = prepare_transfer( db, original_owner.id, chargebacks_account.id, exchange.amount, 'chargeback', original_owner.mangopay_wallet_id, chargebacks_account.mangopay_wallet_id, prefer_bundles_from=exchange.id, ) tr = SettlementTransfer() tr.AuthorId = original_owner.mangopay_user_id tr.CreditedUserId = chargebacks_account.mangopay_user_id tr.CreditedWalletId = chargebacks_account.mangopay_wallet_id tr.DebitedFunds = Money(int(exchange.amount * 100), 'EUR') tr.DebitedWalletId = original_owner.mangopay_wallet_id tr.Fees = Money(0, 'EUR') tr.RepudiationId = repudiation_id tr.Tag = str(t_id) tr.save() return record_transfer_result(db, t_id, tr)