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
@dmitche
Copy link

dmitche commented Jun 13, 2014

Do you believe Stripe will remove the bank verify steps once this is live?

@bkrausz
Copy link

bkrausz commented Jun 18, 2014

@dmitche: we won't be getting rid of verification. We are experimenting with alternative verification methods, but for now you should assume microdeposits will be required.

@ghidinelli
Copy link

In the same way you plan to merge charges and payments, I'd probably like to see a unified view of accounts rather than /customers/{ID}/bank_accounts and cards so I can list all accounts easily.

@raycmorgan
Copy link
Author

@ghidinelli do you mean you would like to see all cards/bank accounts that belong to a customer? If so, you can request the customer and get that information. If not, mind explaining a bit more what you would like to do?

@barkerja
Copy link

Will Stripe ever support ad hoc payments through ACH? Example: making a payment using your checking account in the same manner one would using their credit/debit.

It seems a bit cumbersome (unless this was a decision made for security reasons) that you must first create a customer and verify the account before one can ever make a payment using the account.

@raycmorgan
Copy link
Author

@barkerja We have to verify bank accounts before processing the payments. So for now it makes sense to attach them to a customer where you preform the verification response, payments, etc. We are looking at other verification methods, but they will all most likely involve Stripe customer objects as the object you act upon.

@ribzin
Copy link

ribzin commented Aug 20, 2014

Have you considered a Yodlee integration to get rid of the micro deposit verification?

@raycmorgan
Copy link
Author

@ribzin We are looking into adding instant verification (we are considering a few different providers). If this is a blocker for you, ping me at ray@stripe.com -- love to chat more about a good flow for this.

Copy link

ghost commented Sep 11, 2014

I can see where the micro deposits would be needed. I'm just worried that the majority of my clients are government agencies (schools, etc.) and will not wish to go through these extra steps. Just some thoughts.

@KathyGulledge
Copy link

How long until Customer Sharing support availability?

@KathyGulledge
Copy link

Can a payment be requested by Id to get it's status or are status updates only available via WebHooks?

@hoverlover
Copy link

Two questions:

  1. Are Canadian bank accounts supported?
  2. When will customer sharing be supported?

@SempervirensSoftware
Copy link

+1 for Customer Sharing support. It would be a big win for us.

It looks like you can add the bank_account reference to a user's Customer object, but the payment API returns a (Status 400) No such bankaccount. I've verified that our app's Customer and the User's customer have a reference to the same account token, so unless I'm screwing up somehow, it seems like a pure access issue.

Everything else is working great though. Thanks!

@glade-at-gigwell
Copy link

Has anyone successfully created a payment between two Connect users? Can someone paste a gist of the cURL flow.

We're hitting the same error as SempervirensSoftware, "message": "No such bankaccount: ba_....",

@glade-at-gigwell
Copy link

@SempervirensSoftware - Stripe has fixed this bug. We can process ach transactions between connected users now.

Copy link

ghost commented Oct 29, 2014

Anytime frame before this service goes live? As in, not in Beta.

@raycmorgan
Copy link
Author

@glade-at-gigwell @SempervirensSoftware - just updated the docs to show the customer sharing flow (connect-support).

@raycmorgan
Copy link
Author

@KenGulledge, @hoverlover ^^ on customer sharing

@hoverlover Canadian banks accounts are not yet supported.

@TheGreenPanda no timeframe yet. Things we are working on: finalizing the API (we want to merge it with charges -- though the current API will continue to be supported for beta users), binding support and dashboard support.

@glade-at-gigwell
Copy link

@raycmorgan - are webhooks enabled for test data? We haven't been able to trigger them. Thanks.

@dnlhys
Copy link

dnlhys commented Dec 4, 2014

@raymorgan @bkrausz +1 for instant verification. We're receiving strong feedback from our non-profit partners about this. Thanks again!

@raycmorgan
Copy link
Author

Just a heads up: you can now view ACH payments in your Stripe dashboard. However, at this time it is read-only, but we are adding support for creating and refunding them shortly.

@iautom8things
Copy link

@raycmorgan Is there an ETA for updated Stripe SDKs for working w/ these beta features?

@JonEastman
Copy link

Hi @raycmorgan, will we eventually be able to view attached bank accounts to customers via the admin interface? I know right now we can access them through the API.

@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