Introduction
Glass Register is a toolbox for developers to help create donation pages. It's a layer above the Stripe API that takes care of a lot of drudgery so that you can focus on the most important part: the donation experience.
In the past 3 years our donation pages have collected millions of dollars in donations across a dozen charitable organizations - the experience of which we've distilled into Glass Register's functionality. What we found we wanted the most was:
- A lightweight and foolproof way to transform an HTML page that fulfills our UX goals into one that is capable of processing donations as well. It also has to be so simple that it doesn't get in the way of whatever framework that you'd like to build the page in (e.g. Angular, React, etc.)
- JavaScript functions that automate the parsing and validation of form inputs, and a way of displaying that information not only when form is submitted, but also while the donor is completing each field
- Customizable eReceipts downloadable via an API (rather than locked inside an account page)
- Lots of donation pages and/or themes: for fundraising events, seasonal variations, etc.
- For gift processing departments: an easy way to tailor the CSV or Excel transaction spreadsheet download format (e.g. add, remove, and re-arrange columns)
- An easy way to kick off a variety of events at third party services. E.g. thank you emails, mailing list signups, internal notifications, etc.
- A comprehensive log of everything that happens within the donation system, with customizable notifications for emergencies
All of this is possible with Glass Register.
Building Forms
The way you enable a donation page for with Glass Register is designed to be as non-intrusive as possible: there's no generated HTML, mandatory CSS, or other ways to conflict with your favourite technology stack.
Build your HTML, CSS, and JavaScript any way you like, then: add two script includes, annotate HTML elements with data-* attributes, and call a single function.
HTML
The Basics
Given this form fragment:
You would annotate it like this:
(If you'd like to skip to seeing a fully working donation page, see the Example section)
That enables it to work with Glass Register. A few things to note:
- "data-field" indicates what the field name is - it's the only required annotation
- Because the added attribute starts with data-, it's perfectly valid HTML (this helps with SEO and tools that process HTML)
Two other (optional) annotations are data-parse
and data-validate
. These are to indicate the JavaScript function that should be used to parse and validate the value contained in the element, respectively. We'll talk a bit more about those in a minute.
Warnings
Warnings are what happens when the donor has pressed the "Donate" button, but the form contains invalid values. Here's the same form with a warning:
This is what happens during the validation phase of the form submission:
- The donor presses the "Donate" button
- Every element with a
data-field
attribute is parsed. Either using the default parser (suitable for text inputs and checkboxes), or a function specified in thedata-parse
attribute - Any element with a
data-validate
attribute runs that parsed value through that validator
For each invalid field:
- The element with a matching
data-warning-for
is given the classgr-warning
- A callback is installed to remove that class as soon as any input is detected
The page will also scroll so that the topmost invalid field is within view (unless that feature is disabled). If the field itself is not the most logical place to scroll to, you can designate another element as the scroll target with a data-scroll-target-for
attribute.
Using these attributes, the common requirement to display instructions when the form has been filled out incorrectly is easily fulfilled. There is more advanced functionality also available - we'll look into that a little later.
Feedback
Warnings are essential, but it's a good practice to also display feedback while the donor is filling in the form. Both so that they aren't overwhelmed with errors at the very end, and also to encourage them to continue filling in the form. Here's how to do that:
When focus leaves a field, its contents will be validated using the specified function. Most often, this is the same function as used for the warnings, but if they're different, you can specify a function just for feedback via the data-feedback-validate
attribute.
Depending on the results of the validation, the element with a matching data-feedback-for
attribute will be given the class gr-valid
or gr-invalid
. In the above example, it will display an icon beside the field with those results. When the field regains and then loses focus, this process is repeated.
Note that you can also group feedback - they will behave intelligently (e.g. provide positive feedback unless any one of the group has been entered incorrectly).
Script Includes
That's just about it for changes to the HTML that you'll need to do - the one last modification is in the <head> element, by adding these script includes:
Alternately, you can use your favourite script loader (e.g. RequireJS).
The first script pulls in all of the Stripe functions that we'll need, and the second loads a collection of functions specific to Glass Register. That brings us to the JavaScript that's needed to create a complete donation page.
JavaScript
At the top of one of the Javascript files, you set the clientId and the public Stripe key in global variable:
var grCheckoutConfig = { clientId: 'sample', stripePublicKeyLive: 'pk_live_s5bdJUYBgjBVd3PFIvxyu48Q', };
For a minimum integration the only thing else you'll need on the JavaScript side is to call a single function once the page is loaded:
var donateFn = grInit({ frequency: 'oneTime', tokenCreationError: showError, chargeError: showError, chargeSuccess: chargeSuccess, preCharge: preCharge, });
grInit()
takes a single argument (a configuration object) and returns a function. Use that function like this (using jQuery syntax):
$('#form-submit').on('click', donateFn);
Where #form-submit
is the donate button element - so when the user clicks on the "Donate" button that function will be invoked. The frequency
key should be either "oneTime" or "monthly", depending on the kind of donation that the donor has selected. The rest are callbacks which are invoked at various stages in the form submission process.
Here's the complete sequence, and the associated callback(s) for each step:
Parsing and Validations
The parsing and validation are as described above. After this is (mostly - see below) complete, the optional postParseAndValidate
callback is invoked. The argument is an object with three keys: values
, valids
, and allValid
. The first is the results of the parsing pass (e.g. {_firstName: 'Jane', _lastName: 'Smith'}
), the second is a series of booleans with the result of the validation pass (e.g. {_firstName: true, _lastName: false}
). The final is just a convenience - it's true if every value in the valids
object is true.
We said "mostly" complete above - that's because the assigning of the gr-warning
class to invalid fields and the page scrolling haven't happened yet.
If you'd like to change the results of the parsing or validations, you can alter the object given in the argument and then return it from the callback. The warnings and scrolling will proceed with these new values.
You can also use this callback to completely replace the built-in warning system with your own: just don't use the attribute data-warning-for
anywhere, set the key disableScrolling
to true in the config object passed to grInit()
, and then use this callback to implement your own warning system.
Alternately, there's a callback postWarningDisplay
which is called only if there were form warnings, after those warnings have been displayed and the page scrolled. It has the same arguments as postParseAndValidate
, but its return value is ignored.
Once all the fields are parsed and have passed validation, the form submission moves on to the next step: creating the Stripe credit card token.
Token Creation
Charging a credit card with Stripe is a two-step process - this is so, as a security measure, donors' credit card information is never sent to any non-Stripe server. The three special (required) fields _ccNum
, _ccExp
, and _ccCvc
are sent to Stripe's servers, which create a one-time-use, unique token which represents the ability to create a charge on that credit card. Because it expires, and cannot be traced back to the credit card it represents, this is a lot safer than moving the actual credit card information around.
The (required) callback for this stage is called errorOnTokenCreate
, and its argument is debugging information, including the response from Stripe (i.e. stripeResponse
as to why it failed (e.g. the card was invalid)).
After the token is created, it along with the form data is sent to Glass Register's servers for processing.
Charging
After the token is created, but before the connection to the Glass Register's servers happens, an (optional) callback named preCharge
is called. The first argument is the form as it will be sent to the servers, the second is an object similar to postParseAndValidate
's argument, but with only the valids
and allValid
(the form data is split out into its own argument because the latter two tend not to be useful at this stage).
preCharge
's purpose is twofold - to give you one last chance to make changes to the form before it's sent for processing, and to queue up any REST API calls that you want to make to your 3rd party integrations (e.g. sending a thank you email via a transactional email provider like SendGrid or Mailgun).
Accordingly, the return value from preCharge
can optionally be an object with two keys: form
and restApiCalls
. The former is just the first argument (the form data) with whichever modifications you'd like to make, and the latter being the integration call details: specifically a list of objects with the keys call
and extra
. call
is the name of which REST API call you'd like to make, and extra
is any data that you'd like to pass to the REST API call which isn't already in the form data. Making a call is covered in much greater detail in the dashboard page of these docs.
Once the preCharge
callback is returned from, the form, token, and REST API calls are sent off to Glass Register's servers for processing.
Completion
After the charge request has been processed, one of two required callbacks will be invoked: chargeSuccess
or chargeError
.
The argument for chargeSuccess
is an object with two keys: transactionId
and live
. transactionId
is the Stripe ID of the successful charge. live
is whether the charge was "live", that is, not a test charge made with a test credit card number (see the "Testing" section below).
The argument for chargeError
is an object with two keys: err
and msg
. The first is the error code, and the second is a human-readable explanation for what went wrong to help with debugging (it's still technical, though, so you probably don't want to display it directly to the donor). Here are error codes that you'll likely to run into:
unknown-frequency | The frequency that you've set in the config is not one of "oneTime" or "monthly" |
---|---|
cvc-error | The CVC code that the donor has entered does not match the card |
card-error | Stripe send back an error marked as "card_error", e.g. the card was declined, is not set up for this kind of transaction, etc. |
processing-error | This is any Stripe error that isn't related directly to the card (e.g. a network error, or a 500 error on their API) |
charge-error | This is an error on Glass Register's server. If you get this it will have been logged and one of our engineers will have been notified |
Testing
If you type "asdf" into the "First Name" field (note that the string "asdf" and which field it's typed into are configurable) and tab out, the credit card field will be replaced with various options for how the transaction behaves: success, card-failure, etc. There's no need for a real credit card, or even a test card number.
Any donation made in this mode will show up in the "test" side of the Stripe dashboard, and can be exported from the Glass Register dashboard by selecting "Export Test Transations" on the export page.
Special Fields
Any field name that starts with an underscore is reserved by Glass Register. This is the complete list of form fields which are treated (and stored) specially:
_amount
_eligibleAmount
_ccNum
_ccExp
_ccCvc
_firstName
_lastName
_email
_address
_city
_region
_mailCode
_country
_isOrg
_orgName
_orgAddress
_orgCity
_orgRegion
_orgMailCode
_orgCountry
The special treatment is mostly for ensuring required fields are found, and for functions that require knowledge of the contents of the fields, like choosing the correct address to put on receipts.
Of special note: _amount
is the donation amount in cents, not dollars, euros, etc.
Restrictions
Form Data
For reasons related to data consistency, durability, and searchability, the form data is stored alongside the transaction at Stripe in its metadata. This brings with it a few restrictions:
- All field values will be converted to strings
- For one-time transactions, the total characters used by both the field names and the field contents must be below 9000
- For recurring (e.g. monthly) transactions, the number of fields is limited to 20, with each field name restricted to 40 characters, and each field value restricted to 500 characters
As well, there are some Glass Register-specific restrictions:
- Field keys can only contain the characters a-z, A-Z, 0-9, -, and _
- You cannot create new field names that start with _. Those are reserved for special fields
Browser Support
Glass Register supports the following browsers:
- Chrome/Chromium released within the last 12 months
- FireFox released within the last 12 months
- Internet Explorer 11 and above
- Safari released within the last 12 months
- The default web browsers in Android and iOS relased within the last 3 years
Error Handling
Most of the error handling happens in the callbacks, but the grInit()
function also installs a global error handler that logs the exception, file, and line number of the error to the GR servers.
The function grLog(err, msg, payload)
logs to the same place, and is available for whatever purpose that you'd like.
These logs, along with many others, can be queried in the dashboard.
Dashboard
The Glass Register Dashboard is has two "sides": one for administrators, and one for developers. If your user account is marked as developer you'll see both sides; administrators only see their side.
Administrator Dashboard
Transaction Downloads
This is where admins can download the donation transaction details. They can limit it by a date range, and re-arrange, add, and remove fields from the download. What fields show up, and their details (e.g. title and example values), is configured in the dev side of the dashboard.
You can save multiple configurations (i.e. field arrangements) with the "Add/Delete Configuration" buttons, switch between them using the tabs at the top, and save all of them with the "Save All Changes" button.
Downloads can be in either CSV or Excel format. There's also the choice between which of the Stripe "modes" to download the transactions from - the live or test modes.
Account
Basic information about the organization can be configured here.
Of particular note is the "Statement Descriptor" field - this is what shows up on donor credit card statements. It is restricted to 22 alphanumeric characters.
eReceipt Customization
Glass Register provides an API that can be called to produce a PDF eReceipt for any one-time gift that has been made to your organization (we'll get into the details of that below).
This is where you can customize the content of those receipts. The images must be either JPEG or PNGs, and the text can be HTML with the following tags: <p>, <br>, <b>, <i>.
Click "Preview Receipt" and you'll see a receipt generated using the form data from the most recent test donation made to your organization.
Checkout Data
The purpose of this section is to allow for admins to alter the behaviour of the donation pages without having to involve the developers. It's a very flexible system - we'll explain it fully below in the dev side of the dashboard.
Manage Users
Here you can add and remove users for the organization. Currently all users have the same privileges - currently the only difference between devs and admins is that admins cannot see the dev side of the dashboard.
Any user can add and remove any other user. Once a user has been added, they will receive an email asking them to set their password in order to log in.
Developer Dashboard
General
This is where you can set configuration that's more technical - currently it's the Stripe keys.
Transaction Downloads
Like Checkout Data, this is a dev mirror image of the admin section. For each field that appears in their section, you can change the visibility and appearance of that field here.
For each field there are three things you can configure: the title (this is also what ends up in the spreadsheet header), an example value, and whether it's visible on the admin side.
You can add (within the restrictions listed in the Donation Form section) any fields that you like to a donation form. If you'd like those fields to be downloadable in the admin side of Transaction Downloads, you can add it via the form at the bottom of this section.
Event Log
Everything that happens on the Glass Register servers is logged. You can view all of those logs in this section.
Payload is data that is associated with the error: for instance if a donation failed, the log entry will not only say what when wrong, it will also keep all the related data (the form data, the REST API calls, etc.) in the payload, so you can figure out exactly went wrong.
The search function is based on RE2, a regular expression library designed to run in linear time, so you can search for extremely specific things.
Glass Register API
eReceipt Download API
Here's an example call using CURL:
curl https://api.glassregister.org/apps/receipt -d @callArgs.json
The file callArgs contains a JSON object which is sent as the POST payload, it looks like this:
{ "clientId": "sample", "email": "demo@glassregister.org", "chargeId": "ch_1AF87g2hHuDMwXxgpkhoR8VH" }
The charge ID is available in the first argument of the chargeSuccess
callback on the donation page, or in transaction.id
in the values provided to the REST API calls (which can then be used, for example, as a substitution in an email template).
The response will be either the PDF file, or a JSON object with an err
key with the error code and a msg
key with a more friendly (but still technical) description for debugging.
The requirement for both the transaction ID and the email address is to stop people from guessing one or the other and accessing receipts that they are not supposed to.
Often the way this API is used is as part of a page which is linked to from a thank you email, and from the thank you page after the donor has given a gift.
Example Donation Page
A fully functioning donation page can be found here. Type in "asdf" (whithout the quotes) into the "First Name" field and tab out to perform a complete test donation, including receiving a thank-you email with a link to a receipt.
View source if you'd like to see how a complete Glass Register donation page looks from a code perspective.