@unchainedshop/ticketing 4.3.3 → 4.4.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 +168 -0
- package/lib/express.d.ts +0 -1
- package/lib/express.js +3 -4
- package/lib/fastify.d.ts +0 -1
- package/lib/fastify.js +3 -4
- package/lib/index.d.ts +6 -7
- package/lib/index.js +4 -5
- package/lib/magic-key.d.ts +0 -1
- package/lib/magic-key.js +0 -4
- package/lib/mobile-tickets/apple-handler-express.d.ts +1 -2
- package/lib/mobile-tickets/apple-handler-express.js +1 -8
- package/lib/mobile-tickets/apple-handler-fastify.d.ts +0 -1
- package/lib/mobile-tickets/apple-handler-fastify.js +1 -8
- package/lib/mobile-tickets/apple-wallet.d.ts +0 -1
- package/lib/mobile-tickets/apple-wallet.js +0 -1
- package/lib/mobile-tickets/google-handler-express.d.ts +1 -2
- package/lib/mobile-tickets/google-handler-express.js +0 -1
- package/lib/mobile-tickets/google-handler-fastify.d.ts +0 -1
- package/lib/mobile-tickets/google-handler-fastify.js +0 -1
- package/lib/module.d.ts +4 -5
- package/lib/module.js +3 -10
- package/lib/pdf-tickets/print-handler-express.d.ts +1 -2
- package/lib/pdf-tickets/print-handler-express.js +1 -2
- package/lib/pdf-tickets/print-handler-fastify.d.ts +0 -1
- package/lib/pdf-tickets/print-handler-fastify.js +1 -2
- package/lib/services.d.ts +2 -3
- package/lib/services.js +0 -1
- package/lib/template-registry.d.ts +10 -10
- package/lib/template-registry.js +5 -7
- package/lib/types.d.ts +3 -4
- package/lib/types.js +0 -1
- package/package.json +17 -15
- package/lib/express.d.ts.map +0 -1
- package/lib/express.js.map +0 -1
- package/lib/fastify.d.ts.map +0 -1
- package/lib/fastify.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/magic-key.d.ts.map +0 -1
- package/lib/magic-key.js.map +0 -1
- package/lib/mobile-tickets/apple-handler-express.d.ts.map +0 -1
- package/lib/mobile-tickets/apple-handler-express.js.map +0 -1
- package/lib/mobile-tickets/apple-handler-fastify.d.ts.map +0 -1
- package/lib/mobile-tickets/apple-handler-fastify.js.map +0 -1
- package/lib/mobile-tickets/apple-wallet.d.ts.map +0 -1
- package/lib/mobile-tickets/apple-wallet.js.map +0 -1
- package/lib/mobile-tickets/apple-webservice.d.ts +0 -7
- package/lib/mobile-tickets/apple-webservice.d.ts.map +0 -1
- package/lib/mobile-tickets/apple-webservice.js +0 -177
- package/lib/mobile-tickets/apple-webservice.js.map +0 -1
- package/lib/mobile-tickets/google-handler-express.d.ts.map +0 -1
- package/lib/mobile-tickets/google-handler-express.js.map +0 -1
- package/lib/mobile-tickets/google-handler-fastify.d.ts.map +0 -1
- package/lib/mobile-tickets/google-handler-fastify.js.map +0 -1
- package/lib/mobile-tickets/google-webservice.d.ts +0 -7
- package/lib/mobile-tickets/google-webservice.d.ts.map +0 -1
- package/lib/mobile-tickets/google-webservice.js +0 -52
- package/lib/mobile-tickets/google-webservice.js.map +0 -1
- package/lib/module.d.ts.map +0 -1
- package/lib/module.js.map +0 -1
- package/lib/pdf-tickets/print-handler-express.d.ts.map +0 -1
- package/lib/pdf-tickets/print-handler-express.js.map +0 -1
- package/lib/pdf-tickets/print-handler-fastify.d.ts.map +0 -1
- package/lib/pdf-tickets/print-handler-fastify.js.map +0 -1
- package/lib/pdf-tickets/print-webservice.d.ts +0 -7
- package/lib/pdf-tickets/print-webservice.d.ts.map +0 -1
- package/lib/pdf-tickets/print-webservice.js +0 -31
- package/lib/pdf-tickets/print-webservice.js.map +0 -1
- package/lib/services.d.ts.map +0 -1
- package/lib/services.js.map +0 -1
- package/lib/template-registry.d.ts.map +0 -1
- package/lib/template-registry.js.map +0 -1
- package/lib/ticketing-index.d.ts +0 -19
- package/lib/ticketing-index.d.ts.map +0 -1
- package/lib/ticketing-index.js +0 -40
- package/lib/ticketing-index.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- package/readme.md +0 -118
- package/src/express.ts +0 -35
- package/src/fastify.ts +0 -30
- package/src/index.ts +0 -66
- package/src/magic-key.ts +0 -43
- package/src/mobile-tickets/apple-handler-express.ts +0 -237
- package/src/mobile-tickets/apple-handler-fastify.ts +0 -279
- package/src/mobile-tickets/apple-wallet.ts +0 -31
- package/src/mobile-tickets/google-handler-express.ts +0 -57
- package/src/mobile-tickets/google-handler-fastify.ts +0 -69
- package/src/module.ts +0 -251
- package/src/pdf-tickets/print-handler-express.ts +0 -34
- package/src/pdf-tickets/print-handler-fastify.ts +0 -43
- package/src/services.ts +0 -35
- package/src/template-registry.ts +0 -48
- package/src/types.ts +0 -8
- package/tsconfig.json +0 -10
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ticketing-index.d.ts","sourceRoot":"","sources":["../src/ticketing-index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAoB,MAAM,wBAAwB,CAAC;AACzE,OAAO,gBAAgB,EAAE,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,iBAAiB,EAAE,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAErE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,CAAC;AAQnF,wBAAgB,eAAe,CAAC,EAAE,cAAc,EAAE,EAAE;IAAE,cAAc,EAAE,GAAG,CAAA;CAAE,QAE1E;AAED,wBAAgB,kBAAkB,CAAC,EACjC,sBAAsB,EACtB,qBAAqB,GACtB,EAAE;IACD,sBAAsB,EAAE,GAAG,CAAC;IAC5B,qBAAqB,EAAE,GAAG,CAAC;CAC5B,QAGA;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,YAAY,EAAE,YAAY,EAC1B,EACE,cAAwC,EACxC,qBAAqB,EACrB,sBAAsB,GACvB,EAAE;IACD,cAAc,CAAC,EAAE,GAAG,CAAC;IACrB,qBAAqB,EAAE,GAAG,CAAC;IAC3B,sBAAsB,EAAE,GAAG,CAAC;CAC7B,QA0BF"}
|
package/lib/ticketing-index.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { subscribe } from '@unchainedshop/events';
|
|
2
|
-
import { WorkerEventTypes } from '@unchainedshop/core-worker';
|
|
3
|
-
import { RendererTypes, registerRenderer } from './template-registry.js';
|
|
4
|
-
import ticketingModules from './module.js';
|
|
5
|
-
import setupMagicKey from './magic-key.js';
|
|
6
|
-
import ticketingServices from './services.js';
|
|
7
|
-
export { ticketingServices, ticketingModules };
|
|
8
|
-
const defaultOrderPDFRenderer = async ({ orderId }, context) => {
|
|
9
|
-
const order = await context.modules.orders.findOrder({ orderId });
|
|
10
|
-
console.log(order);
|
|
11
|
-
return;
|
|
12
|
-
};
|
|
13
|
-
export function setupPDFTickets({ renderOrderPDF }) {
|
|
14
|
-
registerRenderer(RendererTypes.ORDER_PDF, renderOrderPDF);
|
|
15
|
-
}
|
|
16
|
-
export function setupMobileTickets({ createGoogleWalletPass, createAppleWalletPass, }) {
|
|
17
|
-
registerRenderer(RendererTypes.GOOGLE_WALLET, createGoogleWalletPass);
|
|
18
|
-
registerRenderer(RendererTypes.APPLE_WALLET, createAppleWalletPass);
|
|
19
|
-
}
|
|
20
|
-
export default function setupTicketing(unchainedAPI, { renderOrderPDF = defaultOrderPDFRenderer, createAppleWalletPass, createGoogleWalletPass, }) {
|
|
21
|
-
setupPDFTickets({
|
|
22
|
-
renderOrderPDF,
|
|
23
|
-
});
|
|
24
|
-
setupMobileTickets({
|
|
25
|
-
createAppleWalletPass,
|
|
26
|
-
createGoogleWalletPass,
|
|
27
|
-
});
|
|
28
|
-
if (!process.env.UNCHAINED_SECRET)
|
|
29
|
-
throw new Error('Unchained Ticketing needs the UNCHAINED_SECRET environment variable to be set in order to allow magic key access to orders and tokens.');
|
|
30
|
-
setupMagicKey();
|
|
31
|
-
subscribe('TOKEN_INVALIDATED', async () => {
|
|
32
|
-
await unchainedAPI.modules.passes.invalidateAppleWalletPasses(unchainedAPI);
|
|
33
|
-
});
|
|
34
|
-
subscribe(WorkerEventTypes.FINISHED, async ({ payload: work }) => {
|
|
35
|
-
if ((work.type === 'EXPORT_TOKEN' || work.type === 'UPDATE_TOKEN_OWNERSHIP') && work.success) {
|
|
36
|
-
await unchainedAPI.modules.passes.invalidateAppleWalletPasses(unchainedAPI);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
//# sourceMappingURL=ticketing-index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ticketing-index.js","sourceRoot":"","sources":["../src/ticketing-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAQ,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,gBAAqC,MAAM,aAAa,CAAC;AAGhE,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAC3C,OAAO,iBAAwC,MAAM,eAAe,CAAC;AAIrE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAsC,CAAC;AACnF,MAAM,uBAAuB,GAAG,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,OAAqB,EAAE,EAAE;IAC3E,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEnB,OAAO;AACT,CAAC,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,EAAE,cAAc,EAA2B;IACzE,gBAAgB,CAAC,aAAa,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,EACjC,sBAAsB,EACtB,qBAAqB,GAItB;IACC,gBAAgB,CAAC,aAAa,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IACtE,gBAAgB,CAAC,aAAa,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,YAA0B,EAC1B,EACE,cAAc,GAAG,uBAAuB,EACxC,qBAAqB,EACrB,sBAAsB,GAKvB;IAED,eAAe,CAAC;QACd,cAAc;KACf,CAAC,CAAC;IACH,kBAAkB,CAAC;QACjB,qBAAqB;QACrB,sBAAsB;KACvB,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAC/B,MAAM,IAAI,KAAK,CACb,wIAAwI,CACzI,CAAC;IAEJ,aAAa,EAAE,CAAC;IAEhB,SAAS,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,YAAY,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAwB,EAAE,EAAE;QACrF,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,KAAK,wBAAwB,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7F,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,YAAY,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/lib/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B,CAAC"}
|
package/lib/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/readme.md
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
# Unchained Ticketing Extension
|
|
2
|
-
|
|
3
|
-
## Prepare Apple Certificate
|
|
4
|
-
|
|
5
|
-
1. Add a new Pass Type ID on [developer.apple.com](https://developer.apple.com/account), then gernerate fresh production certificate. Download the Certificate and import it into your Keychain.
|
|
6
|
-
|
|
7
|
-
2. Export with Keychain: Select the "Certificates" Tab, then the Pass Type ID, then select both the ID and the key, export in p12 format.
|
|
8
|
-
|
|
9
|
-
3. Convert to PEM
|
|
10
|
-
It's important you set a PEM passphrase as it is required for Unchained Ticketing
|
|
11
|
-
```
|
|
12
|
-
openssl pkcs12 -in Zertifikate.p12 -legacy -clcerts -out cert_and_key.pem
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
4. Configure the path and your Team ID by Env:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
PASS_CERTIFICATE_PATH=./cert_and_key.pem
|
|
19
|
-
PASS_CERTIFICATE_SECRET=YOUR_PEM_PASSPHRASE
|
|
20
|
-
PASS_TEAM_ID=SSCB95CV6U
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Setup Example
|
|
24
|
-
|
|
25
|
-
boot.js:
|
|
26
|
-
```js
|
|
27
|
-
import { modules, setupTicketing } from "@unchainedshop/ticketing";
|
|
28
|
-
import { connect } from '@unchainedshop/api/express/index.js';
|
|
29
|
-
|
|
30
|
-
...
|
|
31
|
-
const app = express();
|
|
32
|
-
|
|
33
|
-
const httpServer = http.createServer(app);
|
|
34
|
-
|
|
35
|
-
const engine = await startPlatform({
|
|
36
|
-
modules,
|
|
37
|
-
options: {},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
connect(app, engine, { corsOrigins: [] });
|
|
41
|
-
|
|
42
|
-
// Unchained Ticketing Extension
|
|
43
|
-
setupTicketing(app, engine.unchainedAPI as TicketingAPI, {
|
|
44
|
-
renderOrderPDF,
|
|
45
|
-
createAppleWalletPass,
|
|
46
|
-
createGoogleWalletPass,
|
|
47
|
-
});
|
|
48
|
-
...
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
renderOrderPDF.ts:
|
|
53
|
-
```tsx
|
|
54
|
-
import React from "react";
|
|
55
|
-
import ReactPDF, { Document } from "@react-pdf/renderer";
|
|
56
|
-
|
|
57
|
-
const TicketTemplate = ({ tickets }: { tickets: Array<any> }) => {
|
|
58
|
-
return (
|
|
59
|
-
<Document>
|
|
60
|
-
Hello World
|
|
61
|
-
</Document>
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export default async (
|
|
66
|
-
{ orderId, variant }: { orderId: string },
|
|
67
|
-
{ modules },
|
|
68
|
-
) => {
|
|
69
|
-
const order = await modules.orders.findOrder({ orderId });
|
|
70
|
-
...
|
|
71
|
-
const pdfStream = await ReactPDF.renderToStream(
|
|
72
|
-
<TicketTemplate tickets={tickets} />,
|
|
73
|
-
);
|
|
74
|
-
return pdfStream;
|
|
75
|
-
};
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
createAppleWalletPass.ts:
|
|
79
|
-
```ts
|
|
80
|
-
import { UnchainedCore } from "@unchainedshop/core";
|
|
81
|
-
import { Template, constants } from "@walletpass/pass-js";
|
|
82
|
-
|
|
83
|
-
export default async (token, unchainedAPI: UnchainedCore) => {
|
|
84
|
-
const template = new Template(
|
|
85
|
-
"eventTicket",
|
|
86
|
-
...
|
|
87
|
-
);
|
|
88
|
-
const pass = await template.createPass(...);
|
|
89
|
-
return pass;
|
|
90
|
-
};
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
createGoogleWalletPass.ts:
|
|
94
|
-
```ts
|
|
95
|
-
import { UnchainedCore } from "@unchainedshop/core";
|
|
96
|
-
import { google } from "googleapis";
|
|
97
|
-
import jwt from "jsonwebtoken";
|
|
98
|
-
|
|
99
|
-
export default async (token, unchainedAPI: UnchainedCore) => {
|
|
100
|
-
|
|
101
|
-
const product = ...;
|
|
102
|
-
|
|
103
|
-
// upsert class
|
|
104
|
-
|
|
105
|
-
// upsert object
|
|
106
|
-
|
|
107
|
-
const asURL = async () =>
|
|
108
|
-
createJwtNewObjects(issuerId, product._id, token.tokenSerialNumber);
|
|
109
|
-
|
|
110
|
-
return { asURL };
|
|
111
|
-
};
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## Magic Key Order Access
|
|
115
|
-
|
|
116
|
-
Sometimes it's handy for users to allow them to access their orders and their tickets without actually logging in. For this you can generate a "magic key" that allows access to a single order and it's tickets via a request http header called `x-magic-key`.
|
|
117
|
-
|
|
118
|
-
For example: To retrieve the magic-key when sending an order-confirmation e-mail, just call `await modules.passes.buildMagicKey(orderId);` and append it to an URL like https://my-shop/:orderId?otp=:magicKey and then in the storefront, use the magic key and send it along via x-magic-key http header when using queries that are guarded by the actions `viewOrder`, `updateToken` and `viewToken`
|
package/src/express.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import appleWalletHandler from './mobile-tickets/apple-handler-express.js';
|
|
3
|
-
import googleWalletHandler from './mobile-tickets/google-handler-express.js';
|
|
4
|
-
import printTicketsHandler from './pdf-tickets/print-handler-express.js';
|
|
5
|
-
|
|
6
|
-
export default (app: express.Express) => {
|
|
7
|
-
const {
|
|
8
|
-
APPLE_WALLET_WEBSERVICE_PATH = '/rest/apple-wallet',
|
|
9
|
-
GOOGLE_WALLET_WEBSERVICE_PATH = '/rest/google-wallet',
|
|
10
|
-
UNCHAINED_PDF_PRINT_HANDLER_PATH = '/rest/print_tickets',
|
|
11
|
-
} = process.env;
|
|
12
|
-
|
|
13
|
-
app.use(
|
|
14
|
-
UNCHAINED_PDF_PRINT_HANDLER_PATH,
|
|
15
|
-
express.json({
|
|
16
|
-
type: 'application/json',
|
|
17
|
-
}),
|
|
18
|
-
printTicketsHandler,
|
|
19
|
-
);
|
|
20
|
-
app.use(
|
|
21
|
-
APPLE_WALLET_WEBSERVICE_PATH,
|
|
22
|
-
express.json({
|
|
23
|
-
type: 'application/json',
|
|
24
|
-
}),
|
|
25
|
-
appleWalletHandler,
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
app.use(
|
|
29
|
-
`${GOOGLE_WALLET_WEBSERVICE_PATH}/download/:tokenId`,
|
|
30
|
-
express.json({
|
|
31
|
-
type: 'application/json',
|
|
32
|
-
}),
|
|
33
|
-
googleWalletHandler,
|
|
34
|
-
);
|
|
35
|
-
};
|
package/src/fastify.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { FastifyInstance } from 'fastify';
|
|
2
|
-
import appleWalletHandler from './mobile-tickets/apple-handler-fastify.js';
|
|
3
|
-
import googleWalletHandler from './mobile-tickets/google-handler-fastify.js';
|
|
4
|
-
import printTicketsHandler from './pdf-tickets/print-handler-fastify.js';
|
|
5
|
-
|
|
6
|
-
export default (app: FastifyInstance) => {
|
|
7
|
-
const {
|
|
8
|
-
APPLE_WALLET_WEBSERVICE_PATH = '/rest/apple-wallet',
|
|
9
|
-
GOOGLE_WALLET_WEBSERVICE_PATH = '/rest/google-wallet',
|
|
10
|
-
UNCHAINED_PDF_PRINT_HANDLER_PATH = '/rest/print_tickets',
|
|
11
|
-
} = process.env;
|
|
12
|
-
|
|
13
|
-
app.route({
|
|
14
|
-
url: `${APPLE_WALLET_WEBSERVICE_PATH}*`,
|
|
15
|
-
method: ['GET', 'POST', 'DELETE'],
|
|
16
|
-
handler: appleWalletHandler,
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
app.route({
|
|
20
|
-
url: `${GOOGLE_WALLET_WEBSERVICE_PATH}/download/:tokenId`,
|
|
21
|
-
method: 'GET',
|
|
22
|
-
handler: googleWalletHandler,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
app.route({
|
|
26
|
-
url: `${UNCHAINED_PDF_PRINT_HANDLER_PATH}/*`,
|
|
27
|
-
method: 'GET',
|
|
28
|
-
handler: printTicketsHandler,
|
|
29
|
-
});
|
|
30
|
-
};
|
package/src/index.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { subscribe } from '@unchainedshop/events';
|
|
2
|
-
import { RawPayloadType } from '@unchainedshop/events';
|
|
3
|
-
import { WorkerEventTypes, Work } from '@unchainedshop/core-worker';
|
|
4
|
-
import { RendererTypes, registerRenderer } from './template-registry.js';
|
|
5
|
-
import ticketingModules, { TicketingModule } from './module.js';
|
|
6
|
-
|
|
7
|
-
import { TicketingAPI } from './types.js';
|
|
8
|
-
import setupMagicKey from './magic-key.js';
|
|
9
|
-
import ticketingServices, { TicketingServices } from './services.js';
|
|
10
|
-
|
|
11
|
-
export type { TicketingAPI, RendererTypes };
|
|
12
|
-
|
|
13
|
-
export { ticketingServices, ticketingModules, TicketingModule, TicketingServices };
|
|
14
|
-
|
|
15
|
-
export function setupPDFTickets({ renderOrderPDF }: { renderOrderPDF: any }) {
|
|
16
|
-
registerRenderer(RendererTypes.ORDER_PDF, renderOrderPDF);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function setupMobileTickets({
|
|
20
|
-
createGoogleWalletPass,
|
|
21
|
-
createAppleWalletPass,
|
|
22
|
-
}: {
|
|
23
|
-
createGoogleWalletPass: any;
|
|
24
|
-
createAppleWalletPass: any;
|
|
25
|
-
}) {
|
|
26
|
-
registerRenderer(RendererTypes.GOOGLE_WALLET, createGoogleWalletPass);
|
|
27
|
-
registerRenderer(RendererTypes.APPLE_WALLET, createAppleWalletPass);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default function setupTicketing(
|
|
31
|
-
unchainedAPI: TicketingAPI,
|
|
32
|
-
{
|
|
33
|
-
renderOrderPDF,
|
|
34
|
-
createAppleWalletPass,
|
|
35
|
-
createGoogleWalletPass,
|
|
36
|
-
}: {
|
|
37
|
-
renderOrderPDF: any;
|
|
38
|
-
createAppleWalletPass: any;
|
|
39
|
-
createGoogleWalletPass: any;
|
|
40
|
-
},
|
|
41
|
-
) {
|
|
42
|
-
setupPDFTickets({
|
|
43
|
-
renderOrderPDF,
|
|
44
|
-
});
|
|
45
|
-
setupMobileTickets({
|
|
46
|
-
createAppleWalletPass,
|
|
47
|
-
createGoogleWalletPass,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
if (!process.env.UNCHAINED_SECRET)
|
|
51
|
-
throw new Error(
|
|
52
|
-
'Unchained Ticketing needs the UNCHAINED_SECRET environment variable to be set in order to allow magic key access to orders and tokens.',
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
setupMagicKey();
|
|
56
|
-
|
|
57
|
-
subscribe('TOKEN_INVALIDATED', async () => {
|
|
58
|
-
await unchainedAPI.modules.passes.invalidateAppleWalletPasses(unchainedAPI);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
subscribe(WorkerEventTypes.FINISHED, async ({ payload: work }: RawPayloadType<Work>) => {
|
|
62
|
-
if ((work.type === 'EXPORT_TOKEN' || work.type === 'UPDATE_TOKEN_OWNERSHIP') && work.success) {
|
|
63
|
-
await unchainedAPI.modules.passes.invalidateAppleWalletPasses(unchainedAPI);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
package/src/magic-key.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Context } from '@unchainedshop/api';
|
|
2
|
-
import { roles } from '@unchainedshop/api';
|
|
3
|
-
import { TicketingAPI } from './types.js';
|
|
4
|
-
|
|
5
|
-
const isMagicKeyValidForOrder = async (
|
|
6
|
-
obj: any,
|
|
7
|
-
{ orderId }: { orderId: string },
|
|
8
|
-
{ modules, getHeader }: Context & TicketingAPI,
|
|
9
|
-
) => {
|
|
10
|
-
const order = await modules.orders.findOrder({ orderId });
|
|
11
|
-
// always return true if orderId leads to nothing to show potentially allowed actions
|
|
12
|
-
if (!order) return true;
|
|
13
|
-
|
|
14
|
-
const magicKey = await modules.passes.buildMagicKey(order._id);
|
|
15
|
-
return magicKey === getHeader('x-magic-key');
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const isMagicKeyValidForToken = async (
|
|
19
|
-
root: any,
|
|
20
|
-
params: { tokenId: string } | null,
|
|
21
|
-
{ modules, getHeader }: Context & TicketingAPI,
|
|
22
|
-
) => {
|
|
23
|
-
const tokenId = params?.tokenId || (root && 'tokenSerialNumber' in root && root._id) || null;
|
|
24
|
-
|
|
25
|
-
const token = await modules.warehousing.findToken({ tokenId });
|
|
26
|
-
// always return true if tokenId leads to nothing to show potentially allowed actions
|
|
27
|
-
if (!token) return true;
|
|
28
|
-
|
|
29
|
-
const order = await modules.orders.findOrder({ orderId: token.meta.orderId });
|
|
30
|
-
if (!order) return false;
|
|
31
|
-
|
|
32
|
-
// Restrict access if token does not belong to the original user anymore (for ex. web3 owned)
|
|
33
|
-
if (order.userId !== token.userId) return false;
|
|
34
|
-
|
|
35
|
-
const magicKey = await modules.passes.buildMagicKey(order._id);
|
|
36
|
-
return magicKey === getHeader('x-magic-key');
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export default () => {
|
|
40
|
-
roles.allRoles?.ALL?.allow(roles.actions.viewOrder, isMagicKeyValidForOrder);
|
|
41
|
-
roles.allRoles?.ALL?.allow(roles.actions.viewToken, isMagicKeyValidForToken);
|
|
42
|
-
roles.allRoles?.ALL?.allow(roles.actions.updateToken, isMagicKeyValidForToken);
|
|
43
|
-
};
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
-
import type { Request, Response } from 'express';
|
|
3
|
-
import { TicketingAPI } from '../types.js';
|
|
4
|
-
import { getFileAdapter } from '@unchainedshop/core-files';
|
|
5
|
-
|
|
6
|
-
const logger = createLogger('unchained:apple-wallet-webservice');
|
|
7
|
-
|
|
8
|
-
const isAuthenticationTokenCorrect = (req, authenticationToken) => {
|
|
9
|
-
const expectedAuthorizationValue = `ApplePass ${authenticationToken}`;
|
|
10
|
-
return req.headers.authorization === expectedAuthorizationValue;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAPI }, res: Response) => {
|
|
14
|
-
const resolvedContext = req.unchainedContext;
|
|
15
|
-
const { modules } = resolvedContext;
|
|
16
|
-
logger.info(`${req.path} (${JSON.stringify(req.query)})`);
|
|
17
|
-
|
|
18
|
-
if (req.path.startsWith('/download/')) {
|
|
19
|
-
try {
|
|
20
|
-
const [, , passFileName] = req.path.split('/');
|
|
21
|
-
const [tokenId] = passFileName.split('.pkpass');
|
|
22
|
-
|
|
23
|
-
if (!tokenId) {
|
|
24
|
-
res.status(404).end();
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const token = await modules.warehousing.findToken({
|
|
29
|
-
tokenId,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
if (!token) {
|
|
33
|
-
res.status(404).send('Token not found');
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const { hash } = req.query;
|
|
38
|
-
const correctHash = await modules.warehousing.buildAccessKeyForToken(tokenId);
|
|
39
|
-
if (!hash || hash !== correctHash) {
|
|
40
|
-
res.status(403).send('Token hash invalid for current owner');
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const passFile = await modules.passes.upsertAppleWalletPass(token, resolvedContext);
|
|
45
|
-
|
|
46
|
-
const fileUploadAdapter = getFileAdapter();
|
|
47
|
-
const signedUrl = await fileUploadAdapter.createDownloadURL(passFile);
|
|
48
|
-
const url = signedUrl && (await modules.files.normalizeUrl(signedUrl, {}));
|
|
49
|
-
|
|
50
|
-
if (!url) {
|
|
51
|
-
res.status(500);
|
|
52
|
-
res.send({
|
|
53
|
-
success: false,
|
|
54
|
-
message: 'Could not create download URL',
|
|
55
|
-
name: 'URL_SIGNING_FAILED',
|
|
56
|
-
});
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const response = await fetch(url);
|
|
61
|
-
const data = await response.arrayBuffer();
|
|
62
|
-
const uint8View = new Uint8Array(data);
|
|
63
|
-
|
|
64
|
-
res.status(200);
|
|
65
|
-
res.setHeader('Content-Type', 'application/vnd.apple.pkpass');
|
|
66
|
-
res.setHeader('Content-Disposition', `attachment; filename=${tokenId}.pkpass`);
|
|
67
|
-
res.send(uint8View);
|
|
68
|
-
return;
|
|
69
|
-
} catch (e) {
|
|
70
|
-
logger.error(e);
|
|
71
|
-
}
|
|
72
|
-
res.status(500).end();
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const [, apiVersion, endpoint, ...pathComponents] = req.path.split("/"); /* eslint-disable-line */
|
|
77
|
-
if (endpoint === 'devices') {
|
|
78
|
-
if (req.method === 'POST') {
|
|
79
|
-
// Register Device
|
|
80
|
-
const [deviceLibraryIdentifier, , passTypeIdentifier, serialNumber] = pathComponents;
|
|
81
|
-
const { pushToken } = req.body;
|
|
82
|
-
|
|
83
|
-
const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
|
|
84
|
-
|
|
85
|
-
if (pass) {
|
|
86
|
-
if (
|
|
87
|
-
!isAuthenticationTokenCorrect(
|
|
88
|
-
req,
|
|
89
|
-
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
90
|
-
)
|
|
91
|
-
) {
|
|
92
|
-
res.status(401).end();
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const newRegistration = await modules.passes.registerDeviceForAppleWalletPass(
|
|
97
|
-
passTypeIdentifier,
|
|
98
|
-
serialNumber,
|
|
99
|
-
{
|
|
100
|
-
deviceLibraryIdentifier,
|
|
101
|
-
pushToken,
|
|
102
|
-
},
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
if (!newRegistration) {
|
|
106
|
-
res.status(200).end();
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
res.status(201).end();
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
} else if (req.method === 'GET') {
|
|
114
|
-
// Get the List of Updatable Passes
|
|
115
|
-
const lastUpdated = new Date();
|
|
116
|
-
const [deviceLibraryIdentifier, , passTypeIdentifier] = pathComponents;
|
|
117
|
-
const passesUpdatedSince =
|
|
118
|
-
req.query?.passesUpdatedSince && new Date(req.query.passesUpdatedSince as string);
|
|
119
|
-
|
|
120
|
-
const passes = await modules.passes.findUpdatedAppleWalletPasses(
|
|
121
|
-
passTypeIdentifier,
|
|
122
|
-
deviceLibraryIdentifier,
|
|
123
|
-
passesUpdatedSince,
|
|
124
|
-
);
|
|
125
|
-
const serialNumbers = passes.map((t) => t.meta?.serialNumber).filter(Boolean) as string[];
|
|
126
|
-
|
|
127
|
-
if (serialNumbers?.length) {
|
|
128
|
-
res.status(200).send({ serialNumbers, lastUpdated });
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
res.status(204).end();
|
|
133
|
-
return;
|
|
134
|
-
} else if (req.method === 'DELETE') {
|
|
135
|
-
// Unregister Device
|
|
136
|
-
const [deviceLibraryIdentifier, , passTypeIdentifier, serialNumber] = pathComponents;
|
|
137
|
-
|
|
138
|
-
const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
|
|
139
|
-
|
|
140
|
-
if (pass) {
|
|
141
|
-
if (
|
|
142
|
-
!isAuthenticationTokenCorrect(
|
|
143
|
-
req,
|
|
144
|
-
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
145
|
-
)
|
|
146
|
-
) {
|
|
147
|
-
res.status(401).end();
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
await modules.passes.unregisterDeviceForAppleWalletPass(
|
|
152
|
-
passTypeIdentifier,
|
|
153
|
-
serialNumber,
|
|
154
|
-
deviceLibraryIdentifier,
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
// Unregistered
|
|
158
|
-
res.status(200).end();
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
} else if (endpoint === 'log') {
|
|
163
|
-
// Log Mesage
|
|
164
|
-
const { logs } = req.body;
|
|
165
|
-
logs?.forEach((log) => {
|
|
166
|
-
if (typeof log === 'string') {
|
|
167
|
-
logger.info(log);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
res.status(200).end();
|
|
171
|
-
return;
|
|
172
|
-
} else if (endpoint === 'passes') {
|
|
173
|
-
if (req.method === 'GET') {
|
|
174
|
-
// Get an updated Pass
|
|
175
|
-
const [passTypeIdentifier, serialNumber] = pathComponents;
|
|
176
|
-
|
|
177
|
-
const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
|
|
178
|
-
|
|
179
|
-
if (!pass) {
|
|
180
|
-
res.status(404).end();
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
!isAuthenticationTokenCorrect(
|
|
186
|
-
req,
|
|
187
|
-
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
188
|
-
)
|
|
189
|
-
) {
|
|
190
|
-
res.status(401).end();
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const { updated, created } = pass;
|
|
195
|
-
|
|
196
|
-
const lastModifiedDate = new Date(updated || created);
|
|
197
|
-
lastModifiedDate.setMilliseconds(0);
|
|
198
|
-
|
|
199
|
-
const ifModifiedSinceDate = new Date(req.header('if-modified-since')!);
|
|
200
|
-
ifModifiedSinceDate.setMilliseconds(0);
|
|
201
|
-
|
|
202
|
-
if (ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
|
|
203
|
-
res.status(304).end();
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const fileUploadAdapter = getFileAdapter();
|
|
208
|
-
const signedUrl = await fileUploadAdapter.createDownloadURL(pass);
|
|
209
|
-
const url = signedUrl && (await modules.files.normalizeUrl(signedUrl, {}));
|
|
210
|
-
|
|
211
|
-
if (!url) {
|
|
212
|
-
res.status(500);
|
|
213
|
-
res.send({
|
|
214
|
-
success: false,
|
|
215
|
-
message: 'Could not create download URL',
|
|
216
|
-
name: 'URL_SIGNING_FAILED',
|
|
217
|
-
});
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const result = await fetch(url);
|
|
222
|
-
const data = await result.arrayBuffer();
|
|
223
|
-
const uint8View = new Uint8Array(data);
|
|
224
|
-
|
|
225
|
-
res.writeHead(200, {
|
|
226
|
-
'Content-Type': 'application/vnd.apple.pkpass',
|
|
227
|
-
'Last-Modified': lastModifiedDate.toUTCString(),
|
|
228
|
-
});
|
|
229
|
-
res.send(uint8View);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
res.status(404).end();
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
export default appleWalletHandler;
|