passkit-wallet 0.1.0

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 (66) hide show
  1. package/README.md +190 -0
  2. package/dist/apple/index.d.ts +7 -0
  3. package/dist/apple/index.d.ts.map +1 -0
  4. package/dist/apple/index.js +5 -0
  5. package/dist/apple/index.js.map +1 -0
  6. package/dist/apple/order.d.ts +40 -0
  7. package/dist/apple/order.d.ts.map +1 -0
  8. package/dist/apple/order.js +127 -0
  9. package/dist/apple/order.js.map +1 -0
  10. package/dist/apple/pass.d.ts +36 -0
  11. package/dist/apple/pass.d.ts.map +1 -0
  12. package/dist/apple/pass.js +44 -0
  13. package/dist/apple/pass.js.map +1 -0
  14. package/dist/apple/push.d.ts +14 -0
  15. package/dist/apple/push.d.ts.map +1 -0
  16. package/dist/apple/push.js +37 -0
  17. package/dist/apple/push.js.map +1 -0
  18. package/dist/apple/registration.d.ts +41 -0
  19. package/dist/apple/registration.d.ts.map +1 -0
  20. package/dist/apple/registration.js +40 -0
  21. package/dist/apple/registration.js.map +1 -0
  22. package/dist/apple/types.d.ts +110 -0
  23. package/dist/apple/types.d.ts.map +1 -0
  24. package/dist/apple/types.js +2 -0
  25. package/dist/apple/types.js.map +1 -0
  26. package/dist/common/config.d.ts +247 -0
  27. package/dist/common/config.d.ts.map +1 -0
  28. package/dist/common/config.js +58 -0
  29. package/dist/common/config.js.map +1 -0
  30. package/dist/common/types.d.ts +54 -0
  31. package/dist/common/types.d.ts.map +1 -0
  32. package/dist/common/types.js +20 -0
  33. package/dist/common/types.js.map +1 -0
  34. package/dist/google/index.d.ts +4 -0
  35. package/dist/google/index.d.ts.map +1 -0
  36. package/dist/google/index.js +3 -0
  37. package/dist/google/index.js.map +1 -0
  38. package/dist/google/pass.d.ts +147 -0
  39. package/dist/google/pass.d.ts.map +1 -0
  40. package/dist/google/pass.js +269 -0
  41. package/dist/google/pass.js.map +1 -0
  42. package/dist/google/push.d.ts +15 -0
  43. package/dist/google/push.d.ts.map +1 -0
  44. package/dist/google/push.js +22 -0
  45. package/dist/google/push.js.map +1 -0
  46. package/dist/google/types.d.ts +58 -0
  47. package/dist/google/types.d.ts.map +1 -0
  48. package/dist/google/types.js +2 -0
  49. package/dist/google/types.js.map +1 -0
  50. package/dist/index.d.ts +71 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +74 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/react/AddToAppleWallet.d.ts +20 -0
  55. package/dist/react/AddToAppleWallet.d.ts.map +1 -0
  56. package/dist/react/AddToAppleWallet.js +10 -0
  57. package/dist/react/AddToAppleWallet.js.map +1 -0
  58. package/dist/react/AddToGoogleWallet.d.ts +20 -0
  59. package/dist/react/AddToGoogleWallet.d.ts.map +1 -0
  60. package/dist/react/AddToGoogleWallet.js +10 -0
  61. package/dist/react/AddToGoogleWallet.js.map +1 -0
  62. package/dist/react/index.d.ts +5 -0
  63. package/dist/react/index.d.ts.map +1 -0
  64. package/dist/react/index.js +3 -0
  65. package/dist/react/index.js.map +1 -0
  66. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # passkit-wallet
2
+
3
+ Apple Wallet + Google Wallet pass generation made easy.
4
+
5
+ Generate `.pkpass` files, Apple Order files, and Google Wallet save links from a single, framework-agnostic TypeScript library. Optional React components render the official "Add to Wallet" badges.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install passkit-wallet
11
+ ```
12
+
13
+ React components are optional — if you only use the backend helpers, React is not required.
14
+
15
+ ## Quick Start
16
+
17
+ ### Apple Wallet
18
+
19
+ ```typescript
20
+ import { createAppleWalletClient, mountCouponPassBody } from 'passkit-wallet'
21
+
22
+ const apple = createAppleWalletClient({
23
+ pass: {
24
+ passTypeIdentifier: 'pass.com.yourcompany',
25
+ teamIdentifier: 'ABCDE12345',
26
+ certificate: process.env.APPLE_PASS_CERTIFICATE!,
27
+ privateKey: process.env.APPLE_PASS_PRIVATE_KEY!,
28
+ keyPassword: process.env.APPLE_PASS_KEY_PASSWORD,
29
+ wwdrCertificate: process.env.APPLE_WWDR_CERT!,
30
+ webServiceURL: 'https://yourapp.com/api/passes',
31
+ authenticationToken: 'your-auth-token',
32
+ passTemplatePath: './resources/your.pass',
33
+ },
34
+ })
35
+
36
+ // Load template and create a coupon pass
37
+ const template = await apple.loadPassTemplate()
38
+ const passBody = mountCouponPassBody({
39
+ code: 'SAVE20',
40
+ offerTitle: '20% Off',
41
+ qrCodeUrl: 'https://yourapp.com/discount/SAVE20',
42
+ backFields: [
43
+ { key: 'support', label: 'Support', value: 'help@yourapp.com' },
44
+ ],
45
+ })
46
+
47
+ const pass = template.createPass()
48
+ // ... set pass fields from passBody, then generate the .pkpass buffer
49
+ ```
50
+
51
+ ### Apple Orders
52
+
53
+ ```typescript
54
+ import { createAppleWalletClient, mountOrderInstance } from 'passkit-wallet'
55
+
56
+ const apple = createAppleWalletClient({
57
+ order: {
58
+ orderTypeIdentifier: 'order.com.yourcompany',
59
+ certificate: process.env.APPLE_ORDER_CERTIFICATE!,
60
+ privateKey: process.env.APPLE_ORDER_PRIVATE_KEY!,
61
+ keyPassword: process.env.APPLE_ORDER_KEY_PASSWORD,
62
+ wwdrCertificate: process.env.APPLE_WWDR_G5_CERT!,
63
+ webServiceURL: 'https://yourapp.com/api/orders',
64
+ authenticationToken: 'your-order-auth-token',
65
+ orderModelPath: './resources/model.order',
66
+ },
67
+ })
68
+
69
+ const orderBody = apple.mountOrderInstance({
70
+ orderNumber: 'ORD-12345',
71
+ currency: 'USD',
72
+ totals: { grandTotal: 4999, subTotal: 3999, shipping: 500, tax: 400, tip: 100 },
73
+ orderItems: [{
74
+ image: 'https://cdn.example.com/product.jpg',
75
+ price: 3999,
76
+ quantity: 1,
77
+ productName: 'Widget',
78
+ }],
79
+ customer: { emailAddress: 'customer@example.com', givenName: 'Jane' },
80
+ orderManagementURL: 'https://yourapp.com/orders/ORD-12345',
81
+ })
82
+
83
+ const orderBuffer = await apple.generateOrder(orderBody)
84
+ // Serve orderBuffer as application/vnd.apple.order with Content-Disposition
85
+ ```
86
+
87
+ ### Google Wallet
88
+
89
+ ```typescript
90
+ import {
91
+ createGoogleWalletClient,
92
+ mountOfferClassBody,
93
+ mountOfferObjectBody,
94
+ createClass,
95
+ } from 'passkit-wallet'
96
+
97
+ const gw = createGoogleWalletClient({
98
+ issuerId: '1234567890',
99
+ credentials: JSON.parse(process.env.GOOGLE_CREDENTIALS!),
100
+ origins: ['https://yourapp.com'],
101
+ })
102
+
103
+ // Create an offer class
104
+ const classBody = mountOfferClassBody(gw.config.issuerId, {
105
+ classSuffix: 'my_offers',
106
+ offerTitle: '20% Off Everything',
107
+ reviewStatus: 'DRAFT',
108
+ provider: 'Your Company',
109
+ hexBackgroundColor: '#1a1a2e',
110
+ })
111
+
112
+ await createClass(gw.client, 'offerclass', gw.config.issuerId, 'my_offers', classBody)
113
+
114
+ // Create an offer object
115
+ const objectBody = mountOfferObjectBody(gw.config.issuerId, {
116
+ classSuffix: 'my_offers',
117
+ objectSuffix: 'offer_user123',
118
+ code: 'SAVE20',
119
+ qrCodeUrl: 'https://yourapp.com/discount/SAVE20',
120
+ })
121
+
122
+ // Get the signed save URL
123
+ const saveUrl = gw.getSignedURL('offerObjects', 'offer_user123', 'my_offers')
124
+ // Redirect or display saveUrl to the user
125
+ ```
126
+
127
+ ### React Components
128
+
129
+ ```tsx
130
+ import { AddToAppleWallet, AddToGoogleWallet } from 'passkit-wallet/react'
131
+
132
+ function WalletButtons({ pkpassUrl, googleSaveUrl }) {
133
+ return (
134
+ <div>
135
+ <AddToAppleWallet url={pkpassUrl} />
136
+ <AddToGoogleWallet saveUrl={googleSaveUrl} />
137
+ </div>
138
+ )
139
+ }
140
+ ```
141
+
142
+ ## Apple Certificate Setup
143
+
144
+ 1. Create a Pass Type ID and Order Type ID in the [Apple Developer Portal](https://developer.apple.com/account/resources/identifiers/list/passTypeId).
145
+ 2. Generate certificates for each and export them as `.pem` files.
146
+ 3. Download the [WWDR G5 certificate](https://www.apple.com/certificateauthority/) and convert to PEM.
147
+ 4. Create a `.pass` bundle directory with your `pass.json`, icon, logo, and other assets.
148
+ 5. Create a `model.order` bundle directory with your `order.json`, icon, and assets.
149
+
150
+ ## Google Wallet Setup
151
+
152
+ 1. Create a [Google Wallet API](https://pay.google.com/business/console/) issuer account.
153
+ 2. Create a service account with the **Google Wallet Object Issuer** role.
154
+ 3. Download the service account JSON credentials.
155
+
156
+ ## API Reference
157
+
158
+ ### Factory Functions
159
+
160
+ - `createAppleWalletClient(config)` — returns a client with bound config for Apple pass/order operations.
161
+ - `createGoogleWalletClient(config)` — returns a client with bound config and authenticated Google API client.
162
+
163
+ ### Apple
164
+
165
+ - `loadPassTemplate(config)` — loads a `.pass` template and configures certificates.
166
+ - `mountCouponPassBody(options)` — builds coupon pass field data.
167
+ - `loadOrderModel(config)` — loads an order model from a `.order` bundle.
168
+ - `mountOrderInstance(options, webServiceURL)` — builds order instance data.
169
+ - `generateOrder(instance, config, images?)` — generates a signed `.order` buffer.
170
+ - `sendPushNotification(tokens, topic, options)` — sends APN push to trigger pass/order refresh.
171
+ - `parseRegistrationRequest(request, authToken)` — validates Apple device registration requests.
172
+ - `parseUnregistrationRequest(request, authToken)` — validates device unregistration requests.
173
+
174
+ ### Google
175
+
176
+ - `loadClient(config)` — creates an authenticated Google Wallet API client.
177
+ - `getSignedURL(config, type, objectSuffix, classSuffix)` — generates a signed save-to-wallet JWT URL.
178
+ - `mountOfferClassBody(issuerId, options)` / `mountOfferObjectBody(issuerId, options)` — build offer class/object data.
179
+ - `mountOrderClassBody(issuerId, options)` / `mountOrderObjectBody(issuerId, options)` — build order class/object data.
180
+ - `getClass` / `createClass` / `updateClass` / `updateObject` — CRUD operations on wallet classes and objects.
181
+ - `sendObjectMessage(client, type, resourceId, message)` — attach a message to a wallet object.
182
+
183
+ ### React
184
+
185
+ - `<AddToAppleWallet url={string} />` — Apple Wallet badge link.
186
+ - `<AddToGoogleWallet saveUrl={string} />` — Google Wallet badge link.
187
+
188
+ ## License
189
+
190
+ MIT
@@ -0,0 +1,7 @@
1
+ export { loadPassTemplate, mountCouponPassBody } from "./pass.js";
2
+ export { loadOrderModel, mountOrderInstance, generateOrder, writeOrderCertificates, addImagesToModelDir, removeImagesFromModelDir, } from "./order.js";
3
+ export { sendPushNotification } from "./push.js";
4
+ export { parseRegistrationRequest, parseUnregistrationRequest, } from "./registration.js";
5
+ export type { RegistrationRequest, RegistrationResult, } from "./registration.js";
6
+ export type { OrderInstance, Merchant, Payment, MoneyAmount, SummaryItem, LineItem, Fulfillment, AppleCouponPassOptions, AppleOrderOptions, ApplePushOptions, } from "./types.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/apple/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EACL,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,aAAa,EACb,QAAQ,EACR,OAAO,EACP,WAAW,EACX,WAAW,EACX,QAAQ,EACR,WAAW,EACX,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { loadPassTemplate, mountCouponPassBody } from "./pass.js";
2
+ export { loadOrderModel, mountOrderInstance, generateOrder, writeOrderCertificates, addImagesToModelDir, removeImagesFromModelDir, } from "./order.js";
3
+ export { sendPushNotification } from "./push.js";
4
+ export { parseRegistrationRequest, parseUnregistrationRequest, } from "./registration.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/apple/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EACL,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,40 @@
1
+ import type { AppleOrderConfig } from "../common/config.js";
2
+ import type { AppleOrderOptions, OrderInstance } from "./types.js";
3
+ /**
4
+ * Write order certificates to a temporary directory so
5
+ * `wallet-order-generator` can read them from disk.
6
+ *
7
+ * Returns the temp directory path.
8
+ */
9
+ export declare const writeOrderCertificates: (config: AppleOrderConfig) => string;
10
+ /**
11
+ * Load an order model from a `.order` bundle directory.
12
+ */
13
+ export declare const loadOrderModel: (config: AppleOrderConfig) => Promise<OrderInstance>;
14
+ /**
15
+ * Build a plain order instance body from the provided data.
16
+ *
17
+ * Amounts in `totals` and `orderItems[].price` are expected in the
18
+ * **smallest currency unit** (e.g. cents) and will be divided by 100.
19
+ */
20
+ export declare const mountOrderInstance: (options: AppleOrderOptions, webServiceURL: string) => Record<string, unknown>;
21
+ /**
22
+ * Download images from URLs and save them into the order model directory.
23
+ *
24
+ * Returns the list of saved file names.
25
+ */
26
+ export declare const addImagesToModelDir: (modelDirPath: string, images: string[]) => Promise<string[]>;
27
+ /**
28
+ * Remove previously-added images from the order model directory.
29
+ */
30
+ export declare const removeImagesFromModelDir: (modelDirPath: string, images: string[]) => Promise<void>;
31
+ /**
32
+ * Generate a signed `.order` file buffer.
33
+ *
34
+ * @param orderInstance - The order data object (from `mountOrderInstance` or your own).
35
+ * @param config - Apple order certificate configuration.
36
+ * @param images - List of image file names already present in the model directory.
37
+ * @returns A `Buffer` containing the signed order file.
38
+ */
39
+ export declare const generateOrder: (orderInstance: Record<string, unknown>, config: AppleOrderConfig, images?: string[]) => Promise<Buffer>;
40
+ //# sourceMappingURL=order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../src/apple/order.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAElE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,QAAQ,gBAAgB,KAAG,MAcjE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,gBAAgB,KACvB,OAAO,CAAC,aAAa,CAGvB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,iBAAiB,EAC1B,eAAe,MAAM,KACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAiDxB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,cAAc,MAAM,EACpB,QAAQ,MAAM,EAAE,KACf,OAAO,CAAC,MAAM,EAAE,CAkBlB,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,EACpB,QAAQ,MAAM,EAAE,KACf,OAAO,CAAC,IAAI,CAQd,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACxB,eAAe,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,QAAQ,gBAAgB,EACxB,SAAQ,MAAM,EAAO,KACpB,OAAO,CAAC,MAAM,CAehB,CAAA"}
@@ -0,0 +1,127 @@
1
+ // @ts-expect-error — wallet-order-generator has no type declarations
2
+ import orderGenerator from "wallet-order-generator";
3
+ import { mkdtempSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { unlink } from "node:fs/promises";
7
+ /**
8
+ * Write order certificates to a temporary directory so
9
+ * `wallet-order-generator` can read them from disk.
10
+ *
11
+ * Returns the temp directory path.
12
+ */
13
+ export const writeOrderCertificates = (config) => {
14
+ const tempDir = mkdtempSync(join(tmpdir(), "passkit-certs-"));
15
+ const certs = {
16
+ signerCert: config.certificate,
17
+ signerKey: config.privateKey,
18
+ wwdr: config.wwdrCertificate,
19
+ };
20
+ for (const [name, value] of Object.entries(certs)) {
21
+ writeFileSync(join(tempDir, `${name}.pem`), value);
22
+ }
23
+ return tempDir;
24
+ };
25
+ /**
26
+ * Load an order model from a `.order` bundle directory.
27
+ */
28
+ export const loadOrderModel = async (config) => {
29
+ const orderInstance = await orderGenerator.from(config.orderModelPath);
30
+ return orderInstance;
31
+ };
32
+ /**
33
+ * Build a plain order instance body from the provided data.
34
+ *
35
+ * Amounts in `totals` and `orderItems[].price` are expected in the
36
+ * **smallest currency unit** (e.g. cents) and will be divided by 100.
37
+ */
38
+ export const mountOrderInstance = (options, webServiceURL) => {
39
+ const { orderNumber, totals, currency, orderItems, customer, orderStatus, paymentStatus, fulfillments, orderManagementURL, } = options;
40
+ return {
41
+ orderNumber,
42
+ customer,
43
+ webServiceURL,
44
+ payment: {
45
+ status: paymentStatus || "paid",
46
+ total: {
47
+ amount: totals.grandTotal / 100,
48
+ currency,
49
+ },
50
+ summaryItems: [
51
+ {
52
+ label: "Subtotal",
53
+ value: { amount: totals.subTotal / 100, currency },
54
+ },
55
+ {
56
+ label: "Shipping",
57
+ value: { amount: totals.shipping / 100, currency },
58
+ },
59
+ {
60
+ label: "Tip",
61
+ value: { amount: totals.tip / 100, currency },
62
+ },
63
+ ],
64
+ },
65
+ lineItems: orderItems.map((item) => ({
66
+ image: item.image.split("/").pop(),
67
+ price: { amount: item.price / 100, currency },
68
+ quantity: item.quantity,
69
+ title: item.productName,
70
+ subtitle: item.variantName || "",
71
+ })),
72
+ statusDescription: orderStatus || "PROCESSING",
73
+ orderManagementURL,
74
+ fulfillments,
75
+ };
76
+ };
77
+ /**
78
+ * Download images from URLs and save them into the order model directory.
79
+ *
80
+ * Returns the list of saved file names.
81
+ */
82
+ export const addImagesToModelDir = async (modelDirPath, images) => {
83
+ const results = await Promise.all(images.map(async (imageUrl) => {
84
+ try {
85
+ const response = await fetch(imageUrl);
86
+ const buffer = Buffer.from(await response.arrayBuffer());
87
+ const imageName = imageUrl.split("/").pop();
88
+ const imagePath = join(modelDirPath, imageName);
89
+ writeFileSync(imagePath, buffer);
90
+ return imageName;
91
+ }
92
+ catch (err) {
93
+ console.error("Failed to download image:", err);
94
+ return null;
95
+ }
96
+ }));
97
+ return results.filter((name) => name !== null);
98
+ };
99
+ /**
100
+ * Remove previously-added images from the order model directory.
101
+ */
102
+ export const removeImagesFromModelDir = async (modelDirPath, images) => {
103
+ for (const image of images) {
104
+ try {
105
+ await unlink(join(modelDirPath, image));
106
+ }
107
+ catch {
108
+ // ignore missing files
109
+ }
110
+ }
111
+ };
112
+ /**
113
+ * Generate a signed `.order` file buffer.
114
+ *
115
+ * @param orderInstance - The order data object (from `mountOrderInstance` or your own).
116
+ * @param config - Apple order certificate configuration.
117
+ * @param images - List of image file names already present in the model directory.
118
+ * @returns A `Buffer` containing the signed order file.
119
+ */
120
+ export const generateOrder = async (orderInstance, config, images = []) => {
121
+ const tempCertsDir = writeOrderCertificates(config);
122
+ // Inject the auth token
123
+ orderInstance.authenticationToken = config.authenticationToken;
124
+ const readStream = await orderGenerator.generateOrder(orderInstance, config.keyPassword ?? "", ["icon.png", ...images], config.orderModelPath, tempCertsDir);
125
+ return readStream.toBuffer();
126
+ };
127
+ //# sourceMappingURL=order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order.js","sourceRoot":"","sources":["../../src/apple/order.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { Template } from "@walletpass/pass-js";
2
+ import type { ApplePassConfig } from "../common/config.js";
3
+ import type { AppleCouponPassOptions } from "./types.js";
4
+ /**
5
+ * Load a pass template from a `.pass` bundle directory and configure
6
+ * it with the provided certificates.
7
+ */
8
+ export declare const loadPassTemplate: (config: ApplePassConfig) => Promise<Template>;
9
+ /**
10
+ * Build the body for a coupon-style pass.
11
+ *
12
+ * Returns a plain object suitable for passing to `template.createPass()`.
13
+ */
14
+ export declare const mountCouponPassBody: (options: AppleCouponPassOptions) => {
15
+ primaryFields: {
16
+ key: string;
17
+ label: string;
18
+ value: string;
19
+ }[];
20
+ secondaryFields: {
21
+ key: string;
22
+ label: string;
23
+ value: string;
24
+ }[];
25
+ barcode: {
26
+ format: string;
27
+ message: string;
28
+ messageEncoding: string;
29
+ };
30
+ backFields: {
31
+ key: string;
32
+ label: string;
33
+ value: string;
34
+ }[];
35
+ };
36
+ //# sourceMappingURL=pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pass.d.ts","sourceRoot":"","sources":["../../src/apple/pass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAExD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAU,QAAQ,eAAe,sBAS7D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,sBAAsB;;;;;;;;;;;;;;;;;;;;;CAyBlE,CAAA"}
@@ -0,0 +1,44 @@
1
+ import { Template } from "@walletpass/pass-js";
2
+ /**
3
+ * Load a pass template from a `.pass` bundle directory and configure
4
+ * it with the provided certificates.
5
+ */
6
+ export const loadPassTemplate = async (config) => {
7
+ const template = await Template.load(config.passTemplatePath, "", {
8
+ allowHttp: config.allowHttp ?? false,
9
+ });
10
+ template.setCertificate(config.certificate);
11
+ template.setPrivateKey(config.privateKey, config.keyPassword);
12
+ return template;
13
+ };
14
+ /**
15
+ * Build the body for a coupon-style pass.
16
+ *
17
+ * Returns a plain object suitable for passing to `template.createPass()`.
18
+ */
19
+ export const mountCouponPassBody = (options) => {
20
+ const { code, offerTitle, qrCodeUrl, backFields } = options;
21
+ return {
22
+ primaryFields: [
23
+ {
24
+ key: "offer",
25
+ label: "Discount",
26
+ value: offerTitle,
27
+ },
28
+ ],
29
+ secondaryFields: [
30
+ {
31
+ key: "code",
32
+ label: "Code",
33
+ value: code,
34
+ },
35
+ ],
36
+ barcode: {
37
+ format: "PKBarcodeFormatQR",
38
+ message: qrCodeUrl,
39
+ messageEncoding: "iso-8859-1",
40
+ },
41
+ backFields: backFields ?? [],
42
+ };
43
+ };
44
+ //# sourceMappingURL=pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pass.js","sourceRoot":"","sources":["../../src/apple/pass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAI9C;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAAuB,EAAE,EAAE;IAChE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,EAAE,EAAE;QAChE,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,KAAK;KACrC,CAAC,CAAA;IAEF,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IAC3C,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;IAE7D,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAA+B,EAAE,EAAE;IACrE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAA;IAE3D,OAAO;QACL,aAAa,EAAE;YACb;gBACE,GAAG,EAAE,OAAO;gBACZ,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE,UAAU;aAClB;SACF;QACD,eAAe,EAAE;YACf;gBACE,GAAG,EAAE,MAAM;gBACX,KAAK,EAAE,MAAM;gBACb,KAAK,EAAE,IAAI;aACZ;SACF;QACD,OAAO,EAAE;YACP,MAAM,EAAE,mBAAmB;YAC3B,OAAO,EAAE,SAAS;YAClB,eAAe,EAAE,YAAY;SAC9B;QACD,UAAU,EAAE,UAAU,IAAI,EAAE;KAC7B,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,14 @@
1
+ import type { ApplePushOptions } from "./types.js";
2
+ /**
3
+ * Send an APN push notification to one or more devices to trigger
4
+ * a pass or order refresh.
5
+ *
6
+ * @param pushTokens - A single token or array of device push tokens.
7
+ * @param topic - The pass type identifier or order type identifier.
8
+ * @param options - Certificate and key configuration.
9
+ */
10
+ export declare const sendPushNotification: (pushTokens: string | string[], topic: string, options: ApplePushOptions) => Promise<{
11
+ sent: number;
12
+ failed: number;
13
+ }>;
14
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/apple/push.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,GAAG,MAAM,EAAE,EAC7B,OAAO,MAAM,EACb,SAAS,gBAAgB,KACxB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA2B1C,CAAA"}
@@ -0,0 +1,37 @@
1
+ import apn from "@parse/node-apn";
2
+ /**
3
+ * Send an APN push notification to one or more devices to trigger
4
+ * a pass or order refresh.
5
+ *
6
+ * @param pushTokens - A single token or array of device push tokens.
7
+ * @param topic - The pass type identifier or order type identifier.
8
+ * @param options - Certificate and key configuration.
9
+ */
10
+ export const sendPushNotification = async (pushTokens, topic, options) => {
11
+ const provider = new apn.Provider({
12
+ cert: options.certificate,
13
+ key: options.privateKey,
14
+ passphrase: options.passphrase,
15
+ production: options.production ?? true,
16
+ });
17
+ const note = new apn.Notification();
18
+ note.expiry = Math.floor(Date.now() / 1000) + 3600; // 1 hour
19
+ note.badge = 3;
20
+ note.priority = 10;
21
+ note.pushType = "alert";
22
+ note.sound = "ping.aiff";
23
+ note.alert = "You have updates!";
24
+ note.payload = {};
25
+ note.topic = topic;
26
+ try {
27
+ const result = await provider.send(note, pushTokens);
28
+ return {
29
+ sent: result.sent?.length ?? 0,
30
+ failed: result.failed?.length ?? 0,
31
+ };
32
+ }
33
+ finally {
34
+ provider.shutdown();
35
+ }
36
+ };
37
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/apple/push.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,iBAAiB,CAAA;AAGjC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,EACvC,UAA6B,EAC7B,KAAa,EACb,OAAyB,EACkB,EAAE;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC;QAChC,IAAI,EAAE,OAAO,CAAC,WAAW;QACzB,GAAG,EAAE,OAAO,CAAC,UAAU;QACvB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;KACvC,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,YAAY,EAAE,CAAA;IACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA,CAAC,SAAS;IAC5D,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;IACd,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAA;IAClB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;IACvB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAA;IACxB,IAAI,CAAC,KAAK,GAAG,mBAAmB,CAAA;IAChC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;IACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IAElB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;QACpD,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;YAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;SACnC,CAAA;IACH,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,QAAQ,EAAE,CAAA;IACrB,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Framework-agnostic helpers for Apple Wallet device registration.
3
+ *
4
+ * These helpers validate and parse incoming registration / unregistration
5
+ * requests so you can plug them into any HTTP framework (Express, Fastify,
6
+ * Hono, Elysia, etc.).
7
+ */
8
+ export interface RegistrationRequest {
9
+ /** The `Authorization` header value (e.g. "ApplePass <token>"). */
10
+ authorizationHeader?: string;
11
+ /** Device library identifier from the URL path. */
12
+ deviceLibraryIdentifier: string;
13
+ /** Serial number of the pass being registered. */
14
+ serialNumber: string;
15
+ /** Push token from the request body (register only). */
16
+ pushToken?: string;
17
+ }
18
+ export interface RegistrationResult {
19
+ valid: boolean;
20
+ /** HTTP status code to return. */
21
+ status: number;
22
+ /** Parsed fields (available when valid). */
23
+ deviceLibraryIdentifier?: string;
24
+ serialNumber?: string;
25
+ pushToken?: string;
26
+ authToken?: string;
27
+ }
28
+ /**
29
+ * Validate and parse a device registration request.
30
+ *
31
+ * @param request - The incoming request data.
32
+ * @param expectedAuthToken - The authentication token configured for this pass type.
33
+ */
34
+ export declare const parseRegistrationRequest: (request: RegistrationRequest, expectedAuthToken: string) => RegistrationResult;
35
+ /**
36
+ * Validate and parse a device unregistration request.
37
+ *
38
+ * Same validation as registration but does not require a push token.
39
+ */
40
+ export declare const parseUnregistrationRequest: (request: Omit<RegistrationRequest, "pushToken">, expectedAuthToken: string) => RegistrationResult;
41
+ //# sourceMappingURL=registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/apple/registration.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,mDAAmD;IACnD,uBAAuB,EAAE,MAAM,CAAA;IAC/B,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAA;IACpB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAA;IACd,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,mBAAmB,EAC5B,mBAAmB,MAAM,KACxB,kBAoBF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,GACrC,SAAS,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,EAC/C,mBAAmB,MAAM,KACxB,kBAKF,CAAA"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Framework-agnostic helpers for Apple Wallet device registration.
3
+ *
4
+ * These helpers validate and parse incoming registration / unregistration
5
+ * requests so you can plug them into any HTTP framework (Express, Fastify,
6
+ * Hono, Elysia, etc.).
7
+ */
8
+ /**
9
+ * Validate and parse a device registration request.
10
+ *
11
+ * @param request - The incoming request data.
12
+ * @param expectedAuthToken - The authentication token configured for this pass type.
13
+ */
14
+ export const parseRegistrationRequest = (request, expectedAuthToken) => {
15
+ const authHeader = request.authorizationHeader ?? "";
16
+ const token = authHeader.replace(/^ApplePass\s+/i, "").trim();
17
+ if (!token || token !== expectedAuthToken) {
18
+ return { valid: false, status: 401 };
19
+ }
20
+ if (!request.deviceLibraryIdentifier || !request.serialNumber) {
21
+ return { valid: false, status: 400 };
22
+ }
23
+ return {
24
+ valid: true,
25
+ status: 200,
26
+ deviceLibraryIdentifier: request.deviceLibraryIdentifier,
27
+ serialNumber: request.serialNumber,
28
+ pushToken: request.pushToken,
29
+ authToken: token,
30
+ };
31
+ };
32
+ /**
33
+ * Validate and parse a device unregistration request.
34
+ *
35
+ * Same validation as registration but does not require a push token.
36
+ */
37
+ export const parseUnregistrationRequest = (request, expectedAuthToken) => {
38
+ return parseRegistrationRequest({ ...request, pushToken: undefined }, expectedAuthToken);
39
+ };
40
+ //# sourceMappingURL=registration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/apple/registration.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,OAA4B,EAC5B,iBAAyB,EACL,EAAE;IACtB,MAAM,UAAU,GAAG,OAAO,CAAC,mBAAmB,IAAI,EAAE,CAAA;IACpD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAE7D,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,iBAAiB,EAAE,CAAC;QAC1C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACtC,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,uBAAuB,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QAC9D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACtC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,GAAG;QACX,uBAAuB,EAAE,OAAO,CAAC,uBAAuB;QACxD,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,KAAK;KACjB,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,OAA+C,EAC/C,iBAAyB,EACL,EAAE;IACtB,OAAO,wBAAwB,CAC7B,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,EACpC,iBAAiB,CAClB,CAAA;AACH,CAAC,CAAA"}