@mygigsters/card-sdk 1.0.1 → 1.0.3

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 CHANGED
@@ -1,646 +1,519 @@
1
- # @mygigsters/card-sdk
2
-
3
- > Embed a secure, single-step card saving form into any web application — powered by Airwallex Payment Elements.
4
-
5
- [![npm version](https://img.shields.io/npm/v/@mygigsters/card-sdk)](https://www.npmjs.com/package/@mygigsters/card-sdk)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
7
-
8
- ---
9
-
10
- ## Table of Contents
11
-
12
- - [Overview](#overview)
13
- - [Features](#features)
14
- - [Requirements](#requirements)
15
- - [Installation](#installation)
16
- - [Get Client Info (API)](#get-client-info-api)
17
- - [Quick Start](#quick-start)
18
- - [Module Formats](#module-formats)
19
- - [Usage Examples](#usage-examples)
20
- - [ES Modules (Recommended)](#es-modules-recommended)
21
- - [CommonJS](#commonjs)
22
- - [CDN / Browser Script Tag](#cdn--browser-script-tag)
23
- - [React (useEffect)](#react-useeffect)
24
- - [Configuration Reference](#configuration-reference)
25
- - [API Reference](#api-reference)
26
- - [init(config)](#initconfig)
27
- - [show()](#show)
28
- - [hide()](#hide)
29
- - [setTheme(mode)](#setthememode)
30
- - [destroy()](#destroy)
31
- - [getInstance()](#getinstance-cdn-only)
32
- - [Success Response](#success-response)
33
- - [Callback Reference](#callback-reference)
34
- - [Environments](#environments)
35
- - [Theming](#theming)
36
- - [Error Handling](#error-handling)
37
- - [Security](#security)
38
- - [TypeScript](#typescript)
39
- - [Troubleshooting](#troubleshooting)
40
- - [Changelog](#changelog)
41
-
42
- ---
43
-
44
- ## Overview
45
-
46
- The **MyGigsters Card SDK** renders a secure, self-contained modal that lets your customers save their payment card in a single step. The SDK:
47
-
48
- - Authenticates with the MyGigsters backend using your API credentials.
49
- - Embeds an **Airwallex Payment Element** inside a **closed Shadow DOM** — your host page styles never leak in and vice versa.
50
- - Returns a `paymentConsentId` on success — no raw card data ever touches your server.
51
-
52
- ---
53
-
54
- ## Features
55
-
56
- - **Airwallex-powered card tokenization** — PCI-compliant card element embedded in an isolated iframe.
57
- - **Shadow DOM isolation** — No CSS conflicts with your app.
58
- - **Light & Dark themes** — Toggle at init time or at runtime.
59
- - **Zero React dependency** — Works in any JavaScript app (React, Vue, Angular, plain HTML).
60
- - **Multiple module formats** — ESM, CJS, CDN (UMD).
61
- - **TypeScript** — Full type definitions included (`dist/index.d.ts`).
62
- - **Webhook support** — Optionally POST the success payload to your own endpoint.
63
-
64
- ---
65
-
66
- ## Requirements
67
-
68
- | Requirement | Version |
69
- |-------------|---------|
70
- | Browser | Chrome 80+, Firefox 78+, Safari 14+, Edge 80+ |
71
- | Node.js (build/dev only) | ≥ 14.0.0 |
72
- | React (optional) | ≥ 18.0.0 |
73
-
74
- > **Note:** The SDK must be served over **HTTPS** (or `localhost` for development). Airwallex payment elements require a secure context.
75
-
76
- ---
77
-
78
- ## Installation
79
-
80
- ```bash
81
- # npm
82
- npm install @mygigsters/card-sdk
83
-
84
- # yarn
85
- yarn add @mygigsters/card-sdk
86
-
87
- # pnpm
88
- pnpm add @mygigsters/card-sdk
89
- ```
90
-
91
- ---
92
-
93
- ## Get Client Info (API)
94
-
95
- Before initializing the Card SDK, you need your **`myGigsterId`** and the customer's **`customerId`**. Call the `GET /api/v1/client/me` endpoint using your API credentials (HTTP Basic Auth `uuid:randomPasswordShowOnce`) to retrieve your client account profile and `myGigsterId`. Obtain the `customerId` by creating a customer via `POST /api/v1/customer`.
96
-
97
- ### Endpoint
98
-
99
- ```http
100
- GET /api/v1/client/me
101
- ```
102
-
103
- | Environment | URL |
104
- |-------------|-----|
105
- | Demo / QA | `https://qa-payments.mygigsters.com.au/api/v1/client/me` |
106
- | Production | `https://prod-payments.mygigsters.com.au/api/v1/client/me` |
107
-
108
- ### Headers
109
-
110
- ```http
111
- Content-Type: application/json
112
- x-api-version: 1.2
113
- Authorization: Basic {base64_encoded_combination of uuid:randomPasswordShowOnce}
114
- ```
115
-
116
- ### Example Request
117
-
118
- ```bash
119
- curl --location 'https://qa-payments.mygigsters.com.au/api/v1/client/me' \
120
- --header 'Content-Type: application/json' \
121
- --header 'x-api-version: 1.2' \
122
- --header 'Authorization: Basic MGI3YjFiMTctOTJhZC00Yjc4LWFhN2ItNjI2ODBhOWUzOGExOkt1aFNEa3NCVmFXQFNPQyhZQCFh'
123
- ```
124
-
125
- ### Example Response (200 OK)
126
-
127
- ```json
128
- {
129
- "success": true,
130
- "status": 200,
131
- "data": {
132
- "id": 1,
133
- "myGigsterId": "4407c3ba-10c9-4822-9f51-c2c96692f8d7",
134
- "fullName": "Nitesh ",
135
- "email": "nitesh@megamindcreations.com",
136
- "businessName": "MG Nitesh Agrwal Deswal",
137
- "address": null,
138
- "acn": "123456789",
139
- "phone": "9876543210"
140
- }
141
- }
142
- ```
143
-
144
- ### Response Fields for Card SDK
145
-
146
- | Field | Type | Description |
147
- |-------|------|-------------|
148
- | `data.myGigsterId` | `string` | **Required by SDK:** Your unique MyGigsters client account identifier. Pass as `myGigsterId` in `init()`. |
149
- | `data.fullName` | `string` | Registered full name of the client. |
150
- | `data.email` | `string` | Registered contact email address. |
151
- | `data.businessName` | `string` | Registered business or entity name. |
152
- | `data.phone` | `string` | Contact phone number. |
153
- | `data.acn` | `string` | Australian Company Number (if applicable). |
154
-
155
- ---
156
-
157
- ## Quick Start
158
-
159
- **1. Add a container element in your HTML:**
160
-
161
- ```html
162
- <div id="mygigsters-card"></div>
163
- ```
164
-
165
- **2. Initialize the SDK:**
166
-
167
- ```javascript
168
- import { MygigstersCardSDK } from '@mygigsters/card-sdk';
169
-
170
- const sdk = new MygigstersCardSDK();
171
-
172
- await sdk.init({
173
- apiKey: 'your_api_key',
174
- apiSecret: 'your_api_secret',
175
- customerId: 'cus_hkdmcdjshhknkk4lc2t',
176
- myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
177
- containerId: 'mygigsters-card',
178
- env: 'demo',
179
- onSuccess: (res) => console.log('Card saved! Consent ID:', res.paymentConsentId),
180
- onError: (err) => console.error('Error:', err.message),
181
- onClose: () => console.log('Modal closed'),
182
- });
183
- ```
184
-
185
- The card modal opens automatically after `init()` resolves.
186
-
187
- ---
188
-
189
- ## Module Formats
190
-
191
- | Format | Path | Use Case |
192
- |--------|------|----------|
193
- | **ESM** | `dist/index.esm.js` | Modern bundlers (Vite, Webpack 5, Rollup) |
194
- | **CommonJS** | `dist/index.cjs.js` | Node.js / `require()` environments |
195
- | **CDN (UMD)** | `cdn/card-sdk-entry.js` | Browser `<script>` tag / self-hosted CDN |
196
- | **TypeScript** | `dist/index.d.ts` | Type definitions |
197
-
198
- ---
199
-
200
- ## Usage Examples
201
-
202
- ### ES Modules (Recommended)
203
-
204
- ```javascript
205
- import { MygigstersCardSDK } from '@mygigsters/card-sdk';
206
-
207
- const sdk = new MygigstersCardSDK();
208
-
209
- await sdk.init({
210
- apiKey: 'your_api_key',
211
- apiSecret: 'your_api_secret',
212
- customerId: 'cus_hkdmcdjshhknkk4lc2t',
213
- myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
214
- containerId: 'mygigsters-card',
215
- env: 'demo', // 'demo' | 'prod'
216
- mode: 'dark', // optional: 'dark' (default) | 'light'
217
- onSuccess: (res) => {
218
- console.log('✅ Card saved:', res.paymentConsentId);
219
- // Pass res.paymentConsentId to the MyGigsters API to charge the customer
220
- },
221
- onError: (err) => {
222
- console.error('❌ Error:', err.message);
223
- },
224
- onClose: () => {
225
- console.log('Modal closed by user');
226
- },
227
- });
228
- ```
229
-
230
- ---
231
-
232
- ### CommonJS
233
-
234
- ```javascript
235
- const { MygigstersCardSDK } = require('@mygigsters/card-sdk');
236
-
237
- const sdk = new MygigstersCardSDK();
238
-
239
- sdk.init({
240
- apiKey: 'your_api_key',
241
- apiSecret: 'your_api_secret',
242
- customerId: 'cus_hkdmcdjshhknkk4lc2t',
243
- myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
244
- containerId: 'mygigsters-card',
245
- onSuccess: (res) => console.log('Card saved:', res.paymentConsentId),
246
- onError: (err) => console.error('Error:', err.message),
247
- onClose: () => console.log('Closed'),
248
- });
249
- ```
250
-
251
- ---
252
-
253
- ### CDN / Browser Script Tag
254
-
255
- ```html
256
- <!DOCTYPE html>
257
- <html lang="en">
258
- <head>
259
- <meta charset="UTF-8" />
260
- <title>My App</title>
261
- </head>
262
- <body>
263
-
264
- <!-- 1. Mount target -->
265
- <div id="mygigsters-card"></div>
266
-
267
- <!-- 2. Trigger button (optional) -->
268
- <button onclick="openCardForm()">Save Card</button>
269
-
270
- <!-- 3. Load the SDK -->
271
- <script src="https://qa-payments.mygigsters.com.au/card-sdk-entry.js"></script>
272
-
273
- <script>
274
- async function openCardForm() {
275
- await MygigstersCard.init({
276
- apiKey: 'your_api_key',
277
- apiSecret: 'your_api_secret',
278
- customerId: 'cus_hkdmcdjshhknkk4lc2t',
279
- myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
280
- containerId: 'mygigsters-card',
281
- env: 'demo',
282
- mode: 'dark',
283
- onSuccess: (res) => console.log('✅ Card saved:', res.paymentConsentId),
284
- onError: (err) => console.error('❌ Error:', err.message),
285
- onClose: () => console.log('Closed'),
286
- });
287
- }
288
- </script>
289
-
290
- </body>
291
- </html>
292
- ```
293
-
294
- > **CDN global:** The CDN build exposes `window.MygigstersCard` (not `MygigstersCardSDK`).
295
-
296
- ---
297
-
298
- ### React (useEffect)
299
-
300
- ```jsx
301
- import { useEffect } from 'react';
302
- import { MygigstersCardSDK } from '@mygigsters/card-sdk';
303
-
304
- function SaveCardForm({ customerId, myGigsterId }) {
305
- useEffect(() => {
306
- const sdk = new MygigstersCardSDK();
307
- sdk.init({
308
- apiKey: 'your_api_key',
309
- apiSecret: 'your_api_secret',
310
- customerId,
311
- myGigsterId,
312
- containerId: 'mygigsters-card',
313
- env: 'demo',
314
- mode: 'dark',
315
- onSuccess: (res) => console.log('Card saved!', res.paymentConsentId),
316
- onError: (err) => console.error(err),
317
- onClose: () => console.log('Closed'),
318
- });
319
-
320
- // Cleanup on unmount
321
- return () => sdk.destroy();
322
- }, [customerId, myGigsterId]);
323
-
324
- return <div id="mygigsters-card" />;
325
- }
326
- ```
327
-
328
- ---
329
-
330
- ## Configuration Reference
331
-
332
- Pass these options to `init()`:
333
-
334
- | Parameter | Type | Required | Default | Description |
335
- |-----------|------|:--------:|---------|-------------|
336
- | `apiKey` | `string` | ✅ | — | UUID-style API key issued by MyGigsters |
337
- | `apiSecret` | `string` | ✅ | — | One-time API secret (shown once at creation) |
338
- | `customerId` | `string` | ✅ | — | MyGigsters customer ID for whom the card is saved (from `POST /api/v1/customer`) |
339
- | `myGigsterId` | `string` | ✅ | — | Unique client account identifier (from `GET /api/v1/client/me`) |
340
- | `containerId` | `string` | ✅ | — | `id` of the DOM element to mount the SDK into |
341
- | `env` | `'demo' \| 'prod'` | ❌ | `'demo'` | Target environment |
342
- | `baseUrl` | `string` | ❌ | — | Custom base URL override (for self-hosted deployments) |
343
- | `mode` | `'light' \| 'dark'` | ❌ | `'dark'` | UI color theme |
344
- | `onSuccess` | `(res) => void` | ❌ | no-op | Fired after the card is saved successfully |
345
- | `onError` | `(error) => void` | ❌ | `console.error` | Fired on any SDK or network error |
346
- | `onClose` | `() => void` | ❌ | no-op | Fired when the user closes the modal |
347
- | `webhookUrl` | `string` | ❌ | — | URL to POST the success payload to automatically |
348
- | `onWebhook` | `(payload) => void` | ❌ | no-op | JS callback for receiving the webhook payload in real-time |
349
-
350
- ---
351
-
352
- ## API Reference
353
-
354
- ### `init(config)`
355
-
356
- Authenticates with the MyGigsters backend, builds the Shadow DOM modal, initializes the Airwallex card element, and automatically calls `show()`.
357
-
358
- ```javascript
359
- import { MygigstersCardSDK } from '@mygigsters/card-sdk';
360
-
361
- const sdk = new MygigstersCardSDK();
362
- await sdk.init(config);
363
-
364
- // or, via CDN:
365
- await MygigstersCard.init(config);
366
- ```
367
-
368
- - **Returns:** `Promise<void>`
369
- - **Throws:** if `apiKey`, `apiSecret`, `customerId`, `myGigsterId`, or `containerId` are missing; if the container element is not found; or if authentication fails.
370
- - Each SDK instance maintains its own internal state.
371
- - CDN builds expose a singleton instance automatically.
372
-
373
- ---
374
-
375
- ### `show()`
376
-
377
- Shows the card modal. Called automatically by `init()`, but can be called again after `hide()`.
378
-
379
- ```javascript
380
- sdk.show();
381
- ```
382
-
383
- Throws `Error: SDK not initialized. Call init() first.` if called before `init()`.
384
-
385
- ---
386
-
387
- ### `hide()`
388
-
389
- Hides the modal without destroying the SDK instance. The Airwallex card element state is preserved.
390
-
391
- ```javascript
392
- sdk.hide();
393
- ```
394
-
395
- ---
396
-
397
- ### `setTheme(mode)`
398
-
399
- Switch the UI theme at runtime — no need to re-initialize.
400
-
401
- ```javascript
402
- sdk.setTheme('dark'); // or 'light'
403
- ```
404
-
405
- ```javascript
406
- // Example: sync with OS preference
407
- const mq = window.matchMedia('(prefers-color-scheme: dark)');
408
- mq.addEventListener('change', (e) => {
409
- sdk.setTheme(e.matches ? 'dark' : 'light');
410
- });
411
- ```
412
-
413
- ---
414
-
415
- ### `destroy()`
416
-
417
- Removes the modal from the DOM and resets all internal state. Call this when navigating away or unmounting the host component.
418
-
419
- ```javascript
420
- sdk.destroy();
421
- ```
422
-
423
- ---
424
-
425
- ### `getInstance()` *(CDN only)*
426
-
427
- Returns the underlying SDK instance for advanced use.
428
-
429
- ```javascript
430
- const sdk = MygigstersCard.getInstance();
431
- ```
432
-
433
- ---
434
-
435
- ## Success Response
436
-
437
- The `onSuccess` callback receives a `CardSuccessResponse` object:
438
-
439
- ```javascript
440
- onSuccess: (res) => {
441
- // res.paymentConsentId — Airwallex payment consent ID
442
- // Pass this to the MyGigsters API when creating a charge
443
- // res.customerId — Customer ID associated with this consent
444
- // res.myGigsterId — MyGigster ID associated with this consent
445
- // res.timestamp — ISO-8601 timestamp of the save event
446
- // res.event — 'card.saved'
447
-
448
- console.log('Payment Consent ID:', res.paymentConsentId);
449
- }
450
- ```
451
-
452
- ---
453
-
454
- ## Callback Reference
455
-
456
- ### `onSuccess(res)`
457
-
458
- Called after the card is saved and the Airwallex element confirms the payment consent.
459
-
460
- ```javascript
461
- onSuccess: (res) => {
462
- // res.paymentConsentId → use to charge the customer via Payment Intents API
463
- console.log('Saved!', res);
464
- }
465
- ```
466
-
467
- ### `onError(error)`
468
-
469
- Called when any error occurs — authentication failures, Airwallex element errors, or server rejections.
470
-
471
- ```javascript
472
- onError: (error) => {
473
- // error is a standard JS Error object
474
- console.error(error.message);
475
- }
476
- ```
477
-
478
- ### `onClose()`
479
-
480
- Called when the user dismisses the modal (close button ×, backdrop click, or Escape key).
481
-
482
- ```javascript
483
- onClose: () => {
484
- // Re-show a trigger button, update UI state, etc.
485
- }
486
- ```
487
-
488
- ---
489
-
490
- ## Environments
491
-
492
- | Environment | SDK Script URL | `env` Value |
493
- |-------------|----------------|-------------|
494
- | Demo / QA | `https://qa-payments.mygigsters.com.au/card-sdk-entry.js` | `"demo"` |
495
- | Production | `https://prod-payments.mygigsters.com.au/card-sdk-entry.js` | `"prod"` |
496
-
497
- ---
498
-
499
- ## Theming
500
-
501
- Set `mode: 'light'` or `mode: 'dark'` in the config object (defaults to `'dark'`).
502
-
503
- You can also switch themes after initialization using [`setTheme()`](#setthememode).
504
-
505
- ```javascript
506
- // Toggle dark mode on a button click
507
- const sdk = new MygigstersCardSDK();
508
-
509
- document.getElementById('toggle-theme').addEventListener('click', () => {
510
- sdk.setTheme(
511
- document.body.classList.toggle('dark') ? 'dark' : 'light'
512
- );
513
- });
514
- ```
515
-
516
- ---
517
-
518
- ## Error Handling
519
-
520
- Wrap `init()` in a `try/catch` to handle initialization errors:
521
-
522
- ```javascript
523
- try {
524
- const sdk = new MygigstersCardSDK();
525
-
526
- await sdk.init({ ... });
527
- } catch (err) {
528
- // Common errors:
529
- // "apiKey is required"
530
- // "customerId is required"
531
- // "myGigsterId is required"
532
- // "Container element with ID '...' not found"
533
- // "Invalid API credentials: Please check your apiKey and apiSecret."
534
- // "Authentication failed: ..."
535
- console.error('SDK failed to initialize:', err.message);
536
- }
537
- ```
538
-
539
- Runtime errors (Airwallex element failures, network issues, server rejections) are surfaced via the `onError` callback and displayed inline in the modal.
540
-
541
- ---
542
-
543
- ## Security
544
-
545
- - **Credentials are never stored** in `localStorage` or `sessionStorage`. The auth token lives in memory only.
546
- - **Authentication** uses HTTP Basic Auth (`base64(apiKey:apiSecret)`) over HTTPS. Never expose your `apiSecret` in client-side code committed to a public repository — use environment variables or a backend-for-frontend pattern.
547
- - **PCI-compliant card handling** — Raw card data is handled exclusively by Airwallex's PCI-certified payment element embedded inside an Airwallex-controlled iframe. Your server never receives raw card numbers or CVVs.
548
- - **Shadow DOM** (`mode: 'closed'`) prevents host-page scripts from reaching into the SDK's DOM.
549
-
550
- ---
551
-
552
- ## TypeScript
553
-
554
- Type definitions are included at `dist/index.d.ts`.
555
-
556
- Import the SDK and its types:
557
-
558
- ```tsx
559
- import { useEffect } from 'react';
560
-
561
- import { MygigstersCardSDK } from '@mygigsters/card-sdk';
562
-
563
- import type {
564
- MygigstersCardConfig,
565
- CardSuccessResponse,
566
- } from '@mygigsters/card-sdk';
567
-
568
- function SaveCardForm() {
569
- useEffect(() => {
570
- const initializeSDK = async () => {
571
- const config: MygigstersCardConfig = {
572
- apiKey: 'YOUR_API_KEY',
573
- apiSecret: 'YOUR_API_SECRET',
574
-
575
- customerId: 'cus_hkdmcdjshhknkk4lc2t',
576
- myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
577
-
578
- containerId: 'mygigsters-card',
579
-
580
- env: 'demo',
581
- mode: 'dark',
582
-
583
- onSuccess: (res: CardSuccessResponse) => {
584
- console.log('Card saved!');
585
- console.log('Consent ID:', res.paymentConsentId);
586
- },
587
-
588
- onError: (err) => {
589
- console.error('Card SDK error:', err);
590
- },
591
-
592
- onClose: () => {
593
- console.log('Card form closed');
594
- },
595
- };
596
-
597
- const sdk = new MygigstersCardSDK();
598
-
599
- await sdk.init(config);
600
- };
601
-
602
- initializeSDK();
603
- }, []);
604
-
605
- return <div id="mygigsters-card" />;
606
- }
607
-
608
- export default SaveCardForm;
609
-
610
- ---
611
-
612
- ## Troubleshooting
613
-
614
- **`Container element with ID '...' not found`**
615
- Make sure the target `<div>` exists in the DOM before calling `init()`. If you are using a framework, call `init()` after the component mounts (e.g., inside `useEffect` or `mounted()`).
616
-
617
- **`Invalid API credentials`**
618
- Double-check that `apiKey` and `apiSecret` match the values shown in your MyGigsters dashboard. The secret is shown only once — if lost, generate a new one.
619
-
620
- **`myGigsterId is required`**
621
- Make sure `myGigsterId` is provided in the configuration object passed to `init()`. You can retrieve your `myGigsterId` by calling `GET /api/v1/client/me`. To obtain `customerId`, create a customer via `POST /api/v1/customer`.
622
-
623
- **Airwallex card element fails to load**
624
- Ensure your page is served over **HTTPS**. Also verify that your Airwallex account has card tokenization enabled, and that the `env` setting matches the environment your account credentials belong to.
625
-
626
- **Card modal appears behind other elements**
627
- The modal is mounted on `<body>` with `z-index: 2147483647`. If another element on your page has a higher stacking context, adjust accordingly. Because the SDK uses Shadow DOM, your global CSS reset will not affect it.
628
-
629
- ---
630
-
631
- ## Changelog
632
-
633
- ### 1.0.0
634
- - Initial public release
635
- - Single-step card saving via Airwallex Payment Elements
636
- - Shadow DOM isolation
637
- - Light / dark theming with runtime `setTheme()` support
638
- - ESM, CJS, and CDN (UMD) builds
639
- - TypeScript definitions
640
- - Webhook support via `webhookUrl` and `onWebhook` options
641
-
642
- ---
643
-
644
- ## License
645
-
646
- MIT © [MyGigsters](https://www.mygigsters.com.au)
1
+ # @mygigsters/card-sdk
2
+
3
+ > Embed a secure, single-step card saving form into any web application — powered by Airwallex Payment Elements.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@mygigsters/card-sdk)](https://www.npmjs.com/package/@mygigsters/card-sdk)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ---
9
+
10
+ ## Table of Contents
11
+
12
+ - [Overview](#overview)
13
+ - [Features](#features)
14
+ - [Requirements](#requirements)
15
+ - [Installation](#installation)
16
+ - [Get Client Info (API)](#get-client-info-api)
17
+ - [Quick Start](#quick-start)
18
+ - [Module Formats](#module-formats)
19
+ - [Usage Examples](#usage-examples)
20
+ - [ES Modules (Recommended)](#es-modules-recommended)
21
+ - [CommonJS](#commonjs)
22
+ - [CDN / Browser Script Tag](#cdn--browser-script-tag)
23
+ - [React (useEffect)](#react-useeffect)
24
+ - [Configuration Reference](#configuration-reference)
25
+ - [API Reference](#api-reference)
26
+ - [init(config)](#initconfig)
27
+ - [show()](#show)
28
+ - [hide()](#hide)
29
+ - [setTheme(mode)](#setthememode)
30
+ - [destroy()](#destroy)
31
+ - [getInstance()](#getinstance-cdn-only)
32
+ - [Success Response](#success-response)
33
+ - [Callback Reference](#callback-reference)
34
+ - [Environments](#environments)
35
+ - [Theming](#theming)
36
+ - [Error Handling](#error-handling)
37
+ - [Security](#security)
38
+ - [TypeScript](#typescript)
39
+ - [Troubleshooting](#troubleshooting)
40
+ - [Changelog](#changelog)
41
+
42
+ ---
43
+
44
+ ## Overview
45
+
46
+ The **MyGigsters Card SDK** renders a secure, self-contained modal that lets your customers save their payment card in a single step. The SDK:
47
+
48
+ - Uses a **pre-fetched Bearer token** retrieved from `GET /api/v1/client/me` — no API keys or secrets are ever shared with or stored inside the SDK.
49
+ - Embeds an **Airwallex Payment Element** inside a **closed Shadow DOM** — your host page styles never leak in and vice versa.
50
+ - Returns a `paymentConsentId` on success — no raw card data ever touches your server.
51
+
52
+ ---
53
+
54
+ ## Features
55
+
56
+ - **Pre-fetched Token Auth** — Client pre-fetches a JWT token via `/client/me` before passing it to the SDK.
57
+ - **Airwallex-powered card tokenization** — PCI-compliant card element embedded in an isolated iframe.
58
+ - **Shadow DOM isolation** — No CSS conflicts with your app.
59
+ - **Light & Dark themes** — Toggle at init time or at runtime.
60
+ - **Zero React dependency** — Works in any JavaScript app (React, Vue, Angular, plain HTML).
61
+ - **Multiple module formats** — ESM, CJS, CDN (UMD).
62
+ - **TypeScript** — Full type definitions included (`dist/index.d.ts`).
63
+ - **Webhook support** — Optionally POST the success payload to your own endpoint.
64
+
65
+ ---
66
+
67
+ ## Requirements
68
+
69
+ | Requirement | Version |
70
+ |-------------|---------|
71
+ | Browser | Chrome 80+, Firefox 78+, Safari 14+, Edge 80+ |
72
+ | Node.js (build/dev only) | ≥ 14.0.0 |
73
+ | React (optional) | ≥ 18.0.0 |
74
+
75
+ > **Note:** The SDK must be served over **HTTPS** (or `localhost` for development). Airwallex payment elements require a secure context.
76
+
77
+ ---
78
+
79
+ ## Installation
80
+
81
+ ```bash
82
+ # npm
83
+ npm install @mygigsters/card-sdk
84
+
85
+ # yarn
86
+ yarn add @mygigsters/card-sdk
87
+
88
+ # pnpm
89
+ pnpm add @mygigsters/card-sdk
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Get Client Info (API)
95
+
96
+ Before initializing the Card SDK, you need your Bearer **`token`**, **`myGigsterId`**, and the customer's **`customerId`**. Call `GET /api/v1/client/me` using HTTP Basic Auth (`uuid:randomPasswordShowOnce`) to retrieve your `token` and `myGigsterId`. Obtain `customerId` by creating a customer via `POST /api/v1/customer`.
97
+
98
+ ### Endpoint
99
+
100
+ ```http
101
+ GET /api/v1/client/me
102
+ ```
103
+
104
+ | Environment | URL |
105
+ |-------------|-----|
106
+ | Demo / QA | `https://qa-payments.mygigsters.com.au/api/v1/client/me` |
107
+ | Production | `https://prod-payments.mygigsters.com.au/api/v1/client/me` |
108
+
109
+ ### Headers
110
+
111
+ ```http
112
+ Content-Type: application/json
113
+ x-api-version: 1.1
114
+ Authorization: Basic {base64_encoded_combination of uuid:randomPasswordShowOnce}
115
+ ```
116
+
117
+ ### Example Request
118
+
119
+ ```bash
120
+ curl --location 'https://qa-payments.mygigsters.com.au/api/v1/client/me' \
121
+ --header 'Content-Type: application/json' \
122
+ --header 'x-api-version: 1.1' \
123
+ --header 'Authorization: Basic MGI3YjFiMTctOTJhZC00Yjc4LWFhN2ItNjI2ODBhOWUzOGExOkt1aFNEa3NCVmFXQFNPQyhZQCFh'
124
+ ```
125
+
126
+ ### Example Response (200 OK)
127
+
128
+ ```json
129
+ {
130
+ "success": true,
131
+ "status": 200,
132
+ "data": {
133
+ "id": 1,
134
+ "myGigsterId": "4407c3ba-10c9-4822-9f51-c2c96692f8d7",
135
+ "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
136
+ "fullName": "Nitesh ",
137
+ "email": "nitesh@megamindcreations.com",
138
+ "businessName": "MG Nitesh Agrwal Deswal",
139
+ "address": null,
140
+ "acn": "123456789",
141
+ "phone": "9876543210"
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Response Fields for Card SDK
147
+
148
+ | Field | Type | Description |
149
+ |-------|------|-------------|
150
+ | `data.token` | `string` | **Required by SDK:** Bearer JWT token required to authenticate SDK requests. Pass as `token` in `init()`. |
151
+ | `data.myGigsterId` | `string` | **Required by SDK:** Your unique MyGigsters client account identifier. Pass as `myGigsterId` in `init()`. |
152
+ | `data.fullName` | `string` | Registered full name of the client. |
153
+ | `data.email` | `string` | Registered contact email address. |
154
+ | `data.businessName` | `string` | Registered business or entity name. |
155
+
156
+ ---
157
+
158
+ ## Quick Start
159
+
160
+ **1. Add a container element in your HTML:**
161
+
162
+ ```html
163
+ <div id="mygigsters-card"></div>
164
+ ```
165
+
166
+ **2. Initialize the SDK:**
167
+
168
+ ```javascript
169
+ import { MygigstersCardSDK } from '@mygigsters/card-sdk';
170
+
171
+ const sdk = new MygigstersCardSDK();
172
+
173
+ await sdk.init({
174
+ token: 'your_jwt_token', // from GET /api/v1/client/me
175
+ customerId: 'cus_hkdmcdjshhknkk4lc2t',
176
+ myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
177
+ containerId: 'mygigsters-card',
178
+ env: 'demo',
179
+ onSuccess: (res) => console.log('Card saved! Consent ID:', res.paymentConsentId),
180
+ onError: (err) => console.error('Error:', err.message),
181
+ onClose: () => console.log('Modal closed'),
182
+ });
183
+ ```
184
+
185
+ The card modal opens automatically after `init()` resolves.
186
+
187
+ ---
188
+
189
+ ## Module Formats
190
+
191
+ | Format | Path | Use Case |
192
+ |--------|------|----------|
193
+ | **ESM** | `dist/index.esm.js` | Modern bundlers (Vite, Webpack 5, Rollup) |
194
+ | **CommonJS** | `dist/index.cjs.js` | Node.js / `require()` environments |
195
+ | **CDN (UMD)** | `cdn/card-sdk-entry.js` | Browser `<script>` tag / self-hosted CDN |
196
+ | **TypeScript** | `dist/index.d.ts` | Type definitions |
197
+
198
+ ---
199
+
200
+ ## Usage Examples
201
+
202
+ ### ES Modules (Recommended)
203
+
204
+ ```javascript
205
+ import { MygigstersCardSDK } from '@mygigsters/card-sdk';
206
+
207
+ const sdk = new MygigstersCardSDK();
208
+
209
+ await sdk.init({
210
+ token: 'your_jwt_token', // from GET /api/v1/client/me
211
+ customerId: 'cus_hkdmcdjshhknkk4lc2t',
212
+ myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
213
+ containerId: 'mygigsters-card',
214
+ env: 'demo', // 'demo' | 'prod'
215
+ mode: 'dark', // optional: 'dark' (default) | 'light'
216
+ onSuccess: (res) => {
217
+ console.log('✅ Card saved:', res.paymentConsentId);
218
+ // Pass res.paymentConsentId to the MyGigsters API to charge the customer
219
+ },
220
+ onError: (err) => {
221
+ console.error('❌ Error:', err.message);
222
+ },
223
+ onClose: () => {
224
+ console.log('Modal closed by user');
225
+ },
226
+ });
227
+ ```
228
+
229
+ ---
230
+
231
+ ### CommonJS
232
+
233
+ ```javascript
234
+ const { MygigstersCardSDK } = require('@mygigsters/card-sdk');
235
+
236
+ const sdk = new MygigstersCardSDK();
237
+
238
+ sdk.init({
239
+ token: 'your_jwt_token', // from GET /api/v1/client/me
240
+ customerId: 'cus_hkdmcdjshhknkk4lc2t',
241
+ myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
242
+ containerId: 'mygigsters-card',
243
+ onSuccess: (res) => console.log('Card saved:', res.paymentConsentId),
244
+ onError: (err) => console.error('Error:', err.message),
245
+ onClose: () => console.log('Closed'),
246
+ });
247
+ ```
248
+
249
+ ---
250
+
251
+ ### CDN / Browser Script Tag
252
+
253
+ ```html
254
+ <!DOCTYPE html>
255
+ <html lang="en">
256
+ <head>
257
+ <meta charset="UTF-8" />
258
+ <title>My App</title>
259
+ </head>
260
+ <body>
261
+
262
+ <!-- 1. Mount target -->
263
+ <div id="mygigsters-card"></div>
264
+
265
+ <!-- 2. Load the SDK -->
266
+ <script src="https://qa-payments.mygigsters.com.au/card-sdk-entry.js"></script>
267
+
268
+ <script>
269
+ async function openCardForm(token) {
270
+ await MygigstersCard.init({
271
+ token: token, // from GET /api/v1/client/me
272
+ customerId: 'cus_hkdmcdjshhknkk4lc2t',
273
+ myGigsterId: '4407c3ba-10c9-4822-9f51-c2c96692f8d7',
274
+ containerId: 'mygigsters-card',
275
+ env: 'demo',
276
+ mode: 'dark',
277
+ onSuccess: (res) => console.log('✅ Card saved:', res.paymentConsentId),
278
+ onError: (err) => console.error('❌ Error:', err.message),
279
+ onClose: () => console.log('Closed'),
280
+ });
281
+ }
282
+ </script>
283
+
284
+ </body>
285
+ </html>
286
+ ```
287
+
288
+ > **CDN global:** The CDN build exposes `window.MygigstersCard` (not `MygigstersCardSDK`).
289
+
290
+ ---
291
+
292
+ ### React (useEffect)
293
+
294
+ ```jsx
295
+ import { useEffect } from 'react';
296
+ import { MygigstersCardSDK } from '@mygigsters/card-sdk';
297
+
298
+ function SaveCardForm({ token, customerId, myGigsterId }) {
299
+ useEffect(() => {
300
+ if (!token) return;
301
+ const sdk = new MygigstersCardSDK();
302
+ sdk.init({
303
+ token,
304
+ customerId,
305
+ myGigsterId,
306
+ containerId: 'mygigsters-card',
307
+ env: 'demo',
308
+ mode: 'dark',
309
+ onSuccess: (res) => console.log('Card saved!', res.paymentConsentId),
310
+ onError: (err) => console.error(err),
311
+ onClose: () => console.log('Closed'),
312
+ });
313
+
314
+ // Cleanup on unmount
315
+ return () => sdk.destroy();
316
+ }, [token, customerId, myGigsterId]);
317
+
318
+ return <div id="mygigsters-card" />;
319
+ }
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Configuration Reference
325
+
326
+ Pass these options to `init()`:
327
+
328
+ | Parameter | Type | Required | Default | Description |
329
+ |-----------|------|:--------:|---------|-------------|
330
+ | `token` | `string` | ✅ | — | Bearer JWT token obtained from `GET /api/v1/client/me` |
331
+ | `customerId` | `string` | ✅ | — | MyGigsters customer ID for whom the card is saved (from `POST /api/v1/customer`) |
332
+ | `myGigsterId` | `string` | ✅ | — | Unique client account identifier (from `GET /api/v1/client/me`) |
333
+ | `containerId` | `string` | ✅ | — | `id` of the DOM element to mount the SDK into |
334
+ | `env` | `'dev' \| 'demo' \| 'prod'` | ❌ | `'demo'` | Target environment |
335
+ | `baseUrl` | `string` | ❌ | — | Custom base URL override (for self-hosted deployments) |
336
+ | `mode` | `'light' \| 'dark'` | ❌ | `'dark'` | UI color theme |
337
+ | `onSuccess` | `(res) => void` | ❌ | no-op | Fired after the card is saved successfully |
338
+ | `onError` | `(error) => void` | ❌ | `console.error` | Fired on any SDK or network error |
339
+ | `onClose` | `() => void` | ❌ | no-op | Fired when the user closes the modal |
340
+ | `webhookUrl` | `string` | ❌ | — | URL to POST the success payload to automatically |
341
+ | `onWebhook` | `(payload) => void` | ❌ | no-op | JS callback for receiving the webhook payload in real-time |
342
+
343
+ ---
344
+
345
+ ## API Reference
346
+
347
+ ### `init(config)`
348
+
349
+ Validates configuration, builds the Shadow DOM modal, initializes the Airwallex card element, and automatically calls `show()`.
350
+
351
+ ```javascript
352
+ import { MygigstersCardSDK } from '@mygigsters/card-sdk';
353
+
354
+ const sdk = new MygigstersCardSDK();
355
+ await sdk.init(config);
356
+
357
+ // or, via CDN:
358
+ await MygigstersCard.init(config);
359
+ ```
360
+
361
+ - **Returns:** `Promise<void>`
362
+ - **Throws:** if `token`, `customerId`, `myGigsterId`, or `containerId` are missing; or if the container element is not found.
363
+
364
+ ---
365
+
366
+ ### `show()`
367
+
368
+ Shows the card modal. Called automatically by `init()`, but can be called again after `hide()`.
369
+
370
+ ```javascript
371
+ sdk.show();
372
+ ```
373
+
374
+ ---
375
+
376
+ ### `hide()`
377
+
378
+ Hides the modal without destroying the SDK instance. The Airwallex card element state is preserved.
379
+
380
+ ```javascript
381
+ sdk.hide();
382
+ ```
383
+
384
+ ---
385
+
386
+ ### `setTheme(mode)`
387
+
388
+ Switch the UI theme at runtime — no need to re-initialize.
389
+
390
+ ```javascript
391
+ sdk.setTheme('dark'); // or 'light'
392
+ ```
393
+
394
+ ---
395
+
396
+ ### `destroy()`
397
+
398
+ Removes the modal from the DOM and resets all internal state. Call this when navigating away or unmounting the host component.
399
+
400
+ ```javascript
401
+ sdk.destroy();
402
+ ```
403
+
404
+ ---
405
+
406
+ ## Success Response
407
+
408
+ The `onSuccess` callback receives a `CardSuccessResponse` object:
409
+
410
+ ```javascript
411
+ onSuccess: (res) => {
412
+ // res.paymentConsentId — Airwallex payment consent ID
413
+ // res.customerId — Customer ID associated with this consent
414
+ // res.myGigsterId — MyGigster ID associated with this consent
415
+ // res.timestamp — ISO-8601 timestamp of the save event
416
+ // res.event — 'card.saved'
417
+
418
+ console.log('Payment Consent ID:', res.paymentConsentId);
419
+ }
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Error Handling
425
+
426
+ Wrap `init()` in a `try/catch` to handle initialization errors:
427
+
428
+ ```javascript
429
+ try {
430
+ const sdk = new MygigstersCardSDK();
431
+ await sdk.init({
432
+ token: 'your_jwt_token',
433
+ customerId: 'cus_...',
434
+ myGigsterId: '4407c3ba-...',
435
+ containerId: 'mygigsters-card',
436
+ env: 'demo',
437
+ });
438
+ } catch (err) {
439
+ // Common errors:
440
+ // "token is required. Call GET /api/v1/client/me first..."
441
+ // "customerId is required"
442
+ // "myGigsterId is required"
443
+ // "Container element with ID '...' not found"
444
+ console.error('SDK failed to initialize:', err.message);
445
+ }
446
+ ```
447
+
448
+ ---
449
+
450
+ ## Security
451
+
452
+ - **Pre-fetched Token Auth** — Clients authenticate with `GET /api/v1/client/me` using Basic Auth (`apiKey:apiSecret`) on their server or frontend to retrieve a JWT token. API credentials are never passed to or exposed inside the SDK.
453
+ - **Credentials are never stored** in `localStorage` or `sessionStorage`.
454
+ - **PCI-compliant card handling** — Raw card data is handled exclusively by Airwallex's PCI-certified payment element embedded inside an Airwallex-controlled iframe.
455
+ - **Shadow DOM** (`mode: 'closed'`) prevents host-page scripts from reaching into the SDK's DOM.
456
+
457
+ ---
458
+
459
+ ## TypeScript
460
+
461
+ Type definitions are included at `dist/index.d.ts`.
462
+
463
+ ```tsx
464
+ import { useEffect } from 'react';
465
+ import { MygigstersCardSDK } from '@mygigsters/card-sdk';
466
+ import type { MygigstersCardConfig, CardSuccessResponse } from '@mygigsters/card-sdk';
467
+
468
+ function SaveCardForm({ token, customerId, myGigsterId }: { token: string; customerId: string; myGigsterId: string }) {
469
+ useEffect(() => {
470
+ if (!token) return;
471
+
472
+ const config: MygigstersCardConfig = {
473
+ token,
474
+ customerId,
475
+ myGigsterId,
476
+ containerId: 'mygigsters-card',
477
+ env: 'demo',
478
+ mode: 'dark',
479
+ onSuccess: (res: CardSuccessResponse) => {
480
+ console.log('Card saved! Consent ID:', res.paymentConsentId);
481
+ },
482
+ onError: (err) => {
483
+ console.error('Card SDK error:', err);
484
+ },
485
+ };
486
+
487
+ const sdk = new MygigstersCardSDK();
488
+ sdk.init(config);
489
+
490
+ return () => sdk.destroy();
491
+ }, [token, customerId, myGigsterId]);
492
+
493
+ return <div id="mygigsters-card" />;
494
+ }
495
+
496
+ export default SaveCardForm;
497
+ ```
498
+
499
+ ---
500
+
501
+ ## Changelog
502
+
503
+ ### 1.0.3
504
+ - Added automatic identity verification step calling `profileDetails` API during `init()`.
505
+ - Validates that `myGigsterId` returned by `profileDetails` matches the `myGigsterId` passed into `init()`. If it does not match, SDK initialization is blocked and an error is raised.
506
+
507
+ ### 1.0.2
508
+ - **Breaking Change:** Updated authentication model to **Pre-fetched Token (Flow B)**.
509
+ - `init()` now accepts `token` (retrieved via `GET /api/v1/client/me`) instead of `apiKey` / `apiSecret`.
510
+ - Updated TypeScript definitions and documentation.
511
+
512
+ ### 1.0.0
513
+ - Initial public release.
514
+
515
+ ---
516
+
517
+ ## License
518
+
519
+ MIT © [MyGigsters](https://www.mygigsters.com.au)