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
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # 🎟️ sbcwallet
2
+
3
+ Unified Wallet-Pass SDK for Real-World Credentials
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.
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
18
+
19
+ ```jsnpm install sbcwallet
20
+
21
+ import { createParentSchedule, createChildTicket, getPkpassBuffer } from 'sbcwallet'
22
+
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'
28
+ })
29
+
30
+ // 2️⃣ Claim a child Transport Order
31
+ const to = await createChildTicket({
32
+ parentId: pes.id,
33
+ plate: 'ABC123A',
34
+ carrier: 'Transportes Golfo'
35
+ })
36
+
37
+ // 3️⃣ Generate Apple Wallet pass
38
+ const buf = await getPkpassBuffer('child', to)
39
+ await fs.promises.writeFile('ticket.pkpass', buf)
40
+ ```
41
+
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
47
+
48
+ ```ts
49
+ import {
50
+ createBusiness,
51
+ createCustomerAccount,
52
+ createLoyaltyProgram,
53
+ issueLoyaltyCard,
54
+ updateLoyaltyPoints,
55
+ getGoogleObject
56
+ } from 'sbcwallet'
57
+
58
+ const biz = createBusiness({ name: 'SBC Coffee', pointsLabel: 'Beans' })
59
+ await createLoyaltyProgram({ businessId: biz.id })
60
+
61
+ const customer = createCustomerAccount({ businessId: biz.id, fullName: 'Alice' })
62
+ const card = await issueLoyaltyCard({ businessId: biz.id, customerId: customer.id, initialPoints: 10 })
63
+ await updateLoyaltyPoints({ cardId: card.id, delta: 5 })
64
+
65
+ const { saveUrl } = await getGoogleObject('child', card)
66
+ console.log(saveUrl)
67
+ ```
68
+
69
+ βΈ»
70
+
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
+ ```
89
+
90
+ βΈ»
91
+
92
+ ## 🧩 Profiles
93
+
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
107
+
108
+ Switch profiles dynamically:
109
+ ```js
110
+ await createChildTicket({ profile: 'healthcare', ... })
111
+ ```
112
+
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
+ βΈ»
137
+
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
+ ```sh
150
+ GOOGLE_ISSUER_ID=issuer-id
151
+ GOOGLE_SA_JSON=./google/credentials.json
152
+ ```
153
+
154
+ βΈ»
155
+
156
+ ## 🧾 License
157
+
158
+ Apache License 2.0
159
+ Β© 2025 sbcwallet β€” open and extensible.
160
+
161
+ βΈ»
162
+
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
169
+
170
+ βΈ»
171
+
172
+ ## 🧭 Part of the sbcwallet Ecosystem
173
+
174
+ Repo Purpose
175
+ ```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)
180
+ ```
181
+
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
@@ -0,0 +1,10 @@
1
+ import type { PassData, ApplePassConfig, ProfileConfig } from '../types.js';
2
+ export declare class AppleWalletAdapter {
3
+ private config;
4
+ constructor(config?: Partial<ApplePassConfig>);
5
+ generatePkpass(passData: PassData, profile: ProfileConfig, passType: 'parent' | 'child'): Promise<Buffer>;
6
+ private mergeTemplates;
7
+ private populateTemplate;
8
+ private getFieldValue;
9
+ }
10
+ export default AppleWalletAdapter;
@@ -0,0 +1,153 @@
1
+ import { PKPass } from 'passkit-generator';
2
+ import { readFile } from 'fs/promises';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+ export class AppleWalletAdapter {
8
+ config;
9
+ constructor(config) {
10
+ this.config = {
11
+ teamId: config?.teamId || process.env.APPLE_TEAM_ID || '',
12
+ passTypeId: config?.passTypeId || process.env.APPLE_PASS_TYPE_ID || '',
13
+ certPath: config?.certPath || process.env.APPLE_CERT_PATH || '',
14
+ certPassword: config?.certPassword || process.env.APPLE_CERT_PASSWORD || '',
15
+ wwdrPath: config?.wwdrPath || process.env.APPLE_WWDR_PATH || ''
16
+ };
17
+ }
18
+ async generatePkpass(passData, profile, passType) {
19
+ try {
20
+ // Load template
21
+ const templatePath = join(__dirname, '..', 'templates', 'apple', `${passType}.json`);
22
+ const templateContent = await readFile(templatePath, 'utf-8');
23
+ const baseTemplate = JSON.parse(templateContent);
24
+ // Get profile-specific template
25
+ const profileTemplate = profile.defaultTemplates.apple[passType];
26
+ // Merge templates
27
+ const template = this.mergeTemplates(baseTemplate, profileTemplate);
28
+ // Apply pass data to template
29
+ const populatedTemplate = this.populateTemplate(template, passData, profile, passType);
30
+ // Build pass props from populated template
31
+ const passProps = {
32
+ serialNumber: passData.id,
33
+ description: populatedTemplate.description || 'sbcwallet Pass',
34
+ organizationName: populatedTemplate.organizationName || 'sbcwallet',
35
+ passTypeIdentifier: this.config.passTypeId,
36
+ teamIdentifier: this.config.teamId
37
+ };
38
+ // Add colors
39
+ if (populatedTemplate.backgroundColor) {
40
+ passProps.backgroundColor = populatedTemplate.backgroundColor;
41
+ }
42
+ if (populatedTemplate.foregroundColor) {
43
+ passProps.foregroundColor = populatedTemplate.foregroundColor;
44
+ }
45
+ if (populatedTemplate.labelColor) {
46
+ passProps.labelColor = populatedTemplate.labelColor;
47
+ }
48
+ if (populatedTemplate.logoText) {
49
+ passProps.logoText = populatedTemplate.logoText;
50
+ }
51
+ // Add barcodes
52
+ if (populatedTemplate.barcodes && populatedTemplate.barcodes.length > 0) {
53
+ passProps.barcodes = populatedTemplate.barcodes;
54
+ }
55
+ // Add generic fields
56
+ if (populatedTemplate.generic) {
57
+ passProps.generic = populatedTemplate.generic;
58
+ }
59
+ // Create pass
60
+ const pass = new PKPass({}, {
61
+ wwdr: this.config.wwdrPath,
62
+ signerCert: this.config.certPath,
63
+ signerKey: this.config.certPath,
64
+ signerKeyPassphrase: this.config.certPassword
65
+ }, passProps);
66
+ // Generate buffer
67
+ const buffer = await pass.getAsBuffer();
68
+ return buffer;
69
+ }
70
+ catch (error) {
71
+ throw new Error(`Failed to generate Apple Wallet pass: ${error instanceof Error ? error.message : String(error)}`);
72
+ }
73
+ }
74
+ mergeTemplates(base, profile) {
75
+ return {
76
+ ...base,
77
+ ...profile,
78
+ generic: {
79
+ ...base.generic,
80
+ ...profile.generic,
81
+ primaryFields: profile.generic?.primaryFields || base.generic?.primaryFields,
82
+ secondaryFields: profile.generic?.secondaryFields || base.generic?.secondaryFields,
83
+ auxiliaryFields: profile.generic?.auxiliaryFields || base.generic?.auxiliaryFields,
84
+ backFields: profile.generic?.backFields || base.generic?.backFields,
85
+ headerFields: profile.generic?.headerFields || base.generic?.headerFields
86
+ }
87
+ };
88
+ }
89
+ populateTemplate(template, passData, profile, passType) {
90
+ const populated = { ...template };
91
+ if (populated.generic) {
92
+ // Populate primary fields
93
+ if (populated.generic.primaryFields) {
94
+ populated.generic.primaryFields = populated.generic.primaryFields.map(field => {
95
+ const value = this.getFieldValue(field.key, passData);
96
+ return { ...field, value: value || field.value };
97
+ });
98
+ }
99
+ // Populate secondary fields
100
+ if (populated.generic.secondaryFields) {
101
+ populated.generic.secondaryFields = populated.generic.secondaryFields.map(field => {
102
+ const value = this.getFieldValue(field.key, passData);
103
+ return { ...field, value: value || field.value };
104
+ });
105
+ }
106
+ // Populate auxiliary fields
107
+ if (populated.generic.auxiliaryFields) {
108
+ populated.generic.auxiliaryFields = populated.generic.auxiliaryFields.map(field => {
109
+ const value = this.getFieldValue(field.key, passData);
110
+ return { ...field, value: value || field.value };
111
+ });
112
+ }
113
+ // Populate back fields
114
+ if (populated.generic.backFields) {
115
+ populated.generic.backFields = populated.generic.backFields.map(field => {
116
+ const value = this.getFieldValue(field.key, passData);
117
+ return { ...field, value: value || field.value };
118
+ });
119
+ }
120
+ }
121
+ // Set barcode message
122
+ if (populated.barcodes && populated.barcodes.length > 0) {
123
+ const barcodeValue = passData.memberId || passData.id;
124
+ populated.barcodes[0].message = barcodeValue;
125
+ }
126
+ return populated;
127
+ }
128
+ getFieldValue(key, passData) {
129
+ // Handle nested keys like 'window.from'
130
+ const keys = key.split('.');
131
+ let value = passData;
132
+ for (const k of keys) {
133
+ if (value && typeof value === 'object' && k in value) {
134
+ value = value[k];
135
+ }
136
+ else {
137
+ return '';
138
+ }
139
+ }
140
+ // Map field names for common fields
141
+ if (key === 'scheduleId' || key === 'orderId' || key === 'batchId' || key === 'visitId') {
142
+ return passData.id;
143
+ }
144
+ if (key === 'windowFrom' && passData.type === 'parent' && passData.window) {
145
+ return new Date(passData.window.from).toLocaleString();
146
+ }
147
+ if (key === 'windowTo' && passData.type === 'parent' && passData.window) {
148
+ return new Date(passData.window.to).toLocaleString();
149
+ }
150
+ return value !== undefined && value !== null ? String(value) : '';
151
+ }
152
+ }
153
+ export default AppleWalletAdapter;
@@ -0,0 +1,26 @@
1
+ import type { PassData, GooglePassConfig, GooglePassObject, ProfileConfig } from '../types.js';
2
+ export declare class GoogleWalletAdapter {
3
+ private config;
4
+ private auth;
5
+ constructor(config?: Partial<GooglePassConfig>);
6
+ generatePassObject(passData: PassData, profile: ProfileConfig, passType: 'parent' | 'child'): Promise<{
7
+ object: GooglePassObject;
8
+ saveUrl: string;
9
+ }>;
10
+ private generateLoyaltyClass;
11
+ private populateObject;
12
+ private applyLoyaltyExtras;
13
+ private getFieldValue;
14
+ private generateSaveUrl;
15
+ private upsertInAPI;
16
+ /**
17
+ * Generate and add hero image with progress bar to the pass object
18
+ *
19
+ * Note: Google Wallet API requires publicly accessible URLs for images.
20
+ * This method generates the hero image and saves it locally.
21
+ * For production, upload images to a CDN/cloud storage and use those URLs.
22
+ */
23
+ private addHeroImage;
24
+ createClass(profile: ProfileConfig, passType: 'parent' | 'child'): Promise<void>;
25
+ }
26
+ export default GoogleWalletAdapter;