sbcwallet 0.0.1 β†’ 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +101 -140
  2. package/dist/adapters/apple.js +17 -10
  3. package/dist/adapters/google.d.ts +5 -0
  4. package/dist/adapters/google.js +49 -24
  5. package/dist/api/unified.d.ts +12 -1
  6. package/dist/api/unified.js +63 -9
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +2 -2
  9. package/dist/types.d.ts +57 -0
  10. package/dist/types.js +17 -1
  11. package/dist/utils/logger.d.ts +4 -0
  12. package/dist/utils/logger.js +24 -0
  13. package/package.json +26 -7
  14. package/.env.example +0 -10
  15. package/.github/workflows/build.yml +0 -66
  16. package/.github/workflows/release.yml +0 -57
  17. package/APPLE_WALLET_SETUP.md +0 -318
  18. package/GOOGLE_WALLET_SETUP.md +0 -473
  19. package/examples/.loyalty-fixed-state.json +0 -10
  20. package/examples/claim-flow.ts +0 -163
  21. package/examples/loyalty-admin-server.js +0 -207
  22. package/examples/loyalty-admin.html +0 -260
  23. package/examples/loyalty-fixed-card-server.js +0 -288
  24. package/examples/loyalty-flow.ts +0 -78
  25. package/examples/loyalty-google-issue.js +0 -115
  26. package/scripts/copy-assets.js +0 -35
  27. package/scripts/smoke-dist-import.js +0 -39
  28. package/setup-google-class.js +0 -97
  29. package/setup-google-class.ts +0 -105
  30. package/src/adapters/apple.ts +0 -193
  31. package/src/adapters/google.ts +0 -521
  32. package/src/api/unified.ts +0 -487
  33. package/src/index.ts +0 -74
  34. package/src/profiles/healthcare/index.ts +0 -157
  35. package/src/profiles/logistics/index.ts +0 -158
  36. package/src/profiles/loyalty/index.ts +0 -87
  37. package/src/templates/apple/child.json +0 -59
  38. package/src/templates/apple/parent.json +0 -54
  39. package/src/templates/google/child_object.json +0 -38
  40. package/src/templates/google/loyalty_class.json +0 -7
  41. package/src/templates/google/loyalty_object.json +0 -29
  42. package/src/templates/google/parent_class.json +0 -10
  43. package/src/templates/google/parent_object.json +0 -33
  44. package/src/types.ts +0 -324
  45. package/src/utils/progress-image.ts +0 -130
  46. package/test-google-wallet.js +0 -78
  47. package/test-google-wallet.ts +0 -94
  48. package/tests/adapters.test.ts +0 -244
  49. package/tests/loyalty.test.ts +0 -39
  50. package/tests/unified.test.ts +0 -388
  51. package/tsconfig.json +0 -19
  52. package/vitest.config.ts +0 -12
package/README.md CHANGED
@@ -1,187 +1,148 @@
1
- # 🎟️ sbcwallet
1
+ # sbcwallet
2
2
 
3
- Unified Wallet-Pass SDK for Real-World Credentials
3
+ Unified wallet-pass SDK for Apple Wallet (.pkpass) and Google Wallet.
4
4
 
5
- sbcwallet is a TypeScript SDK for generating, signing, and managing verifiable passes on Apple Wallet and Google Wallet.
6
- Built on @sbcwallet, it bridges cryptographic truth and real-world credentials β€” enabling secure, interoperable workflows for logistics, healthcare, and beyond.
5
+ ## Install
7
6
 
8
- βΈ»
9
-
10
- ## ✨ Overview
11
-
12
- sbcwallet provides a unified abstraction layer for issuing and updating wallet passes across multiple ecosystems.
13
- It standardizes claim flows (like PES β†’ TO) and status pipelines (ISSUED β†’ PRESENCE β†’ OPS β†’ EXITED) while maintaining verifiable hashes, signatures, and anchor integrity via sbcwallet Core.
14
-
15
- βΈ»
7
+ ```sh
8
+ npm install sbcwallet
9
+ ```
16
10
 
17
- ## πŸš€ Quickstart
11
+ ## Quickstart (Loyalty)
18
12
 
19
- ```jsnpm install sbcwallet
13
+ Multi-tenant loyalty is designed for a real-world setup:
14
+ - Each business (tenant) defines its own card design (logo, colors, issuer name).
15
+ - Users add a card using their own `memberId`.
16
+ - Points can be updated for an existing issued card.
20
17
 
21
- import { createParentSchedule, createChildTicket, getPkpassBuffer } from 'sbcwallet'
18
+ ### Define a business (per-tenant theme) and create its program
22
19
 
23
- // 1️⃣ Create a parent PES schedule
24
- const pes = await createParentSchedule({
25
- profile: 'logistics',
26
- programName: 'Morning Yard Veracruz',
27
- site: 'Patio Gate 3'
20
+ ```ts
21
+ import { createBusiness, createLoyaltyProgram } from 'sbcwallet'
22
+
23
+ const biz = createBusiness({
24
+ name: 'X Cafe',
25
+ programName: 'Spirit Rewards',
26
+ pointsLabel: 'Points',
27
+ wallet: {
28
+ googleWallet: {
29
+ issuerName: 'X Cafe',
30
+ backgroundColor: '#111827',
31
+ logoUrl: 'https://example.com/logo.png',
32
+
33
+ // Advanced passthrough: merged into the Google loyaltyClass payload
34
+ classOverrides: {
35
+ reviewStatus: 'UNDER_REVIEW'
36
+ }
37
+ },
38
+ appleWallet: {
39
+ organizationName: 'X Cafe',
40
+ logoText: 'X',
41
+ backgroundColor: 'rgb(17, 24, 39)',
42
+
43
+ // Advanced passthrough: merged into the Apple pass.json payload
44
+ passOverrides: {
45
+ userInfo: { tenant: 'spirit-hub' }
46
+ }
47
+ }
48
+ }
28
49
  })
29
50
 
30
- // 2️⃣ Claim a child Transport Order
31
- const to = await createChildTicket({
32
- parentId: pes.id,
33
- plate: 'ABC123A',
34
- carrier: 'Transportes Golfo'
51
+ await createLoyaltyProgram({
52
+ businessId: biz.id,
53
+ locations: [
54
+ { latitude: 35.6892, longitude: 51.389 },
55
+ { latitude: 35.7, longitude: 51.4 }
56
+ ],
57
+ // Apple Wallet: shown when the pass becomes relevant (e.g., near a location)
58
+ relevantText: 'Welcome back β€” show this card at checkout',
59
+ countryCode: 'OM',
60
+ homepageUrl: 'https://example.com'
35
61
  })
36
-
37
- // 3️⃣ Generate Apple Wallet pass
38
- const buf = await getPkpassBuffer('child', to)
39
- await fs.promises.writeFile('ticket.pkpass', buf)
40
62
  ```
41
63
 
42
- ## 🎁 Loyalty Cards (Multi-tenant)
43
-
44
- Each business defines its own loyalty program, customers create accounts, and each customer gets a loyalty card that shows:
45
- - A QR/barcode identifier (`memberId`)
46
- - Current points (`points`) which can be updated
64
+ ### Issue a card and generate a Save URL
47
65
 
48
66
  ```ts
49
67
  import {
50
- createBusiness,
51
68
  createCustomerAccount,
52
- createLoyaltyProgram,
53
69
  issueLoyaltyCard,
54
70
  updateLoyaltyPoints,
55
71
  getGoogleObject
56
72
  } from 'sbcwallet'
57
73
 
58
- const biz = createBusiness({ name: 'SBC Coffee', pointsLabel: 'Beans' })
59
- await createLoyaltyProgram({ businessId: biz.id })
74
+ const memberId = 'USER-123'
75
+
76
+ const customer = createCustomerAccount({
77
+ businessId: biz.id,
78
+ fullName: 'Alice',
79
+ memberId
80
+ })
81
+
82
+ const card = await issueLoyaltyCard({
83
+ businessId: biz.id,
84
+ customerId: customer.id,
85
+ initialPoints: 10,
86
+ metadata: {
87
+ googleWallet: {
88
+ objectOverrides: {
89
+ linksModuleData: {
90
+ uris: [{ uri: 'https://example.com', description: 'Website' }]
91
+ }
92
+ }
93
+ }
94
+ }
95
+ })
60
96
 
61
- const customer = createCustomerAccount({ businessId: biz.id, fullName: 'Alice' })
62
- const card = await issueLoyaltyCard({ businessId: biz.id, customerId: customer.id, initialPoints: 10 })
63
97
  await updateLoyaltyPoints({ cardId: card.id, delta: 5 })
64
98
 
65
99
  const { saveUrl } = await getGoogleObject('child', card)
66
100
  console.log(saveUrl)
67
101
  ```
68
102
 
69
- βΈ»
103
+ ## Location-based surfacing and notifications
70
104
 
71
- ## 🧠 Architecture
72
- ```bash
73
- sbcwallet
74
- β”œβ”€β”€ adapters/ # Apple + Google Wallet adapters
75
- β”œβ”€β”€ api/ # Unified issuance/update API
76
- β”œβ”€β”€ profiles/ # Domain-specific field maps
77
- β”œβ”€β”€ templates/ # JSON templates for passes
78
- └── types.ts # Shared types and validation
79
- ```
80
- ### Key Components
81
- ```bash
82
- Module Description
83
- adapters/apple.ts Builds and signs .pkpass files using passkit-generator.
84
- adapters/google.ts Creates Google Wallet class/object JSON payloads.
85
- api/unified.ts Unified functions: createParentSchedule, createChildTicket, updatePassStatus.
86
- profiles/ Domain-specific mappings (logistics, healthcare, etc.).
87
- templates/ JSON templates for field mapping and layout.
88
- ```
105
+ This SDK supports two related concepts:
89
106
 
90
- βΈ»
107
+ 1) Location-based surfacing (no server required)
108
+ - Apple Wallet: setting `locations` and `relevantText` in pass.json can surface the pass on the lock screen when the user is near the business.
109
+ - Google Wallet: setting `locations` on the class/object helps Wallet surface the pass contextually.
91
110
 
92
- ## 🧩 Profiles
111
+ 2) Push-style notifications (server required)
112
+ - Google Wallet supports sending a message via the `addMessage` API. Your system decides *when* to send the message (for example, after your app detects the user is near the business).
93
113
 
94
- ### Logistics (default)
95
-
96
- Entity Description Example
97
- Parent (PES) Program Entry Schedule Gate window, site, available slots
98
- Child (TO) Transport Order Plate, carrier, client, status
99
- Statuses ISSUED β†’ PRESENCE β†’ SCALE β†’ OPS β†’ EXITED
100
-
101
- ### Healthcare (reference)
102
-
103
- Entity Description Example
104
- Parent Appointment Batch Doctor, location, date
105
- Child Patient Visit Ticket Patient, procedure, status
106
- Statuses SCHEDULED β†’ CHECKIN β†’ PROCEDURE β†’ DISCHARGED
114
+ ```ts
115
+ import { pushLoyaltyMessage } from 'sbcwallet'
107
116
 
108
- Switch profiles dynamically:
109
- ```js
110
- await createChildTicket({ profile: 'healthcare', ... })
117
+ await pushLoyaltyMessage({
118
+ cardId: card.id,
119
+ header: 'X',
120
+ body: 'You are nearby β€” show this card to earn points.',
121
+ messageType: 'TEXT_AND_NOTIFY'
122
+ })
111
123
  ```
112
124
 
113
- βΈ»
114
-
115
- ## πŸ” Integration with sbcwallet Core
116
-
117
- sbcwallet Pass automatically uses:
118
- β€’ hashEvent() for deterministic hashes
119
- β€’ signCredential() for ECDSA signatures
120
- β€’ dailyMerkle() for anchoring batches
121
-
122
- This ensures every pass is cryptographically verifiable and compatible with sbcwallet’s event audit trail.
123
-
124
- βΈ»
125
-
126
- ## πŸ§ͺ Testing
127
-
128
- `npm run test`
129
-
130
- Tests include:
131
- β€’ Apple .pkpass field mapping
132
- β€’ Google Wallet JSON validity
133
- β€’ Cross-profile field validation
134
- β€’ Core integration (hash + sign + verify)
135
-
136
- βΈ»
125
+ ## Demo server (multi-tenant)
137
126
 
138
- ## βš™οΈ Environment Variables (Apple Wallet)
139
-
140
- ```sh
141
- APPLE_TEAM_ID=ABCD1234
142
- APPLE_PASS_TYPE_ID=pass.com.sbcwallet.logistics
143
- APPLE_CERT_PATH=./certs/pass.p12
144
- APPLE_CERT_PASSWORD=yourpassword
145
- APPLE_WWDR_PATH=./certs/wwdr.pem
146
- ```
147
-
148
- For Google Wallet, include:
149
127
  ```sh
150
- GOOGLE_ISSUER_ID=issuer-id
151
- GOOGLE_SA_JSON=./google/credentials.json
128
+ npm run loyalty:server:multi
152
129
  ```
153
130
 
154
- βΈ»
131
+ Open `http://localhost:5190`.
155
132
 
156
- ## 🧾 License
133
+ ## Configuration
157
134
 
158
- Apache License 2.0
159
- Β© 2025 sbcwallet β€” open and extensible.
135
+ For Google Wallet Save URLs to work on-device you must set:
136
+ - `GOOGLE_ISSUER_ID`
137
+ - `GOOGLE_SA_JSON`
160
138
 
161
- βΈ»
139
+ For Apple Wallet signing, see APPLE_WALLET_SETUP.md.
162
140
 
163
- ## 🀝 Contributing
164
- 1. Fork the repo
165
- 2. Run npm install
166
- 3. Add or improve a profile under src/profiles/
167
- 4. Write tests in tests/
168
- 5. Submit a PR using conventional commits
141
+ ## Development
169
142
 
170
- βΈ»
171
-
172
- ## 🧭 Part of the sbcwallet Ecosystem
173
-
174
- Repo Purpose
175
143
  ```sh
176
- sbcwallet/core Verifiable event SDK β€” hashing, signing, Merkle trees
177
- sbcwallet/pass Wallet-pass abstraction over Core (this repo)
178
- sbcwallet/wallet Reference logistics PWA & API
179
- sbcwallet/id Hosted identity & orchestration layer (SaaS)
144
+ npm run build
145
+ npm test
146
+ npm pack --dry-run
180
147
  ```
181
148
 
182
- βΈ»
183
-
184
- β€œsbcwallet Pass connects cryptographic truth with human experience β€”
185
- turning every credential into a verifiable story.”
186
-
187
- Reflection: evidence βœ“ logic consistent brevity optimized
@@ -27,26 +27,28 @@ export class AppleWalletAdapter {
27
27
  const template = this.mergeTemplates(baseTemplate, profileTemplate);
28
28
  // Apply pass data to template
29
29
  const populatedTemplate = this.populateTemplate(template, passData, profile, passType);
30
+ // Optional per-pass overrides via metadata (useful for per-business theming)
31
+ const appleWallet = passData?.metadata?.appleWallet || {};
30
32
  // Build pass props from populated template
31
33
  const passProps = {
32
34
  serialNumber: passData.id,
33
- description: populatedTemplate.description || 'sbcwallet Pass',
34
- organizationName: populatedTemplate.organizationName || 'sbcwallet',
35
+ description: appleWallet.description || populatedTemplate.description || 'sbcwallet Pass',
36
+ organizationName: appleWallet.organizationName || populatedTemplate.organizationName || 'sbcwallet',
35
37
  passTypeIdentifier: this.config.passTypeId,
36
38
  teamIdentifier: this.config.teamId
37
39
  };
38
40
  // Add colors
39
- if (populatedTemplate.backgroundColor) {
40
- passProps.backgroundColor = populatedTemplate.backgroundColor;
41
+ if (appleWallet.backgroundColor || populatedTemplate.backgroundColor) {
42
+ passProps.backgroundColor = appleWallet.backgroundColor || populatedTemplate.backgroundColor;
41
43
  }
42
- if (populatedTemplate.foregroundColor) {
43
- passProps.foregroundColor = populatedTemplate.foregroundColor;
44
+ if (appleWallet.foregroundColor || populatedTemplate.foregroundColor) {
45
+ passProps.foregroundColor = appleWallet.foregroundColor || populatedTemplate.foregroundColor;
44
46
  }
45
- if (populatedTemplate.labelColor) {
46
- passProps.labelColor = populatedTemplate.labelColor;
47
+ if (appleWallet.labelColor || populatedTemplate.labelColor) {
48
+ passProps.labelColor = appleWallet.labelColor || populatedTemplate.labelColor;
47
49
  }
48
- if (populatedTemplate.logoText) {
49
- passProps.logoText = populatedTemplate.logoText;
50
+ if (appleWallet.logoText || populatedTemplate.logoText) {
51
+ passProps.logoText = appleWallet.logoText || populatedTemplate.logoText;
50
52
  }
51
53
  // Add barcodes
52
54
  if (populatedTemplate.barcodes && populatedTemplate.barcodes.length > 0) {
@@ -56,6 +58,11 @@ export class AppleWalletAdapter {
56
58
  if (populatedTemplate.generic) {
57
59
  passProps.generic = populatedTemplate.generic;
58
60
  }
61
+ // Advanced passthrough: allow issuers to supply any PassKit fields.
62
+ // Example: webServiceURL, authenticationToken, appLaunchURL, userInfo, beacons, nfc, etc.
63
+ if (appleWallet.passOverrides && typeof appleWallet.passOverrides === 'object') {
64
+ Object.assign(passProps, appleWallet.passOverrides);
65
+ }
59
66
  // Create pass
60
67
  const pass = new PKPass({}, {
61
68
  wwdr: this.config.wwdrPath,
@@ -13,6 +13,11 @@ export declare class GoogleWalletAdapter {
13
13
  private getFieldValue;
14
14
  private generateSaveUrl;
15
15
  private upsertInAPI;
16
+ addMessageToLoyaltyObject(loyaltyObjectId: string, message: {
17
+ header: string;
18
+ body: string;
19
+ messageType?: string;
20
+ }): Promise<void>;
16
21
  /**
17
22
  * Generate and add hero image with progress bar to the pass object
18
23
  *
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'url';
3
3
  import { dirname, join } from 'path';
4
4
  import { GoogleAuth } from 'google-auth-library';
5
5
  import { generateLogisticsHeroImage, generateHealthcareHeroImage } from '../utils/progress-image.js';
6
+ import { logDebug, logWarn, logError } from '../utils/logger.js';
6
7
  const __filename = fileURLToPath(import.meta.url);
7
8
  const __dirname = dirname(__filename);
8
9
  export class GoogleWalletAdapter {
@@ -32,14 +33,14 @@ export class GoogleWalletAdapter {
32
33
  await this.upsertInAPI('loyaltyClass', classPayload);
33
34
  }
34
35
  catch (error) {
35
- console.warn('⚠️ Google Wallet API loyaltyClass create/update failed');
36
- console.warn(error);
36
+ logWarn('Google Wallet API loyaltyClass create/update failed');
37
+ logWarn(error);
37
38
  }
38
39
  }
39
40
  else {
40
- console.log('⚠️ No Google Auth - class not created in API (will not work on device)');
41
+ logWarn('No Google Auth - class not created in API (will not work on device)');
41
42
  }
42
- console.log('Google Wallet Class:', JSON.stringify(classPayload, null, 2));
43
+ logDebug('Google Wallet Class:', JSON.stringify(classPayload, null, 2));
43
44
  // Save URL is not applicable for classes.
44
45
  return { object: classPayload, saveUrl: '' };
45
46
  }
@@ -85,20 +86,20 @@ export class GoogleWalletAdapter {
85
86
  }
86
87
  catch (error) {
87
88
  // Keep going so we can still generate a signed Save URL for debugging/testing.
88
- console.warn('⚠️ Google Wallet API object create/update failed; continuing to Save URL generation');
89
- console.warn(error);
89
+ logWarn('Google Wallet API object create/update failed; continuing to Save URL generation');
90
+ logWarn(error);
90
91
  }
91
92
  }
92
93
  else {
93
- console.log('⚠️ No Google Auth - object not created in API (will not work on device)');
94
+ logWarn('No Google Auth - object not created in API (will not work on device)');
94
95
  }
95
96
  // Generate save URL with signed JWT
96
97
  // Prefer embedding the full object in the JWT payload so the Save URL can work
97
98
  // even if the object was not pre-created in the API.
98
99
  const saveUrl = await this.generateSaveUrl(populatedObject, isLoyalty ? 'loyaltyObjects' : 'genericObjects');
99
100
  // Log the object
100
- console.log('Google Wallet Object:', JSON.stringify(populatedObject, null, 2));
101
- console.log('Save URL:', saveUrl);
101
+ logDebug('Google Wallet Object:', JSON.stringify(populatedObject, null, 2));
102
+ logDebug('Save URL:', saveUrl);
102
103
  return {
103
104
  object: populatedObject,
104
105
  saveUrl
@@ -120,10 +121,16 @@ export class GoogleWalletAdapter {
120
121
  ...baseTemplate,
121
122
  ...profileTemplate,
122
123
  id: classId,
123
- issuerName: profileTemplate.issuerName || googleWallet.issuerName || 'sbcwallet',
124
+ // Prefer business/program-provided metadata over template defaults.
125
+ issuerName: googleWallet.issuerName || profileTemplate.issuerName || baseTemplate.issuerName || 'sbcwallet',
124
126
  programName: passData.programName || googleWallet.programName || 'Loyalty',
125
127
  hexBackgroundColor: googleWallet.backgroundColor || baseTemplate.hexBackgroundColor || '#111827'
126
128
  };
129
+ // Geo locations (latitude/longitude pairs) for location-based surfacing.
130
+ const locations = googleWallet.locations || metadata.locations;
131
+ if (Array.isArray(locations) && locations.length > 0) {
132
+ payload.locations = locations;
133
+ }
127
134
  if (googleWallet.countryCode)
128
135
  payload.countryCode = googleWallet.countryCode;
129
136
  if (googleWallet.homepageUrl) {
@@ -280,7 +287,7 @@ export class GoogleWalletAdapter {
280
287
  const baseUrl = 'https://pay.google.com/gp/v/save';
281
288
  const objectId = passObject.id;
282
289
  if (!this.config.serviceAccountPath) {
283
- console.warn('⚠️ No service account - returning unsigned URL (will not work)');
290
+ logWarn('No service account - returning unsigned URL (will not work)');
284
291
  return `${baseUrl}/${encodeURIComponent(objectId)}`;
285
292
  }
286
293
  try {
@@ -306,7 +313,7 @@ export class GoogleWalletAdapter {
306
313
  return `${baseUrl}/${token}`;
307
314
  }
308
315
  catch (error) {
309
- console.error('Error generating signed JWT:', error);
316
+ logError('Error generating signed JWT:', error);
310
317
  return `${baseUrl}/${encodeURIComponent(objectId)}`;
311
318
  }
312
319
  }
@@ -324,12 +331,12 @@ export class GoogleWalletAdapter {
324
331
  method: 'POST',
325
332
  data: payload
326
333
  });
327
- console.log(`βœ… ${kind} created in Google Wallet API`);
334
+ logDebug(`βœ… ${kind} created in Google Wallet API`);
328
335
  }
329
336
  catch (error) {
330
337
  if (error.response?.status === 409) {
331
338
  // Object already exists, try to update it
332
- console.log('ℹ️ Resource exists, updating...');
339
+ logDebug('ℹ️ Resource exists, updating...');
333
340
  try {
334
341
  const client = await this.auth.getClient();
335
342
  const baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1';
@@ -340,19 +347,37 @@ export class GoogleWalletAdapter {
340
347
  method: 'PUT',
341
348
  data: payload
342
349
  });
343
- console.log(`βœ… ${kind} updated in Google Wallet API`);
350
+ logDebug(`βœ… ${kind} updated in Google Wallet API`);
344
351
  }
345
352
  catch (updateError) {
346
- console.error(`❌ Error updating ${kind}:`, updateError);
353
+ logError(`❌ Error updating ${kind}:`, updateError);
347
354
  throw updateError;
348
355
  }
349
356
  }
350
357
  else {
351
- console.error(`❌ Error creating ${kind}:`, error.response?.data || error.message);
358
+ logError(`❌ Error creating ${kind}:`, error.response?.data || error.message);
352
359
  throw error;
353
360
  }
354
361
  }
355
362
  }
363
+ async addMessageToLoyaltyObject(loyaltyObjectId, message) {
364
+ if (!this.auth) {
365
+ throw new Error('Google Auth not initialized');
366
+ }
367
+ const client = await this.auth.getClient();
368
+ const baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1';
369
+ await client.request({
370
+ url: `${baseUrl}/loyaltyObject/${encodeURIComponent(loyaltyObjectId)}/addMessage`,
371
+ method: 'POST',
372
+ data: {
373
+ message: {
374
+ header: message.header,
375
+ body: message.body,
376
+ messageType: message.messageType || 'TEXT_AND_NOTIFY'
377
+ }
378
+ }
379
+ });
380
+ }
356
381
  /**
357
382
  * Generate and add hero image with progress bar to the pass object
358
383
  *
@@ -381,11 +406,11 @@ export class GoogleWalletAdapter {
381
406
  // Ensure directory exists
382
407
  try {
383
408
  await writeFile(imagePath, imageBuffer);
384
- console.log(`✨ Hero image saved: ${imagePath}`);
409
+ logDebug(`✨ Hero image saved: ${imagePath}`);
385
410
  }
386
411
  catch (err) {
387
412
  // Directory might not exist, that's OK - just skip for now
388
- console.log(`ℹ️ Hero image generated (not uploaded - requires public URL)`);
413
+ logDebug('ℹ️ Hero image generated (not uploaded - requires public URL)');
389
414
  }
390
415
  // TODO: Upload to cloud storage and get public URL
391
416
  // For now, we'll skip adding the hero image to the pass object
@@ -410,10 +435,10 @@ export class GoogleWalletAdapter {
410
435
  DISCHARGED: '#7ED321'
411
436
  };
412
437
  passObject.hexBackgroundColor = statusColors[passData.status] || '#4A90E2';
413
- console.log(`✨ Dynamic color applied for status: ${passData.status} (${passObject.hexBackgroundColor})`);
438
+ logDebug(`✨ Dynamic color applied for status: ${passData.status} (${passObject.hexBackgroundColor})`);
414
439
  }
415
440
  catch (error) {
416
- console.error('⚠️ Failed to generate hero image:', error);
441
+ logError('⚠️ Failed to generate hero image:', error);
417
442
  // Continue without hero image if generation fails
418
443
  }
419
444
  }
@@ -421,9 +446,9 @@ export class GoogleWalletAdapter {
421
446
  // Stub for creating a Google Wallet class
422
447
  // In a real implementation, this would call the Google Wallet API
423
448
  const classId = `${this.config.issuerId}.${profile.name}_${passType}`;
424
- console.log(`Creating Google Wallet Class: ${classId}`);
425
- console.log('Profile:', profile.name);
426
- console.log('Type:', passType);
449
+ logDebug(`Creating Google Wallet Class: ${classId}`);
450
+ logDebug('Profile:', profile.name);
451
+ logDebug('Type:', passType);
427
452
  // This would normally make an API call to:
428
453
  // POST https://walletobjects.googleapis.com/walletobjects/v1/genericClass
429
454
  }
@@ -1,4 +1,4 @@
1
- import type { CreateParentInput, CreateChildInput, CreateBusinessInput, CreateCustomerAccountInput, CreateLoyaltyProgramInput, IssueLoyaltyCardInput, UpdateLoyaltyPointsInput, LoyaltyBusiness, LoyaltyCustomerAccount, ParentPassData, ChildPassData, PassData, PassStatus, ProfileType, ProfileConfig, PassGenerationResult } from '../types.js';
1
+ import type { CreateParentInput, CreateChildInput, CreateBusinessInput, CreateCustomerAccountInput, CreateLoyaltyProgramInput, IssueLoyaltyCardInput, UpdateLoyaltyPointsInput, PushLoyaltyMessageInput, LoyaltyBusiness, LoyaltyCustomerAccount, ParentPassData, ChildPassData, PassData, PassStatus, ProfileType, ProfileConfig, PassGenerationResult } from '../types.js';
2
2
  /**
3
3
  * Get a profile by name
4
4
  */
@@ -31,6 +31,17 @@ export declare function issueLoyaltyCard(input: IssueLoyaltyCardInput): Promise<
31
31
  * Update points on a loyalty card.
32
32
  */
33
33
  export declare function updateLoyaltyPoints(input: UpdateLoyaltyPointsInput): Promise<PassData>;
34
+ /**
35
+ * Send a message to a Google Wallet loyalty object.
36
+ *
37
+ * Notes:
38
+ * - This uses the Google Wallet API addMessage endpoint (requires service account credentials).
39
+ * - Location-based surfacing is controlled by the pass locations; your system decides WHEN to send messages.
40
+ */
41
+ export declare function pushLoyaltyMessage(input: PushLoyaltyMessageInput): Promise<{
42
+ ok: true;
43
+ objectId: string;
44
+ }>;
34
45
  /**
35
46
  * Create a parent schedule (PES or AppointmentBatch)
36
47
  */