Skip to content

Instantly share code, notes, and snippets.

@raycmorgan
Forked from amfeng/ach-in
Last active September 24, 2015 04:51
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save raycmorgan/166e899cbb359768ad29 to your computer and use it in GitHub Desktop.
Save raycmorgan/166e899cbb359768ad29 to your computer and use it in GitHub Desktop.
Documentation for Stripe's ACH payment beta.

You can find more up to date documentation here: https://stripe.com/docs/guides/ach-beta

This will continue to live and document /v1/payments but we now recommend using the stripe.com guide and implement against /v1/charges

ACH-in (private beta) documentation

Table of contents


Current Limitations

  • Read only dashboard support: Currently you must go through the API to create and refund payments.
  • No bindings support

ACH Payments

Creating a payment

To create an ACH payment, you send a request to /v1/payments like you would to /v1/charges:

curl https://api.stripe.com/v1/payments \
   -u {API_KEY}: \
   -d customer={ID} \
   -d amount=1000 \
   -d currency=usd \
   -d payment_method=ach

If the customer has multiple bank accounts, you can specify which one to use by supplying a bank_account param:

curl https://api.stripe.com/v1/payments \
   -u {API_KEY}: \
   -d customer={ID} \
   -d bank_account={BANK_ACCOUNT_ID} \
   -d amount=1000 \
   -d currency=usd \
   -d payment_method=ach

(It's worth noting that we're eventually going to merge /v1/charges and /v1/payments, so we may ask you to switch to another API endpoint nearing the end of the beta.)

The payment object looks like:

{
  "id": "py_2jOYfo213EKxIk",
  "object": "payment",
  "created": 1381458902,
  "livemode": false,
  "amount": 100,
  "currency": "usd",
  "payment_method": "ach",
  "payment_source": {
    "object": "bank_account",
    "id": "ba_2gjqUkUwN31jI5",
    "bank_name": "STRIPE TEST BANK",
    "last4": "6789",
    "country": "US",
    "currency": "usd",
    "validated": false,
    "verified": true,
    "fingerprint": "glTJvPVnzCgyOwjh"
  },
  "balance_transaction": "txn_2jOYrlBKKNzCfl",
  "customer": "cus_2gjqT8FOFoESWu",
  "description": null,
  "statement_descriptor": null,
  "status": "pending"
}

Attaching a bank account to a customer

Stripe currently only allows you to debit from a bank account that is attached to a customer and has been verified via micro-deposit.

To add a bank account to a customer, POST to /v1/customers/{ID}/bank_accounts with the bank account details, either as a hash or by using a bank account token created by Stripe.js:

curl https://api.stripe.com/v1/customers/{ID}/bank_accounts \
  -u {API_KEY}: \
  -d bank_account[account_number]=000123456789 \
  -d bank_account[routing_number]=110000000 \
  -d bank_account[country]=US

Listing bank accounts

You can list a customers' bank accounts:

curl https://api.stripe.com/v1/customers/{ID}/bank_accounts \
  -u {API_KEY}:

Retrieve a bank account

curl https://api.stripe.com/v1/customers/{ID}/bank_accounts/{ID} \
  -u {API_KEY}:

Delete a bank account

curl https://api.stripe.com/v1/customers/{ID}/bank_accounts/{ID} \
  -u {API_KEY}:
  -XDELETE

Updating a bank account

The only thing that can be updated on a bank account is the metadata. If you would like to change the account/routing information, you must create a new bank account (and delete the old one if you'd like).

curl https://api.stripe.com/v1/customers/{ID}/bank_accounts/{ID} \
  -u {API_KEY}: \
  -d metadata[bar]=qux

Verify a bank account

Upon attaching a bank account to a customer, Stripe will send two small deposits to the bank account (which should take 1-2 business days to appear in the account). To verify the bank account, have your user confirm the amounts of the two deposits:

curl https://api.stripe.com/v1/customers/{ID}/bank_accounts/{ID}/verify \
  -u {API_KEY}: \
  -d amounts[]=14 \
  -d amounts[]=26

Payout timeline

ACH payments will clear into your Stripe available balance (or automatically transferred) after 5 days. During that time, the payment might fail for various reasons such as an invalid bank account or insufficient funds.

Your customer should expect to see the debit on their bank statement within 1-2 business days.

Webhooks

All payment webhooks contain the payment information as their payload.

  • payment.created - Right after a successful POST /v1/payments. Note that the payment will be in the pending state, and can still fail.
  • payment.paid - This will happen when the payment transitions it's status from pending to paid. This happens after 5 days.
  • payment.failed - Before 5 days, if the payment is returned (insufficient funds, etc) you will receive this hook.

A payment.created webhook will always occur, and only one of payment.paid and payment.failed will occur.

Testing

To mimic success/failed payment behaviors, you can use the following bank account information in test mode:

Routing number: 110000000
Account number: 000123456789 (success), 000111111116 (failure)

To mimic success/failed verifications, you can use the amounts:

[32, 45] (success)
[any other number combinations] (failure)

Connect Support

Applications can create ACH payments on behalf of their connected merchants. Just like /v1/charges you may specify an application_fee to allow your application to take part of the payment as a fee.

Customer sharing

The process to share a bank account across connected accounts is similar to sharing cards. The main difference is that bank accounts must be verified before sharing.

  1. Attach a bank account to a customer in your account (using your secret key)
  2. Verify the bank account using microdeposits
  3. Create a token on the connected account using their access_token
curl https://api.stripe.com/v1/tokens \
  -u {ACCESS_TOKEN}: \
  -d customer={CUSTOMER_ID} \
  -d bank_account={BANK_ACCOUNT_ID}
  1. Attach the bank account token to a customer on the connected account using the access_token.
curl https://api.stripe.com/v1/customers/{OTHER_CUSTOMER_ID}/bank_accounts \
  -u {ACCESS_TOKEN}: \
  -d bank_account={BANK_ACCOUNT_TOKEN}

Refunds

Refunds for ACH payments are handled via ACH transfers back into your customer's account. As such, they take 1-2 business days to appear on your customer's bank statement.

API calls

Create a refund

To create a full refund on a payment all you need to specify is the payment to refund.

curl https://api.stripe.com/v1/refunds \
   -u {API_KEY}: \
   -d payment={PAYMENT_ID}

To create a partial refund of the payment simply add the amount field.

curl https://api.stripe.com/v1/refunds \
   -u {API_KEY}: \
   -d payment={PAYMENT_ID} \
   -d amount=500

If you are using Stripe Connect, you can also refund your application_fee by specifying refund_application_fee. If the refund is a partial refund, the amount of the application_fee that will be refunded is proportional to the amount of the payment and the amount being refunded. For example, if 50% of the original payment is being refunded, 50% of the application_fee will be refunded.

However, if the refund fully refunds the payment (either a full refund, or a partial that refunds the remainder of the payment) then the entire application_fee will be refunded if refund_application_fee is set to true.

curl https://api.stripe.com/v1/refunds \
   -u {API_KEY}: \
   -d payment={PAYMENT_ID} \
   -d refund_application_fee=true

You can also specify metadata on a refund when creating it.

curl https://api.stripe.com/v1/refunds \
   -u {API_KEY}: \
   -d payment={PAYMENT_ID} \
   -d metadata[foo]=bar

Listing refunds

To list all payment refunds.

curl https://api.stripe.com/v1/refunds \
   -u {API_KEY}:

You can also filter by payment.

curl https://api.stripe.com/v1/refunds?payment={PAYMENT_ID} \
   -u {API_KEY}:

Retrieving a refund

curl https://api.stripe.com/v1/refunds/{REFUND_ID} \
   -u {API_KEY}:
# 1. Create a customer
$ curl https://api.stripe.com/v1/customers \
-u {API_KEY}:
{
"id": "cus_123123",
...
}
# 2. Create bank account via token or non-token flow
# 2a. Using the token flow
$ curl https://api.stripe.com/v1/tokens \
-u {API_KEY}: \
-d bank_account[country]=US \
-d bank_account[routing_number]=110000000 \
-d bank_account[account_number]=000123456789
{
"id": "btok_123123",
...
}
$ curl https://api.stripe.com/v1/customers/cus_123123/bank_accounts \
-u {API_KEY}: \
-d bank_account=btok_123123
{
"id": "ba_123123",
...
}
# 2b. Creating bank account without token
$ curl https://api.stripe.com/v1/customers/cus_123123/bank_accounts \
-u {API_KEY}: \
-d bank_account[country]=US \
-d bank_account[routing_number]=110000000 \
-d bank_account[account_number]=000123456789
{
"id": "ba_123123",
...
}
# 3. Microdeposit verification
# The values from this are test amounts Stripe deposited into this customer's bank account.
# There will be 2 values between 1-99¢
$ curl https://api.stripe.com/v1/customers/cus_123123/bank_accounts/ba_123123/verify \
-u {API_KEY}: \
-d amounts[]=12 \
-d amounts[]=54
# 4. Create payment
$ curl https://api.stripe.com/v1/payments \
-u {API_KEY}: \
-d customer=cus_123123 \
-d amount=1000 \
-d currency=usd \
-d payment_method=ach
@postagepirate
Copy link

Ahoy, @raycmorgan! Monroe sent me here to ask y'ee directly:

  1. Can we have a custom description for the verification deposits and withdrawal (so for example we could direct our clients to login to their account and verify them)?
  2. Is there the ability to change the description each time we make an ACH transfer from their account (to include invoice numbers, etc.)?

Currently verification deposits are being recorded in our customer's bank statements as "postage pirate | TRANSFER VERIFICATION X", and transfers are being recorded as "postage pirate | TRANSFER X". We'd love to get our name properly capitalized and something that describes the transaction a little more at least, and ideally we'd love to turn this into a branding & information opportunity with custom text.

Thanks so much for all you're doing! Being able to use ACH through Stripe is awesome.

@jeremyricketts
Copy link

@raycmorgan A huge +1 for instant verification. (Looking at https://www.amazon.com/wallet as an example.)

The micro-transaction verification is something we'll need to build into the software we're creating (not that it's hard). We're mainly concerned about the barrier to entry for our end users. They'll need to wait for micro-transactions to clear, check their bank ledger online, go back to our software, and enter those transactions, etc. If it doesn't work, our support team will then pick up the burden of figuring out where the customer made an error. Most of all, we anticipate losing customers somewhere in a setup process that spans a few days.

PS: Stripe first appeared on my radar at a conference a couple years ago where @collision spoke. Such a great talk!

@barkerja
Copy link

We're mainly concerned about the barrier to entry for our end users.

We have the same concerns, especially since a majority of our transactions would be made from "public" users, which adds a whole other dynamic. We would need to design a way to "track" those public users so that they could return to confirm the micro-deposits.

@TheBerg
Copy link

TheBerg commented Mar 20, 2015

+1 for instant verification.

@rebelvc
Copy link

rebelvc commented Apr 3, 2015

As with many here, logging in to the bank for verification would be really beneficial.

Another strategy that Square employed is using debit card for fixed fee debit transfers. For many, debit card numbers are more reachable than bank account + routing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment