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.
- package/README.md +190 -0
- package/dist/apple/index.d.ts +7 -0
- package/dist/apple/index.d.ts.map +1 -0
- package/dist/apple/index.js +5 -0
- package/dist/apple/index.js.map +1 -0
- package/dist/apple/order.d.ts +40 -0
- package/dist/apple/order.d.ts.map +1 -0
- package/dist/apple/order.js +127 -0
- package/dist/apple/order.js.map +1 -0
- package/dist/apple/pass.d.ts +36 -0
- package/dist/apple/pass.d.ts.map +1 -0
- package/dist/apple/pass.js +44 -0
- package/dist/apple/pass.js.map +1 -0
- package/dist/apple/push.d.ts +14 -0
- package/dist/apple/push.d.ts.map +1 -0
- package/dist/apple/push.js +37 -0
- package/dist/apple/push.js.map +1 -0
- package/dist/apple/registration.d.ts +41 -0
- package/dist/apple/registration.d.ts.map +1 -0
- package/dist/apple/registration.js +40 -0
- package/dist/apple/registration.js.map +1 -0
- package/dist/apple/types.d.ts +110 -0
- package/dist/apple/types.d.ts.map +1 -0
- package/dist/apple/types.js +2 -0
- package/dist/apple/types.js.map +1 -0
- package/dist/common/config.d.ts +247 -0
- package/dist/common/config.d.ts.map +1 -0
- package/dist/common/config.js +58 -0
- package/dist/common/config.js.map +1 -0
- package/dist/common/types.d.ts +54 -0
- package/dist/common/types.d.ts.map +1 -0
- package/dist/common/types.js +20 -0
- package/dist/common/types.js.map +1 -0
- package/dist/google/index.d.ts +4 -0
- package/dist/google/index.d.ts.map +1 -0
- package/dist/google/index.js +3 -0
- package/dist/google/index.js.map +1 -0
- package/dist/google/pass.d.ts +147 -0
- package/dist/google/pass.d.ts.map +1 -0
- package/dist/google/pass.js +269 -0
- package/dist/google/pass.js.map +1 -0
- package/dist/google/push.d.ts +15 -0
- package/dist/google/push.d.ts.map +1 -0
- package/dist/google/push.js +22 -0
- package/dist/google/push.js.map +1 -0
- package/dist/google/types.d.ts +58 -0
- package/dist/google/types.d.ts.map +1 -0
- package/dist/google/types.js +2 -0
- package/dist/google/types.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/react/AddToAppleWallet.d.ts +20 -0
- package/dist/react/AddToAppleWallet.d.ts.map +1 -0
- package/dist/react/AddToAppleWallet.js +10 -0
- package/dist/react/AddToAppleWallet.js.map +1 -0
- package/dist/react/AddToGoogleWallet.d.ts +20 -0
- package/dist/react/AddToGoogleWallet.d.ts.map +1 -0
- package/dist/react/AddToGoogleWallet.js +10 -0
- package/dist/react/AddToGoogleWallet.js.map +1 -0
- package/dist/react/index.d.ts +5 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +1 -0
- 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"}
|