@onesub/server 0.6.1 → 0.6.3
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 +63 -0
- package/dist/__tests__/apple-root-ca.test.d.ts +2 -0
- package/dist/__tests__/apple-root-ca.test.d.ts.map +1 -0
- package/dist/__tests__/apple-root-ca.test.js +41 -0
- package/dist/__tests__/apple-root-ca.test.js.map +1 -0
- package/dist/__tests__/schema.test.d.ts +2 -0
- package/dist/__tests__/schema.test.d.ts.map +1 -0
- package/dist/__tests__/schema.test.js +42 -0
- package/dist/__tests__/schema.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +20 -0
- package/dist/logger.js.map +1 -0
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/providers/apple.js +14 -13
- package/dist/providers/apple.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +12 -11
- package/dist/providers/google.js.map +1 -1
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/routes/purchase.js +29 -32
- package/dist/routes/purchase.js.map +1 -1
- package/dist/routes/status.d.ts.map +1 -1
- package/dist/routes/status.js +2 -1
- package/dist/routes/status.js.map +1 -1
- package/dist/routes/validate.d.ts.map +1 -1
- package/dist/routes/validate.js +2 -1
- package/dist/routes/validate.js.map +1 -1
- package/dist/routes/webhook.d.ts +21 -0
- package/dist/routes/webhook.d.ts.map +1 -1
- package/dist/routes/webhook.js +28 -6
- package/dist/routes/webhook.js.map +1 -1
- package/dist/stores/postgres.d.ts.map +1 -1
- package/dist/stores/postgres.js +7 -38
- package/dist/stores/postgres.js.map +1 -1
- package/dist/stores/schema.d.ts +14 -0
- package/dist/stores/schema.d.ts.map +1 -0
- package/dist/stores/schema.js +50 -0
- package/dist/stores/schema.js.map +1 -0
- package/package.json +4 -3
- package/sql/schema.sql +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @onesub/server
|
|
2
|
+
|
|
3
|
+
Express middleware for Apple StoreKit 2 + Google Play Billing receipt validation, webhooks, and subscription/purchase storage. One line to mount.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @onesub/server
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import express from 'express';
|
|
13
|
+
import { createOneSubMiddleware, PostgresSubscriptionStore, PostgresPurchaseStore } from '@onesub/server';
|
|
14
|
+
|
|
15
|
+
const app = express();
|
|
16
|
+
|
|
17
|
+
app.use(createOneSubMiddleware({
|
|
18
|
+
apple: { bundleId: 'com.yourapp.id', sharedSecret: process.env.APPLE_SHARED_SECRET },
|
|
19
|
+
google: { packageName: 'com.yourapp.id', serviceAccountKey: process.env.GOOGLE_SERVICE_ACCOUNT_KEY },
|
|
20
|
+
database: { url: process.env.DATABASE_URL },
|
|
21
|
+
store: new PostgresSubscriptionStore(process.env.DATABASE_URL),
|
|
22
|
+
purchaseStore: new PostgresPurchaseStore(process.env.DATABASE_URL),
|
|
23
|
+
// Optional:
|
|
24
|
+
adminSecret: process.env.ADMIN_SECRET, // enables /onesub/purchase/admin/*
|
|
25
|
+
logger: require('pino')(), // any { info, warn, error } logger
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
app.listen(4100);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Endpoints
|
|
32
|
+
|
|
33
|
+
| Route | Purpose |
|
|
34
|
+
|------|---------|
|
|
35
|
+
| `POST /onesub/validate` | Verify Apple/Google subscription receipt |
|
|
36
|
+
| `GET /onesub/status?userId=` | Check subscription state |
|
|
37
|
+
| `POST /onesub/webhook/apple` | App Store Server Notifications V2 |
|
|
38
|
+
| `POST /onesub/webhook/google` | Google Play RTDN (Pub/Sub push) |
|
|
39
|
+
| `POST /onesub/purchase/validate` | Verify one-time purchase (consumable / non-consumable) |
|
|
40
|
+
| `GET /onesub/purchase/status?userId=` | List user's one-time purchases |
|
|
41
|
+
| `DELETE /onesub/purchase/admin/:userId/:productId` | Wipe a non-consumable (requires `adminSecret`) |
|
|
42
|
+
| `POST /onesub/purchase/admin/grant` | Manually grant a purchase (requires `adminSecret`) |
|
|
43
|
+
| `POST /onesub/purchase/admin/transfer` | Reassign a `transactionId` to a new `userId` (requires `adminSecret`) |
|
|
44
|
+
|
|
45
|
+
## Schema
|
|
46
|
+
|
|
47
|
+
Canonical Postgres DDL shipped at [`sql/schema.sql`](./sql/schema.sql). Apply with `psql -f` or let `store.initSchema()` run it for you on startup.
|
|
48
|
+
|
|
49
|
+
## Security
|
|
50
|
+
|
|
51
|
+
- Apple JWS signature verified end-to-end against **Apple Root CA G3** (as of `0.6.0`)
|
|
52
|
+
- Google RTDN: `Authorization: Bearer` JWT verified against Google JWKS when `pushAudience` is configured
|
|
53
|
+
- `transactionId` ownership enforced — same receipt can't be reused across users (`0.5.0+`)
|
|
54
|
+
- zod input validation + 50 KB body cap
|
|
55
|
+
- Full write-up: [`docs/SECURITY.md`](../../docs/SECURITY.md)
|
|
56
|
+
|
|
57
|
+
## Links
|
|
58
|
+
|
|
59
|
+
- Repo: <https://github.com/jeonghwanko/onesub>
|
|
60
|
+
- Migration guide: [`docs/MIGRATION.md`](../../docs/MIGRATION.md)
|
|
61
|
+
- Changelog: [`CHANGELOG.md`](../../CHANGELOG.md)
|
|
62
|
+
|
|
63
|
+
MIT © onesub contributors.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple-root-ca.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/apple-root-ca.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { X509Certificate } from 'node:crypto';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { APPLE_ROOT_CA_G3_PEM, APPLE_ROOT_CA_PEMS, } from '../providers/apple-root-ca.js';
|
|
4
|
+
/**
|
|
5
|
+
* Protects against accidental deletion when Apple publishes Root CA G4 and
|
|
6
|
+
* someone "replaces" G3 instead of appending. StoreKit 2 receipts signed
|
|
7
|
+
* under the older root must keep validating after G4 ships.
|
|
8
|
+
*/
|
|
9
|
+
describe('Apple Root CA bundle', () => {
|
|
10
|
+
it('includes at least one root', () => {
|
|
11
|
+
expect(APPLE_ROOT_CA_PEMS.length).toBeGreaterThan(0);
|
|
12
|
+
});
|
|
13
|
+
it('includes G3 verbatim (retained even after G4 is added)', () => {
|
|
14
|
+
expect(APPLE_ROOT_CA_PEMS).toContain(APPLE_ROOT_CA_G3_PEM);
|
|
15
|
+
});
|
|
16
|
+
it('every bundled root is a parseable X.509 certificate', () => {
|
|
17
|
+
for (const pem of APPLE_ROOT_CA_PEMS) {
|
|
18
|
+
expect(() => new X509Certificate(pem)).not.toThrow();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
it('every bundled root is currently within its validity window', () => {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
for (const pem of APPLE_ROOT_CA_PEMS) {
|
|
24
|
+
const cert = new X509Certificate(pem);
|
|
25
|
+
const notBefore = Date.parse(cert.validFrom);
|
|
26
|
+
const notAfter = Date.parse(cert.validTo);
|
|
27
|
+
expect(notBefore).toBeLessThanOrEqual(now);
|
|
28
|
+
expect(notAfter).toBeGreaterThan(now);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
it('G3 is self-signed by Apple (issuer matches subject)', () => {
|
|
32
|
+
const cert = new X509Certificate(APPLE_ROOT_CA_G3_PEM);
|
|
33
|
+
expect(cert.issuer).toBe(cert.subject);
|
|
34
|
+
expect(cert.subject).toContain('Apple Root CA - G3');
|
|
35
|
+
});
|
|
36
|
+
it('no duplicate PEMs in the bundle', () => {
|
|
37
|
+
const set = new Set(APPLE_ROOT_CA_PEMS);
|
|
38
|
+
expect(set.size).toBe(APPLE_ROOT_CA_PEMS.length);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=apple-root-ca.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple-root-ca.test.js","sourceRoot":"","sources":["../../src/__tests__/apple-root-ca.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC;;;;GAIG;AACH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,kBAAkB,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACrC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM,CAAC,SAAS,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;YAC3C,MAAM,CAAC,QAAQ,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC,oBAAoB,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { SUBSCRIPTIONS_SCHEMA_SQL, PURCHASES_SCHEMA_SQL } from '../stores/schema.js';
|
|
6
|
+
/**
|
|
7
|
+
* Parity check between the canonical SQL file (sql/schema.sql) and the
|
|
8
|
+
* embedded string constants used by initSchema() at runtime. If this fails,
|
|
9
|
+
* one of the two was edited without the other.
|
|
10
|
+
*/
|
|
11
|
+
describe('schema SQL parity', () => {
|
|
12
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
// __tests__ lives under src/, so sql/schema.sql is three levels up.
|
|
14
|
+
const schemaPath = join(here, '..', '..', 'sql', 'schema.sql');
|
|
15
|
+
const sqlFile = readFileSync(schemaPath, 'utf8');
|
|
16
|
+
// Normalize both sides to compare by semantics rather than whitespace:
|
|
17
|
+
// strip SQL line comments, collapse whitespace, lowercase.
|
|
18
|
+
const normalize = (sql) => sql
|
|
19
|
+
.split('\n')
|
|
20
|
+
.map((l) => l.replace(/--.*$/, ''))
|
|
21
|
+
.join('\n')
|
|
22
|
+
.replace(/\s+/g, ' ')
|
|
23
|
+
.trim()
|
|
24
|
+
.toLowerCase();
|
|
25
|
+
it('contains both table DDLs in the canonical .sql file', () => {
|
|
26
|
+
const n = normalize(sqlFile);
|
|
27
|
+
expect(n).toContain('create table if not exists onesub_subscriptions');
|
|
28
|
+
expect(n).toContain('create table if not exists onesub_purchases');
|
|
29
|
+
});
|
|
30
|
+
it('subscriptions DDL constant appears verbatim in schema.sql', () => {
|
|
31
|
+
expect(normalize(sqlFile)).toContain(normalize(SUBSCRIPTIONS_SCHEMA_SQL));
|
|
32
|
+
});
|
|
33
|
+
it('purchases DDL constant appears verbatim in schema.sql', () => {
|
|
34
|
+
expect(normalize(sqlFile)).toContain(normalize(PURCHASES_SCHEMA_SQL));
|
|
35
|
+
});
|
|
36
|
+
it('non-consumable partial unique index is present (0.2.0 semantics)', () => {
|
|
37
|
+
const n = normalize(sqlFile);
|
|
38
|
+
expect(n).toContain('create unique index if not exists idx_onesub_purchases_non_consumable');
|
|
39
|
+
expect(n).toContain("where type = 'non_consumable'");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=schema.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAErF;;;;GAIG;AACH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,oEAAoE;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEjD,uEAAuE;IACvE,2DAA2D;IAC3D,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAChC,GAAG;SACA,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;SAClC,IAAI,CAAC,IAAI,CAAC;SACV,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE;SACN,WAAW,EAAE,CAAC;IAEnB,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAC;QACvE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,6CAA6C,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,uEAAuE,CAAC,CAAC;QAC7F,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -49,5 +49,6 @@ export type { SubscriptionStore, PurchaseStore } from './store.js';
|
|
|
49
49
|
export { PostgresSubscriptionStore, PostgresPurchaseStore } from './stores/postgres.js';
|
|
50
50
|
export { validateAppleReceipt } from './providers/apple.js';
|
|
51
51
|
export { validateGoogleReceipt } from './providers/google.js';
|
|
52
|
+
export { setLogger, log } from './logger.js';
|
|
52
53
|
export default createOneSubMiddleware;
|
|
53
54
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQnE;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE;;;OAGG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAqB7E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAW7F;AAGD,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAe,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createStatusRouter } from './routes/status.js';
|
|
|
6
6
|
import { createWebhookRouter } from './routes/webhook.js';
|
|
7
7
|
import { createPurchaseRouter } from './routes/purchase.js';
|
|
8
8
|
import { createAdminRouter } from './routes/admin.js';
|
|
9
|
+
import { setLogger } from './logger.js';
|
|
9
10
|
/**
|
|
10
11
|
* Create an Express Router with all OneSub routes mounted.
|
|
11
12
|
*
|
|
@@ -24,6 +25,7 @@ import { createAdminRouter } from './routes/admin.js';
|
|
|
24
25
|
* POST /onesub/webhook/google
|
|
25
26
|
*/
|
|
26
27
|
export function createOneSubMiddleware(config) {
|
|
28
|
+
setLogger(config.logger);
|
|
27
29
|
const store = config.store ?? new InMemorySubscriptionStore();
|
|
28
30
|
const purchaseStore = config.purchaseStore ?? new InMemoryPurchaseStore();
|
|
29
31
|
const router = Router();
|
|
@@ -62,6 +64,9 @@ export { PostgresSubscriptionStore, PostgresPurchaseStore } from './stores/postg
|
|
|
62
64
|
// Provider functions for direct (non-HTTP) usage
|
|
63
65
|
export { validateAppleReceipt } from './providers/apple.js';
|
|
64
66
|
export { validateGoogleReceipt } from './providers/google.js';
|
|
67
|
+
// Logger plumbing — expose so non-middleware callers (direct provider use)
|
|
68
|
+
// can still redirect logs.
|
|
69
|
+
export { setLogger, log } from './logger.js';
|
|
65
70
|
// Default export: the middleware factory
|
|
66
71
|
export default createOneSubMiddleware;
|
|
67
72
|
// Allow running directly: node dist/index.js
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAcnD,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,CAEhE;AAED,eAAO,MAAM,GAAG,EAAE,YAIjB,CAAC"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide logger used by @onesub/server's providers and routes.
|
|
3
|
+
*
|
|
4
|
+
* Defaults to `console`. `createOneSubMiddleware()` calls `setLogger()` once
|
|
5
|
+
* during setup if `config.logger` is provided. All internal call sites should
|
|
6
|
+
* import `log` from this module instead of calling `console.*` directly so
|
|
7
|
+
* operators can redirect logs (pino / winston / bunyan) with a single config
|
|
8
|
+
* setting.
|
|
9
|
+
*/
|
|
10
|
+
let current = console;
|
|
11
|
+
export function setLogger(logger) {
|
|
12
|
+
if (logger)
|
|
13
|
+
current = logger;
|
|
14
|
+
}
|
|
15
|
+
export const log = {
|
|
16
|
+
info: (...args) => current.info(...args),
|
|
17
|
+
warn: (...args) => current.warn(...args),
|
|
18
|
+
error: (...args) => current.error(...args),
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,IAAI,OAAO,GAAiB,OAAO,CAAC;AAEpC,MAAM,UAAU,SAAS,CAAC,MAAgC;IACxD,IAAI,MAAM;QAAE,OAAO,GAAG,MAAM,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAiB;IAC/B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;CAC3C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAKrG,KAAK,WAAW,GAAG,WAAW,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;AA2F5D;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,UAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAuBpF;AAsBD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAmDlC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAsB,8BAA8B,CAClD,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,WAAW,EACnB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA+EpC;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,EACjC,mBAAmB,UAAQ,GAC1B,OAAO,CAAC;IAAE,qBAAqB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA6B9H"}
|
package/dist/providers/apple.js
CHANGED
|
@@ -2,6 +2,7 @@ import { decodeJwt, decodeProtectedHeader, importX509, jwtVerify } from 'jose';
|
|
|
2
2
|
import { X509Certificate } from 'node:crypto';
|
|
3
3
|
import { SUBSCRIPTION_STATUS } from '@onesub/shared';
|
|
4
4
|
import { APPLE_ROOT_CA_PEMS } from './apple-root-ca.js';
|
|
5
|
+
import { log } from '../logger.js';
|
|
5
6
|
/** Maximum age for consumable receipts (72 hours). Older receipts may indicate replay attacks. */
|
|
6
7
|
const MAX_CONSUMABLE_RECEIPT_AGE_MS = 72 * 60 * 60 * 1000;
|
|
7
8
|
function derBase64ToPem(der) {
|
|
@@ -71,7 +72,7 @@ function verifyAppleCertChain(x5c) {
|
|
|
71
72
|
export async function decodeJws(jws, skipVerification = false) {
|
|
72
73
|
if (skipVerification) {
|
|
73
74
|
if (process.env['NODE_ENV'] === 'production') {
|
|
74
|
-
|
|
75
|
+
log.warn('[onesub/apple] WARNING: skipJwsVerification is enabled in production. ' +
|
|
75
76
|
'JWS signatures are NOT being verified. This is a security risk.');
|
|
76
77
|
}
|
|
77
78
|
return decodeJwt(jws);
|
|
@@ -118,7 +119,7 @@ export async function validateAppleReceipt(receipt, config) {
|
|
|
118
119
|
catch (err) {
|
|
119
120
|
const preview = receipt.slice(0, 60);
|
|
120
121
|
const parts = receipt.split('.').length;
|
|
121
|
-
|
|
122
|
+
log.warn('[onesub/apple] Failed to decode receipt as JWS:', err?.message ?? err, `| preview: "${preview}..." (len=${receipt.length}, parts=${parts})`);
|
|
122
123
|
return null;
|
|
123
124
|
}
|
|
124
125
|
if (!tx.originalTransactionId || !tx.productId || !tx.expiresDate) {
|
|
@@ -126,7 +127,7 @@ export async function validateAppleReceipt(receipt, config) {
|
|
|
126
127
|
}
|
|
127
128
|
// Validate bundle ID — missing or mismatched both rejected
|
|
128
129
|
if (!tx.bundleId || tx.bundleId !== config.bundleId) {
|
|
129
|
-
|
|
130
|
+
log.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);
|
|
130
131
|
return null;
|
|
131
132
|
}
|
|
132
133
|
// Reject Sandbox receipts in production unless explicitly allowed.
|
|
@@ -135,7 +136,7 @@ export async function validateAppleReceipt(receipt, config) {
|
|
|
135
136
|
if (process.env['NODE_ENV'] === 'production' &&
|
|
136
137
|
tx.environment !== 'Production' &&
|
|
137
138
|
process.env['ONESUB_ALLOW_SANDBOX'] !== 'true') {
|
|
138
|
-
|
|
139
|
+
log.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);
|
|
139
140
|
return null;
|
|
140
141
|
}
|
|
141
142
|
const status = deriveStatus(tx, null);
|
|
@@ -170,17 +171,17 @@ export async function validateAppleConsumableReceipt(signedTransaction, config,
|
|
|
170
171
|
const preview = signedTransaction.slice(0, 60);
|
|
171
172
|
const parts = signedTransaction.split('.').length;
|
|
172
173
|
const looksLikeJws = parts === 3;
|
|
173
|
-
|
|
174
|
+
log.warn('[onesub/apple] Failed to decode consumable JWS:', err?.message ?? err, `| receipt preview: "${preview}..." (len=${signedTransaction.length}, parts=${parts}, looksLikeJws=${looksLikeJws})`);
|
|
174
175
|
return null;
|
|
175
176
|
}
|
|
176
177
|
// bundleId must be present and match
|
|
177
178
|
if (!tx.bundleId || tx.bundleId !== config.bundleId) {
|
|
178
|
-
|
|
179
|
+
log.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);
|
|
179
180
|
return null;
|
|
180
181
|
}
|
|
181
182
|
// Must be a one-time purchase type (not a subscription)
|
|
182
183
|
if (tx.type !== 'Consumable' && tx.type !== 'Non-Consumable') {
|
|
183
|
-
|
|
184
|
+
log.warn('[onesub/apple] Invalid purchase type for product validation:', tx.type);
|
|
184
185
|
return null;
|
|
185
186
|
}
|
|
186
187
|
// Reject Sandbox receipts in production unless explicitly allowed.
|
|
@@ -189,25 +190,25 @@ export async function validateAppleConsumableReceipt(signedTransaction, config,
|
|
|
189
190
|
if (process.env['NODE_ENV'] === 'production' &&
|
|
190
191
|
tx.environment !== 'Production' &&
|
|
191
192
|
process.env['ONESUB_ALLOW_SANDBOX'] !== 'true') {
|
|
192
|
-
|
|
193
|
+
log.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);
|
|
193
194
|
return null;
|
|
194
195
|
}
|
|
195
196
|
if (!tx.productId) {
|
|
196
|
-
|
|
197
|
+
log.warn('[onesub/apple] No productId in transaction');
|
|
197
198
|
return null;
|
|
198
199
|
}
|
|
199
200
|
if (expectedProductId && tx.productId !== expectedProductId) {
|
|
200
|
-
|
|
201
|
+
log.warn('[onesub/apple] Product ID mismatch:', tx.productId, '!==', expectedProductId);
|
|
201
202
|
return null;
|
|
202
203
|
}
|
|
203
204
|
// Reject refunded purchases
|
|
204
205
|
if (tx.revocationDate) {
|
|
205
|
-
|
|
206
|
+
log.warn('[onesub/apple] Purchase was revoked/refunded');
|
|
206
207
|
return null;
|
|
207
208
|
}
|
|
208
209
|
// Reject receipts older than 72 hours (replay attack prevention)
|
|
209
210
|
if (tx.purchaseDate && Date.now() - tx.purchaseDate > MAX_CONSUMABLE_RECEIPT_AGE_MS) {
|
|
210
|
-
|
|
211
|
+
log.warn('[onesub/apple] Consumable receipt too old (>72h)');
|
|
211
212
|
return null;
|
|
212
213
|
}
|
|
213
214
|
// For consumables, transactionId is per-purchase unique.
|
|
@@ -215,7 +216,7 @@ export async function validateAppleConsumableReceipt(signedTransaction, config,
|
|
|
215
216
|
// as the deduplication key for consumables.
|
|
216
217
|
const transactionId = tx.transactionId ?? tx.originalTransactionId;
|
|
217
218
|
if (!transactionId) {
|
|
218
|
-
|
|
219
|
+
log.warn('[onesub/apple] No transactionId in consumable transaction');
|
|
219
220
|
return null;
|
|
220
221
|
}
|
|
221
222
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple.js","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"apple.js","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAItG,KAAK,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAuB9D;;;GAGG;AACH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;CAcpB,CAAC;AAEX,KAAK,sBAAsB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,OAAO,wBAAwB,CAAC,CAAC;AAkLvG;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAmBD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA6BlC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,YAAY,GAAG,gBAAmC,GACvD,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAgDrC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG;IAC5E,gBAAgB,EAAE,sBAAsB,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,IAAI,CAuBP;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAQ5F;AAED,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAK9F;AAED,wBAAgB,2BAA2B,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,OAAO,CAE7F"}
|
package/dist/providers/google.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SUBSCRIPTION_STATUS } from '@onesub/shared';
|
|
2
|
+
import { log } from '../logger.js';
|
|
2
3
|
/**
|
|
3
4
|
* Google RTDN notification types.
|
|
4
5
|
* https://developer.android.com/google/play/billing/rtdn-reference
|
|
@@ -144,7 +145,7 @@ export async function consumeGoogleProductReceipt(purchaseToken, productId, conf
|
|
|
144
145
|
accessToken = await getCachedAccessToken(config.serviceAccountKey);
|
|
145
146
|
}
|
|
146
147
|
catch (err) {
|
|
147
|
-
|
|
148
|
+
log.warn('[onesub/google] Could not get access token for consume:', err);
|
|
148
149
|
return;
|
|
149
150
|
}
|
|
150
151
|
const url = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +
|
|
@@ -157,11 +158,11 @@ export async function consumeGoogleProductReceipt(purchaseToken, productId, conf
|
|
|
157
158
|
});
|
|
158
159
|
if (!resp.ok) {
|
|
159
160
|
const body = await resp.text();
|
|
160
|
-
|
|
161
|
+
log.warn(`[onesub/google] Consume API error ${resp.status}: ${body} — auto-refund risk`);
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
164
|
catch (err) {
|
|
164
|
-
|
|
165
|
+
log.warn('[onesub/google] Consume network error — auto-refund risk:', err);
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
/**
|
|
@@ -187,7 +188,7 @@ function deriveStatus(purchase) {
|
|
|
187
188
|
*/
|
|
188
189
|
export async function validateGoogleReceipt(receipt, productId, config) {
|
|
189
190
|
if (!config.serviceAccountKey) {
|
|
190
|
-
|
|
191
|
+
log.warn('[onesub/google] No serviceAccountKey provided — cannot call Play API');
|
|
191
192
|
return null;
|
|
192
193
|
}
|
|
193
194
|
let purchase;
|
|
@@ -196,7 +197,7 @@ export async function validateGoogleReceipt(receipt, productId, config) {
|
|
|
196
197
|
purchase = await fetchSubscriptionPurchase(config.packageName, productId, receipt, token);
|
|
197
198
|
}
|
|
198
199
|
catch (err) {
|
|
199
|
-
|
|
200
|
+
log.error('[onesub/google] Receipt validation failed:', err);
|
|
200
201
|
return null;
|
|
201
202
|
}
|
|
202
203
|
const status = deriveStatus(purchase);
|
|
@@ -225,7 +226,7 @@ export async function validateGoogleReceipt(receipt, productId, config) {
|
|
|
225
226
|
*/
|
|
226
227
|
export async function validateGoogleProductReceipt(purchaseToken, productId, config, type = 'non_consumable') {
|
|
227
228
|
if (!config.serviceAccountKey) {
|
|
228
|
-
|
|
229
|
+
log.warn('[onesub/google] No serviceAccountKey — cannot validate product receipt');
|
|
229
230
|
return null;
|
|
230
231
|
}
|
|
231
232
|
let purchase;
|
|
@@ -234,30 +235,30 @@ export async function validateGoogleProductReceipt(purchaseToken, productId, con
|
|
|
234
235
|
purchase = await fetchProductPurchase(config.packageName, productId, purchaseToken, token);
|
|
235
236
|
}
|
|
236
237
|
catch (err) {
|
|
237
|
-
|
|
238
|
+
log.error('[onesub/google] Product receipt validation failed:', err);
|
|
238
239
|
return null;
|
|
239
240
|
}
|
|
240
241
|
// purchaseState 0 = completed (1 = canceled, 2 = pending)
|
|
241
242
|
if (purchase.purchaseState !== 0) {
|
|
242
|
-
|
|
243
|
+
log.warn('[onesub/google] Purchase not completed, state:', purchase.purchaseState);
|
|
243
244
|
return null;
|
|
244
245
|
}
|
|
245
246
|
// For consumables: consumptionState 1 means already consumed by a previous request.
|
|
246
247
|
// This is the primary replay-attack signal for consumables on Android.
|
|
247
248
|
if (type === 'consumable' && purchase.consumptionState === 1) {
|
|
248
|
-
|
|
249
|
+
log.warn('[onesub/google] Consumable already consumed — possible replay attack');
|
|
249
250
|
return null;
|
|
250
251
|
}
|
|
251
252
|
// Reject receipts older than 72 hours
|
|
252
253
|
if (purchase.purchaseTimeMillis) {
|
|
253
254
|
const purchaseTime = parseInt(purchase.purchaseTimeMillis, 10);
|
|
254
255
|
if (Date.now() - purchaseTime > MAX_PRODUCT_RECEIPT_AGE_MS) {
|
|
255
|
-
|
|
256
|
+
log.warn('[onesub/google] Product receipt too old (>72h)');
|
|
256
257
|
return null;
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
if (!purchase.orderId) {
|
|
260
|
-
|
|
261
|
+
log.warn('[onesub/google] No orderId in product purchase');
|
|
261
262
|
return null;
|
|
262
263
|
}
|
|
263
264
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"purchase.d.ts","sourceRoot":"","sources":["../../src/routes/purchase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAIV,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"purchase.d.ts","sourceRoot":"","sources":["../../src/routes/purchase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAIV,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBjD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,aAAa,EAAE,aAAa,GAC3B,MAAM,CAwNR"}
|