@galva-io/galva-admin-node 1.0.0-dev

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,218 @@
1
+ # Galva Admin Node SDK
2
+
3
+ TypeScript SDK for integrating with Galva Admin API. This SDK provides a type-safe way to sync billing events and manage user data.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install galva-admin-node
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import galvaAdmin from 'galva-admin-node';
15
+
16
+ const galva = galvaAdmin({
17
+ apiKey: 'your-api-key-here',
18
+ baseUrl: 'https://api.galva.io', // optional
19
+ debug: true // optional, enables logging
20
+ });
21
+ ```
22
+
23
+ ## Usage Examples
24
+
25
+ ### Track a Purchase
26
+
27
+ ```typescript
28
+ await galva.trackPurchase({
29
+ id: 'evt_123',
30
+ endUserId: 'user_456',
31
+ appIdentifier: 'com.example.app',
32
+ productIdentifier: 'premium_monthly',
33
+ planIdentifier: 'monthly',
34
+ platform: 'ios',
35
+ eventTimestamp: new Date().toISOString(),
36
+ purchasedAt: new Date().toISOString(),
37
+ expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
38
+ isTrial: false
39
+ });
40
+ ```
41
+
42
+ ### Track a Renewal
43
+
44
+ ```typescript
45
+ await galva.trackRenewal({
46
+ id: 'evt_124',
47
+ endUserId: 'user_456',
48
+ appIdentifier: 'com.example.app',
49
+ productIdentifier: 'premium_monthly',
50
+ planIdentifier: 'monthly',
51
+ platform: 'ios',
52
+ eventTimestamp: new Date().toISOString(),
53
+ purchasedAt: new Date().toISOString(),
54
+ expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
55
+ });
56
+ ```
57
+
58
+ ### Track a Cancellation
59
+
60
+ ```typescript
61
+ await galva.trackCancellation({
62
+ id: 'evt_125',
63
+ endUserId: 'user_456',
64
+ appIdentifier: 'com.example.app',
65
+ productIdentifier: 'premium_monthly',
66
+ planIdentifier: 'monthly',
67
+ platform: 'ios',
68
+ eventTimestamp: new Date().toISOString(),
69
+ cancelReason: 'too_expensive',
70
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
71
+ });
72
+ ```
73
+
74
+ ### Identify a User
75
+
76
+ ```typescript
77
+ await galva.identify({
78
+ userId: 'user_456',
79
+ email: 'user@example.com',
80
+ name: 'John Doe',
81
+ attributes: {
82
+ plan: 'premium',
83
+ signupDate: '2024-01-01',
84
+ referralSource: 'organic'
85
+ }
86
+ });
87
+ ```
88
+
89
+ ### Sync Multiple Events (Batch)
90
+
91
+ ```typescript
92
+ const events = [
93
+ {
94
+ id: 'evt_126',
95
+ type: 'initial-purchase' as const,
96
+ endUserId: 'user_789',
97
+ appIdentifier: 'com.example.app',
98
+ productIdentifier: 'premium_yearly',
99
+ planIdentifier: 'yearly',
100
+ platform: 'android' as const,
101
+ eventTimestamp: new Date().toISOString(),
102
+ purchasedAt: new Date().toISOString(),
103
+ expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString()
104
+ },
105
+ // ... more events
106
+ ];
107
+
108
+ await galva.syncEvents(events);
109
+ ```
110
+
111
+ ### Queue Events for Batch Processing
112
+
113
+ ```typescript
114
+ // Queue events (they'll be automatically batched)
115
+ galva.queueEvent({
116
+ id: 'evt_127',
117
+ type: 'renewal',
118
+ endUserId: 'user_123',
119
+ appIdentifier: 'com.example.app',
120
+ productIdentifier: 'premium_monthly',
121
+ planIdentifier: 'monthly',
122
+ platform: 'stripe',
123
+ eventTimestamp: new Date().toISOString(),
124
+ purchasedAt: new Date().toISOString()
125
+ }, {
126
+ maxBatchSize: 50, // Flush after 50 events
127
+ flushInterval: 10000 // Flush every 10 seconds
128
+ });
129
+
130
+ // Manually flush queued events
131
+ await galva.flush();
132
+ ```
133
+
134
+ ## API Reference
135
+
136
+ ### Configuration
137
+
138
+ ```typescript
139
+ interface GalvaConfig {
140
+ apiKey: string; // Required: Your API key
141
+ baseUrl?: string; // Optional: API base URL (default: https://api.galva.io)
142
+ timeout?: number; // Optional: Request timeout in ms (default: 30000)
143
+ debug?: boolean; // Optional: Enable debug logging (default: false)
144
+ }
145
+ ```
146
+
147
+ ### Methods
148
+
149
+ #### Event Tracking
150
+
151
+ - `trackPurchase(event)` - Track an initial purchase event
152
+ - `trackRenewal(event)` - Track a subscription renewal
153
+ - `trackCancellation(event)` - Track a subscription cancellation
154
+ - `trackBillingIssue(event)` - Track a billing issue
155
+ - `trackExpiration(event)` - Track a subscription expiration
156
+ - `trackUpgrade(event)` - Track a plan upgrade
157
+ - `trackRefund(event)` - Track a refund
158
+
159
+ #### Generic Event Methods
160
+
161
+ - `syncEvent(event)` - Sync a single billing event
162
+ - `syncEvents(events)` - Batch sync multiple events
163
+ - `queueEvent(event, options?)` - Queue an event for batch processing
164
+ - `flush()` - Manually flush the event queue
165
+
166
+ #### User Management
167
+
168
+ - `identify(userData)` - Identify a user and sync their attributes
169
+ - `getUserStatus(userId)` - Get a user's subscription status
170
+
171
+ #### Utility
172
+
173
+ - `healthCheck()` - Check API health status
174
+
175
+ ### Event Types
176
+
177
+ The SDK supports the following billing event types:
178
+
179
+ - `initial-purchase` - New subscription or trial
180
+ - `renewal` - Subscription renewal
181
+ - `cancellation` - Subscription cancellation
182
+ - `billing-issue` - Payment failure or issue
183
+ - `expiration` - Subscription expiration
184
+ - `upgraded` - Plan upgrade
185
+ - `refund` - Refund processed
186
+ - `resubscribe` - User resubscribed
187
+ - `transfer` - Subscription transferred between users
188
+ - `grace_period_start` - Grace period started
189
+
190
+ ### Error Handling
191
+
192
+ ```typescript
193
+ import { GalvaError } from 'galva-admin-node';
194
+
195
+ try {
196
+ await galva.trackPurchase(eventData);
197
+ } catch (error) {
198
+ if (error instanceof GalvaError) {
199
+ console.error('Galva API Error:', {
200
+ code: error.code,
201
+ message: error.message,
202
+ statusCode: error.statusCode,
203
+ details: error.details
204
+ });
205
+ } else {
206
+ console.error('Unexpected error:', error);
207
+ }
208
+ }
209
+ ```
210
+
211
+ ## Requirements
212
+
213
+ - Node.js 18.0 or higher (for native fetch support)
214
+ - TypeScript 5.0 or higher (for development)
215
+
216
+ ## License
217
+
218
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,4 @@
1
+ 'use strict';var googleapis=require('googleapis'),paddleNodeSdk=require('@paddle/paddle-node-sdk');var T=n=>{let e=new googleapis.google.auth.JWT({email:n.email,key:n.key.replace(/\\n/g,`
2
+ `),scopes:["https://www.googleapis.com/auth/androidpublisher"]});return googleapis.google.androidpublisher({version:"v3",auth:e})},p=class{static async getSubscription(e,t,a){return await T({email:a.email,key:a.key}).purchases.subscriptionsv2.get({token:e,packageName:t})}};var u=(o=>(o.TIMEZONE="$gv_timezone",o.LANGUAGE_CODE="$gv_languageCode",o.EMAIL="$gv_email",o.FULL_NAME="$gv_fullName",o.FIRST_NAME="$gv_firstName",o.LAST_NAME="$gv_lastName",o.COUNTRY="$gv_country",o.TOTAL_LIFETIME_VALUE="$gv_totalLifetimeValue",o))(u||{}),N={$gv_timezone:"timezone",$gv_languageCode:"languageCode",$gv_email:"email",$gv_fullName:"fullName",$gv_firstName:"firstName",$gv_lastName:"lastName",$gv_country:"country",$gv_totalLifetimeValue:"totalLifetimeValue"},m={timezone:"$gv_timezone",languageCode:"$gv_languageCode",email:"$gv_email",fullName:"$gv_fullName",firstName:"$gv_firstName",lastName:"$gv_lastName",country:"$gv_country",totalLifetimeValue:"$gv_totalLifetimeValue"};var s=class n extends Error{code;constructor(e){super(e.message),this.name="GalvaError",this.code=e.code,Error.captureStackTrace&&Error.captureStackTrace(this,n);}static from(e){if(e instanceof n)return e;let t=e instanceof Error?e.message:"An unknown error occurred";return new n({code:"UNKNOWN_ERROR",message:t})}};var A="https://api.galva.io",U="https://api.galva.dev",c=class{config;baseUrl;constructor(e){let t=e?.apiKey||process.env.GALVA_API_KEY;if(!t)throw new s({code:"MISSING_API_KEY",message:"API key is required. Provide it in options or set GALVA_API_KEY env variable."});this.config={apiKey:t,environment:e?.environment||(process.env.NODE_ENV==="development"?"development":"production"),timeout:e?.timeout||1e4},this.baseUrl=this.config.environment==="development"?U:A;}async sendRequest(e,t,a){let r=`${this.baseUrl}${t}`,i=new AbortController,l=setTimeout(()=>i.abort(),this.config.timeout);try{let d=await fetch(r,{method:e,headers:{"Content-Type":"application/json","x-api-key":this.config.apiKey},body:a?JSON.stringify(a):void 0,signal:i.signal});if(!d.ok){let o=await d.json();throw new s(o)}}catch(d){throw d instanceof s?d:d instanceof Error&&d.name==="AbortError"?new s({code:"TIMEOUT",message:`Request timed out after ${this.config.timeout}ms`}):s.from(d)}finally{clearTimeout(l);}}endUser={identify:async(e,t)=>{await this.sendRequest("POST","/endUsers:identify",{endUserId:e,timestamp:t?.timestamp,context:t?.context,traits:t?.traits});},updateDefaultInfo:async(e,t)=>{let a={};for(let[r,i]of Object.entries(t))if(i!==void 0){let l=m[r];a[l]=i;}await this.sendRequest("POST","/endUsers:identify",{endUserId:e,traits:a});}}},E=class extends c{credentials;constructor(e,t){super(e),this.credentials=t;}billingEvent={appstore:async(e,t,a)=>{if(!this.credentials.appstore)throw new s({code:"MISSING_CREDENTIALS",message:"App Store credentials are required. Provide them in withCredentials()."});await this.sendRequest("POST","/endUsers/billingEvents",{platform:"appstore",endUserId:e,payload:{signedPayload:t,appAppleId:this.credentials.appstore.appAppleId,bundleId:this.credentials.appstore.bundleId,env:this.config.environment},options:a});},playstore:async(e,t)=>{if(!this.credentials.playstore)throw new s({code:"MISSING_CREDENTIALS",message:"Play Store credentials are required. Provide them in withCredentials()."});let a=Buffer.from(t,"base64").toString(),r=null;try{r=JSON.parse(a);}catch{throw new s({code:"INVALID_PAYLOAD",message:"Invalid base64 payload: unable to parse JSON."})}if(!r||typeof r!="object")throw new s({code:"INVALID_PAYLOAD",message:"Decoded payload is not a valid JSON object."});if(!("subscriptionNotification"in r))throw new s({code:"INVALID_PAYLOAD",message:"Decoded payload does not contain subscriptionNotification field."});let i=await p.getSubscription(r.subscriptionNotification.purchaseToken,r.packageName,this.credentials.playstore),l={...r,subscriptionNotification:{...r.subscriptionNotification,subscriptionPurchase:i.data}};await this.sendRequest("POST","/endUsers/billingEvents",{platform:"playstore",endUserId:e,payload:l});},paddle:async(e,t,a)=>{if(!this.credentials.paddle)throw new s({code:"MISSING_CREDENTIALS",message:"Paddle credentials are required. Provide them in withCredentials()."});let r=new paddleNodeSdk.Paddle(this.credentials.paddle.apiKey),i;try{i=await r.webhooks.unmarshal(t,this.credentials.paddle.secretKey,a);}catch(l){throw new s({code:"INVALID_WEBHOOK",message:`Invalid Paddle webhook data: ${l.message}`})}await this.sendRequest("POST","/endUsers/billingEvents",{platform:"paddle",endUserId:e,payload:i});}}},g=class extends c{constructor(e){super(e);}billingEvent={appstore:async(e,t,a)=>{let{signedPayload:r,bundleId:i,appAppleId:l}=t;await this.sendRequest("POST","/endUsers/billingEvents",{platform:"appstore",endUserId:e,payload:{signedPayload:r,appAppleId:l,bundleId:i,env:this.config.environment},options:a});},playstore:async(e,t)=>{await this.sendRequest("POST","/endUsers/billingEvents",{platform:"playstore",endUserId:e,payload:t});},paddle:async(e,t)=>{await this.sendRequest("POST","/endUsers/billingEvents",{platform:"paddle",endUserId:e,payload:t});}};withCredentials(e){return new E(this.config,e)}};
3
+ exports.END_USER_DEFAULT_INFO_TO_TRAIT_MAP=m;exports.END_USER_DEFAULT_TRAIT_MAP=N;exports.EndUserDefaultTraitName=u;exports.Galva=g;exports.GalvaError=s;//# sourceMappingURL=index.cjs.map
4
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/playstore.ts","../src/types/endUser.ts","../src/types/error.ts","../src/galva.ts"],"names":["authentication","payload","jwtClient","google","PlaystoreService","token","packageName","cred","EndUserDefaultTraitName","END_USER_DEFAULT_TRAIT_MAP","END_USER_DEFAULT_INFO_TO_TRAIT_MAP","GalvaError","_GalvaError","response","error","message","PRODUCTION_API_URL","DEVELOPMENT_API_URL","GalvaBase","options","apiKey","method","endpoint","data","url","controller","timeoutId","errorResponse","endUserId","defaultInfo","traits","key","value","traitName","GalvaWithCreds","config","credentials","signedPayload","base64Payload","decodedPayloadString","decodedPayload","subscription","finalPayload","rawBody","signature","paddle","Paddle","eventData","Galva","bundleId","appAppleId","eventEntity"],"mappings":"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","file":"index.cjs","sourcesContent":["import { PlaystoreCredentials } from '@/galva';\nimport { google } from 'googleapis';\n\nconst authentication = (payload: { email: string; key: string }) => {\n const jwtClient = new google.auth.JWT({\n email: payload.email,\n key: payload.key.replace(/\\\\n/g, '\\n'),\n scopes: ['https://www.googleapis.com/auth/androidpublisher'],\n });\n return google.androidpublisher({\n version: 'v3',\n auth: jwtClient,\n });\n};\n\nexport class PlaystoreService {\n static async getSubscription(\n token: string,\n packageName: string,\n cred: PlaystoreCredentials,\n ): Promise<any> {\n const client = authentication({\n email: cred.email,\n key: cred.key,\n });\n\n const result = await client.purchases.subscriptionsv2.get({\n token,\n packageName,\n });\n\n return result;\n }\n}\n","/**\n * Default trait names for end user profiles.\n * Use these constants for type-safe access to built-in traits.\n */\nexport enum EndUserDefaultTraitName {\n TIMEZONE = \"$gv_timezone\",\n LANGUAGE_CODE = \"$gv_languageCode\",\n EMAIL = \"$gv_email\",\n FULL_NAME = \"$gv_fullName\",\n FIRST_NAME = \"$gv_firstName\",\n LAST_NAME = \"$gv_lastName\",\n COUNTRY = \"$gv_country\",\n TOTAL_LIFETIME_VALUE = \"$gv_totalLifetimeValue\",\n}\n\n/**\n * Built-in trait types with their expected value types.\n */\nexport interface EndUserDefaultTraits {\n [EndUserDefaultTraitName.TIMEZONE]?: string;\n [EndUserDefaultTraitName.LANGUAGE_CODE]?: string;\n [EndUserDefaultTraitName.EMAIL]?: string;\n [EndUserDefaultTraitName.FULL_NAME]?: string;\n [EndUserDefaultTraitName.FIRST_NAME]?: string;\n [EndUserDefaultTraitName.LAST_NAME]?: string;\n [EndUserDefaultTraitName.COUNTRY]?: string;\n [EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE]?: number;\n}\n\n/**\n * End user traits combining default traits with arbitrary custom traits.\n */\nexport type EndUserTraits = EndUserDefaultTraits & Record<string, any>;\n\n/**\n * Friendly parameter names for updating end user default info.\n * Maps to the underlying $gv_ prefixed trait names.\n */\nexport interface EndUserDefaultInfo {\n timezone?: string;\n languageCode?: string;\n email?: string;\n fullName?: string;\n firstName?: string;\n lastName?: string;\n country?: string;\n totalLifetimeValue?: number;\n}\n\n/**\n * Mapping from friendly parameter names to original trait names.\n * TypeScript ensures this mapping contains all keys from EndUserDefaultTraitName.\n */\nexport const END_USER_DEFAULT_TRAIT_MAP: Record<\n EndUserDefaultTraitName,\n keyof EndUserDefaultInfo\n> = {\n [EndUserDefaultTraitName.TIMEZONE]: \"timezone\",\n [EndUserDefaultTraitName.LANGUAGE_CODE]: \"languageCode\",\n [EndUserDefaultTraitName.EMAIL]: \"email\",\n [EndUserDefaultTraitName.FULL_NAME]: \"fullName\",\n [EndUserDefaultTraitName.FIRST_NAME]: \"firstName\",\n [EndUserDefaultTraitName.LAST_NAME]: \"lastName\",\n [EndUserDefaultTraitName.COUNTRY]: \"country\",\n [EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE]: \"totalLifetimeValue\",\n};\n\n/**\n * Reverse mapping from friendly parameter names to trait names.\n * TypeScript ensures this mapping contains all keys from EndUserDefaultInfo.\n */\nexport const END_USER_DEFAULT_INFO_TO_TRAIT_MAP: Record<\n keyof EndUserDefaultInfo,\n EndUserDefaultTraitName\n> = {\n timezone: EndUserDefaultTraitName.TIMEZONE,\n languageCode: EndUserDefaultTraitName.LANGUAGE_CODE,\n email: EndUserDefaultTraitName.EMAIL,\n fullName: EndUserDefaultTraitName.FULL_NAME,\n firstName: EndUserDefaultTraitName.FIRST_NAME,\n lastName: EndUserDefaultTraitName.LAST_NAME,\n country: EndUserDefaultTraitName.COUNTRY,\n totalLifetimeValue: EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE,\n};\n","/**\n * Error response structure from Galva API.\n */\nexport interface GalvaErrorResponse {\n code: string;\n message: string;\n}\n\n/**\n * Custom error class for Galva SDK errors.\n * Extends Error with additional properties from API error responses.\n */\nexport class GalvaError extends Error {\n /** Error code from the API (e.g., 'BAD_REQUEST', 'UNAUTHORIZED') */\n readonly code: string;\n\n constructor(response: GalvaErrorResponse) {\n super(response.message);\n this.name = \"GalvaError\";\n this.code = response.code;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, GalvaError);\n }\n }\n\n /**\n * Creates a GalvaError from an unknown error.\n * If the error is already a GalvaError, returns it as-is.\n * Otherwise, wraps it in a GalvaError with UNKNOWN_ERROR code.\n */\n static from(error: unknown): GalvaError {\n if (error instanceof GalvaError) {\n return error;\n }\n\n const message =\n error instanceof Error ? error.message : \"An unknown error occurred\";\n\n return new GalvaError({\n code: \"UNKNOWN_ERROR\",\n message,\n });\n }\n}\n","import { PlaystoreDeveloperNotification } from './types/playstore';\nimport { PlaystoreService } from './services/playstore';\nimport { EventEntity, Paddle } from '@paddle/paddle-node-sdk';\nimport type { EndUserTraits, EndUserDefaultInfo } from './types/endUser';\nimport { END_USER_DEFAULT_INFO_TO_TRAIT_MAP } from './types/endUser';\nimport { GalvaError } from './types/error';\n\nexport type {\n EndUserDefaultTraits,\n EndUserTraits,\n EndUserDefaultInfo,\n} from './types/endUser';\nexport {\n EndUserDefaultTraitName,\n END_USER_DEFAULT_TRAIT_MAP,\n END_USER_DEFAULT_INFO_TO_TRAIT_MAP,\n} from './types/endUser';\nexport { GalvaError } from './types/error';\nexport type { GalvaErrorResponse } from './types/error';\n\nexport interface GalvaOptions {\n /**\n * Defaults to 'production'. Falls back to NODE_ENV.\n * @type {('production' | 'development')}\n */\n environment?: 'production' | 'development';\n\n /**\n * API key from Galva dashboard. Falls back to GALVA_API_KEY env var.\n * @type {string}\n */\n apiKey?: string;\n\n /**\n * Request timeout in ms. Defaults to 10000.\n * @type {number}\n */\n timeout?: number;\n}\n\n/**\n * Credentials for authenticating with the Google Play Store API.\n *\n * @interface PlaystoreCredentials\n */\nexport interface PlaystoreCredentials {\n /** Service account email address */\n email: string;\n /** Service account private key */\n key: string;\n}\n\n/**\n * Credentials for authenticating and verifying Paddle webhooks.\n *\n * @interface PaddleCredentials\n */\nexport interface PaddleCredentials {\n /** Paddle API key */\n apiKey: string;\n /** Webhook secret key for signature verification */\n secretKey: string;\n}\n\n/**\n * Credentials for Apple App Store.\n *\n * @interface AppstoreCredentials\n */\nexport interface AppstoreCredentials {\n /** The app's bundle identifier (e.g., 'com.example.app') */\n bundleId: string;\n /** The app's Apple ID from App Store Connect */\n appAppleId: number;\n}\n\n/**\n * Credentials configuration for withCredentials method.\n *\n * @interface CredentialsConfig\n */\nexport interface CredentialsConfig {\n /** App Store credentials */\n appstore?: AppstoreCredentials;\n /** Play Store service account credentials */\n playstore?: PlaystoreCredentials;\n /** Paddle API credentials */\n paddle?: PaddleCredentials;\n}\n\nconst PRODUCTION_API_URL = 'https://api.galva.io';\nconst DEVELOPMENT_API_URL = 'https://api.galva.dev';\n\n/**\n * Base Galva SDK client with end user management methods.\n * @class GalvaBase\n */\nclass GalvaBase {\n protected config: GalvaOptions;\n protected readonly baseUrl: string;\n\n /**\n * @param {GalvaOptions} [options] - Configuration options\n * @throws {GalvaError} If API key is not provided\n */\n constructor(options?: GalvaOptions) {\n const apiKey = options?.apiKey || process.env.GALVA_API_KEY;\n if (!apiKey) {\n throw new GalvaError({\n code: 'MISSING_API_KEY',\n message:\n 'API key is required. Provide it in options or set GALVA_API_KEY env variable.',\n });\n }\n\n this.config = {\n apiKey: apiKey,\n environment:\n options?.environment ||\n (process.env.NODE_ENV === 'development' ? 'development' : 'production'),\n timeout: options?.timeout || 10000,\n };\n this.baseUrl =\n this.config.environment === 'development'\n ? DEVELOPMENT_API_URL\n : PRODUCTION_API_URL;\n }\n\n protected async sendRequest(\n method: 'POST' | 'GET',\n endpoint: string,\n data?: unknown,\n ): Promise<void> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey!,\n },\n body: data ? JSON.stringify(data) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorResponse = await response.json();\n throw new GalvaError(errorResponse);\n }\n } catch (error) {\n if (error instanceof GalvaError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new GalvaError({\n code: 'TIMEOUT',\n message: `Request timed out after ${this.config.timeout}ms`,\n });\n }\n throw GalvaError.from(error);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /** End user management methods. */\n public endUser = {\n /**\n * Identifies an end user with optional traits and context.\n * @param {string} endUserId - End user ID in your system\n * @param {Object} [options] - Identification data\n * @param {string} [options.timestamp] - ISO 8601 timestamp\n * @param {Record<string, any>} [options.context] - Additional context\n * @param {EndUserTraits} [options.traits] - User traits. Use EndUserDefaultTraitName for built-in traits.\n * @throws {GalvaError} If API request fails\n * @example\n * ```typescript\n * await galva.endUser.identify('user-123', {\n * traits: { [EndUserDefaultTraitName.EMAIL]: 'user@example.com' },\n * });\n * ```\n */\n identify: async (\n endUserId: string,\n options?: {\n timestamp?: string;\n context?: Record<string, any>;\n traits?: EndUserTraits;\n },\n ): Promise<void> => {\n await this.sendRequest('POST', '/endUsers:identify', {\n endUserId,\n timestamp: options?.timestamp,\n context: options?.context,\n traits: options?.traits,\n });\n },\n\n /**\n * Updates an end user's default profile info.\n * @param {string} endUserId - End user ID in your system\n * @param {EndUserDefaultInfo} defaultInfo - Profile info to update\n * @throws {GalvaError} If API request fails\n * @example\n * ```typescript\n * await galva.endUser.updateDefaultInfo('user-123', {\n * email: 'user@example.com',\n * fullName: 'John Doe',\n * });\n * ```\n */\n updateDefaultInfo: async (\n endUserId: string,\n defaultInfo: EndUserDefaultInfo,\n ): Promise<void> => {\n const traits: EndUserTraits = {};\n\n for (const [key, value] of Object.entries(defaultInfo)) {\n if (value !== undefined) {\n const traitName =\n END_USER_DEFAULT_INFO_TO_TRAIT_MAP[key as keyof EndUserDefaultInfo];\n traits[traitName] = value;\n }\n }\n\n await this.sendRequest('POST', '/endUsers:identify', {\n endUserId,\n traits,\n });\n },\n };\n}\n\nclass GalvaWithCreds extends GalvaBase {\n // private config: GalvaOptions;\n // private readonly baseUrl: string;\n private credentials: CredentialsConfig;\n\n /**\n * @internal Use `galva.withCredentials(...)` instead.\n */\n constructor(config: GalvaOptions, credentials: CredentialsConfig) {\n super(config);\n // this.config = config;\n // this.baseUrl = baseUrl;\n this.credentials = credentials;\n }\n\n /**\n * Billing event handlers with credential support.\n * @property {Function} appstore - App Store events (signed payload only)\n * @property {Function} playstore - Play Store events (raw base64 payload)\n * @property {Function} paddle - Paddle events (raw body with verification)\n */\n public billingEvent = {\n /**\n * Tracks an App Store billing event. Uses credentials from withCredentials().\n * @param {string} endUserId - End user ID\n * @param {string} signedPayload - Signed payload from Apple\n * @param {Record<string, any>} [options] - Additional options\n * @returns {Promise<void>}\n * @throws {GalvaError} If credentials missing or API fails\n * @example\n * ```typescript\n * await galvaWithCreds.billingEvent.appstore('user-123', signedPayload);\n * ```\n */\n appstore: async (\n endUserId: string,\n signedPayload: string,\n options?: Record<string, any>,\n ): Promise<void> => {\n if (!this.credentials.appstore) {\n throw new GalvaError({\n code: 'MISSING_CREDENTIALS',\n message:\n 'App Store credentials are required. Provide them in withCredentials().',\n });\n }\n\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'appstore',\n endUserId,\n payload: {\n signedPayload,\n appAppleId: this.credentials.appstore.appAppleId,\n bundleId: this.credentials.appstore.bundleId,\n env: this.config.environment,\n },\n options,\n });\n },\n\n /**\n * Tracks a Play Store billing event. Decodes payload and fetches subscription details.\n * @param {string} endUserId - End user ID\n * @param {string} base64Payload - Base64 payload from Pub/Sub\n * @returns {Promise<void>}\n * @throws {GalvaError} If credentials missing or API fails\n * @example\n * ```typescript\n * await galvaWithCreds.billingEvent.playstore('user-123', base64Payload);\n * ```\n */\n playstore: async (\n endUserId: string,\n base64Payload: string,\n ): Promise<void> => {\n if (!this.credentials.playstore) {\n throw new GalvaError({\n code: 'MISSING_CREDENTIALS',\n message:\n 'Play Store credentials are required. Provide them in withCredentials().',\n });\n }\n\n const decodedPayloadString = Buffer.from(\n base64Payload,\n 'base64',\n ).toString();\n let decodedPayload: PlaystoreDeveloperNotification | null = null;\n try {\n decodedPayload = JSON.parse(decodedPayloadString);\n } catch (error) {\n throw new GalvaError({\n code: 'INVALID_PAYLOAD',\n message: 'Invalid base64 payload: unable to parse JSON.',\n });\n }\n\n if (!decodedPayload || typeof decodedPayload !== 'object') {\n throw new GalvaError({\n code: 'INVALID_PAYLOAD',\n message: 'Decoded payload is not a valid JSON object.',\n });\n }\n\n if (!('subscriptionNotification' in decodedPayload)) {\n throw new GalvaError({\n code: 'INVALID_PAYLOAD',\n message:\n 'Decoded payload does not contain subscriptionNotification field.',\n });\n }\n\n const subscription = await PlaystoreService.getSubscription(\n decodedPayload.subscriptionNotification.purchaseToken,\n decodedPayload.packageName,\n this.credentials.playstore,\n );\n\n const finalPayload: PlaystoreDeveloperNotification = {\n ...decodedPayload,\n subscriptionNotification: {\n ...decodedPayload.subscriptionNotification,\n subscriptionPurchase: subscription.data,\n },\n };\n\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'playstore',\n endUserId,\n payload: finalPayload,\n });\n },\n\n /**\n * Tracks a Paddle billing event. Verifies signature and unmarshals the event.\n * @param {string} endUserId - End user ID\n * @param {string} rawBody - Raw body from Paddle webhook\n * @param {string} signature - Paddle-Signature header value\n * @returns {Promise<void>}\n * @throws {GalvaError} If credentials missing or API fails\n * @example\n * ```typescript\n * await galvaWithCreds.billingEvent.paddle('user-123', rawBody, signature);\n * ```\n */\n paddle: async (\n endUserId: string,\n rawBody: string,\n signature: string,\n ): Promise<void> => {\n if (!this.credentials.paddle) {\n throw new GalvaError({\n code: 'MISSING_CREDENTIALS',\n message:\n 'Paddle credentials are required. Provide them in withCredentials().',\n });\n }\n\n const paddle = new Paddle(this.credentials.paddle.apiKey);\n let eventData: EventEntity;\n\n try {\n eventData = await paddle.webhooks.unmarshal(\n rawBody,\n this.credentials.paddle.secretKey,\n signature,\n );\n } catch (error) {\n throw new GalvaError({\n code: 'INVALID_WEBHOOK',\n message: `Invalid Paddle webhook data: ${(error as Error).message}`,\n });\n }\n\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'paddle',\n endUserId,\n payload: eventData,\n });\n },\n };\n}\n\nexport class Galva extends GalvaBase {\n constructor(options?: GalvaOptions) {\n super(options);\n }\n\n /**\n * Billing event handlers for different payment platforms.\n * @property {Function} appstore - App Store events\n * @property {Function} playstore - Play Store events (decoded payload)\n * @property {Function} paddle - Paddle events (verified event)\n */\n public billingEvent = {\n /**\n * Tracks an App Store billing event.\n * @param {string} endUserId - End user ID\n * @param {Object} payload - App Store notification payload\n * @param {string} payload.signedPayload - Signed payload from Apple\n * @param {string} payload.bundleId - App bundle identifier\n * @param {number} payload.appAppleId - App Apple ID\n * @param {Record<string, any>} [options] - Additional options\n * @throws {GalvaError} If API request fails\n * @example\n * ```typescript\n * await galva.billingEvent.appstore('user-123', {\n * signedPayload,\n * bundleId: 'com.example.app',\n * appAppleId: 123456789\n * });\n * ```\n */\n appstore: async (\n endUserId: string,\n payload: {\n signedPayload: string;\n bundleId: string;\n appAppleId: number;\n },\n options?: Record<string, any>,\n ): Promise<void> => {\n const { signedPayload, bundleId, appAppleId } = payload;\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'appstore',\n endUserId,\n payload: {\n signedPayload,\n appAppleId: appAppleId,\n bundleId: bundleId,\n env: this.config.environment,\n },\n options,\n });\n },\n\n /**\n * Tracks a Play Store billing event with decoded notification.\n * @param {string} endUserId - End user ID\n * @param {PlaystoreDeveloperNotification} payload - Decoded developer notification\n * @returns {Promise<void>}\n * @example\n * ```typescript\n * await galva.billingEvent.playstore('user-123', decodedNotification);\n * ```\n */\n playstore: async (\n endUserId: string,\n payload: PlaystoreDeveloperNotification,\n ): Promise<void> => {\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'playstore',\n endUserId,\n payload,\n });\n },\n\n /**\n * Tracks a Paddle billing event with verified event entity.\n * @param {string} endUserId - End user ID\n * @param {EventEntity} eventEntity - Verified Paddle event\n * @returns {Promise<void>}\n * @example\n * ```typescript\n * await galva.billingEvent.paddle('user-123', verifiedEvent);\n * ```\n */\n paddle: async (\n endUserId: string,\n eventEntity: EventEntity,\n ): Promise<void> => {\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'paddle',\n endUserId,\n payload: eventEntity,\n });\n },\n };\n\n withCredentials(credentials: CredentialsConfig) {\n return new GalvaWithCreds(this.config, credentials);\n }\n}\n"]}
@@ -0,0 +1,344 @@
1
+ import { androidpublisher_v3 } from 'googleapis';
2
+ import { EventEntity } from '@paddle/paddle-node-sdk';
3
+
4
+ interface PlaystoreOneTimeProductNotification {
5
+ version: string;
6
+ notificationType: number;
7
+ purchaseToken: string;
8
+ sku: string;
9
+ }
10
+ interface PlaystoreVoidedPurchaseNotification {
11
+ purchaseToken: string;
12
+ orderId: string;
13
+ productType: number;
14
+ refundType: number;
15
+ }
16
+ interface PlaystoreSubscriptionNotification {
17
+ version: string;
18
+ notificationType: number;
19
+ purchaseToken: string;
20
+ subscriptionPurchase: androidpublisher_v3.Schema$SubscriptionPurchaseV2;
21
+ }
22
+ interface PlaystoreTestNotification {
23
+ version: string;
24
+ }
25
+ interface PlaystoreDeveloperNotificationBase {
26
+ version: string;
27
+ packageName: string;
28
+ eventTimeMillis: number;
29
+ }
30
+ type PlaystoreDeveloperNotification = (PlaystoreDeveloperNotificationBase & {
31
+ subscriptionNotification: PlaystoreSubscriptionNotification;
32
+ }) | (PlaystoreDeveloperNotificationBase & {
33
+ oneTimeProductNotification: PlaystoreOneTimeProductNotification;
34
+ }) | (PlaystoreDeveloperNotificationBase & {
35
+ voidedPurchaseNotification: PlaystoreVoidedPurchaseNotification;
36
+ }) | (PlaystoreDeveloperNotificationBase & {
37
+ testNotification: PlaystoreTestNotification;
38
+ });
39
+
40
+ /**
41
+ * Default trait names for end user profiles.
42
+ * Use these constants for type-safe access to built-in traits.
43
+ */
44
+ declare enum EndUserDefaultTraitName {
45
+ TIMEZONE = "$gv_timezone",
46
+ LANGUAGE_CODE = "$gv_languageCode",
47
+ EMAIL = "$gv_email",
48
+ FULL_NAME = "$gv_fullName",
49
+ FIRST_NAME = "$gv_firstName",
50
+ LAST_NAME = "$gv_lastName",
51
+ COUNTRY = "$gv_country",
52
+ TOTAL_LIFETIME_VALUE = "$gv_totalLifetimeValue"
53
+ }
54
+ /**
55
+ * Built-in trait types with their expected value types.
56
+ */
57
+ interface EndUserDefaultTraits {
58
+ [EndUserDefaultTraitName.TIMEZONE]?: string;
59
+ [EndUserDefaultTraitName.LANGUAGE_CODE]?: string;
60
+ [EndUserDefaultTraitName.EMAIL]?: string;
61
+ [EndUserDefaultTraitName.FULL_NAME]?: string;
62
+ [EndUserDefaultTraitName.FIRST_NAME]?: string;
63
+ [EndUserDefaultTraitName.LAST_NAME]?: string;
64
+ [EndUserDefaultTraitName.COUNTRY]?: string;
65
+ [EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE]?: number;
66
+ }
67
+ /**
68
+ * End user traits combining default traits with arbitrary custom traits.
69
+ */
70
+ type EndUserTraits = EndUserDefaultTraits & Record<string, any>;
71
+ /**
72
+ * Friendly parameter names for updating end user default info.
73
+ * Maps to the underlying $gv_ prefixed trait names.
74
+ */
75
+ interface EndUserDefaultInfo {
76
+ timezone?: string;
77
+ languageCode?: string;
78
+ email?: string;
79
+ fullName?: string;
80
+ firstName?: string;
81
+ lastName?: string;
82
+ country?: string;
83
+ totalLifetimeValue?: number;
84
+ }
85
+ /**
86
+ * Mapping from friendly parameter names to original trait names.
87
+ * TypeScript ensures this mapping contains all keys from EndUserDefaultTraitName.
88
+ */
89
+ declare const END_USER_DEFAULT_TRAIT_MAP: Record<EndUserDefaultTraitName, keyof EndUserDefaultInfo>;
90
+ /**
91
+ * Reverse mapping from friendly parameter names to trait names.
92
+ * TypeScript ensures this mapping contains all keys from EndUserDefaultInfo.
93
+ */
94
+ declare const END_USER_DEFAULT_INFO_TO_TRAIT_MAP: Record<keyof EndUserDefaultInfo, EndUserDefaultTraitName>;
95
+
96
+ /**
97
+ * Error response structure from Galva API.
98
+ */
99
+ interface GalvaErrorResponse {
100
+ code: string;
101
+ message: string;
102
+ }
103
+ /**
104
+ * Custom error class for Galva SDK errors.
105
+ * Extends Error with additional properties from API error responses.
106
+ */
107
+ declare class GalvaError extends Error {
108
+ /** Error code from the API (e.g., 'BAD_REQUEST', 'UNAUTHORIZED') */
109
+ readonly code: string;
110
+ constructor(response: GalvaErrorResponse);
111
+ /**
112
+ * Creates a GalvaError from an unknown error.
113
+ * If the error is already a GalvaError, returns it as-is.
114
+ * Otherwise, wraps it in a GalvaError with UNKNOWN_ERROR code.
115
+ */
116
+ static from(error: unknown): GalvaError;
117
+ }
118
+
119
+ interface GalvaOptions {
120
+ /**
121
+ * Defaults to 'production'. Falls back to NODE_ENV.
122
+ * @type {('production' | 'development')}
123
+ */
124
+ environment?: 'production' | 'development';
125
+ /**
126
+ * API key from Galva dashboard. Falls back to GALVA_API_KEY env var.
127
+ * @type {string}
128
+ */
129
+ apiKey?: string;
130
+ /**
131
+ * Request timeout in ms. Defaults to 10000.
132
+ * @type {number}
133
+ */
134
+ timeout?: number;
135
+ }
136
+ /**
137
+ * Credentials for authenticating with the Google Play Store API.
138
+ *
139
+ * @interface PlaystoreCredentials
140
+ */
141
+ interface PlaystoreCredentials {
142
+ /** Service account email address */
143
+ email: string;
144
+ /** Service account private key */
145
+ key: string;
146
+ }
147
+ /**
148
+ * Credentials for authenticating and verifying Paddle webhooks.
149
+ *
150
+ * @interface PaddleCredentials
151
+ */
152
+ interface PaddleCredentials {
153
+ /** Paddle API key */
154
+ apiKey: string;
155
+ /** Webhook secret key for signature verification */
156
+ secretKey: string;
157
+ }
158
+ /**
159
+ * Credentials for Apple App Store.
160
+ *
161
+ * @interface AppstoreCredentials
162
+ */
163
+ interface AppstoreCredentials {
164
+ /** The app's bundle identifier (e.g., 'com.example.app') */
165
+ bundleId: string;
166
+ /** The app's Apple ID from App Store Connect */
167
+ appAppleId: number;
168
+ }
169
+ /**
170
+ * Credentials configuration for withCredentials method.
171
+ *
172
+ * @interface CredentialsConfig
173
+ */
174
+ interface CredentialsConfig {
175
+ /** App Store credentials */
176
+ appstore?: AppstoreCredentials;
177
+ /** Play Store service account credentials */
178
+ playstore?: PlaystoreCredentials;
179
+ /** Paddle API credentials */
180
+ paddle?: PaddleCredentials;
181
+ }
182
+ /**
183
+ * Base Galva SDK client with end user management methods.
184
+ * @class GalvaBase
185
+ */
186
+ declare class GalvaBase {
187
+ protected config: GalvaOptions;
188
+ protected readonly baseUrl: string;
189
+ /**
190
+ * @param {GalvaOptions} [options] - Configuration options
191
+ * @throws {GalvaError} If API key is not provided
192
+ */
193
+ constructor(options?: GalvaOptions);
194
+ protected sendRequest(method: 'POST' | 'GET', endpoint: string, data?: unknown): Promise<void>;
195
+ /** End user management methods. */
196
+ endUser: {
197
+ /**
198
+ * Identifies an end user with optional traits and context.
199
+ * @param {string} endUserId - End user ID in your system
200
+ * @param {Object} [options] - Identification data
201
+ * @param {string} [options.timestamp] - ISO 8601 timestamp
202
+ * @param {Record<string, any>} [options.context] - Additional context
203
+ * @param {EndUserTraits} [options.traits] - User traits. Use EndUserDefaultTraitName for built-in traits.
204
+ * @throws {GalvaError} If API request fails
205
+ * @example
206
+ * ```typescript
207
+ * await galva.endUser.identify('user-123', {
208
+ * traits: { [EndUserDefaultTraitName.EMAIL]: 'user@example.com' },
209
+ * });
210
+ * ```
211
+ */
212
+ identify: (endUserId: string, options?: {
213
+ timestamp?: string;
214
+ context?: Record<string, any>;
215
+ traits?: EndUserTraits;
216
+ }) => Promise<void>;
217
+ /**
218
+ * Updates an end user's default profile info.
219
+ * @param {string} endUserId - End user ID in your system
220
+ * @param {EndUserDefaultInfo} defaultInfo - Profile info to update
221
+ * @throws {GalvaError} If API request fails
222
+ * @example
223
+ * ```typescript
224
+ * await galva.endUser.updateDefaultInfo('user-123', {
225
+ * email: 'user@example.com',
226
+ * fullName: 'John Doe',
227
+ * });
228
+ * ```
229
+ */
230
+ updateDefaultInfo: (endUserId: string, defaultInfo: EndUserDefaultInfo) => Promise<void>;
231
+ };
232
+ }
233
+ declare class GalvaWithCreds extends GalvaBase {
234
+ private credentials;
235
+ /**
236
+ * @internal Use `galva.withCredentials(...)` instead.
237
+ */
238
+ constructor(config: GalvaOptions, credentials: CredentialsConfig);
239
+ /**
240
+ * Billing event handlers with credential support.
241
+ * @property {Function} appstore - App Store events (signed payload only)
242
+ * @property {Function} playstore - Play Store events (raw base64 payload)
243
+ * @property {Function} paddle - Paddle events (raw body with verification)
244
+ */
245
+ billingEvent: {
246
+ /**
247
+ * Tracks an App Store billing event. Uses credentials from withCredentials().
248
+ * @param {string} endUserId - End user ID
249
+ * @param {string} signedPayload - Signed payload from Apple
250
+ * @param {Record<string, any>} [options] - Additional options
251
+ * @returns {Promise<void>}
252
+ * @throws {GalvaError} If credentials missing or API fails
253
+ * @example
254
+ * ```typescript
255
+ * await galvaWithCreds.billingEvent.appstore('user-123', signedPayload);
256
+ * ```
257
+ */
258
+ appstore: (endUserId: string, signedPayload: string, options?: Record<string, any>) => Promise<void>;
259
+ /**
260
+ * Tracks a Play Store billing event. Decodes payload and fetches subscription details.
261
+ * @param {string} endUserId - End user ID
262
+ * @param {string} base64Payload - Base64 payload from Pub/Sub
263
+ * @returns {Promise<void>}
264
+ * @throws {GalvaError} If credentials missing or API fails
265
+ * @example
266
+ * ```typescript
267
+ * await galvaWithCreds.billingEvent.playstore('user-123', base64Payload);
268
+ * ```
269
+ */
270
+ playstore: (endUserId: string, base64Payload: string) => Promise<void>;
271
+ /**
272
+ * Tracks a Paddle billing event. Verifies signature and unmarshals the event.
273
+ * @param {string} endUserId - End user ID
274
+ * @param {string} rawBody - Raw body from Paddle webhook
275
+ * @param {string} signature - Paddle-Signature header value
276
+ * @returns {Promise<void>}
277
+ * @throws {GalvaError} If credentials missing or API fails
278
+ * @example
279
+ * ```typescript
280
+ * await galvaWithCreds.billingEvent.paddle('user-123', rawBody, signature);
281
+ * ```
282
+ */
283
+ paddle: (endUserId: string, rawBody: string, signature: string) => Promise<void>;
284
+ };
285
+ }
286
+ declare class Galva extends GalvaBase {
287
+ constructor(options?: GalvaOptions);
288
+ /**
289
+ * Billing event handlers for different payment platforms.
290
+ * @property {Function} appstore - App Store events
291
+ * @property {Function} playstore - Play Store events (decoded payload)
292
+ * @property {Function} paddle - Paddle events (verified event)
293
+ */
294
+ billingEvent: {
295
+ /**
296
+ * Tracks an App Store billing event.
297
+ * @param {string} endUserId - End user ID
298
+ * @param {Object} payload - App Store notification payload
299
+ * @param {string} payload.signedPayload - Signed payload from Apple
300
+ * @param {string} payload.bundleId - App bundle identifier
301
+ * @param {number} payload.appAppleId - App Apple ID
302
+ * @param {Record<string, any>} [options] - Additional options
303
+ * @throws {GalvaError} If API request fails
304
+ * @example
305
+ * ```typescript
306
+ * await galva.billingEvent.appstore('user-123', {
307
+ * signedPayload,
308
+ * bundleId: 'com.example.app',
309
+ * appAppleId: 123456789
310
+ * });
311
+ * ```
312
+ */
313
+ appstore: (endUserId: string, payload: {
314
+ signedPayload: string;
315
+ bundleId: string;
316
+ appAppleId: number;
317
+ }, options?: Record<string, any>) => Promise<void>;
318
+ /**
319
+ * Tracks a Play Store billing event with decoded notification.
320
+ * @param {string} endUserId - End user ID
321
+ * @param {PlaystoreDeveloperNotification} payload - Decoded developer notification
322
+ * @returns {Promise<void>}
323
+ * @example
324
+ * ```typescript
325
+ * await galva.billingEvent.playstore('user-123', decodedNotification);
326
+ * ```
327
+ */
328
+ playstore: (endUserId: string, payload: PlaystoreDeveloperNotification) => Promise<void>;
329
+ /**
330
+ * Tracks a Paddle billing event with verified event entity.
331
+ * @param {string} endUserId - End user ID
332
+ * @param {EventEntity} eventEntity - Verified Paddle event
333
+ * @returns {Promise<void>}
334
+ * @example
335
+ * ```typescript
336
+ * await galva.billingEvent.paddle('user-123', verifiedEvent);
337
+ * ```
338
+ */
339
+ paddle: (endUserId: string, eventEntity: EventEntity) => Promise<void>;
340
+ };
341
+ withCredentials(credentials: CredentialsConfig): GalvaWithCreds;
342
+ }
343
+
344
+ export { type AppstoreCredentials, type CredentialsConfig, END_USER_DEFAULT_INFO_TO_TRAIT_MAP, END_USER_DEFAULT_TRAIT_MAP, type EndUserDefaultInfo, EndUserDefaultTraitName, type EndUserDefaultTraits, type EndUserTraits, Galva, GalvaError, type GalvaErrorResponse, type GalvaOptions, type PaddleCredentials, type PlaystoreCredentials };
@@ -0,0 +1,344 @@
1
+ import { androidpublisher_v3 } from 'googleapis';
2
+ import { EventEntity } from '@paddle/paddle-node-sdk';
3
+
4
+ interface PlaystoreOneTimeProductNotification {
5
+ version: string;
6
+ notificationType: number;
7
+ purchaseToken: string;
8
+ sku: string;
9
+ }
10
+ interface PlaystoreVoidedPurchaseNotification {
11
+ purchaseToken: string;
12
+ orderId: string;
13
+ productType: number;
14
+ refundType: number;
15
+ }
16
+ interface PlaystoreSubscriptionNotification {
17
+ version: string;
18
+ notificationType: number;
19
+ purchaseToken: string;
20
+ subscriptionPurchase: androidpublisher_v3.Schema$SubscriptionPurchaseV2;
21
+ }
22
+ interface PlaystoreTestNotification {
23
+ version: string;
24
+ }
25
+ interface PlaystoreDeveloperNotificationBase {
26
+ version: string;
27
+ packageName: string;
28
+ eventTimeMillis: number;
29
+ }
30
+ type PlaystoreDeveloperNotification = (PlaystoreDeveloperNotificationBase & {
31
+ subscriptionNotification: PlaystoreSubscriptionNotification;
32
+ }) | (PlaystoreDeveloperNotificationBase & {
33
+ oneTimeProductNotification: PlaystoreOneTimeProductNotification;
34
+ }) | (PlaystoreDeveloperNotificationBase & {
35
+ voidedPurchaseNotification: PlaystoreVoidedPurchaseNotification;
36
+ }) | (PlaystoreDeveloperNotificationBase & {
37
+ testNotification: PlaystoreTestNotification;
38
+ });
39
+
40
+ /**
41
+ * Default trait names for end user profiles.
42
+ * Use these constants for type-safe access to built-in traits.
43
+ */
44
+ declare enum EndUserDefaultTraitName {
45
+ TIMEZONE = "$gv_timezone",
46
+ LANGUAGE_CODE = "$gv_languageCode",
47
+ EMAIL = "$gv_email",
48
+ FULL_NAME = "$gv_fullName",
49
+ FIRST_NAME = "$gv_firstName",
50
+ LAST_NAME = "$gv_lastName",
51
+ COUNTRY = "$gv_country",
52
+ TOTAL_LIFETIME_VALUE = "$gv_totalLifetimeValue"
53
+ }
54
+ /**
55
+ * Built-in trait types with their expected value types.
56
+ */
57
+ interface EndUserDefaultTraits {
58
+ [EndUserDefaultTraitName.TIMEZONE]?: string;
59
+ [EndUserDefaultTraitName.LANGUAGE_CODE]?: string;
60
+ [EndUserDefaultTraitName.EMAIL]?: string;
61
+ [EndUserDefaultTraitName.FULL_NAME]?: string;
62
+ [EndUserDefaultTraitName.FIRST_NAME]?: string;
63
+ [EndUserDefaultTraitName.LAST_NAME]?: string;
64
+ [EndUserDefaultTraitName.COUNTRY]?: string;
65
+ [EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE]?: number;
66
+ }
67
+ /**
68
+ * End user traits combining default traits with arbitrary custom traits.
69
+ */
70
+ type EndUserTraits = EndUserDefaultTraits & Record<string, any>;
71
+ /**
72
+ * Friendly parameter names for updating end user default info.
73
+ * Maps to the underlying $gv_ prefixed trait names.
74
+ */
75
+ interface EndUserDefaultInfo {
76
+ timezone?: string;
77
+ languageCode?: string;
78
+ email?: string;
79
+ fullName?: string;
80
+ firstName?: string;
81
+ lastName?: string;
82
+ country?: string;
83
+ totalLifetimeValue?: number;
84
+ }
85
+ /**
86
+ * Mapping from friendly parameter names to original trait names.
87
+ * TypeScript ensures this mapping contains all keys from EndUserDefaultTraitName.
88
+ */
89
+ declare const END_USER_DEFAULT_TRAIT_MAP: Record<EndUserDefaultTraitName, keyof EndUserDefaultInfo>;
90
+ /**
91
+ * Reverse mapping from friendly parameter names to trait names.
92
+ * TypeScript ensures this mapping contains all keys from EndUserDefaultInfo.
93
+ */
94
+ declare const END_USER_DEFAULT_INFO_TO_TRAIT_MAP: Record<keyof EndUserDefaultInfo, EndUserDefaultTraitName>;
95
+
96
+ /**
97
+ * Error response structure from Galva API.
98
+ */
99
+ interface GalvaErrorResponse {
100
+ code: string;
101
+ message: string;
102
+ }
103
+ /**
104
+ * Custom error class for Galva SDK errors.
105
+ * Extends Error with additional properties from API error responses.
106
+ */
107
+ declare class GalvaError extends Error {
108
+ /** Error code from the API (e.g., 'BAD_REQUEST', 'UNAUTHORIZED') */
109
+ readonly code: string;
110
+ constructor(response: GalvaErrorResponse);
111
+ /**
112
+ * Creates a GalvaError from an unknown error.
113
+ * If the error is already a GalvaError, returns it as-is.
114
+ * Otherwise, wraps it in a GalvaError with UNKNOWN_ERROR code.
115
+ */
116
+ static from(error: unknown): GalvaError;
117
+ }
118
+
119
+ interface GalvaOptions {
120
+ /**
121
+ * Defaults to 'production'. Falls back to NODE_ENV.
122
+ * @type {('production' | 'development')}
123
+ */
124
+ environment?: 'production' | 'development';
125
+ /**
126
+ * API key from Galva dashboard. Falls back to GALVA_API_KEY env var.
127
+ * @type {string}
128
+ */
129
+ apiKey?: string;
130
+ /**
131
+ * Request timeout in ms. Defaults to 10000.
132
+ * @type {number}
133
+ */
134
+ timeout?: number;
135
+ }
136
+ /**
137
+ * Credentials for authenticating with the Google Play Store API.
138
+ *
139
+ * @interface PlaystoreCredentials
140
+ */
141
+ interface PlaystoreCredentials {
142
+ /** Service account email address */
143
+ email: string;
144
+ /** Service account private key */
145
+ key: string;
146
+ }
147
+ /**
148
+ * Credentials for authenticating and verifying Paddle webhooks.
149
+ *
150
+ * @interface PaddleCredentials
151
+ */
152
+ interface PaddleCredentials {
153
+ /** Paddle API key */
154
+ apiKey: string;
155
+ /** Webhook secret key for signature verification */
156
+ secretKey: string;
157
+ }
158
+ /**
159
+ * Credentials for Apple App Store.
160
+ *
161
+ * @interface AppstoreCredentials
162
+ */
163
+ interface AppstoreCredentials {
164
+ /** The app's bundle identifier (e.g., 'com.example.app') */
165
+ bundleId: string;
166
+ /** The app's Apple ID from App Store Connect */
167
+ appAppleId: number;
168
+ }
169
+ /**
170
+ * Credentials configuration for withCredentials method.
171
+ *
172
+ * @interface CredentialsConfig
173
+ */
174
+ interface CredentialsConfig {
175
+ /** App Store credentials */
176
+ appstore?: AppstoreCredentials;
177
+ /** Play Store service account credentials */
178
+ playstore?: PlaystoreCredentials;
179
+ /** Paddle API credentials */
180
+ paddle?: PaddleCredentials;
181
+ }
182
+ /**
183
+ * Base Galva SDK client with end user management methods.
184
+ * @class GalvaBase
185
+ */
186
+ declare class GalvaBase {
187
+ protected config: GalvaOptions;
188
+ protected readonly baseUrl: string;
189
+ /**
190
+ * @param {GalvaOptions} [options] - Configuration options
191
+ * @throws {GalvaError} If API key is not provided
192
+ */
193
+ constructor(options?: GalvaOptions);
194
+ protected sendRequest(method: 'POST' | 'GET', endpoint: string, data?: unknown): Promise<void>;
195
+ /** End user management methods. */
196
+ endUser: {
197
+ /**
198
+ * Identifies an end user with optional traits and context.
199
+ * @param {string} endUserId - End user ID in your system
200
+ * @param {Object} [options] - Identification data
201
+ * @param {string} [options.timestamp] - ISO 8601 timestamp
202
+ * @param {Record<string, any>} [options.context] - Additional context
203
+ * @param {EndUserTraits} [options.traits] - User traits. Use EndUserDefaultTraitName for built-in traits.
204
+ * @throws {GalvaError} If API request fails
205
+ * @example
206
+ * ```typescript
207
+ * await galva.endUser.identify('user-123', {
208
+ * traits: { [EndUserDefaultTraitName.EMAIL]: 'user@example.com' },
209
+ * });
210
+ * ```
211
+ */
212
+ identify: (endUserId: string, options?: {
213
+ timestamp?: string;
214
+ context?: Record<string, any>;
215
+ traits?: EndUserTraits;
216
+ }) => Promise<void>;
217
+ /**
218
+ * Updates an end user's default profile info.
219
+ * @param {string} endUserId - End user ID in your system
220
+ * @param {EndUserDefaultInfo} defaultInfo - Profile info to update
221
+ * @throws {GalvaError} If API request fails
222
+ * @example
223
+ * ```typescript
224
+ * await galva.endUser.updateDefaultInfo('user-123', {
225
+ * email: 'user@example.com',
226
+ * fullName: 'John Doe',
227
+ * });
228
+ * ```
229
+ */
230
+ updateDefaultInfo: (endUserId: string, defaultInfo: EndUserDefaultInfo) => Promise<void>;
231
+ };
232
+ }
233
+ declare class GalvaWithCreds extends GalvaBase {
234
+ private credentials;
235
+ /**
236
+ * @internal Use `galva.withCredentials(...)` instead.
237
+ */
238
+ constructor(config: GalvaOptions, credentials: CredentialsConfig);
239
+ /**
240
+ * Billing event handlers with credential support.
241
+ * @property {Function} appstore - App Store events (signed payload only)
242
+ * @property {Function} playstore - Play Store events (raw base64 payload)
243
+ * @property {Function} paddle - Paddle events (raw body with verification)
244
+ */
245
+ billingEvent: {
246
+ /**
247
+ * Tracks an App Store billing event. Uses credentials from withCredentials().
248
+ * @param {string} endUserId - End user ID
249
+ * @param {string} signedPayload - Signed payload from Apple
250
+ * @param {Record<string, any>} [options] - Additional options
251
+ * @returns {Promise<void>}
252
+ * @throws {GalvaError} If credentials missing or API fails
253
+ * @example
254
+ * ```typescript
255
+ * await galvaWithCreds.billingEvent.appstore('user-123', signedPayload);
256
+ * ```
257
+ */
258
+ appstore: (endUserId: string, signedPayload: string, options?: Record<string, any>) => Promise<void>;
259
+ /**
260
+ * Tracks a Play Store billing event. Decodes payload and fetches subscription details.
261
+ * @param {string} endUserId - End user ID
262
+ * @param {string} base64Payload - Base64 payload from Pub/Sub
263
+ * @returns {Promise<void>}
264
+ * @throws {GalvaError} If credentials missing or API fails
265
+ * @example
266
+ * ```typescript
267
+ * await galvaWithCreds.billingEvent.playstore('user-123', base64Payload);
268
+ * ```
269
+ */
270
+ playstore: (endUserId: string, base64Payload: string) => Promise<void>;
271
+ /**
272
+ * Tracks a Paddle billing event. Verifies signature and unmarshals the event.
273
+ * @param {string} endUserId - End user ID
274
+ * @param {string} rawBody - Raw body from Paddle webhook
275
+ * @param {string} signature - Paddle-Signature header value
276
+ * @returns {Promise<void>}
277
+ * @throws {GalvaError} If credentials missing or API fails
278
+ * @example
279
+ * ```typescript
280
+ * await galvaWithCreds.billingEvent.paddle('user-123', rawBody, signature);
281
+ * ```
282
+ */
283
+ paddle: (endUserId: string, rawBody: string, signature: string) => Promise<void>;
284
+ };
285
+ }
286
+ declare class Galva extends GalvaBase {
287
+ constructor(options?: GalvaOptions);
288
+ /**
289
+ * Billing event handlers for different payment platforms.
290
+ * @property {Function} appstore - App Store events
291
+ * @property {Function} playstore - Play Store events (decoded payload)
292
+ * @property {Function} paddle - Paddle events (verified event)
293
+ */
294
+ billingEvent: {
295
+ /**
296
+ * Tracks an App Store billing event.
297
+ * @param {string} endUserId - End user ID
298
+ * @param {Object} payload - App Store notification payload
299
+ * @param {string} payload.signedPayload - Signed payload from Apple
300
+ * @param {string} payload.bundleId - App bundle identifier
301
+ * @param {number} payload.appAppleId - App Apple ID
302
+ * @param {Record<string, any>} [options] - Additional options
303
+ * @throws {GalvaError} If API request fails
304
+ * @example
305
+ * ```typescript
306
+ * await galva.billingEvent.appstore('user-123', {
307
+ * signedPayload,
308
+ * bundleId: 'com.example.app',
309
+ * appAppleId: 123456789
310
+ * });
311
+ * ```
312
+ */
313
+ appstore: (endUserId: string, payload: {
314
+ signedPayload: string;
315
+ bundleId: string;
316
+ appAppleId: number;
317
+ }, options?: Record<string, any>) => Promise<void>;
318
+ /**
319
+ * Tracks a Play Store billing event with decoded notification.
320
+ * @param {string} endUserId - End user ID
321
+ * @param {PlaystoreDeveloperNotification} payload - Decoded developer notification
322
+ * @returns {Promise<void>}
323
+ * @example
324
+ * ```typescript
325
+ * await galva.billingEvent.playstore('user-123', decodedNotification);
326
+ * ```
327
+ */
328
+ playstore: (endUserId: string, payload: PlaystoreDeveloperNotification) => Promise<void>;
329
+ /**
330
+ * Tracks a Paddle billing event with verified event entity.
331
+ * @param {string} endUserId - End user ID
332
+ * @param {EventEntity} eventEntity - Verified Paddle event
333
+ * @returns {Promise<void>}
334
+ * @example
335
+ * ```typescript
336
+ * await galva.billingEvent.paddle('user-123', verifiedEvent);
337
+ * ```
338
+ */
339
+ paddle: (endUserId: string, eventEntity: EventEntity) => Promise<void>;
340
+ };
341
+ withCredentials(credentials: CredentialsConfig): GalvaWithCreds;
342
+ }
343
+
344
+ export { type AppstoreCredentials, type CredentialsConfig, END_USER_DEFAULT_INFO_TO_TRAIT_MAP, END_USER_DEFAULT_TRAIT_MAP, type EndUserDefaultInfo, EndUserDefaultTraitName, type EndUserDefaultTraits, type EndUserTraits, Galva, GalvaError, type GalvaErrorResponse, type GalvaOptions, type PaddleCredentials, type PlaystoreCredentials };
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import {google}from'googleapis';import {Paddle}from'@paddle/paddle-node-sdk';var T=n=>{let e=new google.auth.JWT({email:n.email,key:n.key.replace(/\\n/g,`
2
+ `),scopes:["https://www.googleapis.com/auth/androidpublisher"]});return google.androidpublisher({version:"v3",auth:e})},p=class{static async getSubscription(e,t,a){return await T({email:a.email,key:a.key}).purchases.subscriptionsv2.get({token:e,packageName:t})}};var u=(o=>(o.TIMEZONE="$gv_timezone",o.LANGUAGE_CODE="$gv_languageCode",o.EMAIL="$gv_email",o.FULL_NAME="$gv_fullName",o.FIRST_NAME="$gv_firstName",o.LAST_NAME="$gv_lastName",o.COUNTRY="$gv_country",o.TOTAL_LIFETIME_VALUE="$gv_totalLifetimeValue",o))(u||{}),N={$gv_timezone:"timezone",$gv_languageCode:"languageCode",$gv_email:"email",$gv_fullName:"fullName",$gv_firstName:"firstName",$gv_lastName:"lastName",$gv_country:"country",$gv_totalLifetimeValue:"totalLifetimeValue"},m={timezone:"$gv_timezone",languageCode:"$gv_languageCode",email:"$gv_email",fullName:"$gv_fullName",firstName:"$gv_firstName",lastName:"$gv_lastName",country:"$gv_country",totalLifetimeValue:"$gv_totalLifetimeValue"};var s=class n extends Error{code;constructor(e){super(e.message),this.name="GalvaError",this.code=e.code,Error.captureStackTrace&&Error.captureStackTrace(this,n);}static from(e){if(e instanceof n)return e;let t=e instanceof Error?e.message:"An unknown error occurred";return new n({code:"UNKNOWN_ERROR",message:t})}};var A="https://api.galva.io",U="https://api.galva.dev",c=class{config;baseUrl;constructor(e){let t=e?.apiKey||process.env.GALVA_API_KEY;if(!t)throw new s({code:"MISSING_API_KEY",message:"API key is required. Provide it in options or set GALVA_API_KEY env variable."});this.config={apiKey:t,environment:e?.environment||(process.env.NODE_ENV==="development"?"development":"production"),timeout:e?.timeout||1e4},this.baseUrl=this.config.environment==="development"?U:A;}async sendRequest(e,t,a){let r=`${this.baseUrl}${t}`,i=new AbortController,l=setTimeout(()=>i.abort(),this.config.timeout);try{let d=await fetch(r,{method:e,headers:{"Content-Type":"application/json","x-api-key":this.config.apiKey},body:a?JSON.stringify(a):void 0,signal:i.signal});if(!d.ok){let o=await d.json();throw new s(o)}}catch(d){throw d instanceof s?d:d instanceof Error&&d.name==="AbortError"?new s({code:"TIMEOUT",message:`Request timed out after ${this.config.timeout}ms`}):s.from(d)}finally{clearTimeout(l);}}endUser={identify:async(e,t)=>{await this.sendRequest("POST","/endUsers:identify",{endUserId:e,timestamp:t?.timestamp,context:t?.context,traits:t?.traits});},updateDefaultInfo:async(e,t)=>{let a={};for(let[r,i]of Object.entries(t))if(i!==void 0){let l=m[r];a[l]=i;}await this.sendRequest("POST","/endUsers:identify",{endUserId:e,traits:a});}}},E=class extends c{credentials;constructor(e,t){super(e),this.credentials=t;}billingEvent={appstore:async(e,t,a)=>{if(!this.credentials.appstore)throw new s({code:"MISSING_CREDENTIALS",message:"App Store credentials are required. Provide them in withCredentials()."});await this.sendRequest("POST","/endUsers/billingEvents",{platform:"appstore",endUserId:e,payload:{signedPayload:t,appAppleId:this.credentials.appstore.appAppleId,bundleId:this.credentials.appstore.bundleId,env:this.config.environment},options:a});},playstore:async(e,t)=>{if(!this.credentials.playstore)throw new s({code:"MISSING_CREDENTIALS",message:"Play Store credentials are required. Provide them in withCredentials()."});let a=Buffer.from(t,"base64").toString(),r=null;try{r=JSON.parse(a);}catch{throw new s({code:"INVALID_PAYLOAD",message:"Invalid base64 payload: unable to parse JSON."})}if(!r||typeof r!="object")throw new s({code:"INVALID_PAYLOAD",message:"Decoded payload is not a valid JSON object."});if(!("subscriptionNotification"in r))throw new s({code:"INVALID_PAYLOAD",message:"Decoded payload does not contain subscriptionNotification field."});let i=await p.getSubscription(r.subscriptionNotification.purchaseToken,r.packageName,this.credentials.playstore),l={...r,subscriptionNotification:{...r.subscriptionNotification,subscriptionPurchase:i.data}};await this.sendRequest("POST","/endUsers/billingEvents",{platform:"playstore",endUserId:e,payload:l});},paddle:async(e,t,a)=>{if(!this.credentials.paddle)throw new s({code:"MISSING_CREDENTIALS",message:"Paddle credentials are required. Provide them in withCredentials()."});let r=new Paddle(this.credentials.paddle.apiKey),i;try{i=await r.webhooks.unmarshal(t,this.credentials.paddle.secretKey,a);}catch(l){throw new s({code:"INVALID_WEBHOOK",message:`Invalid Paddle webhook data: ${l.message}`})}await this.sendRequest("POST","/endUsers/billingEvents",{platform:"paddle",endUserId:e,payload:i});}}},g=class extends c{constructor(e){super(e);}billingEvent={appstore:async(e,t,a)=>{let{signedPayload:r,bundleId:i,appAppleId:l}=t;await this.sendRequest("POST","/endUsers/billingEvents",{platform:"appstore",endUserId:e,payload:{signedPayload:r,appAppleId:l,bundleId:i,env:this.config.environment},options:a});},playstore:async(e,t)=>{await this.sendRequest("POST","/endUsers/billingEvents",{platform:"playstore",endUserId:e,payload:t});},paddle:async(e,t)=>{await this.sendRequest("POST","/endUsers/billingEvents",{platform:"paddle",endUserId:e,payload:t});}};withCredentials(e){return new E(this.config,e)}};
3
+ export{m as END_USER_DEFAULT_INFO_TO_TRAIT_MAP,N as END_USER_DEFAULT_TRAIT_MAP,u as EndUserDefaultTraitName,g as Galva,s as GalvaError};//# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/playstore.ts","../src/types/endUser.ts","../src/types/error.ts","../src/galva.ts"],"names":["authentication","payload","jwtClient","google","PlaystoreService","token","packageName","cred","EndUserDefaultTraitName","END_USER_DEFAULT_TRAIT_MAP","END_USER_DEFAULT_INFO_TO_TRAIT_MAP","GalvaError","_GalvaError","response","error","message","PRODUCTION_API_URL","DEVELOPMENT_API_URL","GalvaBase","options","apiKey","method","endpoint","data","url","controller","timeoutId","errorResponse","endUserId","defaultInfo","traits","key","value","traitName","GalvaWithCreds","config","credentials","signedPayload","base64Payload","decodedPayloadString","decodedPayload","subscription","finalPayload","rawBody","signature","paddle","Paddle","eventData","Galva","bundleId","appAppleId","eventEntity"],"mappings":"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","file":"index.js","sourcesContent":["import { PlaystoreCredentials } from '@/galva';\nimport { google } from 'googleapis';\n\nconst authentication = (payload: { email: string; key: string }) => {\n const jwtClient = new google.auth.JWT({\n email: payload.email,\n key: payload.key.replace(/\\\\n/g, '\\n'),\n scopes: ['https://www.googleapis.com/auth/androidpublisher'],\n });\n return google.androidpublisher({\n version: 'v3',\n auth: jwtClient,\n });\n};\n\nexport class PlaystoreService {\n static async getSubscription(\n token: string,\n packageName: string,\n cred: PlaystoreCredentials,\n ): Promise<any> {\n const client = authentication({\n email: cred.email,\n key: cred.key,\n });\n\n const result = await client.purchases.subscriptionsv2.get({\n token,\n packageName,\n });\n\n return result;\n }\n}\n","/**\n * Default trait names for end user profiles.\n * Use these constants for type-safe access to built-in traits.\n */\nexport enum EndUserDefaultTraitName {\n TIMEZONE = \"$gv_timezone\",\n LANGUAGE_CODE = \"$gv_languageCode\",\n EMAIL = \"$gv_email\",\n FULL_NAME = \"$gv_fullName\",\n FIRST_NAME = \"$gv_firstName\",\n LAST_NAME = \"$gv_lastName\",\n COUNTRY = \"$gv_country\",\n TOTAL_LIFETIME_VALUE = \"$gv_totalLifetimeValue\",\n}\n\n/**\n * Built-in trait types with their expected value types.\n */\nexport interface EndUserDefaultTraits {\n [EndUserDefaultTraitName.TIMEZONE]?: string;\n [EndUserDefaultTraitName.LANGUAGE_CODE]?: string;\n [EndUserDefaultTraitName.EMAIL]?: string;\n [EndUserDefaultTraitName.FULL_NAME]?: string;\n [EndUserDefaultTraitName.FIRST_NAME]?: string;\n [EndUserDefaultTraitName.LAST_NAME]?: string;\n [EndUserDefaultTraitName.COUNTRY]?: string;\n [EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE]?: number;\n}\n\n/**\n * End user traits combining default traits with arbitrary custom traits.\n */\nexport type EndUserTraits = EndUserDefaultTraits & Record<string, any>;\n\n/**\n * Friendly parameter names for updating end user default info.\n * Maps to the underlying $gv_ prefixed trait names.\n */\nexport interface EndUserDefaultInfo {\n timezone?: string;\n languageCode?: string;\n email?: string;\n fullName?: string;\n firstName?: string;\n lastName?: string;\n country?: string;\n totalLifetimeValue?: number;\n}\n\n/**\n * Mapping from friendly parameter names to original trait names.\n * TypeScript ensures this mapping contains all keys from EndUserDefaultTraitName.\n */\nexport const END_USER_DEFAULT_TRAIT_MAP: Record<\n EndUserDefaultTraitName,\n keyof EndUserDefaultInfo\n> = {\n [EndUserDefaultTraitName.TIMEZONE]: \"timezone\",\n [EndUserDefaultTraitName.LANGUAGE_CODE]: \"languageCode\",\n [EndUserDefaultTraitName.EMAIL]: \"email\",\n [EndUserDefaultTraitName.FULL_NAME]: \"fullName\",\n [EndUserDefaultTraitName.FIRST_NAME]: \"firstName\",\n [EndUserDefaultTraitName.LAST_NAME]: \"lastName\",\n [EndUserDefaultTraitName.COUNTRY]: \"country\",\n [EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE]: \"totalLifetimeValue\",\n};\n\n/**\n * Reverse mapping from friendly parameter names to trait names.\n * TypeScript ensures this mapping contains all keys from EndUserDefaultInfo.\n */\nexport const END_USER_DEFAULT_INFO_TO_TRAIT_MAP: Record<\n keyof EndUserDefaultInfo,\n EndUserDefaultTraitName\n> = {\n timezone: EndUserDefaultTraitName.TIMEZONE,\n languageCode: EndUserDefaultTraitName.LANGUAGE_CODE,\n email: EndUserDefaultTraitName.EMAIL,\n fullName: EndUserDefaultTraitName.FULL_NAME,\n firstName: EndUserDefaultTraitName.FIRST_NAME,\n lastName: EndUserDefaultTraitName.LAST_NAME,\n country: EndUserDefaultTraitName.COUNTRY,\n totalLifetimeValue: EndUserDefaultTraitName.TOTAL_LIFETIME_VALUE,\n};\n","/**\n * Error response structure from Galva API.\n */\nexport interface GalvaErrorResponse {\n code: string;\n message: string;\n}\n\n/**\n * Custom error class for Galva SDK errors.\n * Extends Error with additional properties from API error responses.\n */\nexport class GalvaError extends Error {\n /** Error code from the API (e.g., 'BAD_REQUEST', 'UNAUTHORIZED') */\n readonly code: string;\n\n constructor(response: GalvaErrorResponse) {\n super(response.message);\n this.name = \"GalvaError\";\n this.code = response.code;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, GalvaError);\n }\n }\n\n /**\n * Creates a GalvaError from an unknown error.\n * If the error is already a GalvaError, returns it as-is.\n * Otherwise, wraps it in a GalvaError with UNKNOWN_ERROR code.\n */\n static from(error: unknown): GalvaError {\n if (error instanceof GalvaError) {\n return error;\n }\n\n const message =\n error instanceof Error ? error.message : \"An unknown error occurred\";\n\n return new GalvaError({\n code: \"UNKNOWN_ERROR\",\n message,\n });\n }\n}\n","import { PlaystoreDeveloperNotification } from './types/playstore';\nimport { PlaystoreService } from './services/playstore';\nimport { EventEntity, Paddle } from '@paddle/paddle-node-sdk';\nimport type { EndUserTraits, EndUserDefaultInfo } from './types/endUser';\nimport { END_USER_DEFAULT_INFO_TO_TRAIT_MAP } from './types/endUser';\nimport { GalvaError } from './types/error';\n\nexport type {\n EndUserDefaultTraits,\n EndUserTraits,\n EndUserDefaultInfo,\n} from './types/endUser';\nexport {\n EndUserDefaultTraitName,\n END_USER_DEFAULT_TRAIT_MAP,\n END_USER_DEFAULT_INFO_TO_TRAIT_MAP,\n} from './types/endUser';\nexport { GalvaError } from './types/error';\nexport type { GalvaErrorResponse } from './types/error';\n\nexport interface GalvaOptions {\n /**\n * Defaults to 'production'. Falls back to NODE_ENV.\n * @type {('production' | 'development')}\n */\n environment?: 'production' | 'development';\n\n /**\n * API key from Galva dashboard. Falls back to GALVA_API_KEY env var.\n * @type {string}\n */\n apiKey?: string;\n\n /**\n * Request timeout in ms. Defaults to 10000.\n * @type {number}\n */\n timeout?: number;\n}\n\n/**\n * Credentials for authenticating with the Google Play Store API.\n *\n * @interface PlaystoreCredentials\n */\nexport interface PlaystoreCredentials {\n /** Service account email address */\n email: string;\n /** Service account private key */\n key: string;\n}\n\n/**\n * Credentials for authenticating and verifying Paddle webhooks.\n *\n * @interface PaddleCredentials\n */\nexport interface PaddleCredentials {\n /** Paddle API key */\n apiKey: string;\n /** Webhook secret key for signature verification */\n secretKey: string;\n}\n\n/**\n * Credentials for Apple App Store.\n *\n * @interface AppstoreCredentials\n */\nexport interface AppstoreCredentials {\n /** The app's bundle identifier (e.g., 'com.example.app') */\n bundleId: string;\n /** The app's Apple ID from App Store Connect */\n appAppleId: number;\n}\n\n/**\n * Credentials configuration for withCredentials method.\n *\n * @interface CredentialsConfig\n */\nexport interface CredentialsConfig {\n /** App Store credentials */\n appstore?: AppstoreCredentials;\n /** Play Store service account credentials */\n playstore?: PlaystoreCredentials;\n /** Paddle API credentials */\n paddle?: PaddleCredentials;\n}\n\nconst PRODUCTION_API_URL = 'https://api.galva.io';\nconst DEVELOPMENT_API_URL = 'https://api.galva.dev';\n\n/**\n * Base Galva SDK client with end user management methods.\n * @class GalvaBase\n */\nclass GalvaBase {\n protected config: GalvaOptions;\n protected readonly baseUrl: string;\n\n /**\n * @param {GalvaOptions} [options] - Configuration options\n * @throws {GalvaError} If API key is not provided\n */\n constructor(options?: GalvaOptions) {\n const apiKey = options?.apiKey || process.env.GALVA_API_KEY;\n if (!apiKey) {\n throw new GalvaError({\n code: 'MISSING_API_KEY',\n message:\n 'API key is required. Provide it in options or set GALVA_API_KEY env variable.',\n });\n }\n\n this.config = {\n apiKey: apiKey,\n environment:\n options?.environment ||\n (process.env.NODE_ENV === 'development' ? 'development' : 'production'),\n timeout: options?.timeout || 10000,\n };\n this.baseUrl =\n this.config.environment === 'development'\n ? DEVELOPMENT_API_URL\n : PRODUCTION_API_URL;\n }\n\n protected async sendRequest(\n method: 'POST' | 'GET',\n endpoint: string,\n data?: unknown,\n ): Promise<void> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.config.apiKey!,\n },\n body: data ? JSON.stringify(data) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorResponse = await response.json();\n throw new GalvaError(errorResponse);\n }\n } catch (error) {\n if (error instanceof GalvaError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new GalvaError({\n code: 'TIMEOUT',\n message: `Request timed out after ${this.config.timeout}ms`,\n });\n }\n throw GalvaError.from(error);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /** End user management methods. */\n public endUser = {\n /**\n * Identifies an end user with optional traits and context.\n * @param {string} endUserId - End user ID in your system\n * @param {Object} [options] - Identification data\n * @param {string} [options.timestamp] - ISO 8601 timestamp\n * @param {Record<string, any>} [options.context] - Additional context\n * @param {EndUserTraits} [options.traits] - User traits. Use EndUserDefaultTraitName for built-in traits.\n * @throws {GalvaError} If API request fails\n * @example\n * ```typescript\n * await galva.endUser.identify('user-123', {\n * traits: { [EndUserDefaultTraitName.EMAIL]: 'user@example.com' },\n * });\n * ```\n */\n identify: async (\n endUserId: string,\n options?: {\n timestamp?: string;\n context?: Record<string, any>;\n traits?: EndUserTraits;\n },\n ): Promise<void> => {\n await this.sendRequest('POST', '/endUsers:identify', {\n endUserId,\n timestamp: options?.timestamp,\n context: options?.context,\n traits: options?.traits,\n });\n },\n\n /**\n * Updates an end user's default profile info.\n * @param {string} endUserId - End user ID in your system\n * @param {EndUserDefaultInfo} defaultInfo - Profile info to update\n * @throws {GalvaError} If API request fails\n * @example\n * ```typescript\n * await galva.endUser.updateDefaultInfo('user-123', {\n * email: 'user@example.com',\n * fullName: 'John Doe',\n * });\n * ```\n */\n updateDefaultInfo: async (\n endUserId: string,\n defaultInfo: EndUserDefaultInfo,\n ): Promise<void> => {\n const traits: EndUserTraits = {};\n\n for (const [key, value] of Object.entries(defaultInfo)) {\n if (value !== undefined) {\n const traitName =\n END_USER_DEFAULT_INFO_TO_TRAIT_MAP[key as keyof EndUserDefaultInfo];\n traits[traitName] = value;\n }\n }\n\n await this.sendRequest('POST', '/endUsers:identify', {\n endUserId,\n traits,\n });\n },\n };\n}\n\nclass GalvaWithCreds extends GalvaBase {\n // private config: GalvaOptions;\n // private readonly baseUrl: string;\n private credentials: CredentialsConfig;\n\n /**\n * @internal Use `galva.withCredentials(...)` instead.\n */\n constructor(config: GalvaOptions, credentials: CredentialsConfig) {\n super(config);\n // this.config = config;\n // this.baseUrl = baseUrl;\n this.credentials = credentials;\n }\n\n /**\n * Billing event handlers with credential support.\n * @property {Function} appstore - App Store events (signed payload only)\n * @property {Function} playstore - Play Store events (raw base64 payload)\n * @property {Function} paddle - Paddle events (raw body with verification)\n */\n public billingEvent = {\n /**\n * Tracks an App Store billing event. Uses credentials from withCredentials().\n * @param {string} endUserId - End user ID\n * @param {string} signedPayload - Signed payload from Apple\n * @param {Record<string, any>} [options] - Additional options\n * @returns {Promise<void>}\n * @throws {GalvaError} If credentials missing or API fails\n * @example\n * ```typescript\n * await galvaWithCreds.billingEvent.appstore('user-123', signedPayload);\n * ```\n */\n appstore: async (\n endUserId: string,\n signedPayload: string,\n options?: Record<string, any>,\n ): Promise<void> => {\n if (!this.credentials.appstore) {\n throw new GalvaError({\n code: 'MISSING_CREDENTIALS',\n message:\n 'App Store credentials are required. Provide them in withCredentials().',\n });\n }\n\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'appstore',\n endUserId,\n payload: {\n signedPayload,\n appAppleId: this.credentials.appstore.appAppleId,\n bundleId: this.credentials.appstore.bundleId,\n env: this.config.environment,\n },\n options,\n });\n },\n\n /**\n * Tracks a Play Store billing event. Decodes payload and fetches subscription details.\n * @param {string} endUserId - End user ID\n * @param {string} base64Payload - Base64 payload from Pub/Sub\n * @returns {Promise<void>}\n * @throws {GalvaError} If credentials missing or API fails\n * @example\n * ```typescript\n * await galvaWithCreds.billingEvent.playstore('user-123', base64Payload);\n * ```\n */\n playstore: async (\n endUserId: string,\n base64Payload: string,\n ): Promise<void> => {\n if (!this.credentials.playstore) {\n throw new GalvaError({\n code: 'MISSING_CREDENTIALS',\n message:\n 'Play Store credentials are required. Provide them in withCredentials().',\n });\n }\n\n const decodedPayloadString = Buffer.from(\n base64Payload,\n 'base64',\n ).toString();\n let decodedPayload: PlaystoreDeveloperNotification | null = null;\n try {\n decodedPayload = JSON.parse(decodedPayloadString);\n } catch (error) {\n throw new GalvaError({\n code: 'INVALID_PAYLOAD',\n message: 'Invalid base64 payload: unable to parse JSON.',\n });\n }\n\n if (!decodedPayload || typeof decodedPayload !== 'object') {\n throw new GalvaError({\n code: 'INVALID_PAYLOAD',\n message: 'Decoded payload is not a valid JSON object.',\n });\n }\n\n if (!('subscriptionNotification' in decodedPayload)) {\n throw new GalvaError({\n code: 'INVALID_PAYLOAD',\n message:\n 'Decoded payload does not contain subscriptionNotification field.',\n });\n }\n\n const subscription = await PlaystoreService.getSubscription(\n decodedPayload.subscriptionNotification.purchaseToken,\n decodedPayload.packageName,\n this.credentials.playstore,\n );\n\n const finalPayload: PlaystoreDeveloperNotification = {\n ...decodedPayload,\n subscriptionNotification: {\n ...decodedPayload.subscriptionNotification,\n subscriptionPurchase: subscription.data,\n },\n };\n\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'playstore',\n endUserId,\n payload: finalPayload,\n });\n },\n\n /**\n * Tracks a Paddle billing event. Verifies signature and unmarshals the event.\n * @param {string} endUserId - End user ID\n * @param {string} rawBody - Raw body from Paddle webhook\n * @param {string} signature - Paddle-Signature header value\n * @returns {Promise<void>}\n * @throws {GalvaError} If credentials missing or API fails\n * @example\n * ```typescript\n * await galvaWithCreds.billingEvent.paddle('user-123', rawBody, signature);\n * ```\n */\n paddle: async (\n endUserId: string,\n rawBody: string,\n signature: string,\n ): Promise<void> => {\n if (!this.credentials.paddle) {\n throw new GalvaError({\n code: 'MISSING_CREDENTIALS',\n message:\n 'Paddle credentials are required. Provide them in withCredentials().',\n });\n }\n\n const paddle = new Paddle(this.credentials.paddle.apiKey);\n let eventData: EventEntity;\n\n try {\n eventData = await paddle.webhooks.unmarshal(\n rawBody,\n this.credentials.paddle.secretKey,\n signature,\n );\n } catch (error) {\n throw new GalvaError({\n code: 'INVALID_WEBHOOK',\n message: `Invalid Paddle webhook data: ${(error as Error).message}`,\n });\n }\n\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'paddle',\n endUserId,\n payload: eventData,\n });\n },\n };\n}\n\nexport class Galva extends GalvaBase {\n constructor(options?: GalvaOptions) {\n super(options);\n }\n\n /**\n * Billing event handlers for different payment platforms.\n * @property {Function} appstore - App Store events\n * @property {Function} playstore - Play Store events (decoded payload)\n * @property {Function} paddle - Paddle events (verified event)\n */\n public billingEvent = {\n /**\n * Tracks an App Store billing event.\n * @param {string} endUserId - End user ID\n * @param {Object} payload - App Store notification payload\n * @param {string} payload.signedPayload - Signed payload from Apple\n * @param {string} payload.bundleId - App bundle identifier\n * @param {number} payload.appAppleId - App Apple ID\n * @param {Record<string, any>} [options] - Additional options\n * @throws {GalvaError} If API request fails\n * @example\n * ```typescript\n * await galva.billingEvent.appstore('user-123', {\n * signedPayload,\n * bundleId: 'com.example.app',\n * appAppleId: 123456789\n * });\n * ```\n */\n appstore: async (\n endUserId: string,\n payload: {\n signedPayload: string;\n bundleId: string;\n appAppleId: number;\n },\n options?: Record<string, any>,\n ): Promise<void> => {\n const { signedPayload, bundleId, appAppleId } = payload;\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'appstore',\n endUserId,\n payload: {\n signedPayload,\n appAppleId: appAppleId,\n bundleId: bundleId,\n env: this.config.environment,\n },\n options,\n });\n },\n\n /**\n * Tracks a Play Store billing event with decoded notification.\n * @param {string} endUserId - End user ID\n * @param {PlaystoreDeveloperNotification} payload - Decoded developer notification\n * @returns {Promise<void>}\n * @example\n * ```typescript\n * await galva.billingEvent.playstore('user-123', decodedNotification);\n * ```\n */\n playstore: async (\n endUserId: string,\n payload: PlaystoreDeveloperNotification,\n ): Promise<void> => {\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'playstore',\n endUserId,\n payload,\n });\n },\n\n /**\n * Tracks a Paddle billing event with verified event entity.\n * @param {string} endUserId - End user ID\n * @param {EventEntity} eventEntity - Verified Paddle event\n * @returns {Promise<void>}\n * @example\n * ```typescript\n * await galva.billingEvent.paddle('user-123', verifiedEvent);\n * ```\n */\n paddle: async (\n endUserId: string,\n eventEntity: EventEntity,\n ): Promise<void> => {\n await this.sendRequest('POST', '/endUsers/billingEvents', {\n platform: 'paddle',\n endUserId,\n payload: eventEntity,\n });\n },\n };\n\n withCredentials(credentials: CredentialsConfig) {\n return new GalvaWithCreds(this.config, credentials);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@galva-io/galva-admin-node",
3
+ "version": "1.0.0-dev",
4
+ "description": "Node.js SDK for Galva Admin API",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "scripts": {
10
+ "build": "tsup",
11
+ "dev": "tsup --watch"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "require": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "keywords": [
21
+ "galva",
22
+ "admin",
23
+ "sdk",
24
+ "billing",
25
+ "subscription",
26
+ "api"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "@paddle/paddle-node-sdk": "^3.6.0",
32
+ "@types/node": "^20.19.33",
33
+ "googleapis": "^171.4.0",
34
+ "tsup": "^8.5.1",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md"
43
+ ],
44
+ "peerDependencies": {
45
+ "@paddle/paddle-node-sdk": "^3.6.0",
46
+ "googleapis": "^171.4.0"
47
+ }
48
+ }