├── .gitignore ├── .markdownlint.jsonc ├── README.md ├── analytics.md ├── api ├── README.md └── v1 │ ├── customers.md │ ├── forms.md │ └── payments.md ├── assets ├── floating-labels.png ├── pre-filled.png └── result.png ├── customize-card-details-field.md ├── embedding.md ├── integration.md ├── liquid ├── README.md ├── examples.md └── variables.md └── webhooks.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.markdownlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": false, 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MoonClerk Developer Documentation 2 | 3 | Welcome! 4 | 5 | ## MoonClerk API 6 | 7 | - [API Documentation](/api/README.md) 8 | - [MoonClerk Webhooks Documentation](/webhooks.md) 9 | 10 | ## Integrating & Customizing 11 | 12 | - [Embedding a Payment Form on Your Site](/embedding.md) 13 | - [Integrating with Your Site](/integration.md) 14 | - [Working with Google Analytics (v4)](/analytics.md) 15 | - [Styling the "Payment Details" section on your payment form](/customize-card-details-field.md) 16 | 17 | ## Notification Email Templating Using Liquid 18 | 19 | - [Liquid Variables & Filters](/liquid/variables.md) 20 | - [Liquid Examples](/liquid/examples.md) 21 | 22 | ## Help us make it better 23 | 24 | Please tell us how we can make these docs better. If you have a specific feature request or if you found a bug, please use GitHub issues. Fork these docs and send a pull request with improvements. 25 | -------------------------------------------------------------------------------- /analytics.md: -------------------------------------------------------------------------------- 1 | # Working with Google Analytics (v4) 2 | 3 | Below is an overview of the URLs of checkout pages and what they mean. Payers paying on your payment forms will be directed to unique URLs once they enter their initial information. 4 | 5 | ## Checkout Page Reference 6 | 7 | ### Visiting a checkout page 8 | 9 | When a potential payer visits a payment form the URL will look something like this: 10 | 11 | ``` 12 | https://app.moonclerk.com/pay/5/3x993hrglt 13 | ``` 14 | 15 | #### Notes 16 | 17 | - When this page is visited we will fire a `checkout_start` event. 18 | - The `3x993hrglt` value in the example above will vary between payment forms and represents the form token. 19 | - The `/5` portion of the URL may be removed in the future. 20 | 21 | ### Moving to the payment page 22 | 23 | After a potential payer has entered their info on the first page of the checkout, they will be directed to a URL similar to: 24 | 25 | ``` 26 | https://app.moonclerk.com/pay/5/3x993hrglt/i/efv7Rb2Hgnb5us2GxmPN4tYW 27 | ``` 28 | 29 | #### Notes 30 | 31 | - When this page is visited we will fire a `checkout_add_payment` event. 32 | - The `efv7Rb2Hgnb5us2GxmPN4tYW` value above will vary between payers' checkout sessions. 33 | 34 | ### Editing the checkout 35 | 36 | If a potential payer goes back to edit info on the first page, the URL will be similar to: 37 | 38 | ``` 39 | https://app.moonclerk.com/pay/5/3x993hrglt/i/efv7Rb2Hgnb5us2GxmPN4tYW/edit 40 | ``` 41 | 42 | - No event is fired for this page. 43 | 44 | **4. Once a payer successfully enters a payment method, they will be directed here:** 45 | 46 | ### Completing a checkout 47 | 48 | Once a payer successfully enters a payment method and completes a checkout with either a successful (or pending in the case of bank payments), the URL will be similar to: 49 | 50 | ``` 51 | https://app.moonclerk.com/pay/5/3x993hrglt/i/efv7Rb2Hgnb5us2GxmPN4tYW/complete 52 | ``` 53 | 54 | #### Notes 55 | 56 | - When this page is visited we will fire a `checkout_complete` event. 57 | 58 | ## Using MoonClerk Events in GA4 59 | 60 | ### Tracking events on a form basis 61 | 62 | All events will contain the `form_token` parameter which is the form token in the URL. This can be useful to set up a [custom dimension](https://support.google.com/analytics/answer/10075209?hl=en) in the GA4 admin which will let you see conversions on a per form basis. 63 | 64 | ### Conversions 65 | 66 | If you want to see conversions in your GA4 dashboard, you configure `checkout_complete` as a [conversion event](https://support.google.com/analytics/answer/12844695?hl=en). 67 | -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | # The MoonClerk API 2 | 3 | This is a **READ ONLY** REST-style API that uses JSON for serialization and an API key for authentication. 4 | 5 | ## Making a request 6 | 7 | All URLs start with `https://api.moonclerk.com/`. **SSL only**. The API version and the authentication token are both sent in the header. 8 | 9 | To make a request for all the payment forms, you'd append the forms index path to the base url to form something like https://api.moonclerk.com/forms. In curl, that looks like: 10 | 11 | ```shell 12 | curl -H "Authorization: Token token=[API Key]" -H "Accept: application/vnd.moonclerk+json;version=1" https://api.moonclerk.com/forms 13 | ``` 14 | 15 | ## Authentication 16 | 17 | You authenticate using your API key. Please keep this private. Anyone with this key can access your account data through the API. To generate and obtain an API key, visit: https://app.moonclerk.com/settings/api-key. Once you have that key, you'll pass it in the **Authorization** header as an authorization token. 18 | 19 | `Authorization: Token token=[API Key]` 20 | 21 | ## Versioning 22 | 23 | You will pass both the version and the format (JSON only) in the **Accept** header as well. The header should look like this: 24 | 25 | `Accept: application/vnd.moonclerk+json;version=1` 26 | 27 | ## Handling errors 28 | 29 | If there is a problem, MoonClerk will render a 5xx error. `500` means that the app is entirely down, but you might also see `502 Bad Gateway`, `503 Service Unavailable`, or `504 Gateway Timeout`. It's your responsibility in all of these cases to retry your request later. 30 | 31 | ## Paging 32 | 33 | By default all lists will return 10 rows. You can pass the `count` parameter to determine the number of rows to return (from 1 to 100) and the `offset` parameter to determine the starting position of the set. 34 | 35 | For example, to get the first 20 customers looks like this: 36 | 37 | https://api.moonclerk.com/customers?count=20 38 | 39 | Then to get the next page of 20: 40 | 41 | https://api.moonclerk.com/customers?count=20&offset=20 42 | 43 | ## Field Conventions 44 | 45 | - All dates are serialized in the ISO 8601 format and are shown in the UTC time zone. 46 | - All money fields integers denominated in cents. Any object with amount fields should have a `currency` field as well. 47 | - Any key ending in `_reference` corresponds to a related Stripe ID 48 | 49 | ## Available API Endpoints 50 | 51 | - [Customers](/api/v1/customers.md) (known as "Plans" in the MoonClerk UI) 52 | - [Payments](/api/v1/payments.md) 53 | - [Forms](/api/v1/forms.md) 54 | 55 | ## API libraries 56 | 57 | - [Unofficial PHP Library](https://github.com/JobBrander/moonclerk-php) 58 | - [Unofficial Ruby Gem](https://github.com/TrevorHinesley/moonclerk) 59 | 60 | ## Throttling 61 | 62 | We expect that everyone using our API will be a good internet citizen and not be abusive to our servers. In an effort to avoid server issues we have throttling controls in place. If you hit a throttling limit you will receive a `429 - Too Many Requests` error. If you get this error, please reduce the number and/frequency of calls. 63 | 64 | ## Help us make it better 65 | 66 | Please tell us how we can make the API better. If you have a specific feature request or if you found a bug, please use GitHub issues. Fork these docs and send a pull request with improvements. 67 | -------------------------------------------------------------------------------- /api/v1/customers.md: -------------------------------------------------------------------------------- 1 | # Customers 2 | 3 | The customer endpoint is used to obtain a list of customers ("Plans" in the MoonClerk web interface) and their subscription info. 4 | 5 | _There is a slight difference in terminology between the MoonClerk web UI and the API. The web UI uses the term "Plan" to refer to the combination of a customer, subscription, and plan. The MoonClerk API is much more in line with the underlying Stripe contructs. In order to query the list of "plans" that one sees in the MoonClerk web UI, you would use the `/customers` endpoint._ 6 | 7 | ## The Customer Object 8 | 9 | ```json 10 | { 11 | "id": 523425, 12 | "account_balance": 0, 13 | "name": "Ryan Wood", 14 | "email": "ryan@moonclerk.com", 15 | "payment_method": { 16 | "type": "card", 17 | "last4": "4242", 18 | "exp_month": 12, 19 | "exp_year": 2018, 20 | "brand": "Visa" 21 | }, 22 | "custom_id": "GHS430", 23 | "customer_reference": "cus_4SOZuEc4cxP5L7", 24 | "discount": { 25 | "coupon": { 26 | "code": "10off", 27 | "duration": "once", 28 | "amount_off": 1000, 29 | "currency": "USD", 30 | "percent_off": null, 31 | "duration_in_months": null, 32 | "max_redemptions": null, 33 | "redeem_by": null 34 | }, 35 | "starts_at": "2013-04-12T20:05:37Z", 36 | "ends_at": "2013-05-12T20:05:37Z" 37 | }, 38 | "delinquent": false, 39 | "management_url": "https://app.moonclerk.com/manage/xyz1234567", 40 | "custom_fields": { 41 | "shirt_size": { 42 | "id": 23452, 43 | "type": "string", 44 | "response": "XL" 45 | }, 46 | "shipping_address": { 47 | "id": 23453, 48 | "type": "address", 49 | "response": { 50 | "line1": "123 Main St.", 51 | "line2": "Ste. 153", 52 | "city": "Greenville", 53 | "state": "SC", 54 | "postal_code": "29651", 55 | "country": "United States" 56 | } 57 | } 58 | }, 59 | "form_id": 101, 60 | "checkout": { 61 | "amount_due": 1700, 62 | "coupon_amount": 1000, 63 | "coupon_code": "10off", 64 | "date": "2014-07-23T13:44:12Z", 65 | "fee": 200, 66 | "subtotal": 1000, 67 | "token": "YUAf1PJDe1Uho7CnQ1BVPuCz", 68 | "total": 1700, 69 | "trial_period_days": null, 70 | "upfront_amount": 500 71 | }, 72 | "subscription": { 73 | "id": 98, 74 | "subscription_reference": "sub_3oLgqlp4MgTZC3", 75 | "status": "active", 76 | "start": "2014-07-23T13:44:16Z", 77 | "first_payment_attempt": "2014-07-23T13:44:16Z", 78 | "next_payment_attempt": "2014-08-23T13:44:16Z", 79 | "current_period_start": "2014-07-23T13:44:16Z", 80 | "current_period_end": "2014-08-23T13:44:16Z", 81 | "trial_start": null, 82 | "trial_end": null, 83 | "trial_period_days": null, 84 | "expires_at": null, 85 | "canceled_at": null, 86 | "ended_at": null, 87 | "plan": { 88 | "id": 131, 89 | "plan_reference": "131", 90 | "amount": 1200, 91 | "amount_description": "Option A", 92 | "currency": "USD", 93 | "interval": "month", 94 | "interval_count": 1 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | Notes: 101 | 102 | - `discount` may be null if there is no current discount 103 | - Custom field keys are configured in the _Additional Information_ section of the payment form builder 104 | - Any JSON key in the customer object ending in `_reference` corresponds to a related Stripe ID 105 | - The `custom_id` field is populated if you use one of our [integration methods](/integration.md). 106 | 107 | ## List Customers 108 | 109 | - `GET /customers` will return all customers 110 | 111 | `https://api.moonclerk.com/customers` 112 | 113 | ```jsonc 114 | { 115 | "customers": [ 116 | { 117 | "id": 523425, 118 | "name": "Ryan Wood", 119 | "email": "ryan@moonclerk.com" 120 | // ... 121 | }, 122 | { 123 | "id": 523458, 124 | "name": "Dodd Caldwell", 125 | "email": "dodd@moonclerk.com" 126 | // ... 127 | } 128 | // ... 129 | ] 130 | } 131 | ``` 132 | 133 | ### Filters 134 | 135 | The following list filters are supported on customers. 136 | 137 | - `form_id` 138 | - `checkout_from` 139 | - `checkout_to` 140 | - `next_payment_from` 141 | - `next_payment_to` 142 | - `status` 143 | 144 | | Parameter | Description | 145 | | ------------------- | -------------------------------------------------------------------------- | 146 | | `form_id` | the associated MoonClerk form ID (/forms/123) | 147 | | `checkout_from` | customers created on or after this date | 148 | | `checkout_to` | customers created on or before this date | 149 | | `next_payment_from` | subscription due to bill on or after this date | 150 | | `next_payment_to` | subscription due to bill on or before this date | 151 | | `status` | valid options are: active, canceled, expired, past_due, pending, or unpaid | 152 | 153 | #### Notes 154 | 155 | - The date parameters should be in the form of "YYYY-MM-DD" 156 | - `*_from` parameter will start at the beginning of the day 157 | - `*_to` will go through the end of that day 158 | - All times are UTC based 159 | - You can combine any of the filter parameters 160 | 161 | #### Examples: 162 | 163 | - `GET /customers?form_id=5346` 164 | - `GET /customers?checkout_from=2014-10-01` 165 | - `GET /customers?next_payment_from=2014-10-01&next_payment_to=2014-10-31` 166 | 167 | ## Get a Customer 168 | 169 | - `GET /customers/:id` will return the specified customer. 170 | 171 | `https://api.moonclerk.com/customers/523425` 172 | 173 | ```jsonc 174 | { 175 | "customer": { 176 | "id": 523425, 177 | "name": "Ryan Wood", 178 | "email": "ryan@moonclerk.com" 179 | // ... 180 | } 181 | } 182 | ``` 183 | -------------------------------------------------------------------------------- /api/v1/forms.md: -------------------------------------------------------------------------------- 1 | # Forms 2 | 3 | ## The Form Object 4 | 5 | ```json 6 | { 7 | "id": 234456, 8 | "title": "Monthly Subscription", 9 | "access_token": "1vqvyh3tco", 10 | "currency": "USD", 11 | "payment_volume": 7665, 12 | "successful_checkout_count": 6, 13 | "created_at": "2014-04-07T19:45:28Z", 14 | "updated_at": "2014-07-20T00:52:31Z" 15 | } 16 | ``` 17 | 18 | ## List Forms 19 | 20 | - `GET /forms` will return all payment forms 21 | 22 | `https://api.moonclerk.com/forms` 23 | 24 | ```jsonc 25 | { 26 | "forms": [ 27 | { 28 | "id": 234456, 29 | "title": "Monthly Subscription" 30 | // ... 31 | }, 32 | { 33 | "id": 234484, 34 | "title": "Annual Subscription" 35 | // ... 36 | } 37 | ] 38 | } 39 | ``` 40 | 41 | ## Get a Form 42 | 43 | - `GET /forms/:id` will return the specified payment form. 44 | 45 | `https://api.moonclerk.com/forms/234456` 46 | 47 | ```jsonc 48 | { 49 | "form": { 50 | "id": 234456, 51 | "title": "Monthly Subscription" 52 | // ... 53 | } 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /api/v1/payments.md: -------------------------------------------------------------------------------- 1 | # Payments 2 | 3 | ## The Payment Object 4 | 5 | ```jsonc 6 | { 7 | "id": 1348394, 8 | "date": "2014-04-08T18:57:26Z", 9 | "status": "successful", 10 | "currency": "USD", 11 | "amount": 1000, 12 | "fee": 59, 13 | "amount_refunded": 0, 14 | "amount_description": "Option A", 15 | "name": "Jim Customer", 16 | "email": "customer@example.com", 17 | "payment_method": { 18 | "type": "card", 19 | "last4": "4242", 20 | "brand": "Visa" 21 | }, 22 | "charge_reference": "ch_3ohpsF8ra5rqjj", 23 | // customer_id and customer_reference are available only if the payment was from a recurring checkout 24 | "customer_id": 53453, 25 | "customer_reference": "cus_4SOZuEc4cxP5L7", 26 | "invoice_reference": "in_1La8pLqS2UnhPZ", 27 | "custom_fields": { 28 | "shirt_size": { 29 | "id": 23452, 30 | "type": "string", 31 | "response": "XL" 32 | }, 33 | "shipping_address": { 34 | "id": 23453, 35 | "type": "address", 36 | "response": { 37 | "line1": "123 Main St.", 38 | "line2": "Ste. 153", 39 | "city": "Greenville", 40 | "state": "SC", 41 | "postal_code": "29651", 42 | "country": "United States" 43 | } 44 | } 45 | }, 46 | "form_id": 112, 47 | "custom_id": "GHS430", 48 | // Checkout data is available only if the payment was from a one-time checkout 49 | "checkout": { 50 | "amount_due": 1000, 51 | "coupon_amount": 1000, 52 | "coupon_code": "10off", 53 | "date": "2014-07-23T13:44:12Z", 54 | "fee": 59, 55 | "subtotal": 1000, 56 | "token": "YUAf1PJDe1Uho7CnQ1BVPuCz", 57 | "total": 1000, 58 | "trial_period_days": null, 59 | "upfront_amount": 0 60 | }, 61 | "coupon": { 62 | "code": "10off", 63 | "duration": "once", 64 | "amount_off": 1000, 65 | "currency": "USD", 66 | "percent_off": null, 67 | "duration_in_months": null, 68 | "max_redemptions": null, 69 | "redeem_by": null 70 | } 71 | } 72 | ``` 73 | 74 | Notes: 75 | 76 | - All amounts are in cents 77 | - `fee` is the Stripe Fee 78 | - `customer_reference` will be present if the payment was attached to a Stripe customer (vs. a one time payment). Otherwise, it will be null. 79 | - `invoice_reference` will be present if the payment was generated from a Stripe invoice (vs. a one off payment). Otherwise, it will be null. 80 | - The `custom_id` field is populated if you use one of our [integration methods](/integration.md). 81 | 82 | ## List Payments 83 | 84 | - `GET /payments` will return all payments 85 | 86 | `https://api.moonclerk.com/payments` 87 | 88 | ```jsonc 89 | { 90 | "payments": [ 91 | { 92 | "id": 1348445, 93 | "date": "2014-04-08T18:58:35Z", 94 | "status": "successful" 95 | // ... 96 | }, 97 | { 98 | "id": 1348394, 99 | "date": "2014-04-08T18:57:26Z", 100 | "status": "successful" 101 | // ... 102 | } 103 | // ... 104 | ] 105 | } 106 | ``` 107 | 108 | ### Filters 109 | 110 | The following list filters are supported on payments. 111 | 112 | | Parameter | Description | 113 | | ------------- | -------------------------------------------------- | 114 | | `form_id` | the associated MoonClerk form ID (/forms/123) | 115 | | `customer_id` | the associated MoonClerk customer ID (/plans/123) | 116 | | `date_from` | payments charged on or after this date | 117 | | `date_to` | payments charged on or before this date | 118 | | `status` | valid options are: successful, refunded, or failed | 119 | 120 | #### Notes 121 | 122 | - All date parameters should be in the form of "YYYY-MM-DD" 123 | - `date_from` parameter will start at the beginning of the day 124 | - `date_to` will go through the end of that day 125 | - All times are UTC based 126 | - You can combine any of the filter parameters 127 | 128 | #### Examples: 129 | 130 | - `GET /payments?form_id=5346` 131 | - `GET /payments?date_from=2014-10-01&date_to=2014-10-31` 132 | - `GET /payments?customer_id=12742&status=active` 133 | 134 | ## Get a Payment 135 | 136 | - `GET /payments/:id` will return the specified payment. 137 | 138 | `https://api.moonclerk.com/payments/1348394` 139 | 140 | ```jsonc 141 | { 142 | "payment": { 143 | "id": 1348394, 144 | "date": "2014-04-08T18:57:26Z", 145 | "status": "successful" 146 | // ... 147 | } 148 | } 149 | ``` 150 | -------------------------------------------------------------------------------- /assets/floating-labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonclerk/developer/b7f8dd90d7c04ba066f0a3435bc96a453bb61194/assets/floating-labels.png -------------------------------------------------------------------------------- /assets/pre-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonclerk/developer/b7f8dd90d7c04ba066f0a3435bc96a453bb61194/assets/pre-filled.png -------------------------------------------------------------------------------- /assets/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonclerk/developer/b7f8dd90d7c04ba066f0a3435bc96a453bb61194/assets/result.png -------------------------------------------------------------------------------- /customize-card-details-field.md: -------------------------------------------------------------------------------- 1 | # Styling the "Payment Details" field on your payment form 2 | 3 | MoonClerk has always supported applying custom CSS to your payment forms to create a pleasing and brand-matched experience for your payers. Due to PCI Security Standards, styling of the new Stripe Payment Element cannot be accomplished using CSS alone. If you need to provide additional styling to your payment form's payment details field, you'll need to use a combination of CSS and custom JSON styling. 4 | 5 | This is an advanced feature and there is a possibility that implementing it incorrectly could cause issues with your checkout. We suggest reading over the documentation carefully and reviewing the entire checkout process after you have customized the form to ensure it functions properly. 6 | 7 | ## Styling around the payment details fields 8 | 9 | If you want to style _around_ the payment details fields, you'll still want to use custom CSS. The Payment Element is contained in the `.StripePaymentElement` class. Applying styles to it will effect the border, padding, margin, etc. **Do not attempt to style anything with CSS inside of the `.StripePaymentElement` class. It will fail and could break your checkout.** Here is an example of applying custom CSS to the `.StripePaymentElement` class: 10 | 11 | ```css 12 | .StripePaymentElement { 13 | background-color: white; 14 | height: 40px; 15 | padding: 10px 12px; 16 | border-radius: 4px; 17 | border: 1px solid transparent; 18 | box-shadow: 0 1px 3px 0 #e6ebf1; 19 | } 20 | ``` 21 | 22 | ## Styling inside the payment details fields 23 | 24 | If you want to make any changes inside of the payment details fields, you'll need to add properly formatted JSON to the "JSON styling for the Payment Element" text area. This can be accessed on an edit page for a [Theme](https://app.moonclerk.com/themes/). 25 | 26 | Stripe provides an [options reference](https://stripe.com/docs/elements/appearance-api) to help familiarize yourself with the options available. 27 | 28 | **While the Stripe documentation shows options in JavaScript object format, MoonClerk requires that all option configuration be in JSON format. While very similar, there are key differences.** 29 | 30 | **BAD!** ❌ 31 | 32 | ```javascript 33 | { 34 | theme: "none", 35 | variables: { 36 | colorDanger: "#A34033", 37 | colorIconTabSelected: "#333333", 38 | colorIconTab: "#333333", 39 | colorPrimary:"#f20055", 40 | colorText: "#30313d" 41 | }, 42 | rules: { 43 | ".Tab": { 44 | border: "1px solid #E0E6EB", 45 | }, 46 | ".Tab:hover": { 47 | color: "var(--colorText)", 48 | }, 49 | ".Tab--selected": { 50 | borderColor: "#E0E6EB", 51 | }, 52 | } 53 | } 54 | ``` 55 | 56 | **GOOD** ✅ 57 | 58 | ```json 59 | { 60 | "theme": "none", 61 | "variables": { 62 | "colorDanger": "#A34033", 63 | "colorIconTabSelected": "#333333", 64 | "colorIconTab": "#333333", 65 | "colorPrimary": "#f20055", 66 | "colorText": "#30313d" 67 | }, 68 | "rules": { 69 | ".Tab": { 70 | "border": "1px solid #E0E6EB" 71 | }, 72 | ".Tab:hover": { 73 | "color": "var(--colorText)" 74 | }, 75 | ".Tab--selected": { 76 | "borderColor": "#E0E6EB" 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | ⚠️ **All Payment Element form field styling should exist (as valid JSON) within a single object, with all keys and values being wrapped in double quotes. We recommend composing your valid JSON in an external text editor or using a linter to assure you are providing valid JSON.** 83 | 84 | ### Floating Labels 85 | 86 | You can choose between "floating" labels or non floating labels (the default). To enable floating labels, specicfy the option in the top-level. For example: 87 | 88 | ```json 89 | { 90 | "labels": "floating" 91 | } 92 | ``` 93 | 94 | ![Floating Labels](assets/floating-labels.png) 95 | 96 | ### Variables 97 | 98 | Variables control the appearance of the different form components. The variables option works like CSS variables. You can specify CSS values for each variable and reference other variables with the var(--myVariable) syntax. 99 | 100 | | Variable | Description | 101 | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 102 | | `fontFamily` | The font family used throughout Elements. Elements supports custom fonts by passing the fonts option to the Elements group. | 103 | | `fontSizeBase` | The font size that’s set on the root of the Element. By default, other font size variables like fontSizeXs or fontSizeSm are scaled from this value using rem units. | 104 | | `spacingUnit` | The base spacing unit that all other spacing is derived from. Increase or decrease this value to make your layout more or less spacious. | 105 | | `borderRadius` | The border radius used for tabs, inputs, and other components in the Element. | 106 | | `colorPrimary` | A primary color used throughout the Element. Set this to your primary brand color. | 107 | | `colorBackground` | The color used for the background of inputs, tabs, and other components in the Element. | 108 | | `colorText` | The default text color used in the Element. | 109 | | `colorDanger` | A color used to indicate errors or destructive actions in the Element. | 110 | 111 | [Much of this documentation was taken from Stripe](https://stripe.com/docs/elements/appearance-api?platform=web#variables). Visit Stripe's documentation for more in-depth information including more options not mentioned here. 112 | 113 | ```json 114 | { 115 | "variables": { 116 | "colorPrimary": "#439558", 117 | "colorBackground": "#d8eede", 118 | "colorText": "#439558", 119 | "colorDanger": "#ff3f23", 120 | "fontFamily": "Tahoma, sans-serif", 121 | "spacingUnit": "6px", 122 | "borderRadius": "8px" 123 | } 124 | } 125 | ``` 126 | 127 | ### Rules 128 | 129 | After defining your variables, use rules to seamlessly integrate Elements to match the design of your theme. Rules are defined by combining a high-level name with modifiers, together referred to as 'selectors'. All of the following are vaild selectors. 130 | 131 | - `.Tab, .Label, .Input` 132 | - `.Tab:focus` 133 | - `.Input--invalid, .Label--invalid` 134 | - `.Input::placeholder` 135 | 136 | Here is a list of basic components 137 | 138 | - `.Tab` 139 | - `.Label` 140 | - `.Input` 141 | - `.PickerItem` 142 | 143 | Here is a list of basic components with some of their modifiers 144 | 145 | - `.Tab:focus` 146 | - `.Tab--selected` 147 | - `.Label--invalid` 148 | - `.Input:focus` 149 | - `.Input--invalid` 150 | 151 | ```json 152 | { 153 | "rules": { 154 | ".Tab, .Input, .PickerItem": { 155 | "borderRadius": "var(--borderRadius)" 156 | }, 157 | ".Tab": { 158 | "border": "1px solid #dddddd", 159 | "boxShadow": "0px 1px 1px rgba(0, 0, 0, 0.03), 0px 3px 6px rgba(18, 42, 66, 0.02)" 160 | }, 161 | ".Tab--selected": { 162 | "backgroundColor": "var(--colorBackground)", 163 | "border": "1px solid var(--colorPrimary)" 164 | }, 165 | ".Label--invalid, .Input--invalid, .Error": { 166 | "color": "var(--colorDanger)" 167 | }, 168 | ".Input--invalid": { 169 | "boxShadow": "0 1px 1px 0 rgba(0, 0, 0, 0.07), 0 0 0 2px var(--colorDanger)" 170 | } 171 | } 172 | } 173 | ``` 174 | 175 | [Much of this documentation was taken from Stripe](https://stripe.com/docs/elements/appearance-api?platform=web#rules). Visit Stripe's documentation for more in-depth information including more options not mentioned here. 176 | 177 | ### Putting it all together 178 | 179 | Here is all the the above sections put together. 180 | 181 | ```json 182 | { 183 | "labels": "floating", 184 | "variables": { 185 | "colorPrimary": "#439558", 186 | "colorBackground": "#d8eede", 187 | "colorText": "#439558", 188 | "colorDanger": "#ff3f23", 189 | "fontFamily": "Tahoma, sans-serif", 190 | "spacingUnit": "6px", 191 | "borderRadius": "8px" 192 | }, 193 | "rules": { 194 | ".Tab, .Input, .PickerItem": { 195 | "borderRadius": "var(--borderRadius)" 196 | }, 197 | ".Tab": { 198 | "border": "1px solid #dddddd", 199 | "boxShadow": "0px 1px 1px rgba(0, 0, 0, 0.03), 0px 3px 6px rgba(18, 42, 66, 0.02)" 200 | }, 201 | ".Tab--selected": { 202 | "backgroundColor": "var(--colorBackground)", 203 | "border": "1px solid var(--colorPrimary)" 204 | }, 205 | ".Label--invalid, .Input--invalid, .Error": { 206 | "color": "var(--colorDanger)" 207 | }, 208 | ".Input--invalid": { 209 | "boxShadow": "0 1px 1px 0 rgba(0, 0, 0, 0.07), 0 0 0 2px var(--colorDanger)" 210 | } 211 | } 212 | } 213 | ``` 214 | 215 | The above styling will yield a result similar to this: 216 | 217 | End Result 218 | -------------------------------------------------------------------------------- /embedding.md: -------------------------------------------------------------------------------- 1 | # Embedding a Payment Form on Your Site 2 | 3 | Embedding a form on your website allows payers to checkout right on your site. Before embedding a payment form on your website, please keep in mind the following information: 4 | 5 | 1. If you have uploaded an logo/image to the theme the form is using, we do not display that image in the embed. In addition, the page background color is set to transparent and the exterior borders of the form are removed. 6 | 2. When the form is embedded, it expands to 100% of the width of the containing element by default. The minimum width for the containing element should be no less than 320 pixels. To edit the width of the embedded form, find the word "width" in the embed code we provide and adjust the pixel count that is listed after the colon. 7 | 3. Some payment methods using [3D Secure](https://stripe.com/docs/payments/3d-secure) may require a redirect to a banking website to authorize the payment. In these cases the checkout cannot be fully completed in the embedded frame. But you can configure your payment form to [redirect back to your site](https://help.moonclerk.com/en/articles/1239873-how-do-i-redirect-payers-to-a-web-page-that-i-designate-after-they-finish-checking-out) to somewhat maintain the seamless experience. 8 | 9 | [View this page](https://help.moonclerk.com/en/articles/1239752-how-do-i-embed-one-of-my-payment-forms-inside-my-website) to see where you can find the embed code for a form. 10 | 11 | ## Options 12 | 13 | | key | description | example value | 14 | | -------------- | ----------------------------------------------------------------------------- | -------------------- | 15 | | `width` | width of the embeded checkout (the default is `"100%"`) | `"100%"` | 16 | | `amount_cents` | prefilled amount (only works when the form has an amount decided at checkout) | `1000` | 17 | | `name` | pre-filled name | `"Leon Bridges"` | 18 | | `email` | pre-filled email | `"leon@example.com"` | 19 | | `cid` | custom ID ([see here](/integration.md#passing-a-custom-id) for more info) | `"2-4757546-456"` | 20 | 21 | Here is an example with all of the options above. 22 | 23 | ```html 24 |
Easy Time
25 | 34 | ``` 35 | 36 | ⚠️ NOTE: All instances of `REPLACE_WITH_TOKEN` must have the correct token value from the form. This value will be auto-generated when you [copy the embed code](https://help.moonclerk.com/en/articles/1239752-how-do-i-embed-one-of-my-payment-forms-inside-my-website) from the MoonClerk dashboard, so always start there and change the `opts` as-needed. 37 | -------------------------------------------------------------------------------- /integration.md: -------------------------------------------------------------------------------- 1 | # Integrating With Your Site 2 | 3 | We offer two ways to integrate with your site: 4 | 5 | 1. Seeding the checkout with the data so your user doesn't have to enter it 6 | 2. Passing a custom ID in order to track an existing user through a checkout 7 | 8 | ## Seeding a payment form with data 9 | 10 | You can pass parameters to the MoonClerk Payment Form that will make select fields pre-filled. This is a great option if you already have customer information and would like to link to a payment form from an app you control. 11 | 12 | ### Available Parameters 13 | 14 | | Name | Example value | 15 | | -------------- | ------------------ | 16 | | `amount_cents` | `1000` | 17 | | `email` | `leon@example.com` | 18 | | `name` | `Leon+Bridges` | 19 | 20 | Note: Payment Form must have an amount that is decided at checkout. 21 | 22 | You can set the URL parameters of the form like so: 23 | 24 | `https://app.moonclerk.com/pay/8h7frjfytj?amount_cents=1000&name=Leon+Bridges&email=leon@example.com` 25 | 26 | Doing so will yield the following result: 27 | 28 | ![Pre-filled form](assets/pre-filled.png) 29 | 30 | [See embedding documentation](/embedding.md) for info on how to accomplish this 31 | with an embedded checkout. 32 | 33 | ## Tracking a user through a checkout with a custom ID 34 | 35 | Sometimes you want to track an existing user on your system through a MoonClerk checkout. Here is the basic flow you will need to follow. 36 | 37 | 1. Create or retrieve your identifier for the customer 38 | 2. Pass the identifier as a custom ID (`cid`) param when the checkout is loaded 39 | 3. Set up and configure a webhook endpoint in the MoonClerk dashboard. 40 | 4. When a checkout is completed, you will receive a webhook with a payment (for one-off payments) or customer (for recurring plans) payload 41 | 5. Compare the `custom_id` field in the payload with the identifier that you passed into the checkout and store the MoonClerk payment ID or customer ID in your database. 42 | 6. Then you'll be able to look up a payment using the payment endpoint or a customer using the customer endpoint in the MoonClerk API. 43 | 44 | ### Passing the custom ID to a checkout 45 | 46 | #### When using a linked checkout 47 | 48 | If your user ID is 234, you can add the following parameter to your checkout URL: 49 | 50 | `https://app.moonclerk.com/pay/8h7frjfytj` 51 | 52 | becomes 53 | 54 | `https://app.moonclerk.com/pay/8h7frjfytj?cid=234` 55 | 56 | **A note about security. All traffic to and from MoonClerk is encrypted with SSL which includes the URL. If you are concerned about sending real IDs over the URL you can create a MD5 or SHA hash based on the id and timestamp, store it with the user in your database, and pass that parameter instead of the actual ID.** 57 | 58 | #### When embedding a checkout 59 | 60 | If you are using [an embed code for your form](/embedding.md), you'll need to dynamically add the `cid` to the `opts` object. Looking inside the embed code, you'll find an `opts` object similar to this: 61 | 62 | `opts={"checkoutToken":"8h7frjfytj","width":"100%"};` 63 | 64 | You'll need to add the `cid` as follows: 65 | 66 | `opts={"checkoutToken":"8h7frjfytj","width":"100%","cid":"234"};` 67 | 68 | Here you can see the entire embed snippet with the updated `opts` object: 69 | 70 | ```html 71 |
72 | My Payment Form 73 |
74 | 92 | ``` 93 | 94 | ### Retrieving the MoonClerk ID associated with your custom ID 95 | 96 | The [Customer](/webhooks.md#example-customer-payload) and 97 | [Payment](/webhooks.md#example-payment-payload) webhook payloads should contain a `custom_id` field which should match the `cid` you provided. 98 | 99 | You will want to query your resource using the `custom_id` and then store the associated `id` field so you can then use it to find the MoonClerk resource via our API. 100 | 101 | --- 102 | 103 | ⚠️ **Important!** Previous versions of this document referenced `payment_id` and `customer_id` parameters for the redirect URL. These parameters are no longer supported. As a replacement, we recommend to use webhooks. 104 | -------------------------------------------------------------------------------- /liquid/README.md: -------------------------------------------------------------------------------- 1 | # Liquid Help for Email Notifications 2 | 3 | Need help formatting Liquid templates with MoonClerk notifications: 4 | 5 | - [Liquid Variables & Filters for MoonClerk](/liquid/variables.md) 6 | - [Liquid Examples for MoonClerk](/liquid/examples.md) 7 | -------------------------------------------------------------------------------- /liquid/examples.md: -------------------------------------------------------------------------------- 1 | # Liquid Examples for MoonClerk 2 | 3 | ## Using custom fields 4 | 5 | There are a variety of ways to use custom field depending on your needs. Here are a few ideas. 6 | 7 | ### Looping over all custom fields 8 | 9 | When looping through fields, the actual variable name will be determined 10 | by how you name the for loop in the template. In this example, the variable 11 | will be called `field`. 12 | 13 | ```Liquid 14 | {% for field in payment.custom_fields %} 15 | {{ field[1].title }}: {{ field[1].response }} 16 | {% endfor %} 17 | ``` 18 | 19 | **Liquid uses the `[1]` to access the value of the field hash.** 20 | 21 | ### Accessing a specific field for display 22 | 23 | Another way to deal with custom field is to access a specific field. It is important to remember that the notification template is across all forms. If you write code to accesses a certain custom field you will want it to degrade nicely if the field doesn't exist on the current payment or plan. 24 | 25 | Let's say you have a `shipping_address` key on many of you forms and want to output it on the payment successful notification to the account holder. 26 | 27 | ```Liquid 28 | {% if payment.custom_fields.shipping_address %} 29 | {% assign address = payment.custom_fields.shipping_address.address %} 30 | {{ address.line1 }}{% if address.line2 %} 31 | {{ address.line2 }}{% endif %} 32 | {{ address.city }}, {{ address.state }} {{ address.postal_code }}{% endif %} 33 | ``` 34 | 35 | ## Setting a different message for each form 36 | 37 | You'll need to know the id of each form for which you want a custom message. You can find that by looking at the url when viewing a form. (These are example non-functional URLs used only for reference. They won't pull up an actual form.) 38 | 39 | ``` 40 | https://app.moonclerk.com/forms/32 41 | https://app.moonclerk.com/forms/97 42 | https://app.moonclerk.com/forms/104 43 | ``` 44 | 45 | The form IDs are 32, 97, and 104 in the above urls. 46 | 47 | ``` 48 | This email confirms that {{ account.name }} has successfully charged your card for {{ payment.amount | money_with_currency }}.{% if form.id == 32 %} 49 | 50 | This is my custom message for Form 32{% elsif form.id == 97 %} 51 | 52 | This is my custom message for Form 97{% elsif form.id == 104 %} 53 | 54 | This is my custom message for Form 104{% endif %} 55 | 56 | Payment Details: 57 | 58 | Charge Date: {{ payment.date | date: '%b %d, %Y' }} 59 | Bill To: {{ payment.name }} 60 | Amount: {{ payment.amount | money_with_currency }} 61 | Payment Method Last 4: {{ payment.payment_method.last4 }}{% if plan %} 62 | 63 | Recurring Plan Details: 64 | 65 | Amount: {{ plan.amount | money_with_currency }} 66 | Frequency: {{ plan.frequency | capitalize }} 67 | Start Date: {{ plan.start | date: '%b %d, %Y' }}{% if plan.expires %} 68 | Expiration: {{ plan.expires | date: '%b %d, %Y' }}{% endif %}{% if plan.next_payment_attempt %} 69 | Next Payment Attempt: {{ plan.next_payment_attempt | date: '%b %d, %Y' }}{% endif %} 70 | 71 | To update your card on file, please visit: 72 | {{ plan.payer_manage_url }}{% endif %} 73 | ``` 74 | -------------------------------------------------------------------------------- /liquid/variables.md: -------------------------------------------------------------------------------- 1 | # Liquid Variables & Filters for MoonClerk 2 | 3 | ## Liquid Variable Reference 4 | 5 | ### Account 6 | 7 | - `account.name` - Your MoonClerk account name 8 | - `account.email` - You MoonClerk account email (may be different from your sign in email) 9 | - `account.currency` - The currency your MoonClerk and Stripe account are using 10 | 11 | ### Plan (Customer) 12 | 13 | - `plan.id` - MoonClerk Plan ID 14 | - `plan.reference` - Stripe Customer ID for this plan 15 | - `plan.url` - a link to this plan on MoonClerk 16 | - `plan.payer_manage_url` - The link for the payer to manage their plan 17 | - `plan.name` - Payer name 18 | - `plan.email` - Payer email 19 | - `plan.payment_method.type` - Method of payment used, i.e. Credit/Debit Card or Bank Account 20 | - `plan.payment_method.last4` - Last four digits of the card or bank account 21 | - `plan.payment_method.brand` - Card Type, i.e. Visa, MasterCard, etc. or Bank Name, i.e. Chase, Wells Fargo, etc. 22 | - `plan.payment_method.expiration` - The month and year the active card expires 23 | - `plan.discount.coupon` - A description of the currently applied coupon, i.e. "10off ($10.00 off once)". This will only exist if there is an active discount. Otherwise it will be empty. 24 | - `plan.discount.expiration` - The date the active discount expires for this plan. This will only exist if there is an active discount. Otherwise it will be empty. 25 | - `plan.status` - The status of the plan (Active, Not Started, Canceled, etc.) 26 | - `plan.next_payment_attempt` - The next scheduled payment attempt on this plan 27 | - `plan.start` - The first scheduled payment attempt 28 | - `plan.expires` - The expiration date of the plan, if applicable 29 | - `plan.amount` - The recurring amount in cents 30 | - `plan.amount_description` - The description of the matching form amount option, if applicable 31 | - `plan.interval` - week, month or year 32 | - `plan.interval_count` - 1 or more 33 | - `plan.frequency` - interval phrase, i.e. "every 3 months” 34 | - `plan.custom_fields` - A Hash of custom field responses based on the form. See [Custom Fields](#custom-fields) section. 35 | - `plan.checkout.date` - The date of the checkout 36 | - `plan.checkout.amount_due` - The total amount of the first payment. This is the big number the payer sees when checking out. Amount is in cents. 37 | - `plan.checkout.subtotal` - The amount of the form or the chosen amount if deferred. Amount is in cents. 38 | - `plan.checkout.fee` - The net fee amount based on a fee amount or percentage as configured in your form. Amount is in cents. 39 | - `plan.checkout.upfront_amount` - The one-time upfront amount that is added to the first payment. (Fees are not applied to this amount.) Amount is in cents. 40 | - `plan.checkout.trial_period_days` - The number of trial period days for the checkout, if applicable. 41 | 42 | ### Payment 43 | 44 | - `payment.id` - the MoonClerk internal payment ID 45 | - `payment.reference` - the Stripe charge ID 46 | - `payment.url` - a link to this plan on MoonClerk 47 | - `payment.date` - create date 48 | - `payment.name` - Payer's name 49 | - `payment.email` - Payer's email 50 | - `payment.amount` - the payment amount in cents 51 | - `payment.amount_description` - The description of the matching form amount option, if applicable 52 | - `payment.fee` - the fee in cents 53 | - `payment.amount_refunded` - the amount refunded in cents 54 | - `payment.status` - payment status in the dashboard 55 | - `payment.payment_method.type` - Method of payment used, i.e. Credit/Debit Card or Bank Account 56 | - `payment.payment_method.last4` - Last four digits of the card or bank account 57 | - `payment.payment_method.brand` - Card Type, i.e. Visa, MasterCard, etc. 58 | - `payment.payment_method.bank_name` - Bank Name, i.e. Chase, Wells Fargo, etc. 59 | - `payment.invoice` - the Stripe invoice ID 60 | - `payment.custom_fields` - A Hash of custom field responses based on the form. See [Custom Fields](#custom-fields) section. 61 | 62 | ### Form 63 | 64 | - `form.id` 65 | - `form.url` 66 | - `form.title` 67 | - `form.description` 68 | 69 | ## Custom Fields 70 | 71 | Fields contain the responses from the custom fields created on your 72 | forms. They are accessed through either `payment.custom_fields` or `plan.custom_fields`. 73 | 74 | The `custom_fields` variable is a Hash. [See how to loop over a Hash in Liquid](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers#for-loops). 75 | 76 | The key will be the key you assign during the creation of your custom field in the payment form builder. The value contain the following fields: 77 | 78 | - `title` - the title of the field 79 | - `response` - the text response or the address on a single line 80 | - `address` - contains field with the address parts. If the field is not an address it will be empty (null). 81 | 82 | If address is not empty, it will contain: 83 | 84 | - `address.line1` 85 | - `address.line2` 86 | - `address.city` 87 | - `address.state` 88 | - `address.postal_code` 89 | - `address.county` 90 | 91 | Here is a sample of the structure of some custom field data: 92 | 93 | ``` 94 | 'shipping_address' => { 95 | 'title' => 'Shipping Address', 96 | 'address' => { 97 | 'line1' => '123 Sycamore St.', 98 | 'line2' => 'Suite 100', 99 | 'city' => 'Greenville', 100 | 'state' => 'SC', 101 | 'postal_code' => '29601', 102 | 'country' => 'United States' 103 | }, 104 | 'response' => '123 Sycamore St., Suite 100, Greenville, SC 29601, United States' 105 | }, 106 | 'tshirt_size' => { 107 | 'title' => 'T-shirt size', 108 | 'address' => nil, 109 | 'response' => 'X-Large' 110 | } 111 | ``` 112 | 113 | [Browse examples of how to use custom fields within a template](/liquid/examples.md#using-custom-fields). 114 | 115 | ### Digital Delivery Order 116 | 117 | - `order.name` 118 | - `order.email` 119 | - `order.access_link` 120 | 121 | ### Digital Delivery Package 122 | 123 | - `package.name` 124 | - `package.description` 125 | 126 | ### Deprecated fields 127 | 128 | We are maintaining deprecated fields for backward compatibility but all new development should use the newer variables described below. 129 | 130 | - `plan.payment_source.last4` is now `plan.payment_method.last4` 131 | - `plan.payment_source.brand` is now `plan.payment_method.brand` 132 | - `plan.payment_source.expiration` is now `plan.payment_method.expiration` 133 | - `payment.payment_source.last4` is now `payment.payment_method.last4` 134 | - `payment.payment_source.brand` is now `payment.payment_method.brand` 135 | 136 | ## Variable Availability 137 | 138 | Which Liquid variables can I use in which email notifications? 139 | 140 | > Keep in mind that even though a variable may be sent to your template, 141 | > it may be empty. Outputting an empty variable should not cause an error. 142 | 143 | | Template | payment.\* | plan.\* | account.\* | form.\* | invoice.\* | order.\* | package.\* | 144 | | --------------------------------- | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | 145 | | Successful Payment | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | | 146 | | Recurring Plan Created | | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | | 147 | | Failed Payment on Recurring Plan | | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | 148 | | Recurring Plan Ended | | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | | 149 | | Card Expiration Date Approaching | | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | | 150 | | Upcoming Payment | | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | | 151 | | Successful Digital Delivery Order | :white_check_mark: | :white_check_mark: | | :white_check_mark: | | :white_check_mark: | :white_check_mark: | 152 | 153 | # Liquid Filters 154 | 155 | ## MoonClerk Filters 156 | 157 | - `money` - formats money with a symbol 158 | - `money_with_currency` - formats money with a symbol and currency 159 | - `money_without_currency` - formats money as a decimal 160 | 161 | ## Official Liquid Docs 162 | 163 | Visit the [Official Liquid Wiki](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers) 164 | to learn more about the standard filters and control flow constructs (if-then, etc.) 165 | that you can use within templates. 166 | -------------------------------------------------------------------------------- /webhooks.md: -------------------------------------------------------------------------------- 1 | # MoonClerk Webhooks 2 | 3 | Once you set up a Webhook Endpoint in the MoonClerk dashboard, MoonClerk will send a POST request to the specified endpoint URL when the specified topic(s) takes place. [Click here](https://app.moonclerk.com/settings/webhooks) to visit webhooks settings in the MoonClerk dashboard. 4 | 5 | MoonClerk expects to receive a **200-204 HTTP** response code from the endpoint. Any non-2XX code will trigger a retry. MoonClerk will retry up to 10 times. After all attempts are exhausted, the endpoint will be automatically set to inactive and future events will not be sent. 6 | 7 | ### Supported Topics 8 | 9 | | Topic | Description | Payload Type | 10 | | ------------------- | ------------------------------------------------------------------------------------ | ------------------------------------- | 11 | | Payment Created | Notifies when a payment is created in any state (failed, succeeded, etc). | [Payment](#example-payment-payload) | 12 | | Payment Succeeded | Notifies when a payment succeeds. | [Payment](#example-payment-payload) | 13 | | Plan Created | Notifies when a plan is created. | [Customer](#example-customer-payload) | 14 | | Plan Ended | Notifies when a plan is canceled or when the set number periods have been exhausted. | [Customer](#example-customer-payload) | 15 | | Plan Payment Failed | Notifies when a payment related to a plan has failed. | [Customer](#example-customer-payload) | 16 | 17 | ### Example Payment Payload 18 | 19 | ```jsonc 20 | { 21 | "event": "payment_created", 22 | "object": "payment", 23 | "data": { 24 | "id": 1348394, 25 | "date": "2022-04-08T18:57:26Z", 26 | "status": "successful", 27 | "currency": "USD", 28 | "amount": 1000, 29 | "fee": 59, 30 | "amount_refunded": 0, 31 | "amount_description": "Option A", 32 | "name": "Jim Customer", 33 | "email": "customer@example.com", 34 | "payment_method": { 35 | "type": "card", 36 | "last4": "4242", 37 | "brand": "Visa" 38 | }, 39 | "charge_reference": "ch_3ohpsF8ra5rqjj", 40 | // customer_id and customer_reference are available only if the payment was from a recurring checkout 41 | "customer_id": 53453, 42 | "customer_reference": "cus_4SOZuEc4cxP5L7", 43 | "invoice_reference": "in_1La8pLqS2UnhPZ", 44 | "custom_fields": { 45 | "shirt_size": { 46 | "id": 23452, 47 | "type": "string", 48 | "response": "XL" 49 | }, 50 | "shipping_address": { 51 | "id": 23453, 52 | "type": "address", 53 | "response": { 54 | "line1": "123 Main St.", 55 | "line2": "Ste. 153", 56 | "city": "Greenville", 57 | "state": "SC", 58 | "postal_code": "29651", 59 | "country": "United States" 60 | } 61 | } 62 | }, 63 | "form_id": 112, 64 | "coupon": { 65 | "code": "10off", 66 | "duration": "once", 67 | "amount_off": 1000, 68 | "currency": "USD", 69 | "percent_off": null, 70 | "duration_in_months": null, 71 | "max_redemptions": null, 72 | "redeem_by": null 73 | }, 74 | "custom_id": "GHS430", 75 | // Checkout data is available only if the payment was from a one-time checkout 76 | "checkout": { 77 | "amount_due": 1000, 78 | "coupon_amount": 0, 79 | "coupon_code": null, 80 | "date": "2022-04-08T18:57:26Z", 81 | "fee": 0, 82 | "subtotal": 1000, 83 | "token": "sGhAhMWZdVdJqEYByx4xcLP9", 84 | "total": 1000, 85 | "trial_period_days": null, 86 | "upfront_amount": 0 87 | } 88 | } 89 | } 90 | ``` 91 | 92 | ### Example Customer Payload 93 | 94 | ```json 95 | { 96 | "event": "plan_created", 97 | "object": "customer", 98 | "data": { 99 | "id": 523425, 100 | "account_balance": 0, 101 | "name": "Jim Customer", 102 | "email": "customer@example.com", 103 | "payment_method": { 104 | "type": "card", 105 | "last4": "4242", 106 | "exp_month": 12, 107 | "exp_year": 2018, 108 | "brand": "Visa" 109 | }, 110 | "custom_id": "GHS430", 111 | "customer_reference": "cus_4SOZuEc4cxP5L7", 112 | "discount": { 113 | "coupon": { 114 | "code": "10off", 115 | "duration": "once", 116 | "amount_off": 1000, 117 | "currency": "USD", 118 | "percent_off": null, 119 | "duration_in_months": null, 120 | "max_redemptions": null, 121 | "redeem_by": null 122 | }, 123 | "starts_at": "2022-04-12T20:05:37Z", 124 | "ends_at": "2022-05-12T20:05:37Z" 125 | }, 126 | "delinquent": false, 127 | "management_url": "https://app.moonclerk.com/manage/xyz1234567", 128 | "custom_fields": { 129 | "shirt_size": { 130 | "id": 23452, 131 | "type": "string", 132 | "response": "XL" 133 | }, 134 | "shipping_address": { 135 | "id": 23453, 136 | "type": "address", 137 | "response": { 138 | "line1": "123 Main St.", 139 | "line2": "Ste. 153", 140 | "city": "Greenville", 141 | "state": "SC", 142 | "postal_code": "29651", 143 | "country": "United States" 144 | } 145 | } 146 | }, 147 | "form_id": 101, 148 | "checkout": { 149 | "amount_due": 1700, 150 | "coupon_amount": 1000, 151 | "coupon_code": "10off", 152 | "date": "2014-07-23T13:44:12Z", 153 | "fee": 200, 154 | "subtotal": 1000, 155 | "token": "YUAf1PJde1Uho7CnQ1BVPuCz", 156 | "total": 1700, 157 | "trial_period_days": null, 158 | "upfront_amount": 500 159 | }, 160 | "subscription": { 161 | "id": 98, 162 | "subscription_reference": "sub_3oLgqlp4MgTZC3", 163 | "status": "active", 164 | "start": "2022-07-23T13:44:16Z", 165 | "first_payment_attempt": "2022-07-23T13:44:16Z", 166 | "next_payment_attempt": "2022-08-23T13:44:16Z", 167 | "current_period_start": "2022-07-23T13:44:16Z", 168 | "current_period_end": "2022-08-23T13:44:16Z", 169 | "trial_start": null, 170 | "trial_end": null, 171 | "trial_period_days": null, 172 | "expires_at": null, 173 | "canceled_at": null, 174 | "ended_at": null, 175 | "plan": { 176 | "id": 131, 177 | "plan_reference": "131", 178 | "amount": 1200, 179 | "amount_description": "Option A", 180 | "currency": "USD", 181 | "interval": "month", 182 | "interval_count": 1 183 | } 184 | } 185 | } 186 | } 187 | ``` 188 | --------------------------------------------------------------------------------