sbcwallet 0.0.2 β 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.
- package/README.md +99 -142
- package/dist/adapters/apple.js +17 -10
- package/dist/adapters/google.d.ts +5 -0
- package/dist/adapters/google.js +49 -24
- package/dist/api/unified.d.ts +12 -1
- package/dist/api/unified.js +63 -9
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/types.d.ts +57 -0
- package/dist/types.js +17 -1
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.js +24 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,191 +1,148 @@
|
|
|
1
|
-
#
|
|
1
|
+
# sbcwallet
|
|
2
2
|
|
|
3
|
-
Unified
|
|
3
|
+
Unified wallet-pass SDK for Apple Wallet (.pkpass) and Google Wallet.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
Built on @sbcwallet, it bridges cryptographic truth and real-world credentials β enabling secure, interoperable workflows for logistics, healthcare, and beyond.
|
|
7
|
-
|
|
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
|
-
βΈ»
|
|
16
|
-
|
|
17
|
-
## π Quickstart
|
|
5
|
+
## Install
|
|
18
6
|
|
|
19
7
|
```sh
|
|
20
8
|
npm install sbcwallet
|
|
21
9
|
```
|
|
22
10
|
|
|
23
|
-
|
|
11
|
+
## Quickstart (Loyalty)
|
|
24
12
|
|
|
25
|
-
|
|
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.
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
const pes = await createParentSchedule({
|
|
29
|
-
profile: 'logistics',
|
|
30
|
-
programName: 'Morning Yard Veracruz',
|
|
31
|
-
site: 'Patio Gate 3'
|
|
32
|
-
})
|
|
18
|
+
### Define a business (per-tenant theme) and create its program
|
|
33
19
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
}
|
|
39
49
|
})
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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'
|
|
61
|
+
})
|
|
44
62
|
```
|
|
45
63
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Each business defines its own loyalty program, customers create accounts, and each customer gets a loyalty card that shows:
|
|
49
|
-
- A QR/barcode identifier (`memberId`)
|
|
50
|
-
- Current points (`points`) which can be updated
|
|
64
|
+
### Issue a card and generate a Save URL
|
|
51
65
|
|
|
52
66
|
```ts
|
|
53
67
|
import {
|
|
54
|
-
createBusiness,
|
|
55
68
|
createCustomerAccount,
|
|
56
|
-
createLoyaltyProgram,
|
|
57
69
|
issueLoyaltyCard,
|
|
58
70
|
updateLoyaltyPoints,
|
|
59
71
|
getGoogleObject
|
|
60
72
|
} from 'sbcwallet'
|
|
61
73
|
|
|
62
|
-
const
|
|
63
|
-
|
|
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
|
+
})
|
|
64
96
|
|
|
65
|
-
const customer = createCustomerAccount({ businessId: biz.id, fullName: 'Alice' })
|
|
66
|
-
const card = await issueLoyaltyCard({ businessId: biz.id, customerId: customer.id, initialPoints: 10 })
|
|
67
97
|
await updateLoyaltyPoints({ cardId: card.id, delta: 5 })
|
|
68
98
|
|
|
69
99
|
const { saveUrl } = await getGoogleObject('child', card)
|
|
70
100
|
console.log(saveUrl)
|
|
71
101
|
```
|
|
72
102
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
## π§ Architecture
|
|
76
|
-
```bash
|
|
77
|
-
sbcwallet
|
|
78
|
-
βββ adapters/ # Apple + Google Wallet adapters
|
|
79
|
-
βββ api/ # Unified issuance/update API
|
|
80
|
-
βββ profiles/ # Domain-specific field maps
|
|
81
|
-
βββ templates/ # JSON templates for passes
|
|
82
|
-
βββ types.ts # Shared types and validation
|
|
83
|
-
```
|
|
84
|
-
### Key Components
|
|
85
|
-
```bash
|
|
86
|
-
Module Description
|
|
87
|
-
adapters/apple.ts Builds and signs .pkpass files using passkit-generator.
|
|
88
|
-
adapters/google.ts Creates Google Wallet class/object JSON payloads.
|
|
89
|
-
api/unified.ts Unified functions: createParentSchedule, createChildTicket, updatePassStatus.
|
|
90
|
-
profiles/ Domain-specific mappings (logistics, healthcare, etc.).
|
|
91
|
-
templates/ JSON templates for field mapping and layout.
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
βΈ»
|
|
95
|
-
|
|
96
|
-
## π§© Profiles
|
|
103
|
+
## Location-based surfacing and notifications
|
|
97
104
|
|
|
98
|
-
|
|
105
|
+
This SDK supports two related concepts:
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Statuses ISSUED β PRESENCE β SCALE β OPS β EXITED
|
|
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.
|
|
104
110
|
|
|
105
|
-
|
|
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).
|
|
106
113
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Child Patient Visit Ticket Patient, procedure, status
|
|
110
|
-
Statuses SCHEDULED β CHECKIN β PROCEDURE β DISCHARGED
|
|
114
|
+
```ts
|
|
115
|
+
import { pushLoyaltyMessage } from 'sbcwallet'
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
+
})
|
|
115
123
|
```
|
|
116
124
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
## π Integration with sbcwallet Core
|
|
120
|
-
|
|
121
|
-
sbcwallet Pass automatically uses:
|
|
122
|
-
β’ hashEvent() for deterministic hashes
|
|
123
|
-
β’ signCredential() for ECDSA signatures
|
|
124
|
-
β’ dailyMerkle() for anchoring batches
|
|
125
|
-
|
|
126
|
-
This ensures every pass is cryptographically verifiable and compatible with sbcwalletβs event audit trail.
|
|
127
|
-
|
|
128
|
-
βΈ»
|
|
129
|
-
|
|
130
|
-
## π§ͺ Testing
|
|
131
|
-
|
|
132
|
-
`npm run test`
|
|
133
|
-
|
|
134
|
-
Tests include:
|
|
135
|
-
β’ Apple .pkpass field mapping
|
|
136
|
-
β’ Google Wallet JSON validity
|
|
137
|
-
β’ Cross-profile field validation
|
|
138
|
-
β’ Core integration (hash + sign + verify)
|
|
125
|
+
## Demo server (multi-tenant)
|
|
139
126
|
|
|
140
|
-
βΈ»
|
|
141
|
-
|
|
142
|
-
## βοΈ Environment Variables (Apple Wallet)
|
|
143
|
-
|
|
144
|
-
```sh
|
|
145
|
-
APPLE_TEAM_ID=ABCD1234
|
|
146
|
-
APPLE_PASS_TYPE_ID=pass.com.sbcwallet.logistics
|
|
147
|
-
APPLE_CERT_PATH=./certs/pass.p12
|
|
148
|
-
APPLE_CERT_PASSWORD=yourpassword
|
|
149
|
-
APPLE_WWDR_PATH=./certs/wwdr.pem
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
For Google Wallet, include:
|
|
153
127
|
```sh
|
|
154
|
-
|
|
155
|
-
GOOGLE_SA_JSON=./google/credentials.json
|
|
128
|
+
npm run loyalty:server:multi
|
|
156
129
|
```
|
|
157
130
|
|
|
158
|
-
|
|
131
|
+
Open `http://localhost:5190`.
|
|
159
132
|
|
|
160
|
-
##
|
|
133
|
+
## Configuration
|
|
161
134
|
|
|
162
|
-
|
|
163
|
-
|
|
135
|
+
For Google Wallet Save URLs to work on-device you must set:
|
|
136
|
+
- `GOOGLE_ISSUER_ID`
|
|
137
|
+
- `GOOGLE_SA_JSON`
|
|
164
138
|
|
|
165
|
-
|
|
139
|
+
For Apple Wallet signing, see APPLE_WALLET_SETUP.md.
|
|
166
140
|
|
|
167
|
-
##
|
|
168
|
-
1. Fork the repo
|
|
169
|
-
2. Run npm install
|
|
170
|
-
3. Add or improve a profile under src/profiles/
|
|
171
|
-
4. Write tests in tests/
|
|
172
|
-
5. Submit a PR using conventional commits
|
|
141
|
+
## Development
|
|
173
142
|
|
|
174
|
-
βΈ»
|
|
175
|
-
|
|
176
|
-
## π§ Part of the sbcwallet Ecosystem
|
|
177
|
-
|
|
178
|
-
Repo Purpose
|
|
179
143
|
```sh
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
sbcwallet/id Hosted identity & orchestration layer (SaaS)
|
|
144
|
+
npm run build
|
|
145
|
+
npm test
|
|
146
|
+
npm pack --dry-run
|
|
184
147
|
```
|
|
185
148
|
|
|
186
|
-
βΈ»
|
|
187
|
-
|
|
188
|
-
βsbcwallet Pass connects cryptographic truth with human experience β
|
|
189
|
-
turning every credential into a verifiable story.β
|
|
190
|
-
|
|
191
|
-
Reflection: evidence β logic consistent brevity optimized
|
package/dist/adapters/apple.js
CHANGED
|
@@ -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
|
*
|
package/dist/adapters/google.js
CHANGED
|
@@ -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
|
-
|
|
36
|
-
|
|
36
|
+
logWarn('Google Wallet API loyaltyClass create/update failed');
|
|
37
|
+
logWarn(error);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
else {
|
|
40
|
-
|
|
41
|
+
logWarn('No Google Auth - class not created in API (will not work on device)');
|
|
41
42
|
}
|
|
42
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
350
|
+
logDebug(`β
${kind} updated in Google Wallet API`);
|
|
344
351
|
}
|
|
345
352
|
catch (updateError) {
|
|
346
|
-
|
|
353
|
+
logError(`β Error updating ${kind}:`, updateError);
|
|
347
354
|
throw updateError;
|
|
348
355
|
}
|
|
349
356
|
}
|
|
350
357
|
else {
|
|
351
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
438
|
+
logDebug(`β¨ Dynamic color applied for status: ${passData.status} (${passObject.hexBackgroundColor})`);
|
|
414
439
|
}
|
|
415
440
|
catch (error) {
|
|
416
|
-
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
}
|
package/dist/api/unified.d.ts
CHANGED
|
@@ -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
|
*/
|
package/dist/api/unified.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { CreateParentInputSchema, CreateChildInputSchema, CreateBusinessInputSchema, CreateCustomerAccountInputSchema, CreateLoyaltyProgramInputSchema, IssueLoyaltyCardInputSchema, UpdateLoyaltyPointsInputSchema } from '../types.js';
|
|
1
|
+
import { CreateParentInputSchema, CreateChildInputSchema, CreateBusinessInputSchema, CreateCustomerAccountInputSchema, CreateLoyaltyProgramInputSchema, IssueLoyaltyCardInputSchema, UpdateLoyaltyPointsInputSchema, PushLoyaltyMessageInputSchema } from '../types.js';
|
|
2
2
|
import { AppleWalletAdapter } from '../adapters/apple.js';
|
|
3
3
|
import { GoogleWalletAdapter } from '../adapters/google.js';
|
|
4
|
+
import { logWarn } from '../utils/logger.js';
|
|
4
5
|
import logisticsProfile from '../profiles/logistics/index.js';
|
|
5
6
|
import healthcareProfile from '../profiles/healthcare/index.js';
|
|
6
7
|
import loyaltyProfile from '../profiles/loyalty/index.js';
|
|
@@ -78,6 +79,7 @@ export function createBusiness(input) {
|
|
|
78
79
|
name: validated.name,
|
|
79
80
|
programName: validated.programName || `${validated.name} Loyalty`,
|
|
80
81
|
pointsLabel: validated.pointsLabel || 'Points',
|
|
82
|
+
wallet: validated.wallet,
|
|
81
83
|
createdAt: now,
|
|
82
84
|
updatedAt: now
|
|
83
85
|
};
|
|
@@ -122,6 +124,38 @@ export async function createLoyaltyProgram(input) {
|
|
|
122
124
|
if (!business) {
|
|
123
125
|
throw new Error(`Business not found: ${validated.businessId}`);
|
|
124
126
|
}
|
|
127
|
+
const businessWallet = business.wallet || {};
|
|
128
|
+
const inputMetadata = (validated.metadata || {});
|
|
129
|
+
const mergedGoogleWallet = {
|
|
130
|
+
...(businessWallet.googleWallet || {}),
|
|
131
|
+
...(inputMetadata.googleWallet || {}),
|
|
132
|
+
locations: validated.locations,
|
|
133
|
+
countryCode: validated.countryCode,
|
|
134
|
+
homepageUrl: validated.homepageUrl
|
|
135
|
+
};
|
|
136
|
+
// If caller didn't set issuerName explicitly, default to the business name
|
|
137
|
+
// so the card shows the tenant brand instead of template defaults.
|
|
138
|
+
if (!mergedGoogleWallet.issuerName) {
|
|
139
|
+
mergedGoogleWallet.issuerName = business.name;
|
|
140
|
+
}
|
|
141
|
+
const mergedAppleWallet = {
|
|
142
|
+
...(businessWallet.appleWallet || {}),
|
|
143
|
+
...(inputMetadata.appleWallet || {})
|
|
144
|
+
};
|
|
145
|
+
// Apple Wallet: location-based surfacing is controlled via pass.json fields.
|
|
146
|
+
// We attach them at the program level so issued cards inherit the behavior.
|
|
147
|
+
if (validated.locations && Array.isArray(validated.locations) && validated.locations.length > 0) {
|
|
148
|
+
mergedAppleWallet.passOverrides ||= {};
|
|
149
|
+
if (!mergedAppleWallet.passOverrides.locations) {
|
|
150
|
+
mergedAppleWallet.passOverrides.locations = validated.locations;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (validated.relevantText) {
|
|
154
|
+
mergedAppleWallet.passOverrides ||= {};
|
|
155
|
+
if (!mergedAppleWallet.passOverrides.relevantText) {
|
|
156
|
+
mergedAppleWallet.passOverrides.relevantText = validated.relevantText;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
125
159
|
const program = await createParentSchedule({
|
|
126
160
|
id: validated.programId,
|
|
127
161
|
profile: 'loyalty',
|
|
@@ -132,12 +166,8 @@ export async function createLoyaltyProgram(input) {
|
|
|
132
166
|
businessId: business.id,
|
|
133
167
|
businessName: business.name,
|
|
134
168
|
pointsLabel: business.pointsLabel,
|
|
135
|
-
googleWallet:
|
|
136
|
-
|
|
137
|
-
locations: validated.locations,
|
|
138
|
-
countryCode: validated.countryCode,
|
|
139
|
-
homepageUrl: validated.homepageUrl
|
|
140
|
-
}
|
|
169
|
+
googleWallet: mergedGoogleWallet,
|
|
170
|
+
appleWallet: mergedAppleWallet
|
|
141
171
|
}
|
|
142
172
|
});
|
|
143
173
|
business.loyaltyProgramId = program.id;
|
|
@@ -164,6 +194,7 @@ export async function issueLoyaltyCard(input) {
|
|
|
164
194
|
}
|
|
165
195
|
const program = passStore.get(business.loyaltyProgramId);
|
|
166
196
|
const programGoogleWallet = program && program.type === 'parent' ? program.metadata?.googleWallet : undefined;
|
|
197
|
+
const programAppleWallet = program && program.type === 'parent' ? program.metadata?.appleWallet : undefined;
|
|
167
198
|
const card = await createChildTicket({
|
|
168
199
|
id: validated.cardId,
|
|
169
200
|
profile: 'loyalty',
|
|
@@ -180,6 +211,10 @@ export async function issueLoyaltyCard(input) {
|
|
|
180
211
|
googleWallet: {
|
|
181
212
|
...(programGoogleWallet || {}),
|
|
182
213
|
...(validated.metadata?.googleWallet || {})
|
|
214
|
+
},
|
|
215
|
+
appleWallet: {
|
|
216
|
+
...(programAppleWallet || {}),
|
|
217
|
+
...(validated.metadata?.appleWallet || {})
|
|
183
218
|
}
|
|
184
219
|
}
|
|
185
220
|
});
|
|
@@ -214,6 +249,25 @@ export async function updateLoyaltyPoints(input) {
|
|
|
214
249
|
passStore.set(pass.id, pass);
|
|
215
250
|
return pass;
|
|
216
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Send a message to a Google Wallet loyalty object.
|
|
254
|
+
*
|
|
255
|
+
* Notes:
|
|
256
|
+
* - This uses the Google Wallet API addMessage endpoint (requires service account credentials).
|
|
257
|
+
* - Location-based surfacing is controlled by the pass locations; your system decides WHEN to send messages.
|
|
258
|
+
*/
|
|
259
|
+
export async function pushLoyaltyMessage(input) {
|
|
260
|
+
const validated = PushLoyaltyMessageInputSchema.parse(input);
|
|
261
|
+
const issuerId = process.env.GOOGLE_ISSUER_ID || 'test-issuer';
|
|
262
|
+
const objectId = validated.objectId || `${issuerId}.${validated.cardId}`;
|
|
263
|
+
const adapter = new GoogleWalletAdapter();
|
|
264
|
+
await adapter.addMessageToLoyaltyObject(objectId, {
|
|
265
|
+
header: validated.header,
|
|
266
|
+
body: validated.body,
|
|
267
|
+
messageType: validated.messageType
|
|
268
|
+
});
|
|
269
|
+
return { ok: true, objectId };
|
|
270
|
+
}
|
|
217
271
|
/**
|
|
218
272
|
* Create a parent schedule (PES or AppointmentBatch)
|
|
219
273
|
*/
|
|
@@ -358,7 +412,7 @@ export async function generatePass(passData, options = { includeApple: true, inc
|
|
|
358
412
|
result.applePkpass = await getPkpassBuffer(passType, passData);
|
|
359
413
|
}
|
|
360
414
|
catch (error) {
|
|
361
|
-
|
|
415
|
+
logWarn('Failed to generate Apple Wallet pass:', error);
|
|
362
416
|
}
|
|
363
417
|
}
|
|
364
418
|
if (options.includeGoogle) {
|
|
@@ -368,7 +422,7 @@ export async function generatePass(passData, options = { includeApple: true, inc
|
|
|
368
422
|
result.googleSaveUrl = googleResult.saveUrl;
|
|
369
423
|
}
|
|
370
424
|
catch (error) {
|
|
371
|
-
|
|
425
|
+
logWarn('Failed to generate Google Wallet object:', error);
|
|
372
426
|
}
|
|
373
427
|
}
|
|
374
428
|
return result;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { createParentSchedule, createChildTicket, updatePassStatus, createBusiness, getBusiness, createCustomerAccount, getCustomerAccount, createLoyaltyProgram, issueLoyaltyCard, updateLoyaltyPoints, getPkpassBuffer, getGoogleObject, listProfiles, getProfile, getPass, generatePass } from './api/unified.js';
|
|
1
|
+
export { createParentSchedule, createChildTicket, updatePassStatus, createBusiness, getBusiness, createCustomerAccount, getCustomerAccount, createLoyaltyProgram, issueLoyaltyCard, updateLoyaltyPoints, pushLoyaltyMessage, getPkpassBuffer, getGoogleObject, listProfiles, getProfile, getPass, generatePass } from './api/unified.js';
|
|
2
2
|
export { AppleWalletAdapter } from './adapters/apple.js';
|
|
3
3
|
export { GoogleWalletAdapter } from './adapters/google.js';
|
|
4
4
|
export { default as logisticsProfile } from './profiles/logistics/index.js';
|
|
5
5
|
export { default as healthcareProfile } from './profiles/healthcare/index.js';
|
|
6
6
|
export { default as loyaltyProfile } from './profiles/loyalty/index.js';
|
|
7
|
-
export type { ProfileType, PassStatus, LogisticsStatus, HealthcareStatus, LoyaltyStatus, GeoLocation, TimeWindow, BasePassData, ParentPassData, ChildPassData, PassData, CreateParentInput, CreateChildInput, LoyaltyBusiness, LoyaltyCustomerAccount, CreateBusinessInput, CreateCustomerAccountInput, CreateLoyaltyProgramInput, IssueLoyaltyCardInput, UpdateLoyaltyPointsInput, ApplePassConfig, ApplePassField, ApplePassTemplate, GooglePassConfig, GoogleTextField, GooglePassClass, GooglePassObject, ProfileFieldMap, ProfileConfig, PassGenerationResult } from './types.js';
|
|
8
|
-
export { CreateParentInputSchema, CreateChildInputSchema, TimeWindowSchema, CreateBusinessInputSchema, CreateCustomerAccountInputSchema, CreateLoyaltyProgramInputSchema, IssueLoyaltyCardInputSchema, UpdateLoyaltyPointsInputSchema } from './types.js';
|
|
7
|
+
export type { ProfileType, PassStatus, LogisticsStatus, HealthcareStatus, LoyaltyStatus, GeoLocation, TimeWindow, BasePassData, ParentPassData, ChildPassData, PassData, CreateParentInput, CreateChildInput, LoyaltyBusiness, LoyaltyCustomerAccount, CreateBusinessInput, CreateCustomerAccountInput, CreateLoyaltyProgramInput, IssueLoyaltyCardInput, UpdateLoyaltyPointsInput, PushLoyaltyMessageInput, ApplePassConfig, ApplePassField, ApplePassTemplate, GooglePassConfig, GoogleTextField, GooglePassClass, GooglePassObject, ProfileFieldMap, ProfileConfig, PassGenerationResult } from './types.js';
|
|
8
|
+
export { CreateParentInputSchema, CreateChildInputSchema, TimeWindowSchema, CreateBusinessInputSchema, CreateCustomerAccountInputSchema, CreateLoyaltyProgramInputSchema, IssueLoyaltyCardInputSchema, UpdateLoyaltyPointsInputSchema, PushLoyaltyMessageInputSchema } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Main exports
|
|
2
|
-
export { createParentSchedule, createChildTicket, updatePassStatus, createBusiness, getBusiness, createCustomerAccount, getCustomerAccount, createLoyaltyProgram, issueLoyaltyCard, updateLoyaltyPoints, getPkpassBuffer, getGoogleObject, listProfiles, getProfile, getPass, generatePass } from './api/unified.js';
|
|
2
|
+
export { createParentSchedule, createChildTicket, updatePassStatus, createBusiness, getBusiness, createCustomerAccount, getCustomerAccount, createLoyaltyProgram, issueLoyaltyCard, updateLoyaltyPoints, pushLoyaltyMessage, getPkpassBuffer, getGoogleObject, listProfiles, getProfile, getPass, generatePass } from './api/unified.js';
|
|
3
3
|
// Adapter exports
|
|
4
4
|
export { AppleWalletAdapter } from './adapters/apple.js';
|
|
5
5
|
export { GoogleWalletAdapter } from './adapters/google.js';
|
|
@@ -8,4 +8,4 @@ export { default as logisticsProfile } from './profiles/logistics/index.js';
|
|
|
8
8
|
export { default as healthcareProfile } from './profiles/healthcare/index.js';
|
|
9
9
|
export { default as loyaltyProfile } from './profiles/loyalty/index.js';
|
|
10
10
|
// Schema exports
|
|
11
|
-
export { CreateParentInputSchema, CreateChildInputSchema, TimeWindowSchema, CreateBusinessInputSchema, CreateCustomerAccountInputSchema, CreateLoyaltyProgramInputSchema, IssueLoyaltyCardInputSchema, UpdateLoyaltyPointsInputSchema } from './types.js';
|
|
11
|
+
export { CreateParentInputSchema, CreateChildInputSchema, TimeWindowSchema, CreateBusinessInputSchema, CreateCustomerAccountInputSchema, CreateLoyaltyProgramInputSchema, IssueLoyaltyCardInputSchema, UpdateLoyaltyPointsInputSchema, PushLoyaltyMessageInputSchema } from './types.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -154,6 +154,10 @@ export interface LoyaltyBusiness {
|
|
|
154
154
|
programName: string;
|
|
155
155
|
pointsLabel: string;
|
|
156
156
|
loyaltyProgramId?: string;
|
|
157
|
+
wallet?: {
|
|
158
|
+
googleWallet?: Record<string, any>;
|
|
159
|
+
appleWallet?: Record<string, any>;
|
|
160
|
+
};
|
|
157
161
|
createdAt: string;
|
|
158
162
|
updatedAt: string;
|
|
159
163
|
}
|
|
@@ -170,16 +174,34 @@ export declare const CreateBusinessInputSchema: z.ZodObject<{
|
|
|
170
174
|
name: z.ZodString;
|
|
171
175
|
programName: z.ZodOptional<z.ZodString>;
|
|
172
176
|
pointsLabel: z.ZodOptional<z.ZodString>;
|
|
177
|
+
wallet: z.ZodOptional<z.ZodObject<{
|
|
178
|
+
googleWallet: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
179
|
+
appleWallet: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
180
|
+
}, "strip", z.ZodTypeAny, {
|
|
181
|
+
googleWallet?: Record<string, any> | undefined;
|
|
182
|
+
appleWallet?: Record<string, any> | undefined;
|
|
183
|
+
}, {
|
|
184
|
+
googleWallet?: Record<string, any> | undefined;
|
|
185
|
+
appleWallet?: Record<string, any> | undefined;
|
|
186
|
+
}>>;
|
|
173
187
|
}, "strip", z.ZodTypeAny, {
|
|
174
188
|
name: string;
|
|
175
189
|
id?: string | undefined;
|
|
176
190
|
programName?: string | undefined;
|
|
177
191
|
pointsLabel?: string | undefined;
|
|
192
|
+
wallet?: {
|
|
193
|
+
googleWallet?: Record<string, any> | undefined;
|
|
194
|
+
appleWallet?: Record<string, any> | undefined;
|
|
195
|
+
} | undefined;
|
|
178
196
|
}, {
|
|
179
197
|
name: string;
|
|
180
198
|
id?: string | undefined;
|
|
181
199
|
programName?: string | undefined;
|
|
182
200
|
pointsLabel?: string | undefined;
|
|
201
|
+
wallet?: {
|
|
202
|
+
googleWallet?: Record<string, any> | undefined;
|
|
203
|
+
appleWallet?: Record<string, any> | undefined;
|
|
204
|
+
} | undefined;
|
|
183
205
|
}>;
|
|
184
206
|
export type CreateBusinessInput = z.infer<typeof CreateBusinessInputSchema>;
|
|
185
207
|
export declare const CreateCustomerAccountInputSchema: z.ZodObject<{
|
|
@@ -214,6 +236,7 @@ export declare const CreateLoyaltyProgramInputSchema: z.ZodObject<{
|
|
|
214
236
|
latitude: number;
|
|
215
237
|
longitude: number;
|
|
216
238
|
}>, "many">>;
|
|
239
|
+
relevantText: z.ZodOptional<z.ZodString>;
|
|
217
240
|
countryCode: z.ZodOptional<z.ZodString>;
|
|
218
241
|
homepageUrl: z.ZodOptional<z.ZodString>;
|
|
219
242
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
@@ -227,6 +250,7 @@ export declare const CreateLoyaltyProgramInputSchema: z.ZodObject<{
|
|
|
227
250
|
latitude: number;
|
|
228
251
|
longitude: number;
|
|
229
252
|
}[] | undefined;
|
|
253
|
+
relevantText?: string | undefined;
|
|
230
254
|
countryCode?: string | undefined;
|
|
231
255
|
homepageUrl?: string | undefined;
|
|
232
256
|
}, {
|
|
@@ -239,6 +263,7 @@ export declare const CreateLoyaltyProgramInputSchema: z.ZodObject<{
|
|
|
239
263
|
latitude: number;
|
|
240
264
|
longitude: number;
|
|
241
265
|
}[] | undefined;
|
|
266
|
+
relevantText?: string | undefined;
|
|
242
267
|
countryCode?: string | undefined;
|
|
243
268
|
homepageUrl?: string | undefined;
|
|
244
269
|
}>;
|
|
@@ -285,6 +310,38 @@ export declare const UpdateLoyaltyPointsInputSchema: z.ZodEffects<z.ZodObject<{
|
|
|
285
310
|
delta?: number | undefined;
|
|
286
311
|
}>;
|
|
287
312
|
export type UpdateLoyaltyPointsInput = z.infer<typeof UpdateLoyaltyPointsInputSchema>;
|
|
313
|
+
export declare const PushLoyaltyMessageInputSchema: z.ZodEffects<z.ZodObject<{
|
|
314
|
+
cardId: z.ZodOptional<z.ZodString>;
|
|
315
|
+
objectId: z.ZodOptional<z.ZodString>;
|
|
316
|
+
header: z.ZodString;
|
|
317
|
+
body: z.ZodString;
|
|
318
|
+
messageType: z.ZodOptional<z.ZodString>;
|
|
319
|
+
}, "strip", z.ZodTypeAny, {
|
|
320
|
+
header: string;
|
|
321
|
+
body: string;
|
|
322
|
+
cardId?: string | undefined;
|
|
323
|
+
objectId?: string | undefined;
|
|
324
|
+
messageType?: string | undefined;
|
|
325
|
+
}, {
|
|
326
|
+
header: string;
|
|
327
|
+
body: string;
|
|
328
|
+
cardId?: string | undefined;
|
|
329
|
+
objectId?: string | undefined;
|
|
330
|
+
messageType?: string | undefined;
|
|
331
|
+
}>, {
|
|
332
|
+
header: string;
|
|
333
|
+
body: string;
|
|
334
|
+
cardId?: string | undefined;
|
|
335
|
+
objectId?: string | undefined;
|
|
336
|
+
messageType?: string | undefined;
|
|
337
|
+
}, {
|
|
338
|
+
header: string;
|
|
339
|
+
body: string;
|
|
340
|
+
cardId?: string | undefined;
|
|
341
|
+
objectId?: string | undefined;
|
|
342
|
+
messageType?: string | undefined;
|
|
343
|
+
}>;
|
|
344
|
+
export type PushLoyaltyMessageInput = z.infer<typeof PushLoyaltyMessageInputSchema>;
|
|
288
345
|
export type GeoLocation = {
|
|
289
346
|
latitude: number;
|
|
290
347
|
longitude: number;
|
package/dist/types.js
CHANGED
|
@@ -40,7 +40,12 @@ export const CreateBusinessInputSchema = z.object({
|
|
|
40
40
|
id: z.string().min(1).optional(),
|
|
41
41
|
name: z.string().min(1),
|
|
42
42
|
programName: z.string().min(1).optional(),
|
|
43
|
-
pointsLabel: z.string().min(1).optional()
|
|
43
|
+
pointsLabel: z.string().min(1).optional(),
|
|
44
|
+
// Per-business theming / design knobs (optional)
|
|
45
|
+
wallet: z.object({
|
|
46
|
+
googleWallet: z.record(z.any()).optional(),
|
|
47
|
+
appleWallet: z.record(z.any()).optional()
|
|
48
|
+
}).optional()
|
|
44
49
|
});
|
|
45
50
|
export const CreateCustomerAccountInputSchema = z.object({
|
|
46
51
|
id: z.string().min(1).optional(),
|
|
@@ -59,6 +64,8 @@ export const CreateLoyaltyProgramInputSchema = z.object({
|
|
|
59
64
|
latitude: z.number().min(-90).max(90),
|
|
60
65
|
longitude: z.number().min(-180).max(180)
|
|
61
66
|
})).optional(),
|
|
67
|
+
// Apple Wallet: text shown when the pass becomes relevant (e.g., near a location)
|
|
68
|
+
relevantText: z.string().min(1).optional(),
|
|
62
69
|
countryCode: z.string().length(2).optional(),
|
|
63
70
|
homepageUrl: z.string().url().optional(),
|
|
64
71
|
metadata: z.record(z.any()).optional()
|
|
@@ -78,3 +85,12 @@ export const UpdateLoyaltyPointsInputSchema = z.object({
|
|
|
78
85
|
}).refine(v => v.setPoints !== undefined || v.delta !== undefined, {
|
|
79
86
|
message: 'Provide either setPoints or delta'
|
|
80
87
|
});
|
|
88
|
+
export const PushLoyaltyMessageInputSchema = z.object({
|
|
89
|
+
cardId: z.string().min(1).optional(),
|
|
90
|
+
objectId: z.string().min(1).optional(),
|
|
91
|
+
header: z.string().min(1),
|
|
92
|
+
body: z.string().min(1),
|
|
93
|
+
messageType: z.string().min(1).optional()
|
|
94
|
+
}).refine(v => v.cardId !== undefined || v.objectId !== undefined, {
|
|
95
|
+
message: 'Provide either cardId or objectId'
|
|
96
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const isTestEnv = process.env.NODE_ENV === 'test' ||
|
|
2
|
+
process.env.VITEST === 'true' ||
|
|
3
|
+
process.env.VITEST === '1';
|
|
4
|
+
const isDebugEnabled = /^(1|true|yes)$/i.test(process.env.SBCWALLET_DEBUG || '');
|
|
5
|
+
export function logDebug(...args) {
|
|
6
|
+
if (!isDebugEnabled)
|
|
7
|
+
return;
|
|
8
|
+
console.log(...args);
|
|
9
|
+
}
|
|
10
|
+
export function logInfo(...args) {
|
|
11
|
+
if (!isDebugEnabled)
|
|
12
|
+
return;
|
|
13
|
+
console.log(...args);
|
|
14
|
+
}
|
|
15
|
+
export function logWarn(...args) {
|
|
16
|
+
if (isTestEnv && !isDebugEnabled)
|
|
17
|
+
return;
|
|
18
|
+
console.warn(...args);
|
|
19
|
+
}
|
|
20
|
+
export function logError(...args) {
|
|
21
|
+
if (isTestEnv && !isDebugEnabled)
|
|
22
|
+
return;
|
|
23
|
+
console.error(...args);
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbcwallet",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Unified Wallet-Pass SDK for Real-World Credentials",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"publish:npm": "npm publish",
|
|
32
32
|
"loyalty:issue": "npm run build && node examples/loyalty-google-issue.js",
|
|
33
33
|
"loyalty:server": "npm run build && node examples/loyalty-admin-server.js",
|
|
34
|
-
"loyalty:server:fixed": "npm run build && node examples/loyalty-fixed-card-server.js"
|
|
34
|
+
"loyalty:server:fixed": "npm run build && node examples/loyalty-fixed-card-server.js",
|
|
35
|
+
"loyalty:server:multi": "npm run build && node examples/loyalty-multi-tenant-server.js"
|
|
35
36
|
},
|
|
36
37
|
"keywords": [
|
|
37
38
|
"wallet",
|