Beispiel #1
0
def _record_transfer_result(db, t_id, status, error=None):
    balance = None
    with db.get_cursor() as c:
        tipper, tippee, amount = c.one("""
            UPDATE transfers
               SET status = %s
                 , error = %s
             WHERE id = %s
         RETURNING tipper, tippee, amount
        """, (status, error, t_id))
        if status == 'succeeded':
            balance = c.one("""

                UPDATE participants
                   SET balance = balance + %(amount)s
                 WHERE id = %(tippee)s;

                UPDATE participants
                   SET balance = balance - %(amount)s
                 WHERE id = %(tipper)s
                   AND balance - %(amount)s >= 0
             RETURNING balance;

            """, locals())
            if balance is None:
                raise NegativeBalance
            bundles = c.all("""
                LOCK TABLE cash_bundles IN EXCLUSIVE MODE;
                SELECT b.*
                  FROM cash_bundles b
                  JOIN exchanges e ON e.id = b.origin
                 WHERE b.owner = %s
              ORDER BY e.participant = %s DESC, b.ts
            """, (tipper, tippee))
            x = amount
            for b in bundles:
                if x >= b.amount:
                    c.run("""
                        UPDATE cash_bundles
                           SET owner = %s
                         WHERE id = %s
                    """, (tippee, b.id))
                    x -= b.amount
                    if x == 0:
                        break
                else:
                    c.run("""
                        UPDATE cash_bundles
                           SET amount = (amount - %s)
                         WHERE id = %s;

                        INSERT INTO cash_bundles
                                    (owner, origin, amount, ts)
                             VALUES (%s, %s, %s, %s);
                    """, (x, b.id, tippee, b.origin, x, b.ts))
                    break
    if balance is not None:
        merge_cash_bundles(db, tippee)
        return balance
    raise TransferError(error)
def record_transfer_result(db, t_id, tr, _raise=False):
    error = repr_error(tr)
    status = tr.Status.lower()
    assert (not error) ^ (status == 'failed')
    r = _record_transfer_result(db, t_id, status, error)
    if _raise and status == 'failed':
        raise TransferError(error)
    return r
def execute_transfer(db, t_id, tr):
    try:
        tr.save()
    except APIError as e:
        error = repr_exception(e)
        _record_transfer_result(db, t_id, 'failed', error)
        from liberapay.website import website
        website.tell_sentry(e, {})
        raise TransferError(error)
    return record_transfer_result(db, t_id, tr, _raise=True)