sbcwallet 0.0.1

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 (67) hide show
  1. package/.env.example +10 -0
  2. package/.github/workflows/build.yml +66 -0
  3. package/.github/workflows/release.yml +57 -0
  4. package/APPLE_WALLET_SETUP.md +318 -0
  5. package/GOOGLE_WALLET_SETUP.md +473 -0
  6. package/LICENSE +201 -0
  7. package/README.md +187 -0
  8. package/dist/adapters/apple.d.ts +10 -0
  9. package/dist/adapters/apple.js +153 -0
  10. package/dist/adapters/google.d.ts +26 -0
  11. package/dist/adapters/google.js +431 -0
  12. package/dist/api/unified.d.ts +67 -0
  13. package/dist/api/unified.js +375 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.js +11 -0
  16. package/dist/profiles/healthcare/index.d.ts +91 -0
  17. package/dist/profiles/healthcare/index.js +151 -0
  18. package/dist/profiles/logistics/index.d.ts +91 -0
  19. package/dist/profiles/logistics/index.js +152 -0
  20. package/dist/profiles/loyalty/index.d.ts +91 -0
  21. package/dist/profiles/loyalty/index.js +81 -0
  22. package/dist/templates/apple/child.json +59 -0
  23. package/dist/templates/apple/parent.json +54 -0
  24. package/dist/templates/google/child_object.json +38 -0
  25. package/dist/templates/google/loyalty_class.json +7 -0
  26. package/dist/templates/google/loyalty_object.json +29 -0
  27. package/dist/templates/google/parent_class.json +10 -0
  28. package/dist/templates/google/parent_object.json +33 -0
  29. package/dist/types.d.ts +422 -0
  30. package/dist/types.js +80 -0
  31. package/dist/utils/progress-image.d.ts +23 -0
  32. package/dist/utils/progress-image.js +94 -0
  33. package/examples/.loyalty-fixed-state.json +10 -0
  34. package/examples/claim-flow.ts +163 -0
  35. package/examples/loyalty-admin-server.js +207 -0
  36. package/examples/loyalty-admin.html +260 -0
  37. package/examples/loyalty-fixed-card-server.js +288 -0
  38. package/examples/loyalty-flow.ts +78 -0
  39. package/examples/loyalty-google-issue.js +115 -0
  40. package/package.json +51 -0
  41. package/scripts/copy-assets.js +35 -0
  42. package/scripts/smoke-dist-import.js +39 -0
  43. package/setup-google-class.js +97 -0
  44. package/setup-google-class.ts +105 -0
  45. package/src/adapters/apple.ts +193 -0
  46. package/src/adapters/google.ts +521 -0
  47. package/src/api/unified.ts +487 -0
  48. package/src/index.ts +74 -0
  49. package/src/profiles/healthcare/index.ts +157 -0
  50. package/src/profiles/logistics/index.ts +158 -0
  51. package/src/profiles/loyalty/index.ts +87 -0
  52. package/src/templates/apple/child.json +59 -0
  53. package/src/templates/apple/parent.json +54 -0
  54. package/src/templates/google/child_object.json +38 -0
  55. package/src/templates/google/loyalty_class.json +7 -0
  56. package/src/templates/google/loyalty_object.json +29 -0
  57. package/src/templates/google/parent_class.json +10 -0
  58. package/src/templates/google/parent_object.json +33 -0
  59. package/src/types.ts +324 -0
  60. package/src/utils/progress-image.ts +130 -0
  61. package/test-google-wallet.js +78 -0
  62. package/test-google-wallet.ts +94 -0
  63. package/tests/adapters.test.ts +244 -0
  64. package/tests/loyalty.test.ts +39 -0
  65. package/tests/unified.test.ts +388 -0
  66. package/tsconfig.json +19 -0
  67. package/vitest.config.ts +12 -0
@@ -0,0 +1,431 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import { GoogleAuth } from 'google-auth-library';
5
+ import { generateLogisticsHeroImage, generateHealthcareHeroImage } from '../utils/progress-image.js';
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ export class GoogleWalletAdapter {
9
+ config;
10
+ auth = null;
11
+ constructor(config) {
12
+ this.config = {
13
+ issuerId: config?.issuerId || process.env.GOOGLE_ISSUER_ID || 'test-issuer',
14
+ serviceAccountPath: config?.serviceAccountPath || process.env.GOOGLE_SA_JSON
15
+ };
16
+ // Initialize Google Auth if credentials are available
17
+ if (this.config.serviceAccountPath) {
18
+ this.auth = new GoogleAuth({
19
+ keyFile: this.config.serviceAccountPath,
20
+ scopes: ['https://www.googleapis.com/auth/wallet_object.issuer']
21
+ });
22
+ }
23
+ }
24
+ async generatePassObject(passData, profile, passType) {
25
+ try {
26
+ const isLoyalty = profile.name === 'loyalty';
27
+ // Loyalty uses a different Google Wallet schema (loyaltyClass/loyaltyObject)
28
+ if (isLoyalty && passType === 'parent' && passData.type === 'parent') {
29
+ const classPayload = await this.generateLoyaltyClass(passData, profile);
30
+ if (this.auth) {
31
+ try {
32
+ await this.upsertInAPI('loyaltyClass', classPayload);
33
+ }
34
+ catch (error) {
35
+ console.warn('⚠️ Google Wallet API loyaltyClass create/update failed');
36
+ console.warn(error);
37
+ }
38
+ }
39
+ else {
40
+ console.log('⚠️ No Google Auth - class not created in API (will not work on device)');
41
+ }
42
+ console.log('Google Wallet Class:', JSON.stringify(classPayload, null, 2));
43
+ // Save URL is not applicable for classes.
44
+ return { object: classPayload, saveUrl: '' };
45
+ }
46
+ // Load template
47
+ const templateFilename = isLoyalty
48
+ ? 'loyalty_object.json'
49
+ : `${passType}_object.json`;
50
+ const templatePath = join(__dirname, '..', 'templates', 'google', templateFilename);
51
+ const templateContent = await readFile(templatePath, 'utf-8');
52
+ const baseTemplate = JSON.parse(templateContent);
53
+ // Get profile-specific template
54
+ const profileTemplate = passType === 'parent'
55
+ ? profile.defaultTemplates.google.parentObject
56
+ : profile.defaultTemplates.google.childObject;
57
+ // Merge templates
58
+ const template = { ...baseTemplate, ...profileTemplate };
59
+ // Populate template with pass data
60
+ const populatedObject = this.populateObject(template, passData, profile, passType);
61
+ // Generate class ID and object ID
62
+ const classId = isLoyalty && passData.type === 'child'
63
+ ? `${this.config.issuerId}.${passData.parentId}`
64
+ : `${this.config.issuerId}.${profile.name}_${passType}`;
65
+ const objectId = `${this.config.issuerId}.${passData.id}`;
66
+ populatedObject.classId = classId;
67
+ populatedObject.id = objectId;
68
+ // Set barcode
69
+ if (populatedObject.barcode) {
70
+ const barcodeValue = passData.memberId || passData.id;
71
+ populatedObject.barcode.value = barcodeValue;
72
+ }
73
+ if (isLoyalty) {
74
+ ;
75
+ populatedObject.accountId = passData.memberId || passData.id;
76
+ populatedObject.accountName = passData.customerName || '';
77
+ this.applyLoyaltyExtras(populatedObject, passData);
78
+ }
79
+ // Generate and add hero image with progress bar
80
+ await this.addHeroImage(populatedObject, passData, profile);
81
+ // Create the object in Google Wallet API if auth is available
82
+ if (this.auth) {
83
+ try {
84
+ await this.upsertInAPI(isLoyalty ? 'loyaltyObject' : 'genericObject', populatedObject);
85
+ }
86
+ catch (error) {
87
+ // 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);
90
+ }
91
+ }
92
+ else {
93
+ console.log('⚠️ No Google Auth - object not created in API (will not work on device)');
94
+ }
95
+ // Generate save URL with signed JWT
96
+ // Prefer embedding the full object in the JWT payload so the Save URL can work
97
+ // even if the object was not pre-created in the API.
98
+ const saveUrl = await this.generateSaveUrl(populatedObject, isLoyalty ? 'loyaltyObjects' : 'genericObjects');
99
+ // Log the object
100
+ console.log('Google Wallet Object:', JSON.stringify(populatedObject, null, 2));
101
+ console.log('Save URL:', saveUrl);
102
+ return {
103
+ object: populatedObject,
104
+ saveUrl
105
+ };
106
+ }
107
+ catch (error) {
108
+ throw new Error(`Failed to generate Google Wallet object: ${error instanceof Error ? error.message : String(error)}`);
109
+ }
110
+ }
111
+ async generateLoyaltyClass(passData, profile) {
112
+ const templatePath = join(__dirname, '..', 'templates', 'google', 'loyalty_class.json');
113
+ const templateContent = await readFile(templatePath, 'utf-8');
114
+ const baseTemplate = JSON.parse(templateContent);
115
+ const profileTemplate = profile.defaultTemplates.google.parentClass || {};
116
+ const classId = `${this.config.issuerId}.${passData.id}`;
117
+ const metadata = passData.metadata || {};
118
+ const googleWallet = metadata.googleWallet || {};
119
+ const payload = {
120
+ ...baseTemplate,
121
+ ...profileTemplate,
122
+ id: classId,
123
+ issuerName: profileTemplate.issuerName || googleWallet.issuerName || 'sbcwallet',
124
+ programName: passData.programName || googleWallet.programName || 'Loyalty',
125
+ hexBackgroundColor: googleWallet.backgroundColor || baseTemplate.hexBackgroundColor || '#111827'
126
+ };
127
+ if (googleWallet.countryCode)
128
+ payload.countryCode = googleWallet.countryCode;
129
+ if (googleWallet.homepageUrl) {
130
+ payload.homepageUri = {
131
+ uri: googleWallet.homepageUrl,
132
+ description: googleWallet.homepageLabel || 'Website'
133
+ };
134
+ }
135
+ // Images: require public URLs. Only include if provided.
136
+ if (googleWallet.logoUrl) {
137
+ payload.programLogo = { sourceUri: { uri: googleWallet.logoUrl } };
138
+ }
139
+ if (googleWallet.heroImageUrl) {
140
+ payload.heroImage = { sourceUri: { uri: googleWallet.heroImageUrl } };
141
+ }
142
+ if (googleWallet.wordMarkUrl) {
143
+ payload.wordMark = { sourceUri: { uri: googleWallet.wordMarkUrl } };
144
+ }
145
+ // Callback settings
146
+ if (googleWallet.updateRequestUrl) {
147
+ payload.callbackOptions = { updateRequestUrl: googleWallet.updateRequestUrl };
148
+ }
149
+ // Advanced/optional: allow raw overrides to be merged in.
150
+ if (googleWallet.classOverrides && typeof googleWallet.classOverrides === 'object') {
151
+ Object.assign(payload, googleWallet.classOverrides);
152
+ }
153
+ return payload;
154
+ }
155
+ populateObject(template, passData, profile, passType) {
156
+ const populated = { ...template };
157
+ // Set card title based on pass type (Google Wallet format)
158
+ if (passType === 'parent' && passData.type === 'parent') {
159
+ const headerText = profile.name === 'logistics'
160
+ ? 'Program Entry Schedule'
161
+ : profile.name === 'healthcare'
162
+ ? 'Appointment Batch'
163
+ : 'Loyalty Program';
164
+ const bodyText = passData.programName;
165
+ // Backward-compatible shape used by existing tests/consumers
166
+ populated.cardTitle = { header: headerText, body: bodyText };
167
+ populated.header = { header: 'Schedule', body: passData.id };
168
+ populated.cardTitle.defaultValue = { language: 'en-US', value: headerText };
169
+ populated.header.defaultValue = { language: 'en-US', value: bodyText };
170
+ }
171
+ else if (passData.type === 'child') {
172
+ const headerText = profile.name === 'logistics'
173
+ ? 'Transport Order'
174
+ : profile.name === 'healthcare'
175
+ ? 'Patient Visit'
176
+ : 'Loyalty Card';
177
+ const bodyText = passData.customerName || passData.memberId || passData.plate || passData.patientName || passData.id;
178
+ // Backward-compatible shape used by existing tests/consumers
179
+ populated.cardTitle = { header: headerText, body: bodyText };
180
+ populated.header = { header: 'Order', body: passData.id };
181
+ populated.cardTitle.defaultValue = { language: 'en-US', value: headerText };
182
+ populated.header.defaultValue = { language: 'en-US', value: bodyText };
183
+ }
184
+ // Populate text modules
185
+ if (populated.textModulesData) {
186
+ populated.textModulesData = populated.textModulesData.map(module => {
187
+ const value = this.getFieldValue(module.id, passData);
188
+ return {
189
+ ...module,
190
+ body: value || module.body
191
+ };
192
+ });
193
+ // Add additional fields based on pass data
194
+ if (passData.type === 'parent' && passData.window) {
195
+ const windowModule = populated.textModulesData.find(m => m.id === 'window');
196
+ if (windowModule) {
197
+ windowModule.body = `${new Date(passData.window.from).toLocaleString()} - ${new Date(passData.window.to).toLocaleString()}`;
198
+ }
199
+ }
200
+ // Add status field
201
+ const statusModule = populated.textModulesData.find(m => m.id === 'status');
202
+ if (statusModule) {
203
+ statusModule.body = passData.status;
204
+ }
205
+ }
206
+ return populated;
207
+ }
208
+ applyLoyaltyExtras(passObject, passData) {
209
+ const metadata = passData.metadata || {};
210
+ const googleWallet = metadata.googleWallet || {};
211
+ // Geo locations
212
+ const locations = googleWallet.locations || metadata.locations;
213
+ if (Array.isArray(locations) && locations.length > 0) {
214
+ passObject.locations = locations;
215
+ }
216
+ // Links
217
+ if (Array.isArray(googleWallet.links) && googleWallet.links.length > 0) {
218
+ passObject.linksModuleData = {
219
+ uris: googleWallet.links.map((l, idx) => ({
220
+ id: l.id || String(idx + 1),
221
+ description: l.label || l.description,
222
+ uri: l.url || l.uri
223
+ })).filter((u) => u.uri)
224
+ };
225
+ }
226
+ // Images
227
+ if (Array.isArray(googleWallet.imageModules) && googleWallet.imageModules.length > 0) {
228
+ passObject.imageModulesData = googleWallet.imageModules
229
+ .map((img) => img?.imageUrl || img?.uri)
230
+ .filter(Boolean)
231
+ .map((uri) => ({
232
+ mainImage: { sourceUri: { uri } }
233
+ }));
234
+ }
235
+ // Messages
236
+ if (Array.isArray(googleWallet.messages) && googleWallet.messages.length > 0) {
237
+ passObject.messages = googleWallet.messages
238
+ .map((m, idx) => ({
239
+ id: m.id || String(idx + 1),
240
+ header: m.header,
241
+ body: m.body,
242
+ messageType: m.messageType
243
+ }))
244
+ .filter((m) => m.header && m.body);
245
+ }
246
+ // Allow raw overrides
247
+ if (googleWallet.objectOverrides && typeof googleWallet.objectOverrides === 'object') {
248
+ Object.assign(passObject, googleWallet.objectOverrides);
249
+ }
250
+ }
251
+ getFieldValue(key, passData) {
252
+ // Handle nested keys
253
+ const keys = key.split('.');
254
+ let value = passData;
255
+ for (const k of keys) {
256
+ if (value && typeof value === 'object' && k in value) {
257
+ value = value[k];
258
+ }
259
+ else {
260
+ return '';
261
+ }
262
+ }
263
+ // Map common fields
264
+ if (key === 'site' && passData.type === 'parent') {
265
+ return passData.site || '';
266
+ }
267
+ if (key === 'carrier' && passData.type === 'child') {
268
+ return passData.carrier || '';
269
+ }
270
+ if (key === 'client' && passData.type === 'child') {
271
+ return passData.client || '';
272
+ }
273
+ if (key === 'status') {
274
+ return passData.status;
275
+ }
276
+ return value !== undefined && value !== null ? String(value) : '';
277
+ }
278
+ async generateSaveUrl(passObject, payloadKey) {
279
+ // Generate signed JWT for Google Wallet save URL
280
+ const baseUrl = 'https://pay.google.com/gp/v/save';
281
+ const objectId = passObject.id;
282
+ if (!this.config.serviceAccountPath) {
283
+ console.warn('⚠️ No service account - returning unsigned URL (will not work)');
284
+ return `${baseUrl}/${encodeURIComponent(objectId)}`;
285
+ }
286
+ try {
287
+ // Read service account
288
+ const serviceAccount = JSON.parse(await readFile(this.config.serviceAccountPath, 'utf-8'));
289
+ // Create JWT claims
290
+ const claims = {
291
+ iss: serviceAccount.client_email,
292
+ aud: 'google',
293
+ origins: [],
294
+ typ: 'savetowallet',
295
+ payload: {
296
+ // Embedding the full object enables a true end-to-end Save URL flow.
297
+ // (Class must still exist and your account must be allowed if issuer/class is in test mode.)
298
+ [payloadKey]: [passObject]
299
+ }
300
+ };
301
+ // Sign the JWT
302
+ const { default: jwt } = await import('jsonwebtoken');
303
+ const token = jwt.sign(claims, serviceAccount.private_key, {
304
+ algorithm: 'RS256'
305
+ });
306
+ return `${baseUrl}/${token}`;
307
+ }
308
+ catch (error) {
309
+ console.error('Error generating signed JWT:', error);
310
+ return `${baseUrl}/${encodeURIComponent(objectId)}`;
311
+ }
312
+ }
313
+ async upsertInAPI(kind, payload) {
314
+ if (!this.auth) {
315
+ throw new Error('Google Auth not initialized');
316
+ }
317
+ try {
318
+ const client = await this.auth.getClient();
319
+ const baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1';
320
+ const resource = kind;
321
+ const url = `${baseUrl}/${resource}`;
322
+ await client.request({
323
+ url,
324
+ method: 'POST',
325
+ data: payload
326
+ });
327
+ console.log(`✅ ${kind} created in Google Wallet API`);
328
+ }
329
+ catch (error) {
330
+ if (error.response?.status === 409) {
331
+ // Object already exists, try to update it
332
+ console.log('ℹ️ Resource exists, updating...');
333
+ try {
334
+ const client = await this.auth.getClient();
335
+ const baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1';
336
+ const resource = kind;
337
+ const resourceId = payload.id;
338
+ await client.request({
339
+ url: `${baseUrl}/${resource}/${resourceId}`,
340
+ method: 'PUT',
341
+ data: payload
342
+ });
343
+ console.log(`✅ ${kind} updated in Google Wallet API`);
344
+ }
345
+ catch (updateError) {
346
+ console.error(`❌ Error updating ${kind}:`, updateError);
347
+ throw updateError;
348
+ }
349
+ }
350
+ else {
351
+ console.error(`❌ Error creating ${kind}:`, error.response?.data || error.message);
352
+ throw error;
353
+ }
354
+ }
355
+ }
356
+ /**
357
+ * Generate and add hero image with progress bar to the pass object
358
+ *
359
+ * Note: Google Wallet API requires publicly accessible URLs for images.
360
+ * This method generates the hero image and saves it locally.
361
+ * For production, upload images to a CDN/cloud storage and use those URLs.
362
+ */
363
+ async addHeroImage(passObject, passData, profile) {
364
+ try {
365
+ if (profile.name !== 'logistics' && profile.name !== 'healthcare') {
366
+ // Loyalty (and any future profiles) skip hero images by default.
367
+ passObject.hexBackgroundColor = '#111827';
368
+ return;
369
+ }
370
+ // Generate hero image based on profile and status
371
+ let imageBuffer;
372
+ if (profile.name === 'logistics') {
373
+ imageBuffer = await generateLogisticsHeroImage(passData.status);
374
+ }
375
+ else {
376
+ imageBuffer = await generateHealthcareHeroImage(passData.status);
377
+ }
378
+ // Save image to local file system
379
+ const { writeFile } = await import('fs/promises');
380
+ const imagePath = join(__dirname, '..', '..', 'hero-images', `${passData.id}.png`);
381
+ // Ensure directory exists
382
+ try {
383
+ await writeFile(imagePath, imageBuffer);
384
+ console.log(`✨ Hero image saved: ${imagePath}`);
385
+ }
386
+ catch (err) {
387
+ // Directory might not exist, that's OK - just skip for now
388
+ console.log(`ℹ️ Hero image generated (not uploaded - requires public URL)`);
389
+ }
390
+ // TODO: Upload to cloud storage and get public URL
391
+ // For now, we'll skip adding the hero image to the pass object
392
+ // since Google Wallet requires a publicly accessible URL
393
+ // If you have a public image hosting URL, uncomment and use:
394
+ // const publicUrl = `https://your-cdn.com/hero-images/${passData.id}.png`
395
+ // passObject.heroImage = {
396
+ // sourceUri: {
397
+ // uri: publicUrl
398
+ // }
399
+ // }
400
+ // Add background color based on status
401
+ const statusColors = {
402
+ ISSUED: '#4A90E2',
403
+ PRESENCE: '#F5A623',
404
+ SCALE: '#7B68EE',
405
+ OPS: '#50E3C2',
406
+ EXITED: '#7ED321',
407
+ SCHEDULED: '#4A90E2',
408
+ CHECKIN: '#F5A623',
409
+ PROCEDURE: '#E94B3C',
410
+ DISCHARGED: '#7ED321'
411
+ };
412
+ passObject.hexBackgroundColor = statusColors[passData.status] || '#4A90E2';
413
+ console.log(`✨ Dynamic color applied for status: ${passData.status} (${passObject.hexBackgroundColor})`);
414
+ }
415
+ catch (error) {
416
+ console.error('⚠️ Failed to generate hero image:', error);
417
+ // Continue without hero image if generation fails
418
+ }
419
+ }
420
+ async createClass(profile, passType) {
421
+ // Stub for creating a Google Wallet class
422
+ // In a real implementation, this would call the Google Wallet API
423
+ 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);
427
+ // This would normally make an API call to:
428
+ // POST https://walletobjects.googleapis.com/walletobjects/v1/genericClass
429
+ }
430
+ }
431
+ export default GoogleWalletAdapter;
@@ -0,0 +1,67 @@
1
+ import type { CreateParentInput, CreateChildInput, CreateBusinessInput, CreateCustomerAccountInput, CreateLoyaltyProgramInput, IssueLoyaltyCardInput, UpdateLoyaltyPointsInput, LoyaltyBusiness, LoyaltyCustomerAccount, ParentPassData, ChildPassData, PassData, PassStatus, ProfileType, ProfileConfig, PassGenerationResult } from '../types.js';
2
+ /**
3
+ * Get a profile by name
4
+ */
5
+ export declare function getProfile(profileType: ProfileType): ProfileConfig;
6
+ /**
7
+ * List all available profiles
8
+ */
9
+ export declare function listProfiles(): ProfileType[];
10
+ /**
11
+ * Create a business (tenant) that owns a loyalty program.
12
+ */
13
+ export declare function createBusiness(input: CreateBusinessInput): LoyaltyBusiness;
14
+ export declare function getBusiness(businessId: string): LoyaltyBusiness | undefined;
15
+ /**
16
+ * Create a customer account under a business.
17
+ */
18
+ export declare function createCustomerAccount(input: CreateCustomerAccountInput): LoyaltyCustomerAccount;
19
+ export declare function getCustomerAccount(customerId: string): LoyaltyCustomerAccount | undefined;
20
+ /**
21
+ * Define (or update) the loyalty program pass for a business.
22
+ * This creates a parent pass with profile=loyalty.
23
+ */
24
+ export declare function createLoyaltyProgram(input: CreateLoyaltyProgramInput): Promise<ParentPassData>;
25
+ /**
26
+ * Issue a loyalty card (child pass) for a customer.
27
+ * QR/barcode value uses memberId.
28
+ */
29
+ export declare function issueLoyaltyCard(input: IssueLoyaltyCardInput): Promise<ChildPassData>;
30
+ /**
31
+ * Update points on a loyalty card.
32
+ */
33
+ export declare function updateLoyaltyPoints(input: UpdateLoyaltyPointsInput): Promise<PassData>;
34
+ /**
35
+ * Create a parent schedule (PES or AppointmentBatch)
36
+ */
37
+ export declare function createParentSchedule(input: CreateParentInput): Promise<ParentPassData>;
38
+ /**
39
+ * Create a child ticket (TO or PatientVisit)
40
+ */
41
+ export declare function createChildTicket(input: CreateChildInput): Promise<ChildPassData>;
42
+ /**
43
+ * Update the status of a pass
44
+ */
45
+ export declare function updatePassStatus(passId: string, newStatus: PassStatus): Promise<PassData>;
46
+ /**
47
+ * Get a pass by ID
48
+ */
49
+ export declare function getPass(passId: string): PassData | undefined;
50
+ /**
51
+ * Get Apple Wallet .pkpass buffer for a pass
52
+ */
53
+ export declare function getPkpassBuffer(passType: 'parent' | 'child', passData: PassData): Promise<Buffer>;
54
+ /**
55
+ * Get Google Wallet object for a pass
56
+ */
57
+ export declare function getGoogleObject(passType: 'parent' | 'child', passData: PassData): Promise<{
58
+ object: any;
59
+ saveUrl: string;
60
+ }>;
61
+ /**
62
+ * Generate a complete pass with both Apple and Google wallet data
63
+ */
64
+ export declare function generatePass(passData: PassData, options?: {
65
+ includeApple?: boolean;
66
+ includeGoogle?: boolean;
67
+ }): Promise<PassGenerationResult>;