sbcwallet 0.0.4 → 0.0.5
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 +586 -124
- package/dist/adapters/apple-wallet.d.ts +66 -0
- package/dist/adapters/apple-wallet.js +546 -0
- package/dist/adapters/google-wallet.d.ts +92 -0
- package/dist/adapters/google-wallet.js +653 -0
- package/dist/api/wallet-pass.d.ts +119 -0
- package/dist/api/wallet-pass.js +423 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +18 -3
- package/dist/templates/apple/boardingPass.json +110 -0
- package/dist/templates/apple/coupon.json +81 -0
- package/dist/templates/apple/eventTicket.json +104 -0
- package/dist/templates/apple/generic.json +76 -0
- package/dist/templates/apple/storeCard.json +81 -0
- package/dist/templates/google/event_ticket_class.json +41 -0
- package/dist/templates/google/event_ticket_object.json +95 -0
- package/dist/templates/google/flight_class.json +43 -0
- package/dist/templates/google/flight_object.json +30 -0
- package/dist/templates/google/generic_class.json +11 -0
- package/dist/templates/google/generic_object.json +38 -0
- package/dist/templates/google/gift_card_class.json +32 -0
- package/dist/templates/google/gift_card_object.json +27 -0
- package/dist/templates/google/offer_class.json +44 -0
- package/dist/templates/google/offer_object.json +42 -0
- package/dist/templates/google/transit_class.json +24 -0
- package/dist/templates/google/transit_object.json +71 -0
- package/dist/types.d.ts +1464 -5
- package/dist/types.js +202 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,195 +2,657 @@
|
|
|
2
2
|
|
|
3
3
|
Unified wallet-pass SDK for Apple Wallet (.pkpass) and Google Wallet.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### Supported Pass Types
|
|
8
|
+
|
|
9
|
+
| Pass Type | Apple Wallet | Google Wallet |
|
|
10
|
+
|-----------|--------------|---------------|
|
|
11
|
+
| Boarding Pass | ✅ boardingPass | ✅ flight, transit |
|
|
12
|
+
| Event Ticket | ✅ eventTicket | ✅ eventTicket |
|
|
13
|
+
| Store Card / Loyalty | ✅ storeCard | ✅ loyalty |
|
|
14
|
+
| Coupon | ✅ coupon | ✅ offer |
|
|
15
|
+
| Gift Card | ✅ storeCard | ✅ giftCard |
|
|
16
|
+
| Transit Pass | ✅ boardingPass | ✅ transit |
|
|
17
|
+
| Generic Pass | ✅ generic | ✅ generic |
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
6
20
|
|
|
7
21
|
```sh
|
|
8
22
|
npm install sbcwallet
|
|
9
23
|
```
|
|
10
24
|
|
|
11
|
-
##
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
### Environment Variables
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Google Wallet Configuration
|
|
31
|
+
GOOGLE_ISSUER_ID=your-issuer-id
|
|
32
|
+
GOOGLE_SA_JSON=/path/to/credentials.json
|
|
33
|
+
|
|
34
|
+
# Apple Wallet Configuration
|
|
35
|
+
APPLE_TEAM_ID=YOUR_TEAM_ID
|
|
36
|
+
APPLE_PASS_TYPE_ID=pass.com.example.app
|
|
37
|
+
APPLE_CERT_PATH=/path/to/certificate.p12
|
|
38
|
+
APPLE_CERT_PASSWORD=your-password
|
|
39
|
+
APPLE_WWDR_PATH=/path/to/wwdr.pem
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For detailed setup instructions:
|
|
43
|
+
- Apple Wallet: [APPLE_WALLET_SETUP.md](APPLE_WALLET_SETUP.md)
|
|
44
|
+
- Google Wallet: [GOOGLE_WALLET_SETUP.md](GOOGLE_WALLET_SETUP.md)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
### 1. Boarding Pass
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { createBoardingPass } from 'sbcwallet'
|
|
54
|
+
|
|
55
|
+
const boardingPass = await createBoardingPass({
|
|
56
|
+
serialNumber: 'FLIGHT-2026-001',
|
|
57
|
+
description: 'Flight from Tehran to Muscat',
|
|
58
|
+
|
|
59
|
+
flight: {
|
|
60
|
+
flightNumber: 'W5-1234',
|
|
61
|
+
departureAirport: 'IKA',
|
|
62
|
+
departureCity: 'Tehran',
|
|
63
|
+
arrivalAirport: 'MCT',
|
|
64
|
+
arrivalCity: 'Muscat',
|
|
65
|
+
departureTime: '2026-02-15T10:30:00Z',
|
|
66
|
+
arrivalTime: '2026-02-15T12:30:00Z',
|
|
67
|
+
boardingTime: '2026-02-15T09:45:00Z',
|
|
68
|
+
gate: 'A12',
|
|
69
|
+
terminal: 'Terminal 1'
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
passenger: {
|
|
73
|
+
name: 'Ali Mohammadi',
|
|
74
|
+
seat: '12A',
|
|
75
|
+
class: 'Economy',
|
|
76
|
+
boardingGroup: 'A',
|
|
77
|
+
confirmationCode: 'ABC123'
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
airline: {
|
|
81
|
+
name: 'Mahan Air',
|
|
82
|
+
logoUrl: 'https://example.com/mahan-logo.png'
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
style: {
|
|
86
|
+
backgroundColor: '#003366',
|
|
87
|
+
foregroundColor: '#FFFFFF',
|
|
88
|
+
labelColor: '#CCCCCC'
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
barcode: {
|
|
92
|
+
type: 'QR',
|
|
93
|
+
message: 'M1MOHAMMADI/ALI EABC123 IKAMCTW5 1234 050Y012A0001 100'
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// Get Google Wallet Save URL
|
|
98
|
+
console.log('Google Save URL:', boardingPass.google?.saveUrl)
|
|
99
|
+
|
|
100
|
+
// Save Apple Wallet file
|
|
101
|
+
import { writeFile } from 'node:fs/promises'
|
|
102
|
+
if (boardingPass.apple?.pkpassBuffer) {
|
|
103
|
+
await writeFile('boarding-pass.pkpass', boardingPass.apple.pkpassBuffer)
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 2. Event Ticket
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { createEventTicket } from 'sbcwallet'
|
|
111
|
+
|
|
112
|
+
const eventTicket = await createEventTicket({
|
|
113
|
+
serialNumber: 'EVENT-2026-001',
|
|
114
|
+
description: 'Traditional Music Concert',
|
|
115
|
+
|
|
116
|
+
event: {
|
|
117
|
+
name: 'Traditional Persian Music Concert',
|
|
118
|
+
startDate: '2026-03-21T19:00:00Z',
|
|
119
|
+
endDate: '2026-03-21T22:00:00Z',
|
|
120
|
+
venue: {
|
|
121
|
+
name: 'Vahdat Hall',
|
|
122
|
+
address: 'Tehran, Hafez Street',
|
|
123
|
+
latitude: 35.6892,
|
|
124
|
+
longitude: 51.3890
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
ticket: {
|
|
129
|
+
section: 'VIP',
|
|
130
|
+
row: 'A',
|
|
131
|
+
seat: '15',
|
|
132
|
+
ticketType: 'General Admission',
|
|
133
|
+
ticketNumber: 'TKT-001234'
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
attendee: {
|
|
137
|
+
name: 'Sara Ahmadi'
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
style: {
|
|
141
|
+
backgroundColor: '#8B0000',
|
|
142
|
+
foregroundColor: '#FFFFFF'
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
barcode: {
|
|
146
|
+
type: 'QR',
|
|
147
|
+
message: 'EVENT-2026-001-TKT-001234'
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 3. Store Card / Loyalty Card
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { createStoreCard } from 'sbcwallet'
|
|
156
|
+
|
|
157
|
+
const storeCard = await createStoreCard({
|
|
158
|
+
serialNumber: 'LOYALTY-001',
|
|
159
|
+
description: 'X Cafe Loyalty Card',
|
|
160
|
+
|
|
161
|
+
program: {
|
|
162
|
+
name: 'X Rewards',
|
|
163
|
+
issuerName: 'X Cafe',
|
|
164
|
+
logoUrl: 'https://example.com/xcafe-logo.png'
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
member: {
|
|
168
|
+
name: 'Mohammad Rezaei',
|
|
169
|
+
memberId: 'MEM-123456',
|
|
170
|
+
tier: 'Gold'
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
balance: {
|
|
174
|
+
points: 1250,
|
|
175
|
+
pointsLabel: 'Points',
|
|
176
|
+
secondaryBalance: 50000,
|
|
177
|
+
secondaryBalanceLabel: 'Credit'
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
style: {
|
|
181
|
+
backgroundColor: '#111827',
|
|
182
|
+
foregroundColor: '#F9FAFB',
|
|
183
|
+
labelColor: '#9CA3AF'
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
barcode: {
|
|
187
|
+
type: 'QR',
|
|
188
|
+
message: 'MEM-123456'
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 4. Coupon
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { createCoupon } from 'sbcwallet'
|
|
197
|
+
|
|
198
|
+
const coupon = await createCoupon({
|
|
199
|
+
serialNumber: 'COUPON-001',
|
|
200
|
+
description: '20% Off Purchase',
|
|
201
|
+
|
|
202
|
+
offer: {
|
|
203
|
+
title: '20% Special Discount',
|
|
204
|
+
description: '20% off on all products',
|
|
205
|
+
discountAmount: '20%',
|
|
206
|
+
promoCode: 'SAVE20',
|
|
207
|
+
issuerName: 'X Store',
|
|
208
|
+
logoUrl: 'https://example.com/store-logo.png'
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
validity: {
|
|
212
|
+
startDate: '2026-01-01T00:00:00Z',
|
|
213
|
+
expirationDate: '2026-03-31T23:59:59Z'
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
terms: 'Minimum purchase $50. Cannot be combined with other offers.',
|
|
217
|
+
|
|
218
|
+
style: {
|
|
219
|
+
backgroundColor: '#DC2626',
|
|
220
|
+
foregroundColor: '#FFFFFF'
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
barcode: {
|
|
224
|
+
type: 'QR',
|
|
225
|
+
message: 'COUPON-SAVE20-001'
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 5. Gift Card
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { createGiftCard } from 'sbcwallet'
|
|
234
|
+
|
|
235
|
+
const giftCard = await createGiftCard({
|
|
236
|
+
serialNumber: 'GIFT-001',
|
|
237
|
+
description: '$500 Gift Card',
|
|
238
|
+
|
|
239
|
+
card: {
|
|
240
|
+
cardNumber: 'GIFT-1234-5678-9012',
|
|
241
|
+
pin: '1234',
|
|
242
|
+
initialBalance: 500,
|
|
243
|
+
currentBalance: 500,
|
|
244
|
+
currencyCode: 'USD',
|
|
245
|
+
issuerName: 'Big Store',
|
|
246
|
+
logoUrl: 'https://example.com/logo.png'
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
validity: {
|
|
250
|
+
expirationDate: '2027-01-31T23:59:59Z'
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
recipient: {
|
|
254
|
+
name: 'Dear Friend',
|
|
255
|
+
message: 'Happy Birthday! 🎂'
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
style: {
|
|
259
|
+
backgroundColor: '#7C3AED',
|
|
260
|
+
foregroundColor: '#FFFFFF'
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
barcode: {
|
|
264
|
+
type: 'QR',
|
|
265
|
+
message: 'GIFT-1234-5678-9012'
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 6. Transit Pass
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { createTransitPass } from 'sbcwallet'
|
|
274
|
+
|
|
275
|
+
const transitPass = await createTransitPass({
|
|
276
|
+
serialNumber: 'TRANSIT-001',
|
|
277
|
+
description: 'Metro Ticket',
|
|
278
|
+
|
|
279
|
+
trip: {
|
|
280
|
+
departureStation: 'Central Station',
|
|
281
|
+
arrivalStation: 'Airport',
|
|
282
|
+
departureTime: '2026-02-15T08:30:00Z',
|
|
283
|
+
transitType: 'metro', // metro, bus, train, ferry, tram
|
|
284
|
+
lineNumber: 'Line 1',
|
|
285
|
+
vehicleNumber: 'Train 123'
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
ticket: {
|
|
289
|
+
ticketNumber: 'MTR-001234',
|
|
290
|
+
ticketType: 'Single Ride',
|
|
291
|
+
validFrom: '2026-02-15T00:00:00Z',
|
|
292
|
+
validUntil: '2026-02-15T23:59:59Z',
|
|
293
|
+
zones: ['Zone 1', 'Zone 2']
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
passenger: {
|
|
297
|
+
name: 'Reza Karimi'
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
operator: {
|
|
301
|
+
name: 'Metro Transit',
|
|
302
|
+
logoUrl: 'https://example.com/metro-logo.png'
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
style: {
|
|
306
|
+
backgroundColor: '#0891B2',
|
|
307
|
+
foregroundColor: '#FFFFFF'
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
barcode: {
|
|
311
|
+
type: 'QR',
|
|
312
|
+
message: 'MTR-001234'
|
|
313
|
+
}
|
|
314
|
+
})
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 7. Generic Pass
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { createGenericPass } from 'sbcwallet'
|
|
321
|
+
|
|
322
|
+
const genericPass = await createGenericPass({
|
|
323
|
+
serialNumber: 'GENERIC-001',
|
|
324
|
+
description: 'Gym Membership Card',
|
|
325
|
+
|
|
326
|
+
header: 'Fitness Club',
|
|
327
|
+
primaryText: 'John Smith',
|
|
328
|
+
secondaryText: 'Gold Membership',
|
|
329
|
+
|
|
330
|
+
auxiliaryFields: [
|
|
331
|
+
{ label: 'Member ID', value: 'MEM-789456' },
|
|
332
|
+
{ label: 'Join Date', value: '2025-06-01' }
|
|
333
|
+
],
|
|
334
|
+
|
|
335
|
+
backFields: [
|
|
336
|
+
{ label: 'Address', value: '123 Main Street, City' },
|
|
337
|
+
{ label: 'Phone', value: '+1-234-567-8900' },
|
|
338
|
+
{ label: 'Hours', value: 'Mon-Fri: 6AM - 11PM' }
|
|
339
|
+
],
|
|
340
|
+
|
|
341
|
+
images: {
|
|
342
|
+
logoUrl: 'https://example.com/gym-logo.png',
|
|
343
|
+
heroImageUrl: 'https://example.com/gym-hero.jpg'
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
style: {
|
|
347
|
+
backgroundColor: '#065F46',
|
|
348
|
+
foregroundColor: '#FFFFFF'
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
barcode: {
|
|
352
|
+
type: 'QR',
|
|
353
|
+
message: 'MEMBER-789456'
|
|
354
|
+
}
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Unified API
|
|
361
|
+
|
|
362
|
+
For more flexibility, you can use the generic `createWalletPass` function:
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
import { createWalletPass } from 'sbcwallet'
|
|
366
|
+
|
|
367
|
+
// Create any pass type by specifying passType
|
|
368
|
+
const pass = await createWalletPass({
|
|
369
|
+
passType: 'eventTicket',
|
|
370
|
+
// ... other parameters based on pass type
|
|
371
|
+
})
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Pass Management
|
|
12
375
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
376
|
+
```ts
|
|
377
|
+
import {
|
|
378
|
+
getWalletPass,
|
|
379
|
+
listWalletPasses,
|
|
380
|
+
updateWalletPassStatus,
|
|
381
|
+
updateLoyaltyBalance,
|
|
382
|
+
updateGiftCardBalance,
|
|
383
|
+
sendPassNotification,
|
|
384
|
+
regeneratePass
|
|
385
|
+
} from 'sbcwallet'
|
|
386
|
+
|
|
387
|
+
// Get pass by ID
|
|
388
|
+
const pass = await getWalletPass('PASS-123')
|
|
389
|
+
|
|
390
|
+
// List passes for a user
|
|
391
|
+
const userPasses = await listWalletPasses({ userId: 'USER-001' })
|
|
392
|
+
|
|
393
|
+
// Update pass status (e.g., for cancelled event)
|
|
394
|
+
await updateWalletPassStatus('PASS-123', 'cancelled')
|
|
395
|
+
|
|
396
|
+
// Update loyalty card points
|
|
397
|
+
await updateLoyaltyBalance('LOYALTY-001', {
|
|
398
|
+
pointsDelta: 100,
|
|
399
|
+
newTier: 'Platinum'
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
// Update gift card balance
|
|
403
|
+
await updateGiftCardBalance('GIFT-001', {
|
|
404
|
+
newBalance: 350
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
// Send notification to pass (Google Wallet)
|
|
408
|
+
await sendPassNotification('PASS-123', {
|
|
409
|
+
header: 'Reminder',
|
|
410
|
+
body: 'Your event is tomorrow!'
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
// Regenerate pass after updates
|
|
414
|
+
const updatedPass = await regeneratePass('PASS-123')
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
17
418
|
|
|
18
|
-
|
|
419
|
+
## Multi-tenant Loyalty
|
|
420
|
+
|
|
421
|
+
For complex scenarios like multi-business platforms:
|
|
422
|
+
|
|
423
|
+
### Define a Business
|
|
19
424
|
|
|
20
425
|
```ts
|
|
21
426
|
import { createBusiness, createLoyaltyProgram } from 'sbcwallet'
|
|
22
427
|
|
|
23
428
|
const biz = createBusiness({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
userInfo: { tenant: 'x' }
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
429
|
+
name: 'X Cafe',
|
|
430
|
+
programName: 'X Rewards',
|
|
431
|
+
pointsLabel: 'Points',
|
|
432
|
+
wallet: {
|
|
433
|
+
googleWallet: {
|
|
434
|
+
issuerName: 'X Cafe',
|
|
435
|
+
backgroundColor: '#111827',
|
|
436
|
+
logoUrl: 'https://example.com/logo.png',
|
|
437
|
+
classOverrides: {
|
|
438
|
+
reviewStatus: 'UNDER_REVIEW'
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
appleWallet: {
|
|
442
|
+
organizationName: 'X Cafe',
|
|
443
|
+
logoText: 'X',
|
|
444
|
+
backgroundColor: 'rgb(17, 24, 39)',
|
|
445
|
+
passOverrides: {
|
|
446
|
+
userInfo: { tenant: 'x' }
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
49
450
|
})
|
|
50
451
|
|
|
51
452
|
await createLoyaltyProgram({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
homepageUrl: 'https://example.com'
|
|
453
|
+
businessId: biz.id,
|
|
454
|
+
locations: [
|
|
455
|
+
{ latitude: 35.6892, longitude: 51.389 },
|
|
456
|
+
{ latitude: 35.7, longitude: 51.4 }
|
|
457
|
+
],
|
|
458
|
+
relevantText: 'Welcome back — show this card at checkout',
|
|
459
|
+
countryCode: 'OM',
|
|
460
|
+
homepageUrl: 'https://example.com'
|
|
61
461
|
})
|
|
62
462
|
```
|
|
63
463
|
|
|
64
|
-
### Issue a
|
|
464
|
+
### Issue a Card
|
|
65
465
|
|
|
66
466
|
```ts
|
|
67
467
|
import {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
468
|
+
createCustomerAccount,
|
|
469
|
+
issueLoyaltyCard,
|
|
470
|
+
updateLoyaltyPoints,
|
|
471
|
+
getGoogleObject,
|
|
472
|
+
getPkpassBuffer
|
|
72
473
|
} from 'sbcwallet'
|
|
73
474
|
|
|
74
|
-
const memberId = 'USER-123'
|
|
75
|
-
|
|
76
475
|
const customer = createCustomerAccount({
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
476
|
+
businessId: biz.id,
|
|
477
|
+
fullName: 'Alice',
|
|
478
|
+
memberId: 'USER-123'
|
|
80
479
|
})
|
|
81
480
|
|
|
82
481
|
const card = await issueLoyaltyCard({
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
metadata: {
|
|
87
|
-
googleWallet: {
|
|
88
|
-
objectOverrides: {
|
|
89
|
-
linksModuleData: {
|
|
90
|
-
uris: [{ uri: 'https://example.com', description: 'Website' }]
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
482
|
+
businessId: biz.id,
|
|
483
|
+
customerId: customer.id,
|
|
484
|
+
initialPoints: 10
|
|
95
485
|
})
|
|
96
486
|
|
|
487
|
+
// Update points
|
|
97
488
|
await updateLoyaltyPoints({ cardId: card.id, delta: 5 })
|
|
98
489
|
|
|
490
|
+
// Get Google Wallet Save URL
|
|
99
491
|
const { saveUrl } = await getGoogleObject('child', card)
|
|
100
492
|
console.log(saveUrl)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Apple Wallet: generate a signed .pkpass
|
|
104
|
-
|
|
105
|
-
Apple Wallet issuance produces a `.pkpass` file that you deliver to the user (download link, email attachment, in-app webview, etc.).
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
import { getPkpassBuffer } from 'sbcwallet'
|
|
109
|
-
import { writeFile } from 'node:fs/promises'
|
|
110
493
|
|
|
494
|
+
// Save Apple Wallet file
|
|
111
495
|
const pkpass = await getPkpassBuffer('child', card)
|
|
112
496
|
await writeFile('loyalty.pkpass', pkpass)
|
|
113
497
|
```
|
|
114
498
|
|
|
115
|
-
|
|
116
|
-
- Apple Wallet passes must be **signed**. Configure the Apple certificate paths/password via environment variables (see Configuration).
|
|
117
|
-
- Multi-tenant branding is controlled via `wallet.appleWallet` (for the business) plus optional per-card overrides in `metadata.appleWallet`.
|
|
118
|
-
- Location surfacing on iOS is controlled by **PassKit fields** (for example `locations` and `relevantText`). This SDK wires these via `createLoyaltyProgram({ locations, relevantText })` and/or `wallet.appleWallet.passOverrides`.
|
|
119
|
-
|
|
120
|
-
## Location-based surfacing and notifications
|
|
121
|
-
|
|
122
|
-
This SDK supports two related concepts:
|
|
499
|
+
---
|
|
123
500
|
|
|
124
|
-
|
|
125
|
-
- Apple Wallet: setting `locations` and `relevantText` in pass.json can surface the pass on the lock screen when the user is near the business.
|
|
126
|
-
- Google Wallet: setting `locations` on the class/object helps Wallet surface the pass contextually.
|
|
501
|
+
## Location-based Features
|
|
127
502
|
|
|
128
|
-
|
|
129
|
-
- 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).
|
|
130
|
-
|
|
131
|
-
Apple Wallet clarification:
|
|
132
|
-
- Apple Wallet does not provide a “send arbitrary push message to the pass” API like Google’s `addMessage`.
|
|
133
|
-
- Apple Wallet pass updates are typically done through the PassKit Web Service (`webServiceURL` + `authenticationToken`) plus APNs.
|
|
134
|
-
- This SDK supports those fields via `appleWallet.passOverrides`, but you must implement the web service and APNs delivery yourself.
|
|
503
|
+
### Lock Screen Surfacing
|
|
135
504
|
|
|
136
505
|
```ts
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
506
|
+
// Apple Wallet: Auto-display when user is near location
|
|
507
|
+
createBusiness({
|
|
508
|
+
wallet: {
|
|
509
|
+
appleWallet: {
|
|
510
|
+
passOverrides: {
|
|
511
|
+
locations: [
|
|
512
|
+
{ latitude: 35.6892, longitude: 51.389 }
|
|
513
|
+
],
|
|
514
|
+
relevantText: 'Welcome — show this card at checkout'
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
144
518
|
})
|
|
145
519
|
```
|
|
146
520
|
|
|
147
|
-
|
|
521
|
+
### Push Notifications
|
|
148
522
|
|
|
149
523
|
```ts
|
|
150
|
-
import {
|
|
524
|
+
import { pushLoyaltyMessage } from 'sbcwallet'
|
|
151
525
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
webServiceURL: 'https://api.example.com/passes',
|
|
159
|
-
authenticationToken: 'your-secret-token',
|
|
160
|
-
// Optional: iOS lock-screen surfacing
|
|
161
|
-
relevantText: 'Welcome back — show this card at checkout'
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
526
|
+
// Google Wallet: Send message to pass
|
|
527
|
+
await pushLoyaltyMessage({
|
|
528
|
+
cardId: card.id,
|
|
529
|
+
header: 'X Cafe',
|
|
530
|
+
body: 'You are nearby — show this card to earn points.',
|
|
531
|
+
messageType: 'TEXT_AND_NOTIFY'
|
|
165
532
|
})
|
|
166
533
|
```
|
|
167
534
|
|
|
168
|
-
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Demo Servers
|
|
169
538
|
|
|
170
539
|
```sh
|
|
540
|
+
# Multi-tenant loyalty server
|
|
171
541
|
npm run loyalty:server:multi
|
|
542
|
+
# Open http://localhost:5190
|
|
543
|
+
|
|
544
|
+
# Simple loyalty server
|
|
545
|
+
npm run loyalty:server
|
|
546
|
+
# Open http://localhost:5189
|
|
172
547
|
```
|
|
173
548
|
|
|
174
|
-
|
|
549
|
+
---
|
|
175
550
|
|
|
176
|
-
##
|
|
551
|
+
## TypeScript Support
|
|
177
552
|
|
|
178
|
-
|
|
179
|
-
- `GOOGLE_ISSUER_ID`
|
|
180
|
-
- `GOOGLE_SA_JSON`
|
|
553
|
+
This SDK is fully written in TypeScript with complete type definitions:
|
|
181
554
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
555
|
+
```ts
|
|
556
|
+
import type {
|
|
557
|
+
// Pass types
|
|
558
|
+
ApplePassType,
|
|
559
|
+
GooglePassType,
|
|
560
|
+
|
|
561
|
+
// Pass inputs
|
|
562
|
+
BoardingPassInput,
|
|
563
|
+
EventTicketInput,
|
|
564
|
+
StoreCardInput,
|
|
565
|
+
CouponInput,
|
|
566
|
+
GiftCardInput,
|
|
567
|
+
TransitPassInput,
|
|
568
|
+
GenericPassInput,
|
|
569
|
+
WalletPassInput,
|
|
570
|
+
|
|
571
|
+
// Pass data
|
|
572
|
+
WalletPassData,
|
|
573
|
+
PassGenerationOptions,
|
|
574
|
+
|
|
575
|
+
// Statuses
|
|
576
|
+
EventStatus,
|
|
577
|
+
FlightStatus,
|
|
578
|
+
TransitStatus,
|
|
579
|
+
OfferStatus,
|
|
580
|
+
GiftCardStatus
|
|
581
|
+
} from 'sbcwallet'
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
---
|
|
188
585
|
|
|
189
586
|
## Development
|
|
190
587
|
|
|
191
588
|
```sh
|
|
589
|
+
# Install dependencies
|
|
590
|
+
npm install
|
|
591
|
+
|
|
592
|
+
# Build project
|
|
192
593
|
npm run build
|
|
594
|
+
|
|
595
|
+
# Run tests
|
|
193
596
|
npm test
|
|
597
|
+
|
|
598
|
+
# Check package before publishing
|
|
194
599
|
npm pack --dry-run
|
|
195
600
|
```
|
|
196
601
|
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## API Reference
|
|
605
|
+
|
|
606
|
+
### Pass Creation Functions
|
|
607
|
+
|
|
608
|
+
| Function | Description |
|
|
609
|
+
|----------|-------------|
|
|
610
|
+
| `createBoardingPass(input)` | Create flight/transit boarding pass |
|
|
611
|
+
| `createEventTicket(input)` | Create event ticket |
|
|
612
|
+
| `createStoreCard(input)` | Create loyalty/store card |
|
|
613
|
+
| `createCoupon(input)` | Create discount coupon |
|
|
614
|
+
| `createGiftCard(input)` | Create gift card |
|
|
615
|
+
| `createTransitPass(input)` | Create transit pass |
|
|
616
|
+
| `createGenericPass(input)` | Create generic pass |
|
|
617
|
+
| `createWalletPass(input)` | Create any pass type |
|
|
618
|
+
|
|
619
|
+
### Pass Management Functions
|
|
620
|
+
|
|
621
|
+
| Function | Description |
|
|
622
|
+
|----------|-------------|
|
|
623
|
+
| `getWalletPass(id)` | Get pass by ID |
|
|
624
|
+
| `listWalletPasses(filter)` | List passes |
|
|
625
|
+
| `updateWalletPassStatus(id, status)` | Update pass status |
|
|
626
|
+
| `updateLoyaltyBalance(id, data)` | Update loyalty points |
|
|
627
|
+
| `updateGiftCardBalance(id, data)` | Update gift card balance |
|
|
628
|
+
| `sendPassNotification(id, message)` | Send notification |
|
|
629
|
+
| `regeneratePass(id)` | Regenerate pass |
|
|
630
|
+
|
|
631
|
+
### Loyalty Functions
|
|
632
|
+
|
|
633
|
+
| Function | Description |
|
|
634
|
+
|----------|-------------|
|
|
635
|
+
| `createBusiness(config)` | Define a business |
|
|
636
|
+
| `createLoyaltyProgram(config)` | Create loyalty program |
|
|
637
|
+
| `createCustomerAccount(data)` | Create customer account |
|
|
638
|
+
| `issueLoyaltyCard(data)` | Issue loyalty card |
|
|
639
|
+
| `updateLoyaltyPoints(data)` | Update points |
|
|
640
|
+
| `pushLoyaltyMessage(data)` | Send message |
|
|
641
|
+
|
|
642
|
+
### Apple Wallet Functions
|
|
643
|
+
|
|
644
|
+
| Function | Description |
|
|
645
|
+
|----------|-------------|
|
|
646
|
+
| `getPkpassBuffer(type, card)` | Get pkpass file buffer |
|
|
647
|
+
|
|
648
|
+
### Google Wallet Functions
|
|
649
|
+
|
|
650
|
+
| Function | Description |
|
|
651
|
+
|----------|-------------|
|
|
652
|
+
| `getGoogleObject(type, card)` | Get Save URL |
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## License
|
|
657
|
+
|
|
658
|
+
MIT License - See [LICENSE](LICENSE) for details.
|