sbcwallet 0.0.4 → 0.0.6
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 +618 -122
- package/dist/adapters/apple-wallet.d.ts +66 -0
- package/dist/adapters/apple-wallet.js +546 -0
- package/dist/adapters/apple.d.ts +14 -1
- package/dist/adapters/apple.js +256 -11
- 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 +7 -3
package/README.md
CHANGED
|
@@ -2,195 +2,691 @@
|
|
|
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
|
+
---
|
|
12
47
|
|
|
13
|
-
|
|
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.
|
|
48
|
+
## Quick Start
|
|
17
49
|
|
|
18
|
-
###
|
|
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
|
|
375
|
+
|
|
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
|
+
---
|
|
418
|
+
|
|
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`.
|
|
499
|
+
---
|
|
119
500
|
|
|
120
|
-
## Location-based
|
|
501
|
+
## Location-based Features
|
|
121
502
|
|
|
122
|
-
|
|
503
|
+
### Lock Screen Surfacing
|
|
123
504
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
505
|
+
```ts
|
|
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
|
+
}
|
|
518
|
+
})
|
|
519
|
+
```
|
|
130
520
|
|
|
131
|
-
|
|
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.
|
|
521
|
+
### Push Notifications
|
|
135
522
|
|
|
136
523
|
```ts
|
|
137
524
|
import { pushLoyaltyMessage } from 'sbcwallet'
|
|
138
525
|
|
|
526
|
+
// Google Wallet: Send message to pass
|
|
139
527
|
await pushLoyaltyMessage({
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
528
|
+
cardId: card.id,
|
|
529
|
+
header: 'X Cafe',
|
|
530
|
+
body: 'You are nearby — show this card to earn points.',
|
|
531
|
+
messageType: 'TEXT_AND_NOTIFY'
|
|
144
532
|
})
|
|
145
533
|
```
|
|
146
534
|
|
|
147
|
-
|
|
535
|
+
---
|
|
148
536
|
|
|
149
|
-
|
|
150
|
-
import { createBusiness } from 'sbcwallet'
|
|
537
|
+
## Demo Servers
|
|
151
538
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
})
|
|
539
|
+
### Google Wallet
|
|
540
|
+
|
|
541
|
+
```sh
|
|
542
|
+
# Multi-tenant loyalty server
|
|
543
|
+
npm run loyalty:server:multi
|
|
544
|
+
# Open http://localhost:5190
|
|
545
|
+
|
|
546
|
+
# Simple loyalty server
|
|
547
|
+
npm run loyalty:server
|
|
548
|
+
# Open http://localhost:5189
|
|
549
|
+
|
|
550
|
+
# Google Wallet issuance (CLI)
|
|
551
|
+
node examples/loyalty-google-issue.js
|
|
166
552
|
```
|
|
167
553
|
|
|
168
|
-
|
|
554
|
+
### Apple Wallet
|
|
169
555
|
|
|
170
556
|
```sh
|
|
171
|
-
|
|
557
|
+
# Apple Wallet server with web UI
|
|
558
|
+
node examples/loyalty-apple-server.js
|
|
559
|
+
# Open http://localhost:3001
|
|
560
|
+
|
|
561
|
+
# Apple Wallet issuance (CLI)
|
|
562
|
+
node examples/loyalty-apple-issue.js
|
|
172
563
|
```
|
|
173
564
|
|
|
174
|
-
|
|
565
|
+
#### Required Environment Variables for Apple Wallet
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
# Apple Developer credentials
|
|
569
|
+
APPLE_TEAM_ID=YOUR_TEAM_ID # Your Apple Developer Team ID
|
|
570
|
+
APPLE_PASS_TYPE_ID=pass.com.x.y # Your Pass Type Identifier
|
|
571
|
+
APPLE_CERT_PATH=/path/to/cert.p12 # Path to signing certificate
|
|
572
|
+
APPLE_CERT_PASSWORD=password # Certificate password (if any)
|
|
573
|
+
APPLE_WWDR_PATH=/path/to/wwdr.pem # Apple WWDR certificate
|
|
574
|
+
|
|
575
|
+
# Optional customization
|
|
576
|
+
LOYALTY_BUSINESS_NAME=SBC Coffee
|
|
577
|
+
LOYALTY_PROGRAM_NAME=SBC Rewards
|
|
578
|
+
LOYALTY_CUSTOMER_NAME=John Doe
|
|
579
|
+
LOYALTY_INITIAL_POINTS=10
|
|
580
|
+
LOYALTY_BG=#111827
|
|
581
|
+
```
|
|
175
582
|
|
|
176
|
-
|
|
583
|
+
---
|
|
177
584
|
|
|
178
|
-
|
|
179
|
-
- `GOOGLE_ISSUER_ID`
|
|
180
|
-
- `GOOGLE_SA_JSON`
|
|
585
|
+
## TypeScript Support
|
|
181
586
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
587
|
+
This SDK is fully written in TypeScript with complete type definitions:
|
|
588
|
+
|
|
589
|
+
```ts
|
|
590
|
+
import type {
|
|
591
|
+
// Pass types
|
|
592
|
+
ApplePassType,
|
|
593
|
+
GooglePassType,
|
|
594
|
+
|
|
595
|
+
// Pass inputs
|
|
596
|
+
BoardingPassInput,
|
|
597
|
+
EventTicketInput,
|
|
598
|
+
StoreCardInput,
|
|
599
|
+
CouponInput,
|
|
600
|
+
GiftCardInput,
|
|
601
|
+
TransitPassInput,
|
|
602
|
+
GenericPassInput,
|
|
603
|
+
WalletPassInput,
|
|
604
|
+
|
|
605
|
+
// Pass data
|
|
606
|
+
WalletPassData,
|
|
607
|
+
PassGenerationOptions,
|
|
608
|
+
|
|
609
|
+
// Statuses
|
|
610
|
+
EventStatus,
|
|
611
|
+
FlightStatus,
|
|
612
|
+
TransitStatus,
|
|
613
|
+
OfferStatus,
|
|
614
|
+
GiftCardStatus
|
|
615
|
+
} from 'sbcwallet'
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
---
|
|
188
619
|
|
|
189
620
|
## Development
|
|
190
621
|
|
|
191
622
|
```sh
|
|
623
|
+
# Install dependencies
|
|
624
|
+
npm install
|
|
625
|
+
|
|
626
|
+
# Build project
|
|
192
627
|
npm run build
|
|
628
|
+
|
|
629
|
+
# Run tests
|
|
193
630
|
npm test
|
|
631
|
+
|
|
632
|
+
# Check package before publishing
|
|
194
633
|
npm pack --dry-run
|
|
195
634
|
```
|
|
196
635
|
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## API Reference
|
|
639
|
+
|
|
640
|
+
### Pass Creation Functions
|
|
641
|
+
|
|
642
|
+
| Function | Description |
|
|
643
|
+
|----------|-------------|
|
|
644
|
+
| `createBoardingPass(input)` | Create flight/transit boarding pass |
|
|
645
|
+
| `createEventTicket(input)` | Create event ticket |
|
|
646
|
+
| `createStoreCard(input)` | Create loyalty/store card |
|
|
647
|
+
| `createCoupon(input)` | Create discount coupon |
|
|
648
|
+
| `createGiftCard(input)` | Create gift card |
|
|
649
|
+
| `createTransitPass(input)` | Create transit pass |
|
|
650
|
+
| `createGenericPass(input)` | Create generic pass |
|
|
651
|
+
| `createWalletPass(input)` | Create any pass type |
|
|
652
|
+
|
|
653
|
+
### Pass Management Functions
|
|
654
|
+
|
|
655
|
+
| Function | Description |
|
|
656
|
+
|----------|-------------|
|
|
657
|
+
| `getWalletPass(id)` | Get pass by ID |
|
|
658
|
+
| `listWalletPasses(filter)` | List passes |
|
|
659
|
+
| `updateWalletPassStatus(id, status)` | Update pass status |
|
|
660
|
+
| `updateLoyaltyBalance(id, data)` | Update loyalty points |
|
|
661
|
+
| `updateGiftCardBalance(id, data)` | Update gift card balance |
|
|
662
|
+
| `sendPassNotification(id, message)` | Send notification |
|
|
663
|
+
| `regeneratePass(id)` | Regenerate pass |
|
|
664
|
+
|
|
665
|
+
### Loyalty Functions
|
|
666
|
+
|
|
667
|
+
| Function | Description |
|
|
668
|
+
|----------|-------------|
|
|
669
|
+
| `createBusiness(config)` | Define a business |
|
|
670
|
+
| `createLoyaltyProgram(config)` | Create loyalty program |
|
|
671
|
+
| `createCustomerAccount(data)` | Create customer account |
|
|
672
|
+
| `issueLoyaltyCard(data)` | Issue loyalty card |
|
|
673
|
+
| `updateLoyaltyPoints(data)` | Update points |
|
|
674
|
+
| `pushLoyaltyMessage(data)` | Send message |
|
|
675
|
+
|
|
676
|
+
### Apple Wallet Functions
|
|
677
|
+
|
|
678
|
+
| Function | Description |
|
|
679
|
+
|----------|-------------|
|
|
680
|
+
| `getPkpassBuffer(type, card)` | Get pkpass file buffer |
|
|
681
|
+
|
|
682
|
+
### Google Wallet Functions
|
|
683
|
+
|
|
684
|
+
| Function | Description |
|
|
685
|
+
|----------|-------------|
|
|
686
|
+
| `getGoogleObject(type, card)` | Get Save URL |
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## License
|
|
691
|
+
|
|
692
|
+
MIT License - See [LICENSE](LICENSE) for details.
|