Tilled.js
Including Tilled.js
Add the following HTML snippet to your web page, preferably in the <head>
tag of your web page:
1<script src="https://js.tilled.com/v2"></script>
Initializing Tilled.js
Instantiate an instance of Tilled
by providing it with your publishable API key and the Tilled account id of the merchant account to perform the action on behalf of.
1const tilled = new Tilled('pk_…', 'acct_…');
Use new Tilled(publishableKey, tilledAccount, options?)
to create an instance of the Tilled
object. The Tilled
object provides access to the rest of the Tilled.js SDK. Your Tilled publishable API key is required when calling this function, replace the sample API key above with your actual API key. You can retrieve your API key by accessing https://app.tilled.com/api-keys.
Method parameters |
---|
publishableKey - string Your publishable key. |
tilledAccount - string Connected account ID (e.g. acct_123... ) to perform actions on behalf of. |
options? - optional object Initialization options. sandbox - boolean Send API requests to the sandbox environment. Defaults to false . |
Sandbox Environment
For the sandbox environment, retrieve your API key from https://sandbox-app.tilled.com/api-keys and instantiate an instance of Tilled
with the following options:
1const tilled = new Tilled('pk_…', 'acct_…', { sandbox: true });
Form and Form Field objects
Tilled Form Field
s are customizable UI components used to collect sensitive information in your payments forms. Use a Form
instance to create and manage a group of individual Form Field
instances.
tilled.form(options?): Promise<Form>
This method creates a Form
instance, which manages a group of form fields.
Method parameters |
---|
options? - optional object Initialization options. payment_method_type? - 'card' | 'ach_debit' The payment method type being created by this form. Defaults to card . |
1// Create a Form instance
2const form = await tilled.form({ payment_method_type: 'card' });
form.createField(formFieldType, options?): FormField
This method creates a Form Field
instance.
Method parameters |
---|
formFieldType - 'cardNumber' | 'cardExpiry' | 'cardCvv' | 'bankAccountNumber' | 'bankRoutingNumber' Field type. |
options? - optional object Initialization options. selector? - string | DOM element A CSS Selector (e.g., #card-number-container ) or a DOM element.styles? - object This option enables the ability to specify several CSS styles for the field. placeholder? - string Specifies short hint that describes the expected value of the input field. Defaults to 'MM / YY' for cardExpiry . |
1// Create a cardNumber Form Field
2const cardNumberField = form.createField('cardNumber');
Form Field Styles
Form Field
objects are styled using a styles
object, which consists of CSS properties nested under objects for any of the following variants:
base
, base variant—all other variants inherit from these stylesvalid
, applied when the FormField has valid inputinvalid
, applied when the FormField has invalid input
The following pseudo-classes and pseudo-element can also be styled using a nested object inside of a variant:
:hover
:focus
::placeholder
The following CSS properties are supported:
color
string : The color CSS property.opacity
string : The opacity CSS property.letterSpacing
string : The leter-spacing CSS property.textAlign
string : The text-align CSS property.textIndent
string : The text-indent CSS property.textDecoration
string : The text-decoration CSS property.textShadow
string : The text-shadow CSS property.font
string : The font CSS property.fontFamily
string : The font-family CSS property.fontSize
string : The font-size CSS property.fontStyle
string : The font-style CSS property.fontWeight
string : The font-weight CSS property.lineWeight
string : The line-weight CSS property.transition
string : The transition CSS property.
1// Style a Form Field
2const fieldOptions = {
3 styles: {
4 base: {
5 fontFamily: "Helvetica Neue, Arial, sans-serif",
6 color: "#304166",
7 fontWeight: "400",
8 fontSize: "14px",
9 "::placeholder": {
10 color: "#94A3B8",
11 },
12 },
13 invalid: {
14 ":hover": {
15 textDecoration: "underline dotted red",
16 },
17 },
18 valid: {
19 color: "#00BDA5",
20 },
21 },
22};
23
24const cardNumberField = form.createField("cardNumber", fieldOptions);
The above example will yield the following styles:
-
base::placeholder
-
invalid:hover
-
valid
field.inject(selector: string | DOM element): this
You need to create a container DOM element to inject a Form Field
into (e.g., <div id="card-number-container"></div>
).
formField.inject
accepts either a CSS Selector (e.g., #card-number-container
) or a DOM element. For most form field types, it behaves the same as passing { selector: string | DOM element }
when creating the Form Field
.
1form.createField('cardNumber', { selector: '#card-number-container' });
2// is the same as
3form.createField('cardNumber').inject('#card-number-container');
When form field type is paymentRequestButton
, field.inject
will insert the native payment request button into the DOM.
field.on(event, handler): void
The primary way to communicate with a Form Field
is by listening to an event.
Method parameters |
---|
event - 'blur' | 'focus' | 'ready' | 'change' The name of the event. |
handler - functionhandler(event) => void A callback function that will be called when the event is fired. |
Change event
The 'change'
event is trigger when the Form Field
's value changes. The event payload will always contain certain keys, in addition to some Form Field
-specific keys.
fieldType
string
The type of field that emitted this event.empty
booleantrue
if the value is empty.valid
booleantrue
if the value is well-formed and ready for submission.error
string
The current validation error, if any.brand
string
The card brand of the card number being entered. Can be one ofamex
,diners
,discover
,jcb
,maestro
,mastercard
,solo
,visa
,visa_debit
,visa_electron
, orunknown
.
Only available whenfieldType = 'cardNumber'
.
1// Example handler event object
2{
3 fieldType: 'cardNumber',
4 empty: false,
5 valid: true,
6 error: undefined,
7 brand: 'visa',
8}
1// Example of change event with card brand
2cardNumberField.on('change', (evt) => {
3 const cardBrand = evt.brand;
4 const icon = document.getElementById('credit-card-logo');
5
6 switch (cardBrand) {
7 case 'amex':
8 icon.classList = 'fa fa-cc-amex';
9 break;
10 case 'mastercard':
11 icon.classList = 'fa fa-cc-mastercard';
12 break;
13 case 'visa':
14 icon.classList = 'fa fa-cc-visa';
15 break;
16 case 'discover':
17 icon.classList = 'fa fa-cc-discover';
18 break;
19 case 'diners':
20 icon.classList = 'fa fa-cc-diners-club';
21 break;
22 default:
23 icon.classList = 'fa fa-credit-card';
24 }
25});
Ready event
Triggered when the Form Field
is fully rendered and can accept input.
Focus event
Triggered when the Form Field
gains focus.
Blur event
Triggered when the Form Field
loses focus.
form.build(): Promise<void>
Injects Form Field
iframes into the DOM. Call this method after all Form Field
s have been created and have their selectors specified (either via createField(, { selector: }
or field.inject()
).
1await form.build();
form.teardown(handler?): Promise<boolean> | void
Removes Form Field
iframes from the DOM. Call this method when you are done using the form.
Method parameters |
---|
handler? - functionhandler(success: boolean) => void An optional callback function that will be called when teardown is complete. If no callback is provided, teardown returns a promise. |
1// Promise
2const success = await form.teardown();
3
4// Callback
5form.teardown((success) => {});
Processing Payments Overview
Prior to displaying your checkout form and confirming the payment, your backend server will need to make an API call to Tilled to create a Payment Intent
with the payment amount. You will pass the intent's client_secret
to your front end. Use the tilled.confirmPayment(client_secret, { payment_method })
method to process a payment with either an existing Payment Method
id
or a new one using this Form
.
tilled.confirmPayment(clientSecret: string, params): Promise<PaymentIntent>
Confirms a Payment Intent
and, optionally, creates a Payment Method
from the Form Field
s in the DOM.
Method parameters |
---|
clientSecret - string The paymentIntent.client_secret generated from your backend server. |
params - object Payment intent confirmation params. payment_method - string | PaymentMethodCreateParams An existing payment method identifier (e.g., pm_123abc456 ) or a Create Payment Method request object, namely billing_details . |
This method returns a Promise
which will resolve with a Payment Intent
object.
1tilled
2 .confirmPayment(paymentIntentClientSecret, {
3 payment_method: {
4 type: 'card',
5 billing_details: {
6 name: 'John Doe',
7 address: {
8 zip: '80021',
9 country: 'US',
10 },
11 },
12 },
13 })
14 .then(
15 (paymentIntent) => {
16 // Be sure to check the `status` and/or `last_error_message`
17 // properties to know if the charge was successful
18 },
19 (error) => {
20 // Typically an error with the request (>400 status code)
21 }
22 );
tilled.createPaymentMethod(params): Promise<PaymentMethod>
Use this method to convert payment information collected by Form Field
s into a Payment Method
object that you safely pass to your server to use in an API call. It can be used to create re-usable payment methods.
Method parameters |
---|
params - objectCreate Payment Method params type - 'card' | 'ach_debit' The type of the Payment Method. billing_details - object Billing information associated with the Payment Method. ach_debit - object Details about the ACH direct debit bank account. Only applicable (and required) for type: ach_debit |
-
billing_details
-
name
the card holder's full name -
address
an object representing the card holder's billing addresscountry
andzip
required forcard
street
,city
, andstate
also required forach_debit
-
email
the email address of the card holder
-
-
ach_debit
(for ACH Debit payments)account_type
Bank account type (checking | savings
)account_holder_name
the name of the customer or company that owns the bank account
1tilled
2 .createPaymentMethod({
3 type: 'card',
4 billing_details: {
5 name: 'John Doe',
6 address: {
7 zip: '80021',
8 country: 'US',
9 },
10 },
11 })
12 .then(
13 (paymentMethod) => {
14 // Pass paymentMethod.id to your backend to attach it
15 // to a customer record for reusability
16 },
17 (error) => {
18 // An error with the request (>400 status code)
19 }
20 );
PaymentRequest (i.e. Apple Pay)
PaymentRequest
instances emit several different types of events.
tilled.paymentRequest(options)
Use tilled.paymentRequest
to create a PaymentRequest
object. In Safari, tilled.paymentRequest
uses Apple Pay.
options properties |
---|
total - object Line item that is shown to the customer in the browser's payment interface. amount - number The amount in the currency's minor unit (e.g. cents) label - string A name that the browser shows the customer in the payment interface. |
style? - optional object Optional styling for the Apple Pay button. type - 'default' | 'buy' | 'book' | 'donate' | 'check-out' | 'subscribe' | 'order' The type of button to display. By default, Buy with Pay. theme - 'black' | 'white' | 'white-outline' The color scheme of the button. |
requestPayerName? - optional boolean By default, the browser's payment interface only asks the customer for actual payment information. A customer name can be collected by setting this option to true . We highly recommend you collect name as this also results in collection of billing address for Apple Pay, which can be used to perform address verification. |
requestPayerEmail? - optional boolean See the requestPayerName option. |
1const paymentRequest = tilled.paymentRequest({
2 total: {
3 label: 'Tilled tee',
4 amount: paymentIntent.amount,
5 },
6 style: {
7 type: 'donate',
8 theme: 'black',
9 },
10 requestPayerName: true,
11 requestPayerEmail: true,
12});
paymentRequest.canMakePayment(): Promise<boolean>
Returns a Promise
that resolves true
if an enabled wallet is ready to pay. If no wallet is available, it resolves with false
;
1var prButton = form.createField('paymentRequestButton', {
2 paymentRequest: paymentRequest,
3});
4
5paymentRequest.canMakePayment().then((result) => {
6 if (result) {
7 // Inject paymentRequestButton Form Field to the DOM
8 prButton.inject('#native-payment-element');
9 }
10});
paymentRequest.on('paymentmethod', handler): void
Tilled.js automatically creates a payment method after the customer is done interacting with the browser's payment interface. To access the created payment method, listen for this event.
Method parameters |
---|
event - 'paymentmethod' The name of the event. |
| handler - functionhandler(event: object) => void
A callback function that will be called when the event is fired.
handler object properties |
---|
paymentMethod - PaymentMethod A Payment Method object. |
complete - functioncomplete(status) => void A Tilled.js provided function. Call this when you have processed the payment method data provided by the API. 'success' - value Report to the browser that the payment was successful, and that it can close any active payment interface. 'fail' - value Report to the browser that the payment was unsuccessful. Browsers may re-show the payment interface, or simply show a message and close. |
1// Example handler event object
2{
3 paymentMethod: {
4 id: 'pm_123456789abc'
5 type: 'card',
6 ...
7 },
8 complete: function(status) {
9 // Call this when you have processed the source data
10 // provided by the API.
11 },
12}
paymentRequest.on('cancel', handler): void
The cancel
event is emitted from a PaymentRequest
when the browser's payment interface is dismissed.
Note that in some browsers, the payment interface may be dismissed by the customer even after they authorize the payment. This means you may receive a cancel
event on your PaymentRequest after receiving a paymentmethod
event. If you're using the cancel
event as a hook for canceling the customer's order, make sure you also refund the payment that you just created.
1paymentRequest.on('cancel', function () {
2 // handle cancel event
3});
Examples
See our simple payment example or our react payment example for full examples.
Credit Card Form Example
1/**
2 * Example assumptions:
3 * The card fields have divs defined in the DOM
4 * <div id="card-number-element"></div>
5 * <div id="card-expiration-element"></div>
6 * <div id="card-cvv-element"></div>
7 *
8 * A submit button is defined
9 * <button id='submit-btn'></button>
10 */
11const tilled = new Tilled('pk_…', 'acct_…');
12
13const form = await tilled.form({
14 payment_method_type: 'card',
15});
16
17const fieldOptions = {
18 styles: {
19 base: {
20 fontFamily: 'Helvetica Neue, Arial, sans-serif',
21 color: '#304166',
22 fontWeight: '400',
23 fontSize: '16px',
24 },
25 invalid: {
26 ':hover': {
27 textDecoration: 'underline dotted red',
28 },
29 },
30 valid: {
31 color: '#00BDA5',
32 },
33 },
34};
35
36form.createField('cardNumber', fieldOptions).inject('#card-number-element');
37// Example of providing selector instead of using inject()
38form.createField('cardExpiry', {
39 ...fieldOptions,
40 selector: '#card-expiration-element',
41});
42form.createField('cardCvv', fieldOptions).inject('#card-cvv-element');
43
44await form.build();
45
46const submitButton = document.getElementById('submit-btn');
47submitButton.on('click', () => {
48 // A payment intent will be created on your backend server and the
49 // payment_intent.client_secret will be passed to your frontend to
50 // be used below.
51 tilled
52 .confirmPayment(paymentIntentClientSecret, {
53 payment_method: {
54 billing_details: {
55 name: 'John Doe',
56 address: {
57 zip: '80021',
58 country: 'US',
59 },
60 },
61 },
62 })
63 .then(
64 (paymentIntent) => {
65 // Be sure to check the `status` and/or `last_payment_error`
66 // properties to know if the charge was successful
67 if (paymentIntent.status === 'succeeded') {
68 alert('Payment successful');
69 } else {
70 const errMsg = paymentIntent.last_payment_error?.message;
71 alert('Payment failed: ' + errMsg);
72 }
73 },
74 (err) => {
75 // Typically an error with the request (>400 status code)
76 }
77 );
78});
ACH Bank Account Form Example
1/**
2 * Example assumptions:
3 * The ach_debit fields have divs defined in the DOM
4 * <div id="bank-account-number-element"></div>
5 * <div id="bank-routing-number-element"></div>
6 *
7 * A submit button is defined
8 * <button id='submit-btn'></button>
9 */
10const tilled = new Tilled('pk_…', 'acct_…');
11
12const form = await tilled.form({
13 payment_method_type: 'ach_debit',
14});
15
16const fieldOptions = {
17 styles: {
18 base: {
19 fontFamily: 'Helvetica Neue, Arial, sans-serif',
20 color: '#304166',
21 fontWeight: '400',
22 fontSize: '16px',
23 },
24 invalid: {
25 ':hover': {
26 textDecoration: 'underline dotted red',
27 },
28 },
29 valid: {
30 color: '#00BDA5',
31 },
32 },
33};
34
35form
36 .createField('bankAccountNumber', fieldOptions)
37 .inject('#bank-account-number-element');
38form
39 .createField('bankRoutingNumber', fieldOptions)
40 .inject('#bank-routing-number-element');
41
42await form.build();
43
44const submitButton = document.getElementById('submit-btn');
45submitButton.on('click', () => {
46 // A payment intent will be created on your backend server and the
47 // payment_intent.client_secret will be passed to your frontend to
48 // be used below.
49 tilled
50 .confirmPayment(paymentIntentClientSecret, {
51 payment_method: {
52 billing_details: {
53 name: 'John Doe',
54 address: {
55 street: '370 Interlocken Blvd',
56 city: 'Broomfield',
57 state: 'CO',
58 zip: '80021',
59 country: 'US',
60 },
61 },
62 ach_debit: {
63 account_type: 'checking',
64 account_holder_name: 'John Doe',
65 },
66 },
67 })
68 .then(
69 (paymentIntent) => {
70 // Be sure to check the `status` and/or `last_payment_error`
71 // properties to know if the charge was successful
72 if (
73 paymentIntent.status === 'succeeded' ||
74 paymentIntent.status === 'processing'
75 ) {
76 alert('Payment successful');
77 } else {
78 const errMsg = paymentIntent.last_payment_error?.message;
79 alert('Payment failed: ' + errMsg);
80 }
81 },
82 (err) => {
83 // Typically an error with the request (>400 status code)
84 }
85 );
86});
PaymentRequest Example
1/**
2 * Example assumptions:
3 * The paymentRequestButton field has a div defined in the DOM
4 * <div id="native-payment-element"></div>
5 *
6 */
7const form = tilled.form({
8 payment_method_type: 'card',
9});
10
11const paymentRequest = tilled.paymentRequest({
12 total: {
13 label: 'Tilled tee',
14 amount: secretData.amount,
15 },
16});
17
18const prButton = form.createField('paymentRequestButton', {
19 paymentRequest: paymentRequest,
20});
21
22paymentRequest.canMakePayment().then((result) => {
23 if (result) {
24 prButton.inject('#native-payment-element');
25 } else {
26 document.getElementById('native-payment-element').style.display = 'none';
27 }
28});
29
30paymentRequest.on('paymentmethod', (ev) => {
31 let paymentMethod = ev.paymentMethod;
32 tilled
33 .confirmPayment(paymentIntentClientSecret, {
34 payment_method: paymentMethod.id,
35 })
36 .then(
37 (paymentIntent) => {
38 // The payment intent confirmation occurred, but the
39 // actual charge may still have failed. Check
40 if (
41 paymentIntent.status === 'succeeded' ||
42 paymentIntent.status === 'processing'
43 ) {
44 ev.complete('success');
45 alert('Successul payment');
46 } else {
47 ev.complete('fail');
48 const errMsg = paymentIntent.last_payment_error?.message;
49 alert('Payment failed: ' + errMsg);
50 }
51 },
52 (err) => {
53 ev.complete('fail');
54 }
55 );
56});