@zippypay/checkout 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/README.md ADDED
@@ -0,0 +1,270 @@
1
+ # @zippypay/checkout
2
+
3
+ Browser checkout SDK for **Zippy Pay** merchants. Embed payment UI inline on a checkout page or open it in a modal. Works with vanilla JavaScript, React, Vue, Angular, and any stack that runs modern JavaScript.
4
+
5
+ Styles are scoped inside Shadow DOM — no extra CSS import is required.
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @zippypay/checkout
13
+ ```
14
+
15
+ ```bash
16
+ yarn add @zippypay/checkout
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Before the SDK runs
22
+
23
+ Your **server** creates a payment session and passes credentials to the checkout page:
24
+
25
+ | Field | Description |
26
+ |---|---|
27
+ | `sessionId` | UUID v4 from `POST /payment-sessions` — safe in URLs |
28
+ | `clientToken` | `zps_…` from the create-session response — **server → browser only** |
29
+
30
+ Never expose `X-Api-Key` or Clerk JWT from browser code. Never put `clientToken` in QR codes, query strings, logs, or analytics.
31
+
32
+ ---
33
+
34
+ ## Quick start
35
+
36
+ ### JavaScript / TypeScript
37
+
38
+ ```typescript
39
+ import { ZippyPay } from '@zippypay/checkout';
40
+
41
+ const checkout = ZippyPay.create({
42
+ environment: 'production',
43
+ sessionId: '11111111-1111-4111-8111-111111111111',
44
+ clientToken: 'zps_…',
45
+ mode: 'inline',
46
+ container: '#zippy-checkout',
47
+ theme: { primary: '#2563eb' },
48
+ onComplete: (session) => console.log('Paid', session),
49
+ });
50
+ ```
51
+
52
+ ### Modal
53
+
54
+ ```typescript
55
+ ZippyPay.create({
56
+ environment: 'production',
57
+ sessionId,
58
+ clientToken,
59
+ mode: 'modal',
60
+ closeOnBackdropClick: true,
61
+ closeOnEscape: true,
62
+ onClose: () => console.log('Modal closed'),
63
+ });
64
+ ```
65
+
66
+ ### Web Component
67
+
68
+ ```html
69
+ <script type="module">
70
+ import '@zippypay/checkout';
71
+ </script>
72
+
73
+ <zippy-pay-checkout
74
+ environment="production"
75
+ session-id="11111111-1111-4111-8111-111111111111"
76
+ mode="inline"
77
+ theme-primary="#2563eb"
78
+ ></zippy-pay-checkout>
79
+
80
+ <script type="module">
81
+ const el = document.querySelector('zippy-pay-checkout');
82
+ el.clientToken = 'zps_…'; // property, not HTML attribute
83
+ el.addEventListener('zippy-complete', (e) => console.log(e.detail));
84
+ </script>
85
+ ```
86
+
87
+ ### Script tag (UMD)
88
+
89
+ Serve or bundle is not required — load from a public npm CDN:
90
+
91
+ ```html
92
+ <script src="https://unpkg.com/@zippypay/checkout@1/dist/index.min.js"></script>
93
+ <script>
94
+ ZippyPay.create({
95
+ environment: 'production',
96
+ sessionId: '…',
97
+ clientToken: 'zps_…',
98
+ mode: 'inline',
99
+ container: '#zippy-checkout',
100
+ });
101
+ </script>
102
+ ```
103
+
104
+ Pin the version in production (`@1.0.0` instead of `@1`).
105
+
106
+ ---
107
+
108
+ ## Environments
109
+
110
+ Pass `environment` — the SDK resolves the API base URL:
111
+
112
+ | Environment | API origin |
113
+ |---|---|
114
+ | `production` | `https://api.zippypay.io` |
115
+ | `staging` | `https://staging.api.zippypay.io` |
116
+ | `development` | Same-origin (empty base — for local proxies) |
117
+
118
+ All requests use the prefix `/api/v1` (e.g. `GET https://api.zippypay.io/api/v1/payment-sessions/:id`).
119
+
120
+ Use the environment where your merchant backend created the session.
121
+
122
+ ---
123
+
124
+ ## Configuration
125
+
126
+ ```typescript
127
+ type ZippyPayConfig = {
128
+ environment: 'production' | 'staging' | 'development';
129
+ sessionId: string;
130
+ clientToken: string;
131
+ mode?: 'inline' | 'modal';
132
+ container?: HTMLElement | string; // required for inline
133
+ theme?: ZippyTheme;
134
+ redirectOnComplete?: boolean;
135
+ redirectOnFailure?: boolean;
136
+ closeOnBackdropClick?: boolean;
137
+ closeOnEscape?: boolean;
138
+ onReady?: (session) => void;
139
+ onSessionUpdate?: (session) => void;
140
+ onComplete?: (session) => void;
141
+ onExpired?: (session) => void;
142
+ onError?: (error: ZippyPayError) => void;
143
+ onClose?: () => void;
144
+ };
145
+ ```
146
+
147
+ **Instance methods:** `open()`, `close()` (modal), `destroy()`
148
+
149
+ ### Theming
150
+
151
+ | Token | CSS variable | Default |
152
+ |---|---|---|
153
+ | `primary` | `--zippy-primary` | `#3f63f3` |
154
+ | `primaryForeground` | `--zippy-primary-foreground` | `#ffffff` |
155
+ | `background` | `--zippy-background` | `#ffffff` |
156
+ | `surface` | `--zippy-surface` | `#f8fafc` |
157
+ | `text` | `--zippy-text` | `#0f172a` |
158
+ | `textMuted` | `--zippy-text-muted` | `#64748b` |
159
+ | `border` | `--zippy-border` | `#e2e8f0` |
160
+ | `borderRadius` | `--zippy-radius` | `20px` |
161
+ | `fontFamily` | `--zippy-font-family` | DM Sans stack |
162
+ | `mode` | — | `light` \| `dark` \| `auto` |
163
+
164
+ Web component: use `theme-primary`, `theme-mode`, etc.
165
+
166
+ ---
167
+
168
+ ## Checkout flow
169
+
170
+ 1. **Choose method** — QR scan or manual Zippy ID (one-way; cannot switch)
171
+ 2. **QR** — `POST …/checkout/start { method: "QR" }` → show QR + pay timer → poll until paid
172
+ 3. **Manual** — verify ID → attach payer → pay timer → poll until paid
173
+
174
+ Display the **amount from the GET session response**, not from your page props.
175
+
176
+ Three time boundaries: `expiresAt` (idle), `paymentExpiryAt` (pay window), `absoluteExpiresAt` (hard max).
177
+
178
+ ---
179
+
180
+ ## Web component events
181
+
182
+ | Event | When |
183
+ |---|---|
184
+ | `zippy-ready` | Session loaded |
185
+ | `zippy-session-update` | Poll update |
186
+ | `zippy-complete` | Payment succeeded |
187
+ | `zippy-expired` | Session expired |
188
+ | `zippy-error` | Terminal / API error |
189
+ | `zippy-close` | Modal closed |
190
+
191
+ ---
192
+
193
+ ## Framework notes
194
+
195
+ ### React
196
+
197
+ ```tsx
198
+ 'use client';
199
+
200
+ import { useEffect, useRef } from 'react';
201
+ import '@zippypay/checkout';
202
+
203
+ export function ZippyCheckout({ sessionId, clientToken }: Props) {
204
+ const ref = useRef<HTMLElement & { clientToken: string }>(null);
205
+
206
+ useEffect(() => {
207
+ const el = ref.current;
208
+ if (!el) return;
209
+ el.clientToken = clientToken;
210
+ const onComplete = (e: Event) => console.log((e as CustomEvent).detail);
211
+ el.addEventListener('zippy-complete', onComplete);
212
+ return () => el.removeEventListener('zippy-complete', onComplete);
213
+ }, [clientToken]);
214
+
215
+ return (
216
+ <zippy-pay-checkout
217
+ ref={ref}
218
+ environment="production"
219
+ session-id={sessionId}
220
+ mode="inline"
221
+ />
222
+ );
223
+ }
224
+ ```
225
+
226
+ Or imperative: `ZippyPay.create({ container: ref.current, … })` and `destroy()` on unmount.
227
+
228
+ ### Vue 3
229
+
230
+ Import `@zippypay/checkout`, set `el.clientToken` in `onMounted`, listen for `@zippy-complete`.
231
+
232
+ ### Angular
233
+
234
+ Import the package, use `CUSTOM_ELEMENTS_SCHEMA`, set `nativeElement.clientToken` in `ngAfterViewInit`.
235
+
236
+ ---
237
+
238
+ ## API authentication (SDK)
239
+
240
+ | Header | Value |
241
+ |---|---|
242
+ | `X-Payment-Session-Token` | `zps_…` (preferred) |
243
+ | `Authorization` | `Bearer zps_…` (fallback) |
244
+
245
+ Errors are RFC 7807 `application/problem+json`, surfaced as `ZippyPayError` with `code`, `status`, and `detail`.
246
+
247
+ On **401**: treat the token as invalid; create a new session — do not retry blindly.
248
+
249
+ ---
250
+
251
+ ## Security checklist
252
+
253
+ 1. Never bundle `X-Api-Key` in browser code.
254
+ 2. Keep `clientToken` in memory only.
255
+ 3. Encode only `sessionId` in QR / deep links — never the client token.
256
+ 4. Display amount from GET session, not merchant page props.
257
+ 5. Ensure your Zippy API environment allows your site origin in CORS for browser calls.
258
+
259
+ ---
260
+
261
+ ## Requirements
262
+
263
+ - Modern browsers with ES2020, `fetch`, Shadow DOM, and Custom Elements
264
+ - Node.js ≥ 18 for local development / bundlers
265
+
266
+ ---
267
+
268
+ ## License
269
+
270
+ MIT © Imagine Innovation Limited