Save details for future payments with pre-authorized debit in Canada
Save payment method details for future Canadian pre-authorized debit payments.
You can use the Setup Intents API to collect payment method details in advance, with the final amount or payment date determined later. This is useful for:
- Saving payment methods to a wallet to streamline future purchases
- Collecting surcharges after fulfilling a service
- Starting a free trial for a subscription
Most bank accounts in Canada hold Canadian dollars (CAD), with a small number of accounts in other currencies, including US dollars (USD). It is possible to accept PAD payments in either CAD or USD, but choosing the correct currency for your customer is important to avoid payment failures.
Unlike many card-based payment methods, you might not be able to successfully debit a CAD account in USD or debit a USD account in CAD. Most often, attempting to do so results in a delayed payment failure that takes up to five business days.
To avoid these failures, it is safest to set up PAD bank accounts in CAD unless you are confident your customer’s account accepts USD debits.
Set up StripeServer-side
First, you need a Stripe account. Register now.
Use our official libraries for access to the Stripe API from your application:
Create or retrieve a CustomerServer-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. If your customer hasn’t created an account, you can still create a Customer object now and associate it with your internal representation of the customer’s account later.
Create a new Customer or retrieve an existing Customer to associate with these payment details. Include the following code on your server to create a new Customer.
Create a SetupIntentServer-side
A SetupIntent is an object that represents your intent to set up a customer’s payment method for future payments. The SetupIntent
tracks the steps of this set-up process.
In order to use Canadian pre-authorized debits, you must obtain authorization from your customer for one-time and recurring debits using a pre-authorized debit agreement (see PAD Mandates). The Mandate object records this agreement and authorization.
Create a SetupIntent on your server with payment_method_types set to acss_
and specify the Customer’s id. In order to define a payment schedule on the Mandate for this SetupIntent, also include the following parameters:
Parameter | Value | Required? |
---|---|---|
payment_ | Currency to use for future payments with this payment method. Must match the customer’s bank account currency. Accepted values are cad or usd . | Yes |
payment_ | 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_ | 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_ specified as interval or combined |
payment_ | 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 |
Retrieve the client secret
The SetupIntent 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.
Collect payment method details and mandate acknowledgementClient-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.
<head> <title>Checkout</title> <script src="https://js.stripe.com/v3/"></script> </head>
Create an instance of Stripe.js with the following JavaScript on your checkout page.
// 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.confirmAcssDebitSetup to collect bank account details and verification, confirm the mandate, and complete the setup when the user submits the form. Including the customer’s email address and the account holder’s name in the billing_
property of the payment_
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 {setupIntent, error} = await stripe.confirmAcssDebitSetup( 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 SetupIntent's status. console.log("SetupIntent ID: " + setupIntent.id); console.log("SetupIntent status: " + setupIntent.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.
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 SetupIntent object, with one of the following possible statuses:
Status | Description | Next step |
---|---|---|
succeeded | The bank account has been instantly verified or verification was not necessary. | No action needed |
requires_ | Further action needed to complete bank account verification. | Step 5: Verifying bank accounts with micro-deposits |
After successfully confirming the SetupIntent, 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-depositsClient-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.
method call is a SetupIntent in the requires_
state. The SetupIntent contains a next_
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_
URL in the next_
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.verifyMicrodepositsForSetup(clientSecret, { amounts: [32, 45], });
When the bank account is successfully verified, Stripe returns the SetupIntent object, with a status
of succeeded
, and sends a setup_
webhook event.
Verification can fail for several reasons. The failure may happen synchronously as a direct error response, or asynchronously through a setup_
webhook event (shown in the following examples).
Error Code | Synchronous or asynchronous | Message | Status Change |
---|---|---|---|
payment_ | Synchronously, or asynchronously through webhook event | Microdeposits failed. Please check the account, institution and transit numbers provided | status is requires_ , and last_ is set. |
payment_ | Synchronously | The amounts provided do not match the amounts that were sent to the bank account. You have {attempts_remaining} verification attempts remaining. | Unchanged |
payment_ | Synchronously, and asynchronously through webhook event | Exceeded number of allowed verification attempts | status is requires_ , and last_ is set. |
payment_ | Asynchronously through webhook event | Microdeposit timeout. Customer hasn’t verified their bank account within the required 10 day period. | status is requires_ , and last_ is set. |
Test your integration
Receive micro-deposit verification email
In order to receive the micro-deposit verification email in test mode after collecting the bank account details and accepting a mandate, provide an email in the payment_
field in the form of {any_
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 Number | Transit Number | Account Number | Scenario |
---|---|---|---|
000 | 11000 | 000123456789 | Succeeds the payment immediately after micro-deposits are verified. |
000 | 11000 | 900123456789 | Succeeds the payment with a three-minute delay after micro-deposits are verified. |
000 | 11000 | 000222222227 | Fails the payment immediately after micro-deposits are verified. |
000 | 11000 | 900222222227 | Fails the payment with a three-minute delay after micro-deposits are verified. |
000 | 11000 | 000666666661 | Fails to send verification micro-deposits. |
000 | 11000 | 000777777771 | Fails the payment due to the payment amount causing the account to exceed its weekly payment volume limit. |
000 | 11000 | 000888888881 | Fails the payment due to the payment amount exceeding the account’s transaction limit. |
To mimic successful or failed bank account verifications in test mode, use these meaningful amounts for micro-deposits:
Micro-deposit Values | Scenario |
---|---|
32 and 45 | Successfully verifies the account. |
Any other number combinations | Fails account verification. |