Closed chadwhitacre closed 4 years ago
Greetings! I have been running a third-party payment processor for the past five years and we are now shutting down our service. We are sitting on approximately $100,000 of escrowed funds and are looking for advice on disposing of it.
Had a call with MarketSphere. They specialize in handling filings for unclaimed property but are not lawyers. The person I talked with was going to recommend some lawyers who could go over our terms and advise us on our liability.
From an unclaimed property point of view, it's unfortunate that don't have national identification info for everyone we're holding money for. At this point in a "clean environment" (i.e., w/ identification on file) we would send hard-copy paper letters to the last known address for everyone affected for three years before turning over the list of names/addresses and dollar amounts to the state of the last known address. For international or unknown people the property reverts to our state of incorporation, namely the Commonwealth of Pennsylvania. The fact that we don't have national identification information on file is a red flag, and could potentially lead to an "unclaimed property audit." Pennsylvania would be the most likely to be interested in conducting an audit, though the person I spoke with considered our exposure to be "minimal" and an audit to be "extremely unlikely." They went so far as to suggest, after I mentioned Readability (btw, archive: .org, .png), that donating the money in a well-documented fashion would be an option worth considering. (Again, not a lawyer.) However, donating the money would create liability for whomever we were donating it to: we'd have to be up-front with them about the source of the funds and make sure they were okay with it. I think both the letter and the spirit of the law suggest that we give the money to the Commonwealth of Pennsylvania.
So basically what we'll do is make a one-time filing with the Commonwealth. The general expectation is that companies who file, file year after year, so we need to make clear that we're closing up shop and this will be our one and only filing, relinquishing all of our liability, and that there won't be any future filings. We'll give them what we have: usernames, email addresses, social media accounts, dollar amounts, and transaction dates. We'll send the money via wire/ACH/check. We'll include a narrative describing the source of the funds and our attempts to notify owners and return the money. Even though the dormancy period is not up we can "force file" because we are going out of business.
Hopefully we can get this done in Q1 2018.
I've received from MarketSphere a list of law firms that specialize in unclaimed property.
Where we have an email address, we can try sending via PayPal even if not explicitly linked to PayPal.
gratipay-bak=# select count(*), sum(balance) volume, email_address is not null as have_email_address from participants where balance > 0 group by email_address is not null;
┌───────┬──────────┬────────────────────┐
│ count │ volume │ have_email_address │
├───────┼──────────┼────────────────────┤
│ 3162 │ 91861.94 │ f │
│ 602 │ 12928.67 │ t │
└───────┴──────────┴────────────────────┘
(2 rows)
gratipay-bak=#
Looks like that's only about 10%. :-/
@JessWhit suggests that we should block this on https://github.com/gratipay/inside.gratipay.com/issues/1220.
This evening, I hand-delivered $420 in cash to someone who is friends with someone who had money escrowed with us. 💃
https://github.com/gratipay/inside.gratipay.com/issues/1219#issuecomment-442311277
I reimbursed myself the $420 via a cash withdrawal from New Alliance.
I note that the tax situation for recipients is as muddy as ever. We are the payment processor, not the employer/contracting company.
Is it better to push the money back upstream? 🤔
Because technically the money was income to people the moment they received it through Gittip/Gratipay, whether or not they reported it. Now it's just a pain. 😞
Would it be less inconvenient for the people who gave it originally to receive it back?
I am revisiting the idea of donating it to open source non-profits.
I sent money to one account under #1225 and emailed the other 12.
By quartile:
n cutoff
------------
3466 69.00
217 227.00
67 838.50
13 4306.93
gratipay-bak=# \i charts.sql
ALTER TABLE
UPDATE 3467
UPDATE 217
UPDATE 67
UPDATE 13
ALTER TABLE
UPDATE 2112
┌──────┬──────────┐
│ n │ s │
├──────┼──────────┤
│ 13 │ 24934.11 │
│ 67 │ 27197.25 │
│ 217 │ 26367.88 │
│ 3467 │ 26291.37 │
└──────┴──────────┘
(4 rows)
┌──────┬──────────┐
│ n │ s │
├──────┼──────────┤
│ 7 │ 14153.18 │
│ 42 │ 16848.07 │
│ 125 │ 15790.67 │
│ 1938 │ 15280.23 │
└──────┴──────────┘
(4 rows)
gratipay-bak=#
alter table tmp add column bucket int default 0;
update tmp set bucket=0 where balance > 0 and balance <= 69;
update tmp set bucket=1 where balance > 69 and balance <= 227;
update tmp set bucket=2 where balance > 227 and balance <= 838.50;
update tmp set bucket=4 where balance > 838.50;
alter table tmp add column has_email bool default false;
update tmp
set has_email=true
where coalesce(address_1, address_2, address_3, address_4, address_5) is not null
;
select count(balance) n, sum(balance) s from tmp group by bucket order by bucket desc;
select count(balance) n, sum(balance) s from tmp where has_email group by bucket order by bucket desc;
#!/usr/bin/env python3
import csv
from collections import OrderedDict
from decimal import Decimal as D
payouts = csv.reader(open('payouts.csv'))
masspay = csv.writer(open('masspay.csv', 'w+'))
headers = next(payouts)
blacklist = set(open('blacklist.csv').read().splitlines())
TOTAL = total = settled = D('0.00')
amounts = []
for row in payouts:
paid, balance = bool(int(row[0])), D(row[1])
amounts.append(balance)
TOTAL += balance
if paid:
settled += balance
continue
addresses = list(OrderedDict.fromkeys(row[4:]))
for address in addresses:
if not address or address in blacklist:
continue
masspay.writerow([address, balance, 'usd'])
total += balance
break
print(f'{settled} / {total} ({(settled / total * 100):.1f}%)')
# Print out quartiles
quartile = TOTAL / 4
current = quartile
print(TOTAL, quartile)
running = D('0.00')
amounts.sort()
j = 0
def log():
print(f"{current:>9.02f} {running:>9.02f} {i-j:>4d} {amount:>7.02f}")
for i, amount in enumerate(amounts):
if running >= current:
log()
current += quartile
j = i
running += amount
log()
Based on https://github.com/gratipay/inside.gratipay.com/issues/1205#issuecomment-446011658 I think we should manually reach out (#1225) to the top two quartiles. Sending another 67 emails in order to clear another 25% of the escrow is a good trade-off. From there we can move back to #1219 for the bottom half.
It seems like I should spin Gratipay.com back up locally and track these exchanges so we have a clear record.
gratipay-bak=# select done, sum(balance) from tmp group by done;
┌──────┬──────────┐
│ done │ sum │
├──────┼──────────┤
│ f │ 45282.42 │
│ t │ 59508.19 │
└──────┴──────────┘
(2 rows)
Alright! MassPay Number 1 is done! Well, started. It looks like we successfully sent $31k+ (30%) right off the bat, with another $11k (10%) still potentially to be claimed.
We're now down to 1700 that aren't done and have no email. Here's the breakdown:
gratipay-bak=# select bucket, count(balance), sum(balance) from tmp where not done and n = 0 group by bucket order by bucket;
┌────────┬───────┬──────────┐
│ bucket │ count │ sum │
├────────┼───────┼──────────┤
│ 1 │ 1580 │ 11385.23 │
│ 2 │ 95 │ 10961.71 │
│ 3 │ 25 │ 9732.61 │
└────────┴───────┴──────────┘
(3 rows)
The buckets are:
I think I'll take a pass through buckets 2 and 3 and clear low-hanging fruit.
Note that the "done" amount of $59k includes $9k in pending payouts to the five top-13 folks that are actually still pending (#1225) though I've made initial contact with four and have a plan for the fifth.
The no email numbers are a little fudgy, another way I counted them I got 1650. ¯\_(ツ)_/¯
category | amount ($) | % | estimate | $ |
---|---|---|---|---|
total | 104,790.61 | |||
done done! | 50,357.64 | 48.1 | ||
remaining | 54,432.97 | 51.9 | ||
pending on #1225 | 9,150.55 | 8.7 | 1.0 | 9,150.55 |
pending on #1219 | 12,302.29 | 11.7 | 0.5 | 6,151.15 |
denied on #1219 | 2,318.57 | 2.2 | 0.0 | 0 |
no email | 30,661.22 | 29.3 | 0.1 | 3,066.12 |
remaining maybe? | 18,367.82 | |||
total maybe? | 65.6 | 68,725.46 |
^^^ Table updated with some estimates of what we'll be able to flush. Target: 2/3! 👍
I think I can surpass $3,066.12 for no email
(state=4
):
gratipay-bak=# select count(balance), sum(balance) from tmp where state=4 and balance >= 100;
┌───────┬──────────┐
│ count │ sum │
├───────┼──────────┤
│ 75 │ 16225.47 │
└───────┴──────────┘
(1 row)
gratipay-bak=#
Excelsior!
Per the above table there would be $36,065.15 remaining.
2/3 gone would be $34,930.20 left.
Anything under $40,000 and especially if we could get under $30,000(!) I would start to feel pretty good about donating the rest to selected charities.
lol that's one way
lol owed Hank Green dollars:
double lol
I requested another PayPal Debit Card so I can pay out some accounts where there's a donation option on their website but they don't support PayPal.
tfw you manually trigger GitHub's rate-limiting 😬
$20,000 has cleared New Alliance.
PayPal debit card is in the mail.
I am updating the script to process payments.
Then:
[folded into ticket description]
Email harvesting script moved to a Gist:
https://gist.github.com/chadwhitacre/d4cb21261e41a2da1dc3bc7f86e6879c
Scripts updated in https://github.com/gratipay/logs/commit/c8d654b7157aed9194beb982d07ad2c05535c0b4.
$ ./tick.py
n $
------------- ------------------
ready 96 ( 2.6%) 15,280.99 (14.6%)
pending 512 (13.6%) 14,473.53 (13.8%)
complete 1,538 (40.9%) 61,690.20 (58.9%)
failed 1,618 (43.0%) 13,345.89 (12.7%)
total 3,764 104,790.61
masspay 96 ( 2.6%)
target 15,280.99
remainder - 0.00
----------
actual 15,280.99 (14.6%)
fee - 91.28
----------
net 15,189.71
$
Okay! Gonna take five, and then see about issuing this next masspay ...
Current balances:
Account | Amount ($) |
---|---|
New Alliance | 19,684.45 |
PayPal | 3,243.00 |
total | 22,927.45 |
The PayPal balance doesn't include pending money, so that's going to spring back significantly.
https://github.com/gratipay/logs/commit/7ed351d3c8e3edaa005104a1774b9d927c192b23
$ ./tick.py
n $
------------- ------------------
ready 0 ( 0.0%) 0.00 ( 0.0%)
pending 475 (12.6%) 14,617.08 (13.9%)
complete 1,663 (44.2%) 76,175.55 (72.7%)
failed 1,626 (43.2%) 13,997.98 (13.4%)
total 3,764 104,790.61
masspay 0 ( 0.0%)
target 0.00
remainder - 0.00
----------
actual 0.00 ( 0.0%)
fee - 0.00
----------
net 0.00
$
Damn. This feels like we are succeeding here!
Emails deduped in payouts.csv
in https://github.com/gratipay/logs/commit/9ae34407f88f8d52bfd9413278cfa3f8dd14c355.
#!/usr/bin/env python
import csv
import lib
payouts_header, payouts = lib.load_payouts()
for row in payouts:
addresses = lib.get_addresses(row)
addresses += ([''] * (4-len(addresses)))
row[5:] = addresses
csv.writer(open('payouts.csv', 'w+')).writerows([payouts_header] + payouts)
None of the current failures have a second email address. So they are failed indeed.
$ grep ',unaddressable,failed.*@.*@' payouts.csv
$
... or are they!?
I've gone through five failed payouts > $100 (not including ones where I couldn't find an email address the other day), one I was able to pay out via PayPal based on an updated email address from GitHub, the other four I emailed to ask for options. One of them is @techtonik! 👋 😍
Forty of the pending masspays have a second email address, so I guess that's an upper limit on the third MassPay we're expecting to do in ~30 days when the results are in from the first two. With those kind of numbers I don't anticipate a fourth.
$ grep 'masspay,pending.*@.*@' payouts.csv | wc -l
40
$
Got the debit card!
Five people to pay ...
First wanted Cash.me. Won't take the new debit card because "blah blah issuing bank charges additional fees" 😖. Tried my own card "that looks like a credit card" tried my own other card. Worked! PayPaling myself the reimbursement ...
The rest of the debit card folks were straightforward. 👍
https://github.com/gratipay/logs/commit/0458e7288a60dc10ce4830193f019c385cc9c2e7
$ ./tick.py
n $
------------- ------------------
ready 0 ( 0.0%) 0.00 ( 0.0%)
pending 473 (12.6%) 14,969.60 (14.3%)
complete 1,670 (44.4%) 77,566.64 (74.0%)
failed 1,621 (43.1%) 12,254.37 (11.7%)
total 3,764 104,790.61
masspay 0 ( 0.0%)
target 0.00
remainder - 0.00
----------
actual 0.00 ( 0.0%)
fee - 0.00
----------
net 0.00
$
Yeah, this is going great. We've got ~$1,600 in flight on TransferWise that is all but certain to clear on Monday, plus five Patreon payouts that are supposed to clear on Jan 1. Those alone will put us over 3/4 complete at $79,867.06 (76.2%). 👍
Waddya think? Can we make 80%? 💃
Can we semi-automate harvesting GitHub emails for the 1500+ people that are failed unaddressable?
Because I'm pretty sure we were just looking at emails explicitly published on a GitHub account, vs. emails on commit logs.
That sounds like a new ticket: https://github.com/gratipay/inside.gratipay.com/issues/1226.
Gosh. What if we could clear 90%? I think we might be able to get there with #1226, though that will take a lot of manual review. It would really be worth it, though. If we have less than $10,000 left unsendable then we could really declare victory! And then just donate the rest.
I went back and revisited the Readability case. They donated 90% of all of the money they collected, which came to about $150,000. We collected $1,003,648.55 over the lifetime of Gratipay:
gratipay-bak=# select sum(amount) from exchanges where amount > 0 and status = 'succeeded';
┌────────────┐
│ sum │
├────────────┤
│ 1003648.55 │
└────────────┘
(1 row)
gratipay-bak=#
So to only have $104,790.61 left to dispose of is already only 10.4% of our total volume—the inverse of Readability's situation. And as of today we've cleared $78,266.88 of that, leaving only $26,523.73, which is only 2.6% of our total volume ever processed. In other words, we're already in a very different situation than Readability, and I think we can feel good about where we stand today even if we make no further progress. Stretch goal: < 1% remaining! That'd be < $10,036.48. Can we get there?!?!? 😮
75% 💃
https://github.com/gratipay/logs/commit/05795f25caa75c3c28e9881250d031194bc47c54
$ ./tick.py
n $
------------- ------------------
ready 0 ( 0.0%) 0.00 ( 0.0%)
pending 471 (12.5%) 13,890.95 (13.3%)
complete 1,672 (44.4%) 78,645.29 (75.0%)
failed 1,621 (43.1%) 12,254.37 (11.7%)
total 3,764 104,790.61
incomplete 2,092 (55.6%) 26,145.32 (25.0%)
$
75.3%
https://github.com/gratipay/logs/commit/c96285952cd6f5bcd7b0d00aa30f0d14b3086eec
$ ./tick.py
n $
------------- ------------------
ready 0 ( 0.0%) 0.00 ( 0.0%)
pending 469 (12.5%) 13,660.95 (13.0%)
complete 1,674 (44.5%) 78,875.29 (75.3%)
failed 1,621 (43.1%) 12,254.37 (11.7%)
total 3,764 104,790.61
incomplete 2,090 (55.5%) 25,915.32 (24.7%)
$
I updated the blog post to emphasize the $1,000,000 base:
https://github.com/gratipay/logs/commit/a41024a92ec651e6b514a267f2dbce4d570d689c
$ ./tick.py
n $
------------- ------------------
ready 0 ( 0.0%) 0.00 ( 0.0%)
pending 469 (12.5%) 13,660.95 (13.0%)
complete 1,674 (44.5%) 78,875.29 (75.3%)
failed 1,621 (43.1%) 12,254.37 (11.7%)
total 3,764 104,790.61
incomplete 2,090 (55.5%) 25,915.32 (24.7%)
volume 1,003,648.55
disbursed 977,733.23 (97.4)%
$
When we started this would've been:
volume 1,003,648.55
disbursed 898,857.94 (89.6)%
Reticketing from #1196.
maybe wait 30 days and do a third MassPay? Need to look at amounts ...probably not going to be worth it: https://github.com/gratipay/inside.gratipay.com/issues/1205#issuecomment-449612538