Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Revenue
Platforms and marketplaces
Money management
Developer resources
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseManaged Payments
Use Payment Links
Build a checkout page
Build an advanced integration
Build an in-app integration
Payment Methods
Add payment methods
    Overview
    Payment method integration options
    Manage default payment methods in the Dashboard
    Payment method types
    Cards
    Pay with Stripe balance
    Crypto
    Bank debits
      ACH Direct Debit
      Bacs Direct Debit
      Pre-authorised debit in Canada
        Accept a payment
        Save bank details
        Custom mandate agreements
      Australia BECS Direct Debit
      New Zeland BECS Direct Debit
      SEPA Direct Debit
    Bank redirects
    Bank transfers
    Credit transfers (Sources)
    Buy now, pay later
    Real-time payments
    Vouchers
    Wallets
    Enable local payment methods by country
    Custom payment methods
Manage payment methods
Faster checkout with Link
Payment interfaces
Payment Links
Checkout
Web Elements
In-app Elements
Payment scenarios
Handle multiple currencies
Custom payment flows
Flexible acquiring
Orchestration
In-person payments
Terminal
Beyond payments
Incorporate your company
Crypto
Financial Connections
Climate
Understand fraud
Radar fraud protection
Manage disputes
Verify identities
HomePaymentsAdd payment methodsBank debitsPre-authorized debit in Canada

Accept a Canadian pre-authorized debit payment

Build a custom payment form or use Stripe Checkout to accept payments with pre-authorized debit in Canada.

Accepting Canadian pre-authorized debit (PAD) payments on your website consists of creating an object to track a payment, collecting payment method information and mandate acknowledgement, submitting the payment to Stripe for processing and verifying your customer’s bank account.

Stripe uses this payment object, the Payment Intent, to track and handle all the states of the payment until the payment completes.

Set up Stripe
Server-side

First, you need a Stripe account. Register now.

Use our official libraries for access to the Stripe API from your application:

Command Line
Ruby
Python
PHP
Java
Node
Go
.NET
No results
# Available as a gem sudo gem install stripe
Gemfile
Ruby
Python
PHP
Java
Node
Go
.NET
No results
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

Create or retrieve a Customer
Server-side

To reuse a bank account for future payments, it must be attached to a Customer.

You should create a Customer object when your customer creates an account with your business. Associating the ID of the Customer object with your own internal representation of a customer enables you to retrieve and use the stored payment method details later.

Create a new Customer or retrieve an existing Customer to associate with this payment. Include the following code on your server to create a new Customer.

Command Line
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node
Go
.NET
No results
curl -X POST https://api.stripe.com/v1/customers \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"

Create a PaymentIntent
Server-side

A PaymentIntent is an object that represents your intent to collect a payment from a customer and tracks the lifecycle of the payment process through each stage.

To use Canadian pre-authorised debits, you must obtain authorisation from your customer for one-time and recurring debits using a pre-authorised debit agreement (see PAD Mandates). The Mandate object records this agreement and authorisation.

First, create a PaymentIntent on your server and specify the amount to collect and currency (usually cad). If you already have another integration using the Payment Intents API, add acss_debit to the list of payment method types for your PaymentIntent. Specify the id of the Customer.

If you want to reuse the payment method in the future, provide the setup_future_usage parameter with a value of off_session.

To define a payment schedule and verification method on the Mandate for this PaymentIntent, also include the following parameters:

ParameterValueRequired?
payment_method_options[acss_debit][mandate_options][payment_schedule]The mandate payment schedule. Accepted values are interval, sporadic, or combined. See the PAD Mandates overview to help you select the right schedule for your business.Yes
payment_method_options[acss_debit][mandate_options][interval_description]Text description of the interval of payment schedule. See the PAD Mandates overview to help you construct the right interval description for your business.If payment_schedule is specified as interval or combined
payment_method_options[acss_debit][mandate_options][transaction_type]The type of the mandate you are creating, either personal (if your customer is an individual) or business (if your customer is a business).Yes
Command Line
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node
Go
.NET
No results
curl https://api.stripe.com/v1/payment_intents \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d amount=1099 \ -d currency=cad \ -d setup_future_usage=off_session \ -d customer={{CUSTOMER_ID}} \ -d "payment_method_types[]"=acss_debit \ -d "payment_method_options[acss_debit][mandate_options][payment_schedule]"=interval \ -d "payment_method_options[acss_debit][mandate_options][interval_description]"="First day of every month" \ -d "payment_method_options[acss_debit][mandate_options][transaction_type]"=personal

Retrieve the client secret

The PaymentIntent includes a client secret that the client side uses to securely complete the payment process. You can use different approaches to pass the client secret to the client side.

Retrieve the client secret from an endpoint on your server, using the browser’s fetch function. This approach is best if your client side is a single-page application, particularly one built with a modern front-end framework such as React. Create the server endpoint that serves the client secret:

main.rb
Ruby
Python
PHP
Java
Node
Go
.NET
No results
get '/secret' do intent = # ... Create or retrieve the PaymentIntent {client_secret: intent.client_secret}.to_json end

And then fetch the client secret with JavaScript on the client side:

(async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Render the form using the clientSecret })();

Collect payment method details and submit
Client-side

When a customer clicks to pay with Canadian pre-authorized debit, we recommend you use Stripe.js to submit the payment to Stripe. Stripe.js is our foundational JavaScript library for building payment flows. It will automatically handle integration complexities, and enables you to easily extend your integration to other payment methods in the future.

Include the Stripe.js script on your checkout page by adding it to the head of your HTML file.

checkout.html
<head> <title>Checkout</title> <script src="https://js.stripe.com/basil/stripe.js"></script> </head>

Create an instance of Stripe.js with the following JavaScript on your checkout page.

client.js
// Set your publishable key. Remember to change this to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/apikeys const stripe = Stripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
);

Rather than sending the entire PaymentIntent object to the client, use its client secret from the previous step. This is different from your API keys that authenticate Stripe API requests.

The client secret should still be handled carefully because it can complete the charge. Do not log it, embed it in URLs, or expose it to anyone but the customer.

Use stripe.confirmAcssDebitPayment to collect bank account details and verification, confirm the mandate, and complete the payment when the user submits the form. Including the customer’s email address and the account holder’s name in the billing_details property of the payment_method parameter is required to create a PAD payment method.

const form = document.getElementById('payment-form'); const accountholderName = document.getElementById('accountholder-name'); const email = document.getElementById('email'); const submitButton = document.getElementById('submit-button'); const clientSecret = submitButton.dataset.secret; form.addEventListener('submit', async (event) => { event.preventDefault(); const {paymentIntent, error} = await stripe.confirmAcssDebitPayment( clientSecret, { payment_method: { billing_details: { name: accountholderName.value, email: email.value, }, }, } ); if (error) { // Inform the customer that there was an error. console.log(error.message); } else { // Handle next step based on PaymentIntent's status. console.log("PaymentIntent ID: " + paymentIntent.id); console.log("PaymentIntent status: " + paymentIntent.status); } });

Stripe.js then loads an on-page modal UI that handles bank account details collection and verification, presents a hosted mandate agreement and collects authorization.

Note

stripe.confirmAcssDebitPayment may take several seconds to complete. During that time, disable your form from being resubmitted and show a waiting indicator like a spinner. If you receive an error, show it to the customer, re-enable the form, and hide the waiting indicator.

If successful, Stripe returns a PaymentIntent object, with one of the following possible statuses:

StatusDescriptionNext step
processingThe bank account has been instantly verified or verification was not necessary.​​​​​​Step 6: Confirm the PaymentIntent succeeded
requires_actionFurther action needed to complete bank account verification.Step 5: Verifying bank accounts with micro-deposits

After successfully confirming the PaymentIntent, an email confirmation of the mandate and collected bank account details must be sent to your customer. Stripe handles these by default, but you can choose to send custom notifications if you prefer.

Note

Mandate confirmation emails will not be sent to the customer’s email address when testing the integration.

If the customer chooses to close the modal without completing the verification flow, Stripe.js returns the following error:

{ "error": { "type": "validation_error", "code": "incomplete_payment_details", "message": "Please provide complete payment details." } }

Verify bank account with micro-deposits
Client-side

Not all customers can verify the bank account instantly. This step only applies if your customer has elected to opt out of the instant verification flow in the previous step.

In this case, Stripe automatically sends two micro-deposits to the customer’s bank account. These deposits take 1-2 business days to appear on the customer’s online statement and have statement descriptors that include ACCTVERIFY.

The result of the stripe.confirmAcssDebitPayment method call in the previous step is a PaymentIntent in the requires_action state. The PaymentIntent contains a next_action field that contains some useful information for completing the verification.

Stripe notifies your customer at the billing email when the deposits are expected to arrive. The email includes a link to a Stripe-hosted verification page where they can confirm the amounts of the deposits and complete verification.

There is a limit of three failed verification attempts. If this limit is exceeded, the bank account can no longer be verified. In addition, there is a timeout for micro-deposit verifications of 10 days. If micro-deposits are not verified in that time, the PaymentIntent reverts to requiring new payment method details. Clear messaging about what these micro-deposits are and how you use them can help your customers avoid verification issues.

Optional: Custom email and verification page

If you choose to send custom email notifications, you have to email your customer instead. To do this, you can use the verify_with_microdeposits[hosted_verification_url] URL in the next_action object to direct your customer to complete the verification process.

If you are sending custom emails and don’t want to use the Stripe hosted verification page, you can create a form on your site for your customers to relay these amounts to you and verify the bank account using Stripe.js.

stripe.verifyMicrodepositsForPayment(clientSecret, { amounts: [32, 45], });

When the bank account is successfully verified, Stripe returns the PaymentIntent object with a status of processing, and sends a payment_intent.processing webhook event.

Verification can fail for several reasons. The failure may happen synchronously as a direct error response, or asynchronously through a payment_intent.payment_failed webhook event (shown in the following examples).

{ "error": { "code": "payment_method_microdeposit_verification_amounts_mismatch", "message": "The amounts provided do not match the amounts that were sent to the bank account. You have {attempts_remaining} verification attempts remaining.", "type": "invalid_request_error" } }
Error CodeSynchronous or asynchronousMessageStatus Change
payment_method_microdeposit_failedSynchronously, or asynchronously through webhook eventMicrodeposits failed. Please check the account, institution and transit numbers providedstatus is requires_payment_method, and last_payment_error is set.
payment_method_microdeposit_verification_amounts_mismatchSynchronouslyThe amounts provided do not match the amounts that were sent to the bank account. You have {attempts_remaining} verification attempts remaining.Unchanged
payment_method_microdeposit_verification_attempts_exceededSynchronously, and asynchronously through webhook eventExceeded number of allowed verification attemptsstatus is requires_payment_method, and last_payment_error is set.
payment_method_microdeposit_verification_timeoutAsynchronously through webhook eventMicrodeposit timeout. Customer hasn’t verified their bank account within the required 10-day period.status is requires_payment_method, and last_payment_error is set.

Confirm the PaymentIntent succeeded
Server-side

Canadian pre-authorised debits are a delayed notification payment method. This means that it can take up to five business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of processing. Successful completion of the payment updates the PaymentIntent status from processing to succeeded.

The following events are sent when the PaymentIntent status is updated:

EventDescriptionNext step
payment_intent.processingThe customer’s payment was submitted to Stripe successfully.Wait for the initiated payment to succeed or fail.
payment_intent.succeededThe customer’s payment succeeded.Fulfill the goods or services that the customer purchased.
payment_intent.payment_failedThe customer’s payment was declined. This can also apply to a failed microdeposit verification.Contact the customer through email or push notification and request another payment method. If the webhook was sent due to a failed microdeposit verification, the user needs to enter in their bank account details again and a new set of microdeposits will be deposited in their account.

We recommend using webhooks to confirm the charge has succeeded and to notify the customer that the payment is complete. You can also view events on the Stripe Dashboard.

Test your integration

Receive micro-deposit verification email

To receive the micro-deposit verification email in a sandbox after collecting the bank account details and accepting a mandate, provide an email address in the payment_method[billing_details][email] field in the form of {any_prefix}+test_email@{any_domain} when confirming the payment method details.

Test account numbers

Stripe provides several test account numbers you can use to make sure your integration for manually-entered bank accounts is ready for production. All test accounts that automatically succeed or fail the payment must be verified using the test micro-deposit amounts below before they can be completed.

Institution NumberTransit NumberAccount NumberScenario
00011000000123456789Succeeds the payment immediately after micro-deposits are verified.
00011000900123456789Succeeds the payment with a three-minute delay after micro-deposits are verified.
00011000000222222227Fails the payment immediately after micro-deposits are verified.
00011000900222222227Fails the payment with a three-minute delay after micro-deposits are verified.
00011000000666666661Fails to send verification micro-deposits.
00011000000777777771Fails the payment due to the payment amount causing the account to exceed its weekly payment volume limit.
00011000000888888881Fails the payment due to the payment amount exceeding the account’s transaction limit.

To mimic successful or failed bank account verifications in a sandbox, use these meaningful amounts for micro-deposits:

Micro-deposit ValuesScenario
32 and 45Successfully verifies the account.
10 and 11Simulates exceeding the number of allowed verification attempts.
Any other number combinationsFails account verification.

OptionalInstant only verification
Server-side

OptionalMicro-deposit only verification
Server-side

See also

  • Save Canadian pre-authorised debit details for future payments
Was this page helpful?
YesNo
  • Need help? Contact Support.
  • Join our early access programme.
  • Check out our changelog.
  • Questions? Contact Sales.
  • LLM? Read llms.txt.
  • Powered by Markdoc