ACH Credit Transfer Payments with Sources Public Beta

    Use Sources to accept ACH payments sent directly from your customers.

    Stripe users in the United States can receive ACH Credit Transfers directly from customers using Sources—a single integration path for creating payments using any supported method.

    During the payment process, a Source object is created and your customer is provided with bank account information to send a required amount to. Your customer uses this information to make a transfer from their bank account using the U.S. ACH system or domestic wire. After the transfer is received, your integration uses the source to make a charge request and complete the payment.

    ACH Credit Transfers is a push-based and reusable method of payment. This means your customer must take action to send funds to you, which can take a few days to arrive. Once the funds have arrived, there is synchronous confirmation of any charge request made.

    Step 1: Create a Source object

    A Source object is either created client-side using Stripe.js or server-side using the Source creation endpoint, with the following parameters:

    Parameter Value
    type ach_credit_transfer
    currency usd (ACH Credit Transfer payments must be in US Dollars)
    owner[email] the full email address of the customer
    stripe.createSource({
      type: 'ach_credit_transfer',
      currency: 'usd',
      owner: {
        email: 'jenny.rosen@example.com',
      },
    }).then(function(result) {
      // handle result.error or result.source
    });
    

    Using either method, Stripe returns a Source object containing the relevant details for the method of payment used. Information specific to ACH is provided within the ach_credit_transfer subhash.

    {
      "id": "src_18cPLvAHEMiOZZp1YBngt6En",
      "object": "source",
      "currency": "usd",
      "flow": "receiver",
      "livemode": false,
      "receiver": {
        "address": "121042882-38381234567890123",
        "amount_received": 0,
        "amount_charged": 0,
    See all 35 lines "amount_returned": 0, "refund_attributes_status": "missing", "refund_attributes_method": "email" }, "ach_credit_transfer": { "account_number": "test_52796e3294dc", "routing_number": "110000000", "fingerprint": "ecpwEzmBOSMOqQTL", "bank_name": "TEST BANK", "swift_code": "TSTEZ122" }, "owner": { "address": null, "email": "jenny.rosen@example.com", "name": null, "phone": null, "verified_address": null, "verified_email": null, "verified_name": null, "verified_phone": null }, "status": "pending", "type": "ach_credit_transfer", "usage": "reusable" }

    Error codes

    Source creation for ACH Credit Transfer payments may return any of the following errors:

    Error Description
    payment_method_not_available The payment method is currently not available. You should invite your customer to fallback to another payment method to proceed.
    processing_error An unexpected error occurred preventing us from creating the source. The source creation should be retried.

    Step 2: Have the customer push funds

    When creating a source, its status is initially set to pending and cannot yet be used to make a charge request. In addition, receiver[amount_received] is set to zero since no funds have yet been transferred. Your customer must transfer the amount you request so that the necessary funds are available.

    After creating a source, there are four pieces of available information that you should provide to your customer:

    • ach_credit_transfer[routing_number]: The routing number of the account to transfer funds to
    • ach_credit_transfer[account_number]: The account number to transfer funds to

    In addition, you should specify an amount that you need the customer to send. Customers create a transfer with their bank, using the information you provide. Bank transfers can take up to five days to complete.

    ACH Credit Transfer sources are reusable and can be used for recurring payments. The information provided to the customer can be reused whenever they need to send additional funds.

    Step 3: Charge the Source

    Once the customer makes a transfer, receiver[amount_received] is updated to reflect the total amount of all received transfers, and the status of the source transitions to chargeable. Your customer can transfer any amount across multiple transfers. Each transfer they make is represented by a source transaction.

    Since these transfers happen asynchronously (and can take days), it is essential that your integration rely on webhooks to determine when the source becomes chargeable in order to create a charge. Please refer to our best practices for more details on how to best integrate payment methods using webhooks.

    The following webhook events are sent to notify you about changes to the status of an ACH Credit Transfer source:

    Event Description
    source.pending A Source object becomes pending once it has been successfully created.
    source.chargeable A Source object becomes chargeable once it has received funds.
    source.transaction.created Your customer has sent a transfer and a new source transaction has been created.

    After the Source becomes chargeable, and before creating a charge request to complete the payment, you should attach it to a Customer for later reuse.

    Attaching the Source to a Customer

    Attaching the Source to a Customer is required for you to reuse it for future payments. Please refer to our Sources & Customers guide for more details on how to attach Sources to new or existing Customers and how the two objects interact together. The following snippet attaches the Source to a new Customer:

    curl https://api.stripe.com/v1/customers \
       -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
       -d email="paying.user@example.com" \
       -d source=src_18eYalAHEMiOZZp1l9ZTjSU0
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    customer = Stripe::Customer.create({
      email: 'paying.user@example.com',
      source: 'src_18eYalAHEMiOZZp1l9ZTjSU0',
    })
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    customer = stripe.Customer.create(
      email='paying.user@example.com',
      source='src_18eYalAHEMiOZZp1l9ZTjSU0',
    )
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    $customer = \Stripe\Customer::create(array(
      "email" => "paying.user@example.com",
      "source" => "src_18eYalAHEMiOZZp1l9ZTjSU0",
    ));
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";
    
    Map<String, Object> customerParams = new HashMap<String, Object>();
    customerParams.put("email", "paying.user@example.com");
    customerParams.put("source", "src_18eYalAHEMiOZZp1l9ZTjSU0");
    
    Customer customer = Customer.create(customerParams);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    stripe.customers.create({
      email: "paying.user@example.com",
      source: "src_18eYalAHEMiOZZp1l9ZTjSU0",
    }, function(err, customer) {
      // asynchronously called
    });
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.Key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    customerParams := &stripe.CustomerParams{
      Email: "paying.user@example.com",
    }
    customerParams.SetSource("src_18eYalAHEMiOZZp1l9ZTjSU0")
    c, err := customer.New(customerParams)
    

    Making a charge request to finalize the payment

    Once attached, you can use the Source object’s ID along with the Customer object’s ID to perform a charge request and finalize the payment.

    curl https://api.stripe.com/v1/charges \
       -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
       -d amount=1000 \
       -d currency=usd \
       -d customer=cus_AFGbOSiITuJVDs \
       -d source=src_18eYalAHEMiOZZp1l9ZTjSU0
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    charge = Stripe::Charge.create({
      amount: 1000,
      currency: 'usd',
      customer: 'cus_AFGbOSiITuJVDs',
      source: 'src_18eYalAHEMiOZZp1l9ZTjSU0',
    })
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    charge = stripe.Charge.create(
      amount=1000,
      currency='usd',
      customer='cus_AFGbOSiITuJVDs',
      source='src_18eYalAHEMiOZZp1l9ZTjSU0',
    )
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    $charge = \Stripe\Charge::create(array(
      "amount" => 1000,
      "currency" => "usd",
      "customer" => "cus_AFGbOSiITuJVDs",
      "source" => "src_18eYalAHEMiOZZp1l9ZTjSU0",
    ));
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";
    
    Map<String, Object> chargeParams = new HashMap<String, Object>();
    chargeParams.put("amount", 1000);
    chargeParams.put("currency", "usd");
    chargeParams.put("customer", "cus_AFGbOSiITuJVDs");
    chargeParams.put("source", "src_18eYalAHEMiOZZp1l9ZTjSU0");
    
    Charge charge = Charge.create(chargeParams);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    stripe.charges.create({
      amount: 1000,
      currency: "usd",
      customer: "cus_AFGbOSiITuJVDs",
      source: "src_18eYalAHEMiOZZp1l9ZTjSU0",
    }, function(err, charge) {
      // asynchronously called
    });
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.Key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    chargeParams := &stripe.ChargeParams{
      Amount: 1000,
      Currency: "usd",
      Customer: "cus_AFGbOSiITuJVDs",
    }
    chargeParams.SetSource("src_18eYalAHEMiOZZp1l9ZTjSU0")
    ch, err := charge.New(chargeParams)
    

    When a charge request is made, the source’s receiver[amount_charged] is updated with the amount that has been used. The receiver[amount_received] does not change. The available amount left to charge is the difference between these two values.

    For instance, if the value for receiver[amount_received] is initially 2500 ($25) and two charges of 1100 ($11) and 900 ($9) are created at separate times, the value for receiver[amount_charged] is 2000 ($20). The remaining amount available for charges is 500 ($5).

    Once all the funds have been used, the two amounts are the same. When this occurs, the status of the source transitions back to pending. Should the customer make any additional transfers, the source again transitions to chargeable.

    Step 4: Confirm that the charge has succeeded

    Since the customer has already pushed the funds at the time the Source becomes chargeable, unless there is an unexpected error, the Charge will immediately succeed.

    You will also receive the following webhook event as the charge is created:

    Event Description
    charge.succeeded The charge succeeded and the payment is complete.

    We recommend that you rely on the charge.succeeded webhook event to notify your customer that the payment process has been completed and their order is confirmed. Please refer to our best practices for more details on how to best integrate payment methods using webhooks.

    Refunding payments

    ACH Credit Transfer payments can be refunded through either the Dashboard or API. However, the account information that the funds should be returned to needs to be provided by the customer. By default, we automatically contact the customer at the email address provided during source creation when a refund is created. Once the customer provides us with their account information, we process the refund automatically.

    The refund’s initial status is pending. If it fails, the charge.refund.updated event is sent and its status transitions to failed. This means that we have been unable to process the refund, and you must return the funds to your customer outside of Stripe. This is a rare occurrence and can happen if the account the refund is being sent to has been frozen. Refunds that have been completed have the status succeeded.

    Disputed payments

    Unlike traditional ACH debit transactions, ACH Credit Transfer payments can’t be reversed. A customer may request for their funds back, at which point Stripe reviews each request and can take action if it is deemed necessary.

    Testing ACH Credit Transfer payments

    When creating a Source object using your test API keys, a transaction is automatically created on the source. A source.chargeable and a source.transaction.created webhook event are sent immediately.

    The amount included in the test transaction defaults to $10.00. You can customize this amount by providing an email address of amount_[custom_amount]@example.com as the value for owner[email]. For instance, to create a test transaction of the amount $42.42, use amount_4242@example.com.

    stripe.createSource({
      type: 'ach_credit_transfer',
      currency: 'usd',
      owner: {
        email: 'amount_4242@example.com',
      },
    }).then(function(result) {
      // handle result.error or result.source
    });
    

    You can also test multiple pushes to a given source by updating the owner[email] property using the API in a similar way.

    curl https://api.stripe.com/v1/sources/src_18cPLvAHEMiOZZp1YBngt6En \
       -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
       -d currency=usd \
       -d owner[email]="amount_2424@rosen.net"
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    source = Stripe::Source.retrieve("src_18cPLvAHEMiOZZp1YBngt6En")
    source.owner["email"] = "amount_2424@rosen.net"
    source.save
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
    
    source = stripe.Source.retrieve("src_18cPLvAHEMiOZZp1YBngt6En")
    source.owner["email"] = "amount_2424@rosen.net"
    source.save()
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    $source = StripeSource::retrieve("src_18cPLvAHEMiOZZp1YBngt6En");
    $source->owner["email"] = "amount_2424@rosen.net";
    $source->save();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";
    
    Source source = Source.retrieve("src_18cPLvAHEMiOZZp1YBngt6En");
    Map owner = new HashMap();
    owner.put("email", "amount_2424@rosen.net");
    Map params = new HashMap();
    params.put("owner", owner);
    source.update(params);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
    
    stripe.sources.update("src_18cPLvAHEMiOZZp1YBngt6En", {
      owner: {
        email: "amount_2424@rosen.net",
      },
    )
    

    This example creates a SourceTransaction for the specified source, with the amount property set to 2424.

    Source transactions

    Unlike other push-based payment methods, an ACH Credit Transfer source has no required amount that your customer must send. You can instruct your customer to send any amount, and it can be made up of multiple transfers. Your customers can also send additional amounts when necessary (e.g., recurring payments). As such, sources can have multiple associated transactions. After receiving a transfer from the customer of any amount, the source becomes chargeable and is ready to use.

    For instance, if you request your customer to send $100, they can send the full amount in a single transfer or multiple transfers of smaller amounts (e.g., four transfers of $25). In the case of recurring payments, your customer could create a recurring transfer with their bank for a specified amount, ensuring that the source always has the correct amount of funds for you to create the necessary charge requests. If the recurring amount varies, your customer can send the correct amount whenever necessary.

    Once a transfer has been received, the source’s receiver[amount_received] value represents the total that has been received and available for creating a charge with. Further transfers result in additional transactions being created, and the amount is added to the available value for receiver[amount_received].

    For the purposes of a refund, the receiver[refund_attributes_status] attribute is set to available once there are available funds and the customer has provided the necessary account information.

    ...
    "receiver": {
      "address": "121042882-38381234567890123",
      "amount_received": 1000,
      "amount_charged": 0,
      "amount_returned": 0,
      "refund_attributes_status": "available",
      "refund_attributes_method": "auto"
    },
    ...
    

    Retrieving transaction history

    You can retrieve a list of all transactions associated with a specific source using the following API request:

    curl https://api.stripe.com/v1/sources/src_18cPLvAHEMiOZZp1YBngt6En/source_transactions \
       -u sk_test_BQokikJOvBiI2HlWgH4olfQ2:
    

    Each transaction is listed with the amount that the customer transferred, along with additional information about the transfer.

    {
      "object": "list",
      "url": "/v1/sources/src_18cPLvAHEMiOZZp1YBngt6En/source_transactions",
      "has_more": false,
      "data": [
        {
          "id": "srctxn_ldfj129843jhs09u09",
          "object": "source_transaction",
          "amount": 1000,
          "created": 1472746608,
          "customer_data": "some customer defined string",
          "currency": "usd",
          "type": "ach_credit_transfer",
          "ach_credit_transfer": {
            "last4": 7890,
            "routing_number": "121042892",
            "fingerprint": "FX76YHN",
          },
        }
      ],
    }
    

    Related resources