@maytes/checkout-button 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/checkout-button.2a6f6a35.cjs +806 -0
- package/dist/checkout-button.2a6f6a35.cjs.map +1 -0
- package/dist/checkout-button.3cf081dc.js +151 -0
- package/dist/checkout-button.3cf081dc.js.map +1 -0
- package/dist/checkout-button.5403f24b.mjs +783 -0
- package/dist/checkout-button.5403f24b.mjs.map +1 -0
- package/dist/checkout-button.cjs +806 -0
- package/dist/checkout-button.cjs.map +1 -0
- package/dist/checkout-button.d.cts +55 -0
- package/dist/checkout-button.d.ts +55 -0
- package/dist/checkout-button.js +151 -0
- package/dist/checkout-button.js.map +1 -0
- package/dist/checkout-button.mjs +783 -0
- package/dist/checkout-button.mjs.map +1 -0
- package/dist/integrity.json +39 -0
- package/package.json +76 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [`c17857f`](https://github.com/kttipay/checkout-button/commit/c17857fcb38bf045ae89fde207a1859ae12214e3) Thanks [@Merkost](https://github.com/Merkost)! - First public release on npm.
|
|
8
|
+
|
|
9
|
+
The button is now installable as `@maytes/checkout-button` alongside the existing
|
|
10
|
+
CDN `<script>` distribution — same package, same API, both fed from this source.
|
|
11
|
+
|
|
12
|
+
Framework apps (React, Angular, Vue, Svelte, plain bundled JS) should import the
|
|
13
|
+
factory directly:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Maytes } from "@maytes/checkout-button";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
That removes the script-injection dance those apps previously needed: no
|
|
20
|
+
`window.Maytes` global to feature-detect, no polling for the CDN script to
|
|
21
|
+
finish loading before mounting, and no hand-written `declare global` typings —
|
|
22
|
+
the package ships its own `.d.ts`.
|
|
23
|
+
|
|
24
|
+
The `<script>` tag remains the right integration for no-build sites and jQuery,
|
|
25
|
+
and is unchanged: pin a SemVer or content-hash URL with SRI in every environment,
|
|
26
|
+
development included.
|
|
27
|
+
|
|
28
|
+
### Minor Changes
|
|
29
|
+
|
|
30
|
+
- [`c17857f`](https://github.com/kttipay/checkout-button/commit/c17857fcb38bf045ae89fde207a1859ae12214e3) Thanks [@Merkost](https://github.com/Merkost)! - Brand the checkout button with the Maytes wordmark logo and maroon brand color, and make its size auto-scale from a single `--maytes-button-font-size` custom property (default 14px).
|
|
31
|
+
|
|
32
|
+
- [`c17857f`](https://github.com/kttipay/checkout-button/commit/c17857fcb38bf045ae89fde207a1859ae12214e3) Thanks [@Merkost](https://github.com/Merkost)! - Add a launch `mode` to `renderButton` and a branded popup loading screen.
|
|
33
|
+
|
|
34
|
+
- `renderButton(container, { mode })` accepts `'redirect'` (new default) or `'popup'`. Invalid values throw `MaytesError(CONFIG)`.
|
|
35
|
+
- `mode: 'popup'` opens a blank popup synchronously on click (preserving the user gesture so browsers do not block it), paints a branded Maytes loading screen into it (logo, spinner, `role="status"`/`aria-live`, a slow-network message that appears after ~8s, and a `prefers-reduced-motion` guard), then `popup.location.replace(url)`s once `createCheckout` resolves.
|
|
36
|
+
- On mobile viewports `mode: 'popup'` behaves as a same-window redirect.
|
|
37
|
+
- The misleading `maytes:checkout-failed` (`reason: 'popup-blocked'`) is replaced by a new `maytes:checkout-redirected` event (`detail.url`). A same-window redirect is a normal outcome, not a failure. The `failed` event now only carries `'create-checkout-rejected'` | `'invalid-shape'`.
|
|
38
|
+
- A popup opened before `createCheckout` resolves is now closed if the call rejects, returns an invalid shape, or the instance is destroyed mid-flight. `destroy()` also closes any open popup.
|
|
39
|
+
- Exported `POPUP_LOADING_CSS` for the loader stylesheet.
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- [`c17857f`](https://github.com/kttipay/checkout-button/commit/c17857fcb38bf045ae89fde207a1859ae12214e3) Thanks [@Merkost](https://github.com/Merkost)! - Harden the checkout button popup lifecycle:
|
|
44
|
+
|
|
45
|
+
- Open the hosted checkout URL exactly once (it was navigated twice — `window.open(url)` plus a redundant `location.href` write).
|
|
46
|
+
- Give each SDK instance a unique popup window name so concurrent merchant tabs no longer share or steal one popup; this also removes the cross-origin `SecurityError` that could hang the overlay.
|
|
47
|
+
- `destroy()` now detaches every button's click listener and is ignored by an in-flight `createCheckout` that resolves after teardown (no late popup).
|
|
48
|
+
- Remove the cloned overlay `<style>` from the top document on teardown (same-origin iframe embeds).
|
|
49
|
+
- Scope the injected style marker and ref-count by SDK version so two SDK versions on one page don't clash.
|
|
50
|
+
- Clear the overlay immediately when the shopper presses Escape (was delayed up to one poll interval).
|
|
51
|
+
- Validate that a merchant-supplied `checkoutUrl` is an `http(s)` URL before opening it — matching the protocol guard already applied to the constructed URL, so a buggy backend can't turn the button into a `javascript:`/`data:` popup.
|
|
52
|
+
|
|
53
|
+
- [`c17857f`](https://github.com/kttipay/checkout-button/commit/c17857fcb38bf045ae89fde207a1859ae12214e3) Thanks [@Merkost](https://github.com/Merkost)! - Take the Maytes brand colours from the design foundation (`kttipay/designsystem` v0.3.0, vendored by `npm run sync:foundation` and pinned in `foundation.lock.json`) instead of hand-written hexes. The only rendered change: the button's hover shade is now the foundation's burgundy/700 `#3B021D` (was `#3A021C`).
|
|
54
|
+
|
|
55
|
+
- [`c17857f`](https://github.com/kttipay/checkout-button/commit/c17857fcb38bf045ae89fde207a1859ae12214e3) Thanks [@Merkost](https://github.com/Merkost)! - Serve the CDN (IIFE) bundle minified, with a sourcemap. The ESM/CJS npm bundles stay unminified so the consumer's bundler can tree-shake them.
|
|
56
|
+
|
|
57
|
+
- [`c17857f`](https://github.com/kttipay/checkout-button/commit/c17857fcb38bf045ae89fde207a1859ae12214e3) Thanks [@Merkost](https://github.com/Merkost)! - Fix a race in `mode: 'popup'`: if the shopper closed the popup while `createCheckout` was still in flight, the SDK would try to navigate (or, on rejection, report a failure for) a window that was already gone once the promise settled — on top of the `maytes:checkout-closed` event the close-poll had already fired. Both the success and rejection paths now check whether the popup is still open before acting, so closing the popup early produces exactly one `checkout-closed` event and nothing else.
|
|
58
|
+
|
|
59
|
+
This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
60
|
+
|
|
61
|
+
<!-- @hash-sri-start -->
|
|
62
|
+
|
|
63
|
+
**SRI hashes** (use these in `<script integrity="..." crossorigin="anonymous">`):
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
checkout-button.js sha384-dZQS1KceW5Q/UDS8qafQmJFIvORLSL/aNtpkbU94yqZCLB+ifJfxkgNLjHpFADkt
|
|
67
|
+
checkout-button.mjs sha384-jQbytMpQ6WWb5T8Xd0+f8+B6QclSeIgF7AaYiVCe6UdXK6nYUoC/x+VM3YWTWxH1
|
|
68
|
+
checkout-button.cjs sha384-XpEcs+bC2pKL1C8nhXv0sPSGWeWHGKrOnOJ/FGhseY0uIY6al831WIS+be4BQTIY
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**SemVer CDN links** (readable production pins; use with the SRI hashes above):
|
|
72
|
+
|
|
73
|
+
- [checkout-button.js](https://js.maytes.co/v1.0.0/checkout-button.js)
|
|
74
|
+
- [checkout-button.mjs](https://js.maytes.co/v1.0.0/checkout-button.mjs)
|
|
75
|
+
- [checkout-button.cjs](https://js.maytes.co/v1.0.0/checkout-button.cjs)
|
|
76
|
+
|
|
77
|
+
**Hashed CDN links** (byte-level production pins; use with the SRI hashes above):
|
|
78
|
+
|
|
79
|
+
- [checkout-button.3cf081dc.js](https://js.maytes.co/checkout-button.3cf081dc.js)
|
|
80
|
+
- [checkout-button.5403f24b.mjs](https://js.maytes.co/checkout-button.5403f24b.mjs)
|
|
81
|
+
- [checkout-button.2a6f6a35.cjs](https://js.maytes.co/checkout-button.2a6f6a35.cjs)
|
|
82
|
+
|
|
83
|
+
<!-- @hash-sri-end -->
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maytes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @maytes/checkout-button
|
|
2
|
+
|
|
3
|
+
Drop-in **Split with Maytes** button. Renders a button and launches the Maytes-hosted checkout. Default mode is same-window redirect; `mode: 'popup'` opts desktop browsers into a centered popup with a branded loader and redirect fallback on mobile or when blocked.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
### Script tag (CDN)
|
|
8
|
+
|
|
9
|
+
For production, pin an exact release. The readable SemVer URL and the content-hash URL are two names for the **same release bytes** and share the **same SRI value** — pick either:
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<script src="https://js.maytes.co/v1.0.0/checkout-button.js"
|
|
13
|
+
integrity="sha384-…"
|
|
14
|
+
crossorigin="anonymous"></script>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<script src="https://js.maytes.co/checkout-button.795d508b.js"
|
|
19
|
+
integrity="sha384-…"
|
|
20
|
+
crossorigin="anonymous"></script>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`v1.0.0` / `795d508b` are examples. The real version, hash, and SRI for each release live in [`CHANGELOG.md`](./CHANGELOG.md), the matching [release](https://github.com/kttipay/checkout-button/releases), and `https://js.maytes.co/integrity.json`.
|
|
24
|
+
|
|
25
|
+
Use a pinned URL in every environment, development included — pinning is what makes the bytes you tested the bytes your shoppers get.
|
|
26
|
+
|
|
27
|
+
### npm
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @maytes/checkout-button
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
`Maytes` is a callable factory (Stripe-style). Each call returns an isolated SDK instance.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { Maytes } from '@maytes/checkout-button';
|
|
39
|
+
|
|
40
|
+
const maytes = Maytes({
|
|
41
|
+
createCheckout: async () => {
|
|
42
|
+
const res = await fetch('/api/maytes/create-checkout', { method: 'POST' });
|
|
43
|
+
return res.json(); // { checkoutId: string, checkoutUrl?: string }
|
|
44
|
+
},
|
|
45
|
+
environment: 'sandbox',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const cleanup = maytes.renderButton(document.getElementById('slot'), { block: true });
|
|
49
|
+
|
|
50
|
+
// Optional popup mode:
|
|
51
|
+
maytes.renderButton(document.getElementById('popup-slot'), { block: true, mode: 'popup' });
|
|
52
|
+
|
|
53
|
+
// Imperative alternatives:
|
|
54
|
+
maytes.redirectToCheckout({ checkoutId });
|
|
55
|
+
const url = maytes.checkoutUrl({ checkoutId });
|
|
56
|
+
|
|
57
|
+
// Teardown:
|
|
58
|
+
cleanup();
|
|
59
|
+
maytes.destroy();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Via the script tag, the same factory is available as the global `window.Maytes(...)`.
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
| Method | Purpose |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `Maytes(options)` | Create an SDK instance. `options.createCheckout` mints a checkout server-side; `options.environment` selects the Maytes environment. |
|
|
69
|
+
| `renderButton(container, options?)` | Render the button into `container`; returns a cleanup function. Options include `label`, `block`, and `mode: 'redirect' \| 'popup'`. |
|
|
70
|
+
| `redirectToCheckout(options)` | Launch checkout directly (no button). |
|
|
71
|
+
| `checkoutUrl(options)` | Build the hosted checkout URL. |
|
|
72
|
+
| `destroy()` | Tear down the instance and its listeners. |
|
|
73
|
+
|
|
74
|
+
## Mobile app
|
|
75
|
+
|
|
76
|
+
After checkout, shoppers split the cost with friends using Maytes payment links (`app.maytes.co/…`). On a phone with the Maytes app installed, those links open directly in the app (iOS Universal Links / Android App Links); otherwise they open in the browser. Maytes handles this end to end — merchants integrate only the button and configure nothing for the app.
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm ci
|
|
82
|
+
npm run dev # tsup watch
|
|
83
|
+
npm run typecheck
|
|
84
|
+
npm run test:run
|
|
85
|
+
npm run build # bundles + SRI hashes + CSP scan
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Versioning and changelog are managed with [Changesets](./.changeset/README.md). Run `npm run changeset` with your PR.
|
|
89
|
+
|
|
90
|
+
## Brand colours come from the design foundation
|
|
91
|
+
|
|
92
|
+
The Maytes brand colours the button paints (`brand.primary` burgundy, `brand.secondary` orange, the burgundy/700 hover shade, and the inverse text white) are **not hand-written** in this package. They are vendored from the design foundation — `kttipay/designsystem` `web/tokens.ts` — into `src/foundation/brand.generated.ts`, pinned to a release tag recorded in `foundation.lock.json`:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{ "repo": "kttipay/designsystem", "tag": "v0.3.0", "source": "web/tokens.ts" }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- `src/branding.ts`, `src/styles.ts` and `src/button.ts` read every brand colour from that generated module. Never type a Maytes hex into `src/`.
|
|
99
|
+
- To move to a newer foundation release: `npm run sync:foundation -- --tag vX.Y.Z` (fetches the file at that tag with `gh api`, so you need a logged-in `gh`; `--from ../path/to/designsystem/web/tokens.ts` works offline). The script rewrites the generated module and the lock file. Commit both together.
|
|
100
|
+
- A vitest test fails if the generated header's tag drifts from `foundation.lock.json`, so the file can only change through the script.
|
|
101
|
+
|
|
102
|
+
The design-system repo does not publish an npm package yet. When it does (`@kttipay/foundation` on GitHub Packages), the upgrade path is to replace the vendored module with that dependency and delete the script and lock file.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|