@onesub/server 0.12.1 → 0.13.1
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/dist/__tests__/admin-customer-detail.test.js +146 -0
- package/dist/__tests__/admin-customer-detail.test.js.map +1 -0
- package/dist/__tests__/admin-list-subscriptions.test.js +192 -0
- package/dist/__tests__/admin-list-subscriptions.test.js.map +1 -0
- package/dist/__tests__/admin-subscription-detail.test.js +112 -0
- package/dist/__tests__/admin-subscription-detail.test.js.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__/apple-status-api.test.js +345 -0
- package/dist/__tests__/apple-status-api.test.js.map +1 -0
- package/dist/__tests__/cache.test.js +34 -0
- package/dist/__tests__/cache.test.js.map +1 -0
- package/dist/__tests__/entitlements.test.js +268 -0
- package/dist/__tests__/entitlements.test.js.map +1 -0
- package/dist/__tests__/error-codes.test.js +173 -0
- package/dist/__tests__/error-codes.test.js.map +1 -0
- package/dist/__tests__/fetch-hardening.test.js +192 -0
- package/dist/__tests__/fetch-hardening.test.js.map +1 -0
- package/dist/__tests__/google-paused.test.js +126 -0
- package/dist/__tests__/google-paused.test.js.map +1 -0
- package/dist/__tests__/google-price-change.test.js +186 -0
- package/dist/__tests__/google-price-change.test.js.map +1 -0
- package/dist/__tests__/google-subscriptions-v2.test.js +206 -0
- package/dist/__tests__/google-subscriptions-v2.test.js.map +1 -0
- package/dist/__tests__/lifecycle-scenarios.test.js +579 -0
- package/dist/__tests__/lifecycle-scenarios.test.js.map +1 -0
- package/dist/__tests__/lifecycle-states.test.js +423 -0
- package/dist/__tests__/lifecycle-states.test.js.map +1 -0
- package/dist/__tests__/linked-purchase-token.test.js +227 -0
- package/dist/__tests__/linked-purchase-token.test.js.map +1 -0
- package/dist/__tests__/metrics.test.js +340 -0
- package/dist/__tests__/metrics.test.js.map +1 -0
- package/dist/__tests__/mock-provider.test.js +188 -0
- package/dist/__tests__/mock-provider.test.js.map +1 -0
- package/dist/__tests__/openapi.test.js +32 -0
- package/dist/__tests__/openapi.test.js.map +1 -0
- package/dist/__tests__/paused-auto-resume.test.js +162 -0
- package/dist/__tests__/paused-auto-resume.test.js.map +1 -0
- package/dist/__tests__/providers.test.js +321 -0
- package/dist/__tests__/providers.test.js.map +1 -0
- package/dist/__tests__/redis-store.test.js +166 -0
- package/dist/__tests__/redis-store.test.js.map +1 -0
- package/dist/__tests__/refund-policy.test.js +317 -0
- package/dist/__tests__/refund-policy.test.js.map +1 -0
- package/dist/__tests__/schema.test.js +46 -0
- package/dist/__tests__/schema.test.js.map +1 -0
- package/dist/__tests__/store.test.js +172 -0
- package/dist/__tests__/store.test.js.map +1 -0
- package/dist/__tests__/test-utils.js +30 -0
- package/dist/__tests__/test-utils.js.map +1 -0
- package/dist/__tests__/webhook-apple.test.js +247 -0
- package/dist/__tests__/webhook-apple.test.js.map +1 -0
- package/dist/__tests__/webhook-events.test.js +36 -0
- package/dist/__tests__/webhook-events.test.js.map +1 -0
- package/dist/__tests__/webhook-google.test.js +255 -0
- package/dist/__tests__/webhook-google.test.js.map +1 -0
- package/dist/__tests__/webhook-queue.test.js +26 -0
- package/dist/__tests__/webhook-queue.test.js.map +1 -0
- package/dist/__tests__/webhook-refund.test.js +313 -0
- package/dist/__tests__/webhook-refund.test.js.map +1 -0
- package/dist/cache.js +39 -0
- package/dist/cache.js.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/http.js +36 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +241 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +241 -11
- package/dist/index.js.map +1 -1
- package/dist/logger.js +20 -0
- package/dist/logger.js.map +1 -0
- package/dist/openapi.js +196 -0
- package/dist/openapi.js.map +1 -0
- package/dist/providers/apple-root-ca.js +38 -0
- package/dist/providers/apple-root-ca.js.map +1 -0
- package/dist/providers/apple.d.ts +76 -0
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/providers/apple.js +630 -0
- package/dist/providers/apple.js.map +1 -0
- package/dist/providers/google.d.ts +25 -0
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +564 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/mock.js +93 -0
- package/dist/providers/mock.js.map +1 -0
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/admin.js +327 -0
- package/dist/routes/admin.js.map +1 -0
- package/dist/routes/apple-offer.d.ts +21 -0
- package/dist/routes/apple-offer.d.ts.map +1 -0
- package/dist/routes/apple-offer.js +72 -0
- package/dist/routes/apple-offer.js.map +1 -0
- package/dist/routes/entitlements.js +130 -0
- package/dist/routes/entitlements.js.map +1 -0
- package/dist/routes/metrics.js +281 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/routes/purchase.js +204 -0
- package/dist/routes/purchase.js.map +1 -0
- package/dist/routes/status.js +55 -0
- package/dist/routes/status.js.map +1 -0
- package/dist/routes/validate.js +70 -0
- package/dist/routes/validate.js.map +1 -0
- package/dist/routes/webhook-apple.d.ts.map +1 -1
- package/dist/routes/webhook-apple.js +145 -0
- package/dist/routes/webhook-apple.js.map +1 -0
- package/dist/routes/webhook-google.d.ts.map +1 -1
- package/dist/routes/webhook-google.js +204 -0
- package/dist/routes/webhook-google.js.map +1 -0
- package/dist/routes/webhook.js +19 -0
- package/dist/routes/webhook.js.map +1 -0
- package/dist/store.js +145 -0
- package/dist/store.js.map +1 -0
- package/dist/stores/postgres.js +329 -0
- package/dist/stores/postgres.js.map +1 -0
- package/dist/stores/redis.js +297 -0
- package/dist/stores/redis.js.map +1 -0
- package/dist/stores/schema.js +67 -0
- package/dist/stores/schema.js.map +1 -0
- package/dist/tracing.js +74 -0
- package/dist/tracing.js.map +1 -0
- package/dist/webhook-events.js +58 -0
- package/dist/webhook-events.js.map +1 -0
- package/dist/webhook-queue.js +108 -0
- package/dist/webhook-queue.js.map +1 -0
- package/package.json +4 -4
package/dist/store.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory implementation — suitable for development and testing.
|
|
3
|
+
* Data is lost on process restart.
|
|
4
|
+
*/
|
|
5
|
+
export class InMemorySubscriptionStore {
|
|
6
|
+
// Multiple records per user (different originalTransactionIds) — required
|
|
7
|
+
// for entitlement evaluation across multiple productIds. Ordering is
|
|
8
|
+
// last-written-first so getByUserId returns "most recent" naturally.
|
|
9
|
+
byUserId = new Map();
|
|
10
|
+
byTransactionId = new Map();
|
|
11
|
+
async save(sub) {
|
|
12
|
+
this.byTransactionId.set(sub.originalTransactionId, sub);
|
|
13
|
+
const existing = this.byUserId.get(sub.userId) ?? [];
|
|
14
|
+
// Replace any prior record with the same originalTransactionId, then
|
|
15
|
+
// unshift to the front so the latest write is index 0 (= getByUserId result).
|
|
16
|
+
const filtered = existing.filter((s) => s.originalTransactionId !== sub.originalTransactionId);
|
|
17
|
+
filtered.unshift(sub);
|
|
18
|
+
this.byUserId.set(sub.userId, filtered);
|
|
19
|
+
}
|
|
20
|
+
async getByUserId(userId) {
|
|
21
|
+
const list = this.byUserId.get(userId);
|
|
22
|
+
return list?.[0] ?? null;
|
|
23
|
+
}
|
|
24
|
+
async getAllByUserId(userId) {
|
|
25
|
+
return [...(this.byUserId.get(userId) ?? [])];
|
|
26
|
+
}
|
|
27
|
+
async getByTransactionId(txId) {
|
|
28
|
+
return this.byTransactionId.get(txId) ?? null;
|
|
29
|
+
}
|
|
30
|
+
async listAll() {
|
|
31
|
+
return [...this.byTransactionId.values()];
|
|
32
|
+
}
|
|
33
|
+
async listFiltered(opts) {
|
|
34
|
+
const limit = opts.limit ?? 50;
|
|
35
|
+
const offset = opts.offset ?? 0;
|
|
36
|
+
// Iterate via byUserId so insertion-order is "newest first" within each
|
|
37
|
+
// user (matches PostgresStore's `updated_at DESC` semantic). Across users
|
|
38
|
+
// we collect in Map iteration order — stable for a given run.
|
|
39
|
+
const all = [];
|
|
40
|
+
for (const list of this.byUserId.values()) {
|
|
41
|
+
for (const s of list)
|
|
42
|
+
all.push(s);
|
|
43
|
+
}
|
|
44
|
+
const filtered = all.filter((s) => {
|
|
45
|
+
if (opts.userId && s.userId !== opts.userId)
|
|
46
|
+
return false;
|
|
47
|
+
if (opts.status && s.status !== opts.status)
|
|
48
|
+
return false;
|
|
49
|
+
if (opts.productId && s.productId !== opts.productId)
|
|
50
|
+
return false;
|
|
51
|
+
if (opts.platform && s.platform !== opts.platform)
|
|
52
|
+
return false;
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
items: filtered.slice(offset, offset + limit),
|
|
57
|
+
total: filtered.length,
|
|
58
|
+
limit,
|
|
59
|
+
offset,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* In-memory implementation of PurchaseStore — suitable for development and testing.
|
|
65
|
+
* Data is lost on process restart.
|
|
66
|
+
*/
|
|
67
|
+
export class InMemoryPurchaseStore {
|
|
68
|
+
byTransactionId = new Map();
|
|
69
|
+
byUserId = new Map();
|
|
70
|
+
async savePurchase(purchase) {
|
|
71
|
+
const existing = this.byTransactionId.get(purchase.transactionId);
|
|
72
|
+
if (existing) {
|
|
73
|
+
if (existing.userId !== purchase.userId) {
|
|
74
|
+
const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER');
|
|
75
|
+
err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
return; // same user — idempotent
|
|
79
|
+
}
|
|
80
|
+
this.byTransactionId.set(purchase.transactionId, purchase);
|
|
81
|
+
const list = this.byUserId.get(purchase.userId) ?? [];
|
|
82
|
+
list.push(purchase);
|
|
83
|
+
this.byUserId.set(purchase.userId, list);
|
|
84
|
+
}
|
|
85
|
+
async getPurchasesByUserId(userId) {
|
|
86
|
+
return this.byUserId.get(userId) ?? [];
|
|
87
|
+
}
|
|
88
|
+
async getPurchaseByTransactionId(txId) {
|
|
89
|
+
return this.byTransactionId.get(txId) ?? null;
|
|
90
|
+
}
|
|
91
|
+
async hasPurchased(userId, productId) {
|
|
92
|
+
const purchases = this.byUserId.get(userId);
|
|
93
|
+
if (!purchases)
|
|
94
|
+
return false;
|
|
95
|
+
return purchases.some((p) => p.productId === productId);
|
|
96
|
+
}
|
|
97
|
+
async reassignPurchase(transactionId, newUserId) {
|
|
98
|
+
const existing = this.byTransactionId.get(transactionId);
|
|
99
|
+
if (!existing)
|
|
100
|
+
return false;
|
|
101
|
+
const oldUserId = existing.userId;
|
|
102
|
+
if (oldUserId === newUserId)
|
|
103
|
+
return true;
|
|
104
|
+
const updated = { ...existing, userId: newUserId };
|
|
105
|
+
this.byTransactionId.set(transactionId, updated);
|
|
106
|
+
// remove from old userId index
|
|
107
|
+
const oldList = (this.byUserId.get(oldUserId) ?? []).filter((p) => p.transactionId !== transactionId);
|
|
108
|
+
if (oldList.length)
|
|
109
|
+
this.byUserId.set(oldUserId, oldList);
|
|
110
|
+
else
|
|
111
|
+
this.byUserId.delete(oldUserId);
|
|
112
|
+
// add to new userId index
|
|
113
|
+
const newList = this.byUserId.get(newUserId) ?? [];
|
|
114
|
+
newList.push(updated);
|
|
115
|
+
this.byUserId.set(newUserId, newList);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
async deletePurchases(userId, productId) {
|
|
119
|
+
const list = this.byUserId.get(userId) ?? [];
|
|
120
|
+
const kept = list.filter((p) => p.productId !== productId);
|
|
121
|
+
const deleted = list.length - kept.length;
|
|
122
|
+
this.byUserId.set(userId, kept);
|
|
123
|
+
for (const p of list) {
|
|
124
|
+
if (p.productId === productId)
|
|
125
|
+
this.byTransactionId.delete(p.transactionId);
|
|
126
|
+
}
|
|
127
|
+
return deleted;
|
|
128
|
+
}
|
|
129
|
+
async listAll() {
|
|
130
|
+
return [...this.byTransactionId.values()];
|
|
131
|
+
}
|
|
132
|
+
async deletePurchaseByTransactionId(transactionId) {
|
|
133
|
+
const existing = this.byTransactionId.get(transactionId);
|
|
134
|
+
if (!existing)
|
|
135
|
+
return false;
|
|
136
|
+
this.byTransactionId.delete(transactionId);
|
|
137
|
+
const list = (this.byUserId.get(existing.userId) ?? []).filter((p) => p.transactionId !== transactionId);
|
|
138
|
+
if (list.length)
|
|
139
|
+
this.byUserId.set(existing.userId, list);
|
|
140
|
+
else
|
|
141
|
+
this.byUserId.delete(existing.userId);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { SUBSCRIPTIONS_SCHEMA_SQL, PURCHASES_SCHEMA_SQL } from './schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* PostgreSQL-backed subscription store.
|
|
4
|
+
*
|
|
5
|
+
* Uses raw `pg` (node-postgres) to avoid Prisma / ORM dependencies.
|
|
6
|
+
* The `pg` package must be installed separately:
|
|
7
|
+
*
|
|
8
|
+
* npm install pg
|
|
9
|
+
* npm install -D @types/pg
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const store = new PostgresSubscriptionStore(process.env.DATABASE_URL!);
|
|
13
|
+
* await store.initSchema();
|
|
14
|
+
* app.use(createOneSubMiddleware({ ...config, store }));
|
|
15
|
+
*/
|
|
16
|
+
export class PostgresSubscriptionStore {
|
|
17
|
+
connectionString;
|
|
18
|
+
// Lazy-loaded to avoid requiring `pg` unless this class is actually instantiated.
|
|
19
|
+
poolPromise = null;
|
|
20
|
+
constructor(connectionString) {
|
|
21
|
+
this.connectionString = connectionString;
|
|
22
|
+
}
|
|
23
|
+
getPool() {
|
|
24
|
+
if (!this.poolPromise) {
|
|
25
|
+
this.poolPromise = (async () => {
|
|
26
|
+
// Dynamic import so that `pg` is an optional peer dependency —
|
|
27
|
+
// consumers who only use InMemorySubscriptionStore pay no cost.
|
|
28
|
+
const pg = await import('pg').catch(() => {
|
|
29
|
+
throw new Error('[onesub] PostgresSubscriptionStore requires the `pg` package. ' +
|
|
30
|
+
'Run: npm install pg');
|
|
31
|
+
});
|
|
32
|
+
const Pool = pg.default?.Pool ?? pg.Pool;
|
|
33
|
+
return new Pool({ connectionString: this.connectionString, max: 10 });
|
|
34
|
+
})();
|
|
35
|
+
}
|
|
36
|
+
return this.poolPromise;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Creates the `onesub_subscriptions` table and indexes if they do not
|
|
40
|
+
* already exist. Call this once during application startup.
|
|
41
|
+
*/
|
|
42
|
+
async initSchema() {
|
|
43
|
+
const pool = await this.getPool();
|
|
44
|
+
// DDL mirrored in packages/server/sql/schema.sql — kept in sync by
|
|
45
|
+
// schema parity test. Edit both (or only the .sql file and regenerate).
|
|
46
|
+
await pool.query(SUBSCRIPTIONS_SCHEMA_SQL);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Upserts the given subscription.
|
|
50
|
+
* If a row with the same `original_transaction_id` already exists it is
|
|
51
|
+
* updated in place; otherwise a new row is inserted.
|
|
52
|
+
*/
|
|
53
|
+
async save(sub) {
|
|
54
|
+
const pool = await this.getPool();
|
|
55
|
+
await pool.query(`INSERT INTO onesub_subscriptions
|
|
56
|
+
(original_transaction_id, user_id, product_id, platform, status,
|
|
57
|
+
expires_at, purchased_at, will_renew, linked_purchase_token,
|
|
58
|
+
auto_resume_time, updated_at)
|
|
59
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())
|
|
60
|
+
ON CONFLICT (original_transaction_id) DO UPDATE SET
|
|
61
|
+
user_id = EXCLUDED.user_id,
|
|
62
|
+
product_id = EXCLUDED.product_id,
|
|
63
|
+
platform = EXCLUDED.platform,
|
|
64
|
+
status = EXCLUDED.status,
|
|
65
|
+
expires_at = EXCLUDED.expires_at,
|
|
66
|
+
purchased_at = EXCLUDED.purchased_at,
|
|
67
|
+
will_renew = EXCLUDED.will_renew,
|
|
68
|
+
linked_purchase_token = EXCLUDED.linked_purchase_token,
|
|
69
|
+
auto_resume_time = EXCLUDED.auto_resume_time,
|
|
70
|
+
updated_at = NOW()`, [
|
|
71
|
+
sub.originalTransactionId,
|
|
72
|
+
sub.userId,
|
|
73
|
+
sub.productId,
|
|
74
|
+
sub.platform,
|
|
75
|
+
sub.status,
|
|
76
|
+
sub.expiresAt,
|
|
77
|
+
sub.purchasedAt,
|
|
78
|
+
sub.willRenew,
|
|
79
|
+
sub.linkedPurchaseToken ?? null,
|
|
80
|
+
sub.autoResumeTime ?? null,
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns the most recently updated subscription for the given user, or
|
|
85
|
+
* `null` if no record exists.
|
|
86
|
+
*
|
|
87
|
+
* "Most recent" is determined by `updated_at DESC` so that the latest
|
|
88
|
+
* status change is always returned when a user has multiple subscriptions.
|
|
89
|
+
*/
|
|
90
|
+
async getByUserId(userId) {
|
|
91
|
+
const pool = await this.getPool();
|
|
92
|
+
const result = await pool.query(`SELECT *
|
|
93
|
+
FROM onesub_subscriptions
|
|
94
|
+
WHERE user_id = $1
|
|
95
|
+
ORDER BY updated_at DESC
|
|
96
|
+
LIMIT 1`, [userId]);
|
|
97
|
+
return result.rows.length > 0 ? rowToSubscriptionInfo(result.rows[0]) : null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Returns every subscription record for the user (across all productIds),
|
|
101
|
+
* ordered most-recent-first. Used by entitlement evaluation, which needs to
|
|
102
|
+
* see all of a user's active subscriptions to decide whether any of them
|
|
103
|
+
* grants the requested entitlement.
|
|
104
|
+
*/
|
|
105
|
+
async getAllByUserId(userId) {
|
|
106
|
+
const pool = await this.getPool();
|
|
107
|
+
const result = await pool.query(`SELECT *
|
|
108
|
+
FROM onesub_subscriptions
|
|
109
|
+
WHERE user_id = $1
|
|
110
|
+
ORDER BY updated_at DESC`, [userId]);
|
|
111
|
+
return result.rows.map(rowToSubscriptionInfo);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Returns the subscription identified by `originalTransactionId` (the
|
|
115
|
+
* table primary key), or `null` if it does not exist.
|
|
116
|
+
*/
|
|
117
|
+
async getByTransactionId(txId) {
|
|
118
|
+
const pool = await this.getPool();
|
|
119
|
+
const result = await pool.query(`SELECT *
|
|
120
|
+
FROM onesub_subscriptions
|
|
121
|
+
WHERE original_transaction_id = $1`, [txId]);
|
|
122
|
+
return result.rows.length > 0 ? rowToSubscriptionInfo(result.rows[0]) : null;
|
|
123
|
+
}
|
|
124
|
+
async listAll() {
|
|
125
|
+
const pool = await this.getPool();
|
|
126
|
+
const result = await pool.query(`SELECT * FROM onesub_subscriptions`);
|
|
127
|
+
return result.rows.map(rowToSubscriptionInfo);
|
|
128
|
+
}
|
|
129
|
+
async listFiltered(opts) {
|
|
130
|
+
const pool = await this.getPool();
|
|
131
|
+
const limit = opts.limit ?? 50;
|
|
132
|
+
const offset = opts.offset ?? 0;
|
|
133
|
+
// Build the WHERE clause incrementally. Each non-undefined filter adds an
|
|
134
|
+
// AND condition with a parameterised value (no SQL injection surface).
|
|
135
|
+
const conditions = [];
|
|
136
|
+
const params = [];
|
|
137
|
+
if (opts.userId) {
|
|
138
|
+
params.push(opts.userId);
|
|
139
|
+
conditions.push(`user_id = $${params.length}`);
|
|
140
|
+
}
|
|
141
|
+
if (opts.status) {
|
|
142
|
+
params.push(opts.status);
|
|
143
|
+
conditions.push(`status = $${params.length}`);
|
|
144
|
+
}
|
|
145
|
+
if (opts.productId) {
|
|
146
|
+
params.push(opts.productId);
|
|
147
|
+
conditions.push(`product_id = $${params.length}`);
|
|
148
|
+
}
|
|
149
|
+
if (opts.platform) {
|
|
150
|
+
params.push(opts.platform);
|
|
151
|
+
conditions.push(`platform = $${params.length}`);
|
|
152
|
+
}
|
|
153
|
+
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
154
|
+
// Run the count + page in parallel — both queries hit the same WHERE clause
|
|
155
|
+
// so they share the index plan, and we save a round-trip.
|
|
156
|
+
const [countResult, pageResult] = await Promise.all([
|
|
157
|
+
pool.query(`SELECT COUNT(*)::text AS total FROM onesub_subscriptions ${where}`, params),
|
|
158
|
+
pool.query(`SELECT * FROM onesub_subscriptions ${where}
|
|
159
|
+
ORDER BY updated_at DESC
|
|
160
|
+
LIMIT $${params.length + 1} OFFSET $${params.length + 2}`, [...params, limit, offset]),
|
|
161
|
+
]);
|
|
162
|
+
return {
|
|
163
|
+
items: pageResult.rows.map(rowToSubscriptionInfo),
|
|
164
|
+
total: parseInt(countResult.rows[0]?.total ?? '0', 10),
|
|
165
|
+
limit,
|
|
166
|
+
offset,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Gracefully close the underlying connection pool. */
|
|
170
|
+
async close() {
|
|
171
|
+
if (this.poolPromise) {
|
|
172
|
+
const pool = await this.poolPromise;
|
|
173
|
+
await pool.end();
|
|
174
|
+
this.poolPromise = null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* PostgreSQL-backed purchase store for consumables and non-consumables.
|
|
180
|
+
*
|
|
181
|
+
* Uses raw `pg` (node-postgres). The `pg` package must be installed separately:
|
|
182
|
+
*
|
|
183
|
+
* npm install pg
|
|
184
|
+
* npm install -D @types/pg
|
|
185
|
+
*
|
|
186
|
+
* Usage:
|
|
187
|
+
* const store = new PostgresPurchaseStore(process.env.DATABASE_URL!);
|
|
188
|
+
* await store.initSchema();
|
|
189
|
+
* app.use(createOneSubMiddleware({ ...config, purchaseStore: store }));
|
|
190
|
+
*/
|
|
191
|
+
export class PostgresPurchaseStore {
|
|
192
|
+
connectionString;
|
|
193
|
+
poolPromise = null;
|
|
194
|
+
constructor(connectionString) {
|
|
195
|
+
this.connectionString = connectionString;
|
|
196
|
+
}
|
|
197
|
+
getPool() {
|
|
198
|
+
if (!this.poolPromise) {
|
|
199
|
+
this.poolPromise = (async () => {
|
|
200
|
+
const pg = await import('pg').catch(() => {
|
|
201
|
+
throw new Error('[onesub] PostgresPurchaseStore requires the `pg` package. ' +
|
|
202
|
+
'Run: npm install pg');
|
|
203
|
+
});
|
|
204
|
+
const Pool = pg.default?.Pool ?? pg.Pool;
|
|
205
|
+
return new Pool({ connectionString: this.connectionString, max: 10 });
|
|
206
|
+
})();
|
|
207
|
+
}
|
|
208
|
+
return this.poolPromise;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Creates the `onesub_purchases` table and indexes if they do not already exist.
|
|
212
|
+
* Call this once during application startup.
|
|
213
|
+
*/
|
|
214
|
+
async initSchema() {
|
|
215
|
+
const pool = await this.getPool();
|
|
216
|
+
// DDL mirrored in packages/server/sql/schema.sql — kept in sync by
|
|
217
|
+
// schema parity test.
|
|
218
|
+
await pool.query(PURCHASES_SCHEMA_SQL);
|
|
219
|
+
}
|
|
220
|
+
async savePurchase(purchase) {
|
|
221
|
+
const pool = await this.getPool();
|
|
222
|
+
// transactionId is PRIMARY KEY. If the same transactionId already belongs
|
|
223
|
+
// to a different userId we refuse the save — the second call is either a
|
|
224
|
+
// fraudulent receipt-reuse attempt or a legitimate device migration that
|
|
225
|
+
// should go through an explicit admin transfer instead. Returning silently
|
|
226
|
+
// (the old ON CONFLICT DO NOTHING behavior) leaves the server reporting
|
|
227
|
+
// valid:true while the DB has no row for the current user, which lets the
|
|
228
|
+
// caller keep re-"buying" forever.
|
|
229
|
+
const existing = await pool.query(`SELECT user_id FROM onesub_purchases WHERE transaction_id = $1`, [purchase.transactionId]);
|
|
230
|
+
if (existing.rows.length > 0) {
|
|
231
|
+
const owner = existing.rows[0].user_id;
|
|
232
|
+
if (owner !== purchase.userId) {
|
|
233
|
+
const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER');
|
|
234
|
+
err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
// same user — idempotent no-op
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await pool.query(`INSERT INTO onesub_purchases
|
|
241
|
+
(transaction_id, user_id, product_id, platform, type, quantity, purchased_at)
|
|
242
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
|
|
243
|
+
purchase.transactionId,
|
|
244
|
+
purchase.userId,
|
|
245
|
+
purchase.productId,
|
|
246
|
+
purchase.platform,
|
|
247
|
+
purchase.type,
|
|
248
|
+
purchase.quantity,
|
|
249
|
+
purchase.purchasedAt,
|
|
250
|
+
]);
|
|
251
|
+
}
|
|
252
|
+
async getPurchasesByUserId(userId) {
|
|
253
|
+
const pool = await this.getPool();
|
|
254
|
+
const result = await pool.query(`SELECT *
|
|
255
|
+
FROM onesub_purchases
|
|
256
|
+
WHERE user_id = $1
|
|
257
|
+
ORDER BY purchased_at DESC`, [userId]);
|
|
258
|
+
return result.rows.map(rowToPurchaseInfo);
|
|
259
|
+
}
|
|
260
|
+
async getPurchaseByTransactionId(txId) {
|
|
261
|
+
const pool = await this.getPool();
|
|
262
|
+
const result = await pool.query(`SELECT *
|
|
263
|
+
FROM onesub_purchases
|
|
264
|
+
WHERE transaction_id = $1`, [txId]);
|
|
265
|
+
return result.rows.length > 0 ? rowToPurchaseInfo(result.rows[0]) : null;
|
|
266
|
+
}
|
|
267
|
+
async hasPurchased(userId, productId) {
|
|
268
|
+
const pool = await this.getPool();
|
|
269
|
+
const result = await pool.query(`SELECT EXISTS(
|
|
270
|
+
SELECT 1 FROM onesub_purchases
|
|
271
|
+
WHERE user_id = $1 AND product_id = $2
|
|
272
|
+
) AS exists`, [userId, productId]);
|
|
273
|
+
return result.rows[0]?.exists === true;
|
|
274
|
+
}
|
|
275
|
+
async reassignPurchase(transactionId, newUserId) {
|
|
276
|
+
const pool = await this.getPool();
|
|
277
|
+
const result = await pool.query(`UPDATE onesub_purchases SET user_id = $2 WHERE transaction_id = $1`, [transactionId, newUserId]);
|
|
278
|
+
return (result.rowCount ?? 0) > 0;
|
|
279
|
+
}
|
|
280
|
+
async deletePurchases(userId, productId) {
|
|
281
|
+
const pool = await this.getPool();
|
|
282
|
+
const result = await pool.query(`DELETE FROM onesub_purchases WHERE user_id = $1 AND product_id = $2`, [userId, productId]);
|
|
283
|
+
return result.rowCount ?? 0;
|
|
284
|
+
}
|
|
285
|
+
async deletePurchaseByTransactionId(transactionId) {
|
|
286
|
+
const pool = await this.getPool();
|
|
287
|
+
const result = await pool.query(`DELETE FROM onesub_purchases WHERE transaction_id = $1`, [transactionId]);
|
|
288
|
+
return (result.rowCount ?? 0) > 0;
|
|
289
|
+
}
|
|
290
|
+
async listAll() {
|
|
291
|
+
const pool = await this.getPool();
|
|
292
|
+
const result = await pool.query(`SELECT * FROM onesub_purchases`);
|
|
293
|
+
return result.rows.map(rowToPurchaseInfo);
|
|
294
|
+
}
|
|
295
|
+
/** Gracefully close the underlying connection pool. */
|
|
296
|
+
async close() {
|
|
297
|
+
if (this.poolPromise) {
|
|
298
|
+
const pool = await this.poolPromise;
|
|
299
|
+
await pool.end();
|
|
300
|
+
this.poolPromise = null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function rowToSubscriptionInfo(row) {
|
|
305
|
+
return {
|
|
306
|
+
originalTransactionId: row.original_transaction_id,
|
|
307
|
+
userId: row.user_id,
|
|
308
|
+
productId: row.product_id,
|
|
309
|
+
platform: row.platform,
|
|
310
|
+
status: row.status,
|
|
311
|
+
expiresAt: row.expires_at.toISOString(),
|
|
312
|
+
purchasedAt: row.purchased_at.toISOString(),
|
|
313
|
+
willRenew: row.will_renew,
|
|
314
|
+
linkedPurchaseToken: row.linked_purchase_token ?? undefined,
|
|
315
|
+
autoResumeTime: row.auto_resume_time?.toISOString(),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function rowToPurchaseInfo(row) {
|
|
319
|
+
return {
|
|
320
|
+
transactionId: row.transaction_id,
|
|
321
|
+
userId: row.user_id,
|
|
322
|
+
productId: row.product_id,
|
|
323
|
+
platform: row.platform,
|
|
324
|
+
type: row.type,
|
|
325
|
+
quantity: row.quantity,
|
|
326
|
+
purchasedAt: row.purchased_at.toISOString(),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/stores/postgres.ts"],"names":[],"mappings":"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"}
|