def refund_payin(db, exchange, amount, participant): """Refund a specific payin. """ assert participant.id == exchange.participant # Record the refund attempt fee = vat = amount.zero() with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") bundles = cursor.all( """ SELECT b.id FROM cash_bundles b WHERE b.owner = %s """, (participant.id, )) e_refund = cursor.one( """ INSERT INTO exchanges (participant, amount, fee, vat, route, status, refund_ref, wallet_id) VALUES (%s, %s, %s, %s, %s, 'pre', %s, %s) RETURNING * """, (participant.id, -amount, fee, vat, exchange.route, exchange.id, exchange.wallet_id)) cursor.run( """ INSERT INTO exchange_events (timestamp, exchange, status, wallet_delta) VALUES (%s, %s, 'pre', %s) """, (e_refund.timestamp, e_refund.id, e_refund.amount - e_refund.fee)) propagate_exchange(cursor, participant, e_refund, None, e_refund.amount, bundles=bundles) # Submit the refund wallet = db.one("SELECT * FROM wallets WHERE remote_id = %s", (exchange.wallet_id, )) m_refund = PayInRefund(payin_id=exchange.remote_id) m_refund.AuthorId = wallet.remote_owner_id m_refund.Tag = str(e_refund.id) m_refund.DebitedFunds = amount.int() m_refund.Fees = -fee.int() try: m_refund.save() except Exception as e: error = repr_exception(e) e_refund = record_exchange_result(db, e_refund.id, '', 'failed', error, participant) return 'exception', e_refund e_refund = record_exchange_result(db, e_refund.id, m_refund.Id, m_refund.Status.lower(), repr_error(m_refund), participant) return e_refund.status, e_refund
def refund_payin(db, exchange, amount, participant): """Refund a specific payin. """ assert participant.id == exchange.participant # Record the refund attempt fee = vat = amount.zero() with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") bundles = cursor.all(""" SELECT b.id FROM cash_bundles b WHERE b.owner = %s """, (participant.id,)) e_refund = cursor.one(""" INSERT INTO exchanges (participant, amount, fee, vat, route, status, refund_ref, wallet_id) VALUES (%s, %s, %s, %s, %s, 'pre', %s, %s) RETURNING * """, (participant.id, -amount, fee, vat, exchange.route, exchange.id, exchange.wallet_id)) cursor.run(""" INSERT INTO exchange_events (timestamp, exchange, status, wallet_delta) VALUES (%s, %s, 'pre', %s) """, (e_refund.timestamp, e_refund.id, e_refund.amount - e_refund.fee)) propagate_exchange(cursor, participant, e_refund, None, e_refund.amount, bundles=bundles) # Submit the refund wallet = db.one("SELECT * FROM wallets WHERE remote_id = %s", (exchange.wallet_id,)) m_refund = PayInRefund(payin_id=exchange.remote_id) m_refund.AuthorId = wallet.remote_owner_id m_refund.Tag = str(e_refund.id) m_refund.DebitedFunds = Money_to_cents(amount) m_refund.Fees = -Money_to_cents(fee) try: m_refund.save() except Exception as e: error = repr_exception(e) e_refund = record_exchange_result(db, e_refund.id, '', 'failed', error, participant) return 'exception', e_refund e_refund = record_exchange_result( db, e_refund.id, m_refund.Id, m_refund.Status.lower(), repr_error(m_refund), participant ) return e_refund.status, e_refund
def refund_payin(db, exchange, create_debts=False, refund_fee=False, dry_run=False): """Refund a specific payin. """ assert exchange.status == 'succeeded' and exchange.remote_id, exchange e_refund = db.one("SELECT e.* FROM exchanges e WHERE e.refund_ref = %s", (exchange.id,)) if e_refund and e_refund.status == 'succeeded': return 'already done', e_refund # Lock the bundles and try to swap them with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") bundles = [NS(d._asdict()) for d in cursor.all(""" UPDATE cash_bundles SET disputed = true WHERE origin = %s RETURNING * """, (exchange.id,))] bundles_sum = sum(b.amount for b in bundles) assert bundles_sum == exchange.amount original_owner = exchange.participant for b in bundles: if b.owner == original_owner: continue try_to_swap_bundle(cursor, b, original_owner) # Move the funds back to the original wallet LiberapayOrg = Participant.from_username('LiberapayOrg') assert LiberapayOrg return_payin_bundles_to_origin(db, exchange, LiberapayOrg, create_debts) # Add a debt for the fee if create_debts and refund_fee: create_debt(db, original_owner, LiberapayOrg.id, exchange.fee, exchange.id) # Compute and check the amount wallet = db.one("SELECT * FROM wallets WHERE remote_id = %s", (exchange.wallet_id,)) if e_refund and e_refund.status == 'pre': amount = -e_refund.amount else: amount = min(wallet.balance, exchange.amount) if amount <= 0: return ('not enough money: wallet balance = %s' % wallet.balance), None # Stop here if this is a dry run zero = exchange.fee.zero() fee, vat = (exchange.fee, exchange.vat) if refund_fee else (zero, zero) if dry_run: msg = ( '[dry run] full refund of payin #%s (liberapay id %s): amount = %s, fee = %s' % (exchange.remote_id, exchange.id, exchange.amount, exchange.fee) ) if amount + fee == exchange.amount + exchange.fee else ( '[dry run] partial refund of payin #%s (liberapay id %s): %s of %s, fee %s of %s' % (exchange.remote_id, exchange.id, amount, exchange.amount, fee, exchange.fee) ) return msg, None # Record the refund attempt participant = Participant.from_id(exchange.participant) if not (e_refund and e_refund.status == 'pre'): with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") bundles = [NS(d._asdict()) for d in cursor.all(""" SELECT * FROM cash_bundles WHERE origin = %s AND wallet_id = %s AND disputed = true """, (exchange.id, exchange.wallet_id))] e_refund = cursor.one(""" INSERT INTO exchanges (participant, amount, fee, vat, route, status, refund_ref, wallet_id) VALUES (%s, %s, %s, %s, %s, 'pre', %s, %s) RETURNING * """, (participant.id, -amount, -fee, -vat, exchange.route, exchange.id, exchange.wallet_id)) propagate_exchange(cursor, participant, e_refund, None, e_refund.amount, bundles=bundles) # Submit the refund m_refund = PayInRefund(payin_id=exchange.remote_id) m_refund.AuthorId = wallet.remote_owner_id m_refund.Tag = str(e_refund.id) m_refund.DebitedFunds = amount.int() m_refund.Fees = -fee.int() try: m_refund.save() except Exception as e: error = repr_exception(e) e_refund = record_exchange_result(db, e_refund.id, '', 'failed', error, participant) return 'exception', e_refund e_refund = record_exchange_result( db, e_refund.id, m_refund.Id, m_refund.Status.lower(), repr_error(m_refund), participant ) return e_refund.status, e_refund
def refund_disputed_payin(db, exchange, create_debts=False, refund_fee=False, dry_run=False): """Refund a specific disputed payin. """ assert exchange.status == 'succeeded' and exchange.remote_id, exchange e_refund = db.one("SELECT e.* FROM exchanges e WHERE e.refund_ref = %s", (exchange.id,)) if e_refund and e_refund.status == 'succeeded': return 'already done', e_refund # Lock the bundles and try to swap them with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") bundles = [NS(d._asdict()) for d in cursor.all(""" UPDATE cash_bundles SET disputed = true WHERE origin = %s RETURNING * """, (exchange.id,))] bundles_sum = sum(b.amount for b in bundles) assert bundles_sum == exchange.amount original_owner = exchange.participant for b in bundles: if b.owner == original_owner: continue try_to_swap_bundle(cursor, b, original_owner) # Move the funds back to the original wallet LiberapayOrg = Participant.from_username('LiberapayOrg') assert LiberapayOrg return_payin_bundles_to_origin(db, exchange, LiberapayOrg, create_debts) # Add a debt for the fee if create_debts and refund_fee: create_debt(db, original_owner, LiberapayOrg.id, exchange.fee, exchange.id) # Compute and check the amount wallet = db.one("SELECT * FROM wallets WHERE remote_id = %s", (exchange.wallet_id,)) if e_refund and e_refund.status == 'pre': amount = -e_refund.amount else: amount = min(wallet.balance, exchange.amount) if amount <= 0: return ('not enough money: wallet balance = %s' % wallet.balance), None # Stop here if this is a dry run zero = exchange.fee.zero() fee, vat = (exchange.fee, exchange.vat) if refund_fee else (zero, zero) if dry_run: msg = ( '[dry run] full refund of payin #%s (liberapay id %s): amount = %s, fee = %s' % (exchange.remote_id, exchange.id, exchange.amount, exchange.fee) ) if amount + fee == exchange.amount + exchange.fee else ( '[dry run] partial refund of payin #%s (liberapay id %s): %s of %s, fee %s of %s' % (exchange.remote_id, exchange.id, amount, exchange.amount, fee, exchange.fee) ) return msg, None # Record the refund attempt participant = Participant.from_id(exchange.participant) if not (e_refund and e_refund.status == 'pre'): with db.get_cursor() as cursor: cursor.run("LOCK TABLE cash_bundles IN EXCLUSIVE MODE") bundles = cursor.all(""" SELECT id FROM cash_bundles WHERE origin = %s AND wallet_id = %s AND disputed = true """, (exchange.id, exchange.wallet_id)) e_refund = cursor.one(""" INSERT INTO exchanges (participant, amount, fee, vat, route, status, refund_ref, wallet_id) VALUES (%s, %s, %s, %s, %s, 'pre', %s, %s) RETURNING * """, (participant.id, -amount, -fee, -vat, exchange.route, exchange.id, exchange.wallet_id)) cursor.run(""" INSERT INTO exchange_events (timestamp, exchange, status, wallet_delta) VALUES (%s, %s, 'pre', %s) """, (e_refund.timestamp, e_refund.id, e_refund.amount - e_refund.fee)) propagate_exchange(cursor, participant, e_refund, None, e_refund.amount, bundles=bundles) # Submit the refund m_refund = PayInRefund(payin_id=exchange.remote_id) m_refund.AuthorId = wallet.remote_owner_id m_refund.Tag = str(e_refund.id) m_refund.DebitedFunds = Money_to_cents(amount) m_refund.Fees = -Money_to_cents(fee) try: m_refund.save() except Exception as e: error = repr_exception(e) e_refund = record_exchange_result(db, e_refund.id, '', 'failed', error, participant) return 'exception', e_refund e_refund = record_exchange_result( db, e_refund.id, m_refund.Id, m_refund.Status.lower(), repr_error(m_refund), participant ) return e_refund.status, e_refund