elysium-attribution 0.0.0-stage → 1.0.2
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 +364 -2
- package/index.d.ts +213 -0
- package/package.json +41 -4
- package/src/config/index.js +45 -0
- package/src/config/schema.js +85 -0
- package/src/dom/buttonDetector.js +128 -0
- package/src/gateways/GatewayRegistry.js +75 -0
- package/src/gateways/GenericGateway.js +12 -0
- package/src/gateways/PayPalGateway.js +83 -0
- package/src/gateways/PaymentGateway.js +173 -0
- package/src/gateways/RazorpayGateway.js +88 -0
- package/src/gateways/StripeGateway.js +89 -0
- package/src/gateways/index.js +6 -0
- package/src/index.js +151 -0
- package/src/signals/params.js +68 -0
- package/src/signals/referrer.js +106 -0
- package/src/signals/session.js +134 -0
- package/src/signals/touchEngine.js +211 -0
- package/src/signals/transport.js +64 -0
- package/src/signals/visitor.js +82 -0
- package/src/tracking/params.js +68 -0
- package/src/tracking/referrer.js +106 -0
- package/src/tracking/session.js +134 -0
- package/src/tracking/touchTracker.js +202 -0
- package/src/tracking/transport.js +60 -0
- package/src/tracking/visitor.js +82 -0
- package/src/utils/attributionInit.js +56 -0
- package/src/utils/cli.js +69 -0
- package/src/utils/logger.js +31 -0
- package/src/utils/storage.js +110 -0
- package/test/buttonDetector.test.js +26 -0
- package/test/config.test.js +35 -0
- package/test/gateways.test.js +112 -0
- package/test/params.test.js +51 -0
- package/test/referrer.test.js +41 -0
- package/test/session.test.js +112 -0
- package/test/visitor.test.js +35 -0
package/README.md
CHANGED
|
@@ -1,3 +1,365 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ElysiumSDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Production-grade JavaScript/TypeScript SDK for global multi-touch marketing attribution, visitor session tracking, and payment gateway integration across any web application or e-commerce storefront.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
1. [Architecture Overview](#architecture-overview)
|
|
9
|
+
2. [Existing Touch Tracking Analysis & Fixes](#existing-touch-tracking-analysis--fixes)
|
|
10
|
+
3. [Installation](#installation)
|
|
11
|
+
4. [Configuration (`elysium.config.js`)](#configuration)
|
|
12
|
+
5. [Quick Start](#quick-start)
|
|
13
|
+
6. [Payment Gateway Integrations](#payment-gateway-integrations)
|
|
14
|
+
- [Stripe](#stripe)
|
|
15
|
+
- [Razorpay](#razorpay)
|
|
16
|
+
- [PayPal](#paypal)
|
|
17
|
+
- [Custom Gateways](#custom-gateways)
|
|
18
|
+
7. [Payment Button Detection](#payment-button-detection)
|
|
19
|
+
8. [Backend Integration & Limitations](#backend-integration--limitations)
|
|
20
|
+
9. [API Reference](#api-reference)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Architecture Overview
|
|
25
|
+
|
|
26
|
+
ElysiumSDK is structured with strict separation of concerns, framework independence, and zero external runtime dependencies:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
elysiumSDK/
|
|
30
|
+
├── src/
|
|
31
|
+
│ ├── index.js # Unified SDK interface (window.Elysium & ES module exports)
|
|
32
|
+
│ ├── config/
|
|
33
|
+
│ │ ├── index.js # Configuration manager & dynamic updates
|
|
34
|
+
│ │ └── schema.js # Defaults & client-side secret-key detection / audit
|
|
35
|
+
│ ├── tracking/
|
|
36
|
+
│ │ ├── visitor.js # Visitor UUID generator with localStorage + Cookie 2-way sync
|
|
37
|
+
│ │ ├── params.js # Multi-platform click IDs (gclid, fbclid, wbraid, ttclid, etc.)
|
|
38
|
+
│ │ ├── referrer.js # Internal referrer filtering & organic search/social detection
|
|
39
|
+
│ │ ├── session.js # 30-min session windowing, touch deduplication, FIFO bounding
|
|
40
|
+
│ │ ├── transport.js # Resilient HTTP fetch (keepalive, timeouts, dual headers)
|
|
41
|
+
│ │ └── touchTracker.js # Core orchestrator: recordTouch, identifyVisitor, createCheckoutSession
|
|
42
|
+
│ ├── gateways/
|
|
43
|
+
│ │ ├── PaymentGateway.js # Abstract base class with lifecycle hooks and event emitter
|
|
44
|
+
│ │ ├── GatewayRegistry.js # Registry for resolving & registering gateways
|
|
45
|
+
│ │ ├── StripeGateway.js # Stripe Elements, 3DS redirect return checks, PaymentIntent tracking
|
|
46
|
+
│ │ ├── RazorpayGateway.js # Razorpay options wrapping, handler interception, failure events
|
|
47
|
+
│ │ ├── PayPalGateway.js # PayPal JS SDK Buttons interception (createOrder, onApprove, onError)
|
|
48
|
+
│ │ └── GenericGateway.js # Fallback & custom provider support
|
|
49
|
+
│ ├── dom/
|
|
50
|
+
│ │ └── buttonDetector.js # Delegated click listener & MutationObserver for .elysium-payment-button
|
|
51
|
+
│ └── utils/
|
|
52
|
+
│ ├── storage.js # Resilient storage (in-memory fallback, ITP-aware cookie handling)
|
|
53
|
+
│ ├── logger.js # Safe prefixed logging with debug toggle
|
|
54
|
+
│ └── cli.js # CLI tool to initialize elysium.config.js
|
|
55
|
+
├── test/ # Comprehensive unit tests (node:test)
|
|
56
|
+
├── index.d.ts # Complete TypeScript definitions
|
|
57
|
+
└── package.json
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 2. Existing Touch Tracking Analysis & Fixes
|
|
63
|
+
|
|
64
|
+
During the audit of `touch-tracking.ts` and `attributionInit.js`, the following critical flaws, security concerns, and edge cases were identified and resolved:
|
|
65
|
+
|
|
66
|
+
| Area | Issue in Original Code | Fixed in ElysiumSDK |
|
|
67
|
+
| :--- | :--- | :--- |
|
|
68
|
+
| **Bundler / Environment Lock-in** | Relied on `import.meta.env.VITE_ATTRIBUTION_*`, which crashed or failed in non-Vite environments, script tags, Webpack, Next.js, and vanilla HTML. | Framework-agnostic configuration via `elysium.config.js`, runtime options to `init()`, and safe global fallback. |
|
|
69
|
+
| **Storage Quota & Memory Leak** | `saveSuccessfulTouch` appended entries indefinitely to `localStorage['aid_touches']` without limits, eventually throwing `QuotaExceededError` on frequent visitors. | Capped history to a maximum of 50 records with automatic FIFO pruning. Added in-memory fallback for Private Browsing mode. |
|
|
70
|
+
| **Organic & Referral Traffic Loss** | `shouldSendTouch` checked only marketing parameters (`utm_` and `gclid/fbclid`). Visitors arriving via organic Google search, DuckDuckGo, Reddit, or Instagram were completely dropped from attribution. | Added `referrer.js` that detects organic search engines and social platforms, populating source/medium when UTMs are absent. |
|
|
71
|
+
| **Internal Navigation Misattribution** | `document.referrer` recorded internal site page-to-page navigation as external referrals, polluting attribution data. | Added domain-level internal referrer filtering that strips same-domain referrers. |
|
|
72
|
+
| **Modern Ad Click IDs Missing** | Only captured `gclid`, `fbclid`, `ttclid`, `msclkid`. Missed iOS privacy click IDs (`wbraid`, `gbraid`), Google Display (`dclid`), Twitter/X (`twclid`), LinkedIn (`li_fat_id`), and Reddit (`rdt_cid`). | Added comprehensive ad platform click ID capture and cookie sync (`_fbc`, `_fbp`, `_ttp`, `_ga`). |
|
|
73
|
+
| **Safari ITP / Cross-Domain Identity Loss** | Relied primarily on localStorage; cookies were only set as a fallback if localStorage failed, breaking cross-subdomain tracking (`shop.store.com` -> `checkout.store.com`). | Dual-writes to both `localStorage` and `SameSite=Lax` cookies with configurable `cookieDomain` to preserve visitor identity across subdomains and Safari ITP resets. |
|
|
74
|
+
| **Payment Button Assumption Flaw** | None existed in original code; clicks cannot be assumed as payment success. | Implemented separate `PaymentGateway` architecture treating clicks as payment *attempts*, and only recording *success* after provider verification or redirect resolution. |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Installation
|
|
79
|
+
|
|
80
|
+
Install via npm:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm install elysium-sdk
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or via CDN script tag:
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<script src="https://cdn.jsdelivr.net/npm/elysium-sdk/dist/elysium.min.js"></script>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Initialize your configuration file:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx elysium
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 4. Configuration (`elysium.config.js`)
|
|
101
|
+
|
|
102
|
+
Create or update `elysium.config.js` in your project root:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
/**
|
|
106
|
+
* ElysiumSDK Configuration File
|
|
107
|
+
*
|
|
108
|
+
* IMPORTANT SECURITY NOTICE:
|
|
109
|
+
* This file is bundled for the browser. NEVER include backend secret keys
|
|
110
|
+
* (e.g. Stripe secret key "sk_live_...", Razorpay key secret).
|
|
111
|
+
*/
|
|
112
|
+
export default {
|
|
113
|
+
// Public attribution API Key provided by Elysium
|
|
114
|
+
apiKey: "pk_test_b-ej3xXtpwZUdV6BJdu0NTlILFnMsfszkeiJPfQ9CL0",
|
|
115
|
+
|
|
116
|
+
// Attribution API Backend endpoint
|
|
117
|
+
apiBaseUrl: "https://asbTemp-production.up.railway.app",
|
|
118
|
+
|
|
119
|
+
// Enable verbose debugging logs in browser console
|
|
120
|
+
debug: true,
|
|
121
|
+
|
|
122
|
+
// Session inactivity timeout in milliseconds (default: 30 minutes)
|
|
123
|
+
sessionWindowMs: 30 * 60 * 1000,
|
|
124
|
+
|
|
125
|
+
// CSS selector or class for detecting checkout/payment buttons
|
|
126
|
+
buttonSelector: ".elysium-payment-button",
|
|
127
|
+
|
|
128
|
+
// Automatically record touch on page load
|
|
129
|
+
autoTrack: true,
|
|
130
|
+
|
|
131
|
+
// Optional: restrict touch tracking to specific path regex (e.g. product pages only)
|
|
132
|
+
// productPathRegex: /^\/product\/[^/]+\/?$/,
|
|
133
|
+
|
|
134
|
+
// Domain for cross-subdomain cookie tracking (e.g., ".yourstore.com")
|
|
135
|
+
cookieDomain: undefined,
|
|
136
|
+
|
|
137
|
+
// Additional custom query parameters to capture and attribute
|
|
138
|
+
customParams: ["affiliate_id", "promo_code"],
|
|
139
|
+
|
|
140
|
+
// Supported payment gateways configuration (PUBLIC keys only)
|
|
141
|
+
gateways: {
|
|
142
|
+
stripe: {
|
|
143
|
+
enabled: true,
|
|
144
|
+
publishableKey: "pk_test_...",
|
|
145
|
+
},
|
|
146
|
+
razorpay: {
|
|
147
|
+
enabled: true,
|
|
148
|
+
keyId: "rzp_test_...",
|
|
149
|
+
},
|
|
150
|
+
paypal: {
|
|
151
|
+
enabled: true,
|
|
152
|
+
clientId: "YOUR_PAYPAL_CLIENT_ID",
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 5. Quick Start
|
|
161
|
+
|
|
162
|
+
### In React / Next.js / Vue / Vanilla JS:
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
import Elysium from 'elysium-sdk';
|
|
166
|
+
import elysiumConfig from './elysium.config.js';
|
|
167
|
+
|
|
168
|
+
// Initialize on application startup
|
|
169
|
+
Elysium.init(elysiumConfig);
|
|
170
|
+
|
|
171
|
+
// Get current visitor ID
|
|
172
|
+
const visitorId = Elysium.getVisitorId();
|
|
173
|
+
|
|
174
|
+
// When customer logs in or submits their email in checkout:
|
|
175
|
+
await Elysium.identifyVisitor('customer@example.com', {
|
|
176
|
+
name: 'Jane Doe',
|
|
177
|
+
loyaltyTier: 'VIP'
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 6. Payment Gateway Integrations
|
|
184
|
+
|
|
185
|
+
### Stripe
|
|
186
|
+
|
|
187
|
+
The Stripe gateway supports:
|
|
188
|
+
1. **Stripe Elements (`stripe.confirmPayment`)**: Automatic promise interception
|
|
189
|
+
2. **Redirect Returns**: Automatically detects `?payment_intent=pi_...&redirect_status=succeeded` when customers return from 3D Secure or Stripe Checkout.
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
import Elysium from 'elysium-sdk';
|
|
193
|
+
|
|
194
|
+
const stripeGateway = Elysium.getGateway('stripe');
|
|
195
|
+
|
|
196
|
+
// Instrument Stripe instance
|
|
197
|
+
const stripe = stripeGateway.wrapStripe(window.Stripe('pk_test_...'));
|
|
198
|
+
|
|
199
|
+
// Call confirmPayment normally - Elysium automatically records attempt and verified success!
|
|
200
|
+
const { error } = await stripe.confirmPayment({
|
|
201
|
+
elements,
|
|
202
|
+
confirmParams: {
|
|
203
|
+
return_url: 'https://mystore.com/order-confirmation',
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Razorpay
|
|
209
|
+
|
|
210
|
+
Wrap your Razorpay checkout options. Elysium automatically hooks into the `handler` callback and listens for `payment.failed` events:
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
import Elysium from 'elysium-sdk';
|
|
214
|
+
|
|
215
|
+
const rzpGateway = Elysium.getGateway('razorpay');
|
|
216
|
+
|
|
217
|
+
const options = rzpGateway.wrapOptions({
|
|
218
|
+
key: 'rzp_test_...',
|
|
219
|
+
amount: 50000,
|
|
220
|
+
currency: 'INR',
|
|
221
|
+
name: 'Acme Store',
|
|
222
|
+
order_id: 'order_123',
|
|
223
|
+
handler: function (response) {
|
|
224
|
+
// response.razorpay_payment_id
|
|
225
|
+
// response.razorpay_order_id
|
|
226
|
+
// response.razorpay_signature
|
|
227
|
+
console.log('Payment completed!');
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const rzp = new window.Razorpay(options);
|
|
232
|
+
rzpGateway.wrapInstance(rzp); // Attaches failure telemetry
|
|
233
|
+
rzp.open();
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### PayPal
|
|
237
|
+
|
|
238
|
+
Wrap your PayPal JavaScript SDK Button options:
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
import Elysium from 'elysium-sdk';
|
|
242
|
+
|
|
243
|
+
const paypalGateway = Elysium.getGateway('paypal');
|
|
244
|
+
|
|
245
|
+
window.paypal.Buttons(paypalGateway.wrapButtons({
|
|
246
|
+
createOrder: (data, actions) => {
|
|
247
|
+
return actions.order.create({
|
|
248
|
+
purchase_units: [{ amount: { value: '149.00' } }]
|
|
249
|
+
});
|
|
250
|
+
},
|
|
251
|
+
onApprove: async (data, actions) => {
|
|
252
|
+
const details = await actions.order.capture();
|
|
253
|
+
console.log('PayPal transaction completed:', details);
|
|
254
|
+
}
|
|
255
|
+
})).render('#paypal-button-container');
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Custom Gateways
|
|
259
|
+
|
|
260
|
+
You can implement custom payment gateways by extending `PaymentGateway`:
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
import { PaymentGateway, registerGateway } from 'elysium-sdk';
|
|
264
|
+
|
|
265
|
+
class CryptoPaymentGateway extends PaymentGateway {
|
|
266
|
+
constructor() {
|
|
267
|
+
super('crypto');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
canHandle(buttonOrContext) {
|
|
271
|
+
return buttonOrContext.dataset?.gateway === 'crypto';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async handleButtonClick(button, context) {
|
|
275
|
+
await this.recordPaymentAttempt({ cryptoCurrency: 'ETH', ...context });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
registerGateway('crypto', new CryptoPaymentGateway());
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 7. Payment Button Detection
|
|
285
|
+
|
|
286
|
+
Elysium automatically detects interactions with payment and checkout buttons using a lightweight, delegated event listener and `MutationObserver` (supporting React hydration, Vue, and dynamically rendered buttons).
|
|
287
|
+
|
|
288
|
+
Simply add the configured CSS class (default `elysium-payment-button`) and optional `data-gateway` attributes:
|
|
289
|
+
|
|
290
|
+
```html
|
|
291
|
+
<!-- Stripe Checkout Button -->
|
|
292
|
+
<button
|
|
293
|
+
class="elysium-payment-button"
|
|
294
|
+
data-gateway="stripe"
|
|
295
|
+
data-amount="149.00"
|
|
296
|
+
data-currency="USD"
|
|
297
|
+
>
|
|
298
|
+
Pay with Card (Stripe)
|
|
299
|
+
</button>
|
|
300
|
+
|
|
301
|
+
<!-- Razorpay Button -->
|
|
302
|
+
<button
|
|
303
|
+
class="elysium-payment-button"
|
|
304
|
+
data-gateway="razorpay"
|
|
305
|
+
data-amount="149.00"
|
|
306
|
+
data-currency="USD"
|
|
307
|
+
>
|
|
308
|
+
Pay with UPI / Cards (Razorpay)
|
|
309
|
+
</button>
|
|
310
|
+
|
|
311
|
+
<!-- PayPal Button -->
|
|
312
|
+
<button
|
|
313
|
+
class="elysium-payment-button"
|
|
314
|
+
data-gateway="paypal"
|
|
315
|
+
>
|
|
316
|
+
PayPal Express Checkout
|
|
317
|
+
</button>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Lifecycle Events Dispatched
|
|
321
|
+
|
|
322
|
+
| Event Name | Description | Detail Object |
|
|
323
|
+
| :--- | :--- | :--- |
|
|
324
|
+
| `elysium:touch_recorded` | Fired when a page touch is transmitted and accepted | `{ payload, response }` |
|
|
325
|
+
| `elysium:button_detected` | Fired when a payment button is observed in DOM | `{ button }` |
|
|
326
|
+
| `elysium:payment_attempt` | Fired when user clicks payment button (intent only) | `{ gateway, button, context }` |
|
|
327
|
+
| `elysium:payment_success` | Fired when payment is confirmed by gateway | `{ gateway, transactionId, amount, ... }` |
|
|
328
|
+
| `elysium:payment_failure` | Fired on checkout cancellation or card decline | `{ gateway, errorCode, errorMessage }` |
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 8. Backend Integration & Limitations
|
|
333
|
+
|
|
334
|
+
### Critical Security Rule: Secret Keys on Backend
|
|
335
|
+
Client-side code operates in an untrusted environment. Only publishable API keys may be used in `elysium.config.js`. Secret keys (e.g., Stripe `sk_live_...`, Razorpay `key_secret`) must **NEVER** be placed in frontend code.
|
|
336
|
+
|
|
337
|
+
### Backend Verification via Webhooks
|
|
338
|
+
While the client-side SDK captures visitor IDs, checkout sessions, and browser-confirmed transactions, **authoritative payment attribution must be validated via server-side webhooks**:
|
|
339
|
+
|
|
340
|
+
1. **Checkout Session Creation**:
|
|
341
|
+
The frontend calls `Elysium.createCheckoutSession()` before starting payment to get an attribution token.
|
|
342
|
+
2. **Metadata Attachment**:
|
|
343
|
+
Attach the `checkoutSessionId` and `visitorId` to the gateway's metadata:
|
|
344
|
+
- Stripe: `payment_intent_data.metadata: { elysium_session_id: session.checkoutSessionId, elysium_visitor_id: session.visitorId }`
|
|
345
|
+
- Razorpay: `notes: { elysium_session_id: session.checkoutSessionId }`
|
|
346
|
+
- PayPal: `custom_id: session.checkoutSessionId`
|
|
347
|
+
3. **Backend Webhook Handler**:
|
|
348
|
+
When receiving `payment_intent.succeeded` (Stripe) or `order.paid` (Razorpay), your backend verifies the cryptographic webhook signature, extracts the `elysium_session_id`, and informs the Elysium Attribution API to lock in the conversion.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 9. API Reference
|
|
353
|
+
|
|
354
|
+
- `Elysium.init(config)`: Initializes tracking, button observers, and gateways.
|
|
355
|
+
- `Elysium.configure(updates)`: Updates configuration dynamically at runtime.
|
|
356
|
+
- `Elysium.getVisitorId()`: Returns active visitor ID.
|
|
357
|
+
- `Elysium.setVisitorId(id)`: Overrides active visitor ID.
|
|
358
|
+
- `Elysium.recordTouch(options)`: Explicitly sends a touchpoint.
|
|
359
|
+
- `Elysium.recordProductTouch()`: Preserved backward-compatible product touch tracking.
|
|
360
|
+
- `Elysium.identifyVisitor(email, traits)`: Links visitor ID to a customer profile.
|
|
361
|
+
- `Elysium.createCheckoutSession()`: Obtains checkout attribution token.
|
|
362
|
+
- `Elysium.recordPaymentSuccess(details)`: Records verified payment.
|
|
363
|
+
- `Elysium.recordPaymentFailure(details)`: Records payment failure or cancellation.
|
|
364
|
+
- `Elysium.registerGateway(name, instance)`: Registers custom payment gateway.
|
|
365
|
+
- `Elysium.getGateway(name)`: Retrieves gateway by name.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ElysiumSDK TypeScript Definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ElysiumGatewayConfig {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
publishableKey?: string;
|
|
8
|
+
keyId?: string;
|
|
9
|
+
clientId?: string;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ElysiumConfig {
|
|
14
|
+
/** Elysium Public API Key */
|
|
15
|
+
apiKey: string;
|
|
16
|
+
/** Elysium Backend API URL (default: https://asbTemp-production.up.railway.app) */
|
|
17
|
+
apiBaseUrl?: string;
|
|
18
|
+
/** Enable verbose console debug logs */
|
|
19
|
+
debug?: boolean;
|
|
20
|
+
/** Session timeout window in milliseconds (default: 30 minutes) */
|
|
21
|
+
sessionWindowMs?: number;
|
|
22
|
+
/** CSS selector for payment buttons (default: .elysium-payment-button) */
|
|
23
|
+
buttonSelector?: string;
|
|
24
|
+
/** Automatically track touch on initialization (default: true) */
|
|
25
|
+
autoTrack?: boolean;
|
|
26
|
+
/** Optional RegExp to filter paths for touch tracking (e.g. product pages only) */
|
|
27
|
+
productPathRegex?: RegExp | string | null;
|
|
28
|
+
/** Cookie domain for cross-subdomain tracking (e.g. .example.com) */
|
|
29
|
+
cookieDomain?: string;
|
|
30
|
+
/** Additional custom query parameters to track */
|
|
31
|
+
customParams?: string[];
|
|
32
|
+
/** Supported payment gateway options */
|
|
33
|
+
gateways?: {
|
|
34
|
+
stripe?: ElysiumGatewayConfig;
|
|
35
|
+
razorpay?: ElysiumGatewayConfig;
|
|
36
|
+
paypal?: ElysiumGatewayConfig;
|
|
37
|
+
[key: string]: ElysiumGatewayConfig | undefined;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TouchPayload {
|
|
42
|
+
visitorId: string;
|
|
43
|
+
touchedAt: string;
|
|
44
|
+
source: string | null;
|
|
45
|
+
medium: string | null;
|
|
46
|
+
campaign: string | null;
|
|
47
|
+
term: string | null;
|
|
48
|
+
content: string | null;
|
|
49
|
+
referrer: string | null;
|
|
50
|
+
landingPage: string;
|
|
51
|
+
pageTitle: string;
|
|
52
|
+
channel?: string;
|
|
53
|
+
gclid?: string | null;
|
|
54
|
+
dclid?: string | null;
|
|
55
|
+
wbraid?: string | null;
|
|
56
|
+
gbraid?: string | null;
|
|
57
|
+
srsltid?: string | null;
|
|
58
|
+
fbclid?: string | null;
|
|
59
|
+
ttclid?: string | null;
|
|
60
|
+
msclkid?: string | null;
|
|
61
|
+
twclid?: string | null;
|
|
62
|
+
li_fat_id?: string | null;
|
|
63
|
+
rdt_cid?: string | null;
|
|
64
|
+
fbc?: string | null;
|
|
65
|
+
fbp?: string | null;
|
|
66
|
+
ttp?: string | null;
|
|
67
|
+
ga?: string | null;
|
|
68
|
+
custom?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface TouchResponse {
|
|
72
|
+
id: string;
|
|
73
|
+
visitorId: string;
|
|
74
|
+
channel: string;
|
|
75
|
+
touchedAt: string;
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface StoredTouch {
|
|
80
|
+
payload: TouchPayload;
|
|
81
|
+
response: TouchResponse;
|
|
82
|
+
recordedAt: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface CheckoutSession {
|
|
86
|
+
checkoutSessionId: string;
|
|
87
|
+
token: string;
|
|
88
|
+
visitorId: string;
|
|
89
|
+
visitorRecordId: string;
|
|
90
|
+
touchIds: string[];
|
|
91
|
+
touchCount: number;
|
|
92
|
+
expiresAt: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface PaymentAttemptDetails {
|
|
96
|
+
gateway?: string;
|
|
97
|
+
buttonId?: string | null;
|
|
98
|
+
buttonClass?: string | null;
|
|
99
|
+
buttonText?: string | null;
|
|
100
|
+
amount?: number | null;
|
|
101
|
+
currency?: string | null;
|
|
102
|
+
productId?: string | null;
|
|
103
|
+
[key: string]: unknown;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface PaymentSuccessDetails {
|
|
107
|
+
gateway?: string;
|
|
108
|
+
transactionId?: string | null;
|
|
109
|
+
orderId?: string | null;
|
|
110
|
+
amount?: number | null;
|
|
111
|
+
currency?: string | null;
|
|
112
|
+
signature?: string | null;
|
|
113
|
+
[key: string]: unknown;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface PaymentFailureDetails {
|
|
117
|
+
gateway?: string;
|
|
118
|
+
code?: string | null;
|
|
119
|
+
message?: string | null;
|
|
120
|
+
[key: string]: unknown;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export abstract class PaymentGateway {
|
|
124
|
+
name: string;
|
|
125
|
+
config: ElysiumGatewayConfig;
|
|
126
|
+
constructor(name: string, config?: ElysiumGatewayConfig);
|
|
127
|
+
init(config?: ElysiumGatewayConfig): void;
|
|
128
|
+
canHandle(buttonOrContext: HTMLElement | Record<string, unknown>): boolean;
|
|
129
|
+
handleButtonClick(button: HTMLElement | null, context?: Record<string, unknown>): Promise<unknown>;
|
|
130
|
+
recordPaymentAttempt(details?: PaymentAttemptDetails): Promise<unknown>;
|
|
131
|
+
recordPaymentSuccess(paymentData?: PaymentSuccessDetails): Promise<unknown>;
|
|
132
|
+
recordPaymentFailure(errorData?: PaymentFailureDetails): Promise<unknown>;
|
|
133
|
+
on(event: 'attempt' | 'success' | 'failure', callback: (data: unknown) => void): () => void;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export class StripeGateway extends PaymentGateway {
|
|
137
|
+
constructor(config?: ElysiumGatewayConfig);
|
|
138
|
+
checkRedirectReturn(): void;
|
|
139
|
+
wrapStripe<T>(stripeInstance: T): T;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export class RazorpayGateway extends PaymentGateway {
|
|
143
|
+
constructor(config?: ElysiumGatewayConfig);
|
|
144
|
+
wrapOptions<T extends Record<string, unknown>>(options: T): T;
|
|
145
|
+
wrapInstance<T>(rzpInstance: T): T;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export class PayPalGateway extends PaymentGateway {
|
|
149
|
+
constructor(config?: ElysiumGatewayConfig);
|
|
150
|
+
wrapButtons<T extends Record<string, unknown>>(options: T): T;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export class GenericGateway extends PaymentGateway {
|
|
154
|
+
constructor(name?: string, config?: ElysiumGatewayConfig);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class GatewayRegistry {
|
|
158
|
+
register(name: string, gatewayInstance: PaymentGateway): PaymentGateway;
|
|
159
|
+
get(name: string): PaymentGateway | null;
|
|
160
|
+
getAll(): PaymentGateway[];
|
|
161
|
+
resolveForButton(buttonElement: HTMLElement | null): PaymentGateway;
|
|
162
|
+
configureAll(gatewaysConfig?: Record<string, ElysiumGatewayConfig>): void;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function init(config?: Partial<ElysiumConfig>): typeof Elysium;
|
|
166
|
+
export function configure(updates?: Partial<ElysiumConfig>): ElysiumConfig;
|
|
167
|
+
export function getConfig(): ElysiumConfig;
|
|
168
|
+
export function getVisitorId(): string;
|
|
169
|
+
export function setVisitorId(newId: string): void;
|
|
170
|
+
export function recordTouch(options?: { force?: boolean; customData?: Record<string, unknown> }): Promise<{ ok: boolean; data?: TouchResponse; error?: string; skipped?: boolean }>;
|
|
171
|
+
export function recordProductTouch(): Promise<unknown>;
|
|
172
|
+
export function identifyVisitor(email: string, traits?: Record<string, unknown>): Promise<boolean>;
|
|
173
|
+
export function createCheckoutSession(): Promise<CheckoutSession>;
|
|
174
|
+
export function buildPayload(customData?: Record<string, unknown>): TouchPayload;
|
|
175
|
+
export function readTouchHistory(): StoredTouch[];
|
|
176
|
+
export function getTouchSignature(payload: TouchPayload): string;
|
|
177
|
+
export function initPaymentButtonDetection(customSelector?: string | null): () => void;
|
|
178
|
+
export function registerGateway(name: string, gatewayInstance: PaymentGateway): PaymentGateway;
|
|
179
|
+
export function getGateway(name: string): PaymentGateway | null;
|
|
180
|
+
export function recordPaymentAttempt(details?: PaymentAttemptDetails): Promise<unknown>;
|
|
181
|
+
export function recordPaymentSuccess(details?: PaymentSuccessDetails): Promise<unknown>;
|
|
182
|
+
export function recordPaymentFailure(details?: PaymentFailureDetails): Promise<unknown>;
|
|
183
|
+
|
|
184
|
+
export const Elysium: {
|
|
185
|
+
init: typeof init;
|
|
186
|
+
configure: typeof configure;
|
|
187
|
+
getConfig: typeof getConfig;
|
|
188
|
+
getVisitorId: typeof getVisitorId;
|
|
189
|
+
setVisitorId: typeof setVisitorId;
|
|
190
|
+
recordTouch: typeof recordTouch;
|
|
191
|
+
recordProductTouch: typeof recordProductTouch;
|
|
192
|
+
identifyVisitor: typeof identifyVisitor;
|
|
193
|
+
createCheckoutSession: typeof createCheckoutSession;
|
|
194
|
+
buildPayload: typeof buildPayload;
|
|
195
|
+
readTouchHistory: typeof readTouchHistory;
|
|
196
|
+
getTouchSignature: typeof getTouchSignature;
|
|
197
|
+
initPaymentButtonDetection: typeof initPaymentButtonDetection;
|
|
198
|
+
registerGateway: typeof registerGateway;
|
|
199
|
+
getGateway: typeof getGateway;
|
|
200
|
+
recordPaymentAttempt: typeof recordPaymentAttempt;
|
|
201
|
+
recordPaymentSuccess: typeof recordPaymentSuccess;
|
|
202
|
+
recordPaymentFailure: typeof recordPaymentFailure;
|
|
203
|
+
gateways: {
|
|
204
|
+
PaymentGateway: typeof PaymentGateway;
|
|
205
|
+
StripeGateway: typeof StripeGateway;
|
|
206
|
+
RazorpayGateway: typeof RazorpayGateway;
|
|
207
|
+
PayPalGateway: typeof PayPalGateway;
|
|
208
|
+
GenericGateway: typeof GenericGateway;
|
|
209
|
+
registry: GatewayRegistry;
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export default Elysium;
|
package/package.json
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elysium-attribution",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Multi-touch marketing attribution and payment gateway integration SDK for web storefronts and e-commerce platforms",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"module": "./src/index.js",
|
|
8
|
+
"types": "./index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"import": "./src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./gateways": {
|
|
15
|
+
"import": "./src/gateways/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./tracking": {
|
|
18
|
+
"import": "./src/tracking/touchTracker.js"
|
|
19
|
+
},
|
|
20
|
+
"./config": {
|
|
21
|
+
"import": "./src/config/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"elysium": "./src/utils/cli.js"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "node --test test/**/*.test.js"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"attribution",
|
|
32
|
+
"touch-tracking",
|
|
33
|
+
"marketing",
|
|
34
|
+
"utm",
|
|
35
|
+
"stripe",
|
|
36
|
+
"razorpay",
|
|
37
|
+
"paypal",
|
|
38
|
+
"payment-gateway",
|
|
39
|
+
"e-commerce"
|
|
40
|
+
],
|
|
41
|
+
"author": "Elysium",
|
|
42
|
+
"license": "ISC"
|
|
43
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DEFAULT_CONFIG, validateAndSanitizeConfig } from './schema.js';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
|
|
4
|
+
class ConfigManager {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
7
|
+
logger.getDebug = () => this.config.debug;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
init(userConfig = {}) {
|
|
11
|
+
// If window.ELYSIUM_CONFIG exists in browser runtime, merge it first
|
|
12
|
+
let globalWindowConfig = {};
|
|
13
|
+
if (typeof window !== 'undefined' && window.ELYSIUM_CONFIG) {
|
|
14
|
+
globalWindowConfig = window.ELYSIUM_CONFIG;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.config = validateAndSanitizeConfig({
|
|
18
|
+
...globalWindowConfig,
|
|
19
|
+
...userConfig,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
logger.debug('Configuration initialized:', {
|
|
23
|
+
...this.config,
|
|
24
|
+
apiKey: this.config.apiKey ? `${this.config.apiKey.slice(0, 7)}...` : '(empty)',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return this.config;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get() {
|
|
31
|
+
return this.config;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
set(updates = {}) {
|
|
35
|
+
this.config = validateAndSanitizeConfig({
|
|
36
|
+
...this.config,
|
|
37
|
+
...updates,
|
|
38
|
+
});
|
|
39
|
+
return this.config;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const configManager = new ConfigManager();
|
|
44
|
+
export const getConfig = () => configManager.get();
|
|
45
|
+
export const setConfig = (cfg) => configManager.set(cfg);
|