Collecting Payment Information

You do not have to collect payment information. Memberships and Patients can exist without it, but you likely will want to. Hint supports 2 ways to gather payment information: manual entry of card and bank information, or verifying bank information with bank login data.

Transaction fees are lower for ACH payments made from a patient's bank account. The easiest way to connect a patient's bank account is having them authenticate with their banks' login credentials using Plaid Link. Plaid verifies the bank information and allows it be charged by Hint.


Do Not Send Hint Sensitive Payment Details

Sensitive payment details like card numbers and bank account numbers should never be sent to Hint. If you are collecting payment details, they should always be sent to Stripe first, so they can be tokenized, and then you send the token to Hint.

Unless you are PCI compliant, you probably don't want card/bank numbers to hit your servers either, which is why the Stripe Elements library sends this information from the user's client directly to Stripe (more detail here:

Collecting Bank Information

We allow collecting of bank information in two ways:

  1. Having the patient use their online bank login to instantly verify their account information at over 1,500 banking institutions
  2. Manual entry of bank account and bank routing information

We encourage you to use path one, and we make it easy to do so with our partnership with Plaid. Following the code examples below you embed this into your web page with minimal effort. This option is easier for most users who have online banking.

For those patients who do not have or do not want to use their online bank information, you can use stripe.js to tokenize their account and routing numbers. You will need to add this flow as a backup in case a banking institution is not on Plaid's link platform.

When manually collecting bank information ensure you are doing the following:

  1. You are requiring dual entry of account number by the patient (obscure the first entry so it can’t be seen when confirming the number the 2nd time) before sending it to stripe OR Hint. This significantly reduces typos which can result in fines or other consequences if the wrong account is debited.
  2. You are collecting at least bank routing, account number, account holder name, and account type (individual or company)
  3. You get authorization from bank account owner to debit the account as part of your form.
  4. You are using Hint’s public keys

more info on authorization requirements


Use Hint's Public Keys

Stripe Production Public Key: pk_live_ImPWqYGkbMfjwg22MiAmB6u4
Stripe Test Public Key: pk_test_Y7byiIyWmjqy4Xy7fjArJdPl

Plaid Public Key: 3d8ae5f373153608c9d3ad43fe88db
Plaid Client Id: 5695b686a4ce3935462bb5bb

// Plaid Public Key: '3d8ae5f373153608c9d3ad43fe88db'
// Plaid Client Id: '5695b686a4ce3935462bb5bb'
// Sandbox passwords are shown at the bottom of the link js modal
// Code Example Adapted From:

<button id="link-button">Link Account</button>
<script type="text/javascript">
(function($) {
  var handler = Plaid.create({
    apiVersion: 'v2',
    // Enter your organization name here
    clientName: 'Plaid Quickstart',
    // Optional, specify an array of ISO-3166-1 alpha-2 country
    // codes to initialize Link; European countries will have GDPR
    // consent panel
    countryCodes: ['US'],
    // Optional, specify a language to localize Link
    language: 'en',
    env: 'sandbox', // or 'production'
    key: '3d8ae5f373153608c9d3ad43fe88db',
    product: ['auth'],
    // Optional, use webhooks to get transaction and error updates
    webhook: '',
    onLoad: function() {
      // Optional, called when Link loads
    onSuccess: function(public_token, metadata) {
      // Send the public_token to Hint's server.
      // The metadata object contains info about the institution the
      // user selected and the account ID selected.
      $.post('/bank_accounts', {
        plaid_public_token: public_token,
        plaid_account_id: metadata.account_id,
    onExit: function(err, metadata) {
      // The user exited the Link flow.
      if (err != null) {
        // The user encountered a Plaid API error prior to exiting.
      // metadata contains information about the institution
      // that the user selected and the most recent API request IDs.
      // Storing this information can be helpful for support.
    onEvent: function(eventName, metadata) {
      // Optionally capture Link flow events, streamed through
      // this callback as your users connect an Item to Plaid.
      // For example:
      // eventName = "TRANSITION_VIEW"
      // metadata  = {
      //   link_session_id: "123-abc",
      //   mfa_type:        "questions",
      //   timestamp:       "2017-09-14T14:42:19.350Z",
      //   view_name:       "MFA",
      // }

  $('#link-button').on('click', function(e) {;
var stripe = Stripe('pk_test_Y7byiIyWmjqy4Xy7fjArJdPl');

  .createToken('bank_account', {
    country: 'US',
    currency: 'usd',
    routing_number: '110000000',
    account_number: '000123456789',
    account_holder_name: 'Jenny Rosen',
    account_holder_type: 'individual',
  .then(function(token) {
     post('/bank_accounts', {
       last_four: token.bank_account.last4

Posting the data to Hint

You must attach this payment information to a patient. When posting a bank account we require either the stripe_token or both plaid_public_token & plaid_account_id

Testing Bank Accounts / ACH

You can mimic successful and failed ACH charges using the following bank routing and account numbers:

  • Routing number: 110000000
  • Account number:
    • 000123456789 (success)
    • 000111111116 (failure upon use)
    • 000111111113 (account closed)
    • 000222222227 (NSF/insufficient funds)
    • 000333333335 (debit not authorized)
    • 000444444440 (invalid currency)

The Plaid modal should show the test username and passwords in a banner at the bottom, the most common ones are user_good and pass_good.

Collecting Card Information

In order to collect credit or debit card information you will use Stripe Elements. It will create the input fields, tokenize the card, and return information to you via javascript.

// Stripe Production Public Key: 'pk_live_ImPWqYGkbMfjwg22MiAmB6u4'
// Stripe Test Public Key: 'pk_test_Y7byiIyWmjqy4Xy7fjArJdPl'
// Test card/bank numbers:
// Code Example Adapted From:

// Create a Stripe client.
var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');

// Create an instance of Elements.
var elements = stripe.elements();

// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
  base: {
    color: '#32325d',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#aab7c4'
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'

// Create an instance of the card Element.
var card = elements.create('card', {style: style});

// Add an instance of the card Element into the `card-element` <div>.

// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';

// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {

  // Billing Details hash is optional -
  stripe.createPaymentMethod(type: 'card', card: card, billing_details: extraParams).then(function(result) {
    if (result.error) {
      // Inform the user if there was an error.
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = result.error.message;
    } else {
      // Send the stripe_id to Hint's server.
      post('/cards', {
        stripe_id: stripe_id

Testing Cards

You can use anything for the zip code and cvc check. Expiration dates can be any future date.

4242424242424242 Visa - Successful
5200828282828210 Mastercard (debit) - Successful
378282246310005 American Express - Successful

4000000000000002 Charge is declined with a card_declined code.
4000000000000127 Charge is declined with an incorrect_cvc code.

Handling Errors

When errors occur, it's critical that you inform the user by showing a useful error message. Hint's API helps you with this by providing useful error messages by default, but you will still need to display them back to the user. For more info on Hint API errors, see our page on Handling Errors

What’s Next