@onesub/server 0.20.0 → 0.21.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/dist/__tests__/purchase-product-lookup.test.d.ts +17 -0
- package/dist/__tests__/purchase-product-lookup.test.d.ts.map +1 -0
- package/dist/index.cjs +54 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +54 -9
- package/dist/index.js.map +1 -1
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/store.d.ts +22 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/stores/postgres.d.ts +10 -0
- package/dist/stores/postgres.d.ts.map +1 -1
- package/dist/stores/redis.d.ts +12 -0
- package/dist/stores/redis.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -84,11 +84,17 @@ var InMemoryPurchaseStore = class {
|
|
|
84
84
|
}
|
|
85
85
|
this.byTransactionId.set(purchase.transactionId, purchase);
|
|
86
86
|
const list = this.byUserId.get(purchase.userId) ?? [];
|
|
87
|
-
|
|
87
|
+
const at = Date.parse(purchase.purchasedAt);
|
|
88
|
+
const idx = list.findIndex((p) => Date.parse(p.purchasedAt) < at);
|
|
89
|
+
if (idx === -1) list.push(purchase);
|
|
90
|
+
else list.splice(idx, 0, purchase);
|
|
88
91
|
this.byUserId.set(purchase.userId, list);
|
|
89
92
|
}
|
|
90
93
|
async getPurchasesByUserId(userId) {
|
|
91
|
-
return this.byUserId.get(userId) ?? [];
|
|
94
|
+
return [...this.byUserId.get(userId) ?? []];
|
|
95
|
+
}
|
|
96
|
+
async getPurchasesForProduct(userId, productId) {
|
|
97
|
+
return (this.byUserId.get(userId) ?? []).filter((p) => p.productId === productId);
|
|
92
98
|
}
|
|
93
99
|
async getPurchaseByTransactionId(txId) {
|
|
94
100
|
return this.byTransactionId.get(txId) ?? null;
|
|
@@ -1974,6 +1980,13 @@ var purchaseStatusQuerySchema = z.object({
|
|
|
1974
1980
|
userId: z.string().min(1).max(256),
|
|
1975
1981
|
productId: z.string().min(1).max(256).optional()
|
|
1976
1982
|
});
|
|
1983
|
+
async function purchasesForProduct(purchaseStore, userId, productId) {
|
|
1984
|
+
if (purchaseStore.getPurchasesForProduct) {
|
|
1985
|
+
return purchaseStore.getPurchasesForProduct(userId, productId);
|
|
1986
|
+
}
|
|
1987
|
+
const all = await purchaseStore.getPurchasesByUserId(userId);
|
|
1988
|
+
return all.filter((p) => p.productId === productId);
|
|
1989
|
+
}
|
|
1977
1990
|
function createPurchaseRouter(config, purchaseStore) {
|
|
1978
1991
|
const router = Router();
|
|
1979
1992
|
const registry = getAppRegistry(config);
|
|
@@ -1995,9 +2008,7 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
1995
2008
|
});
|
|
1996
2009
|
try {
|
|
1997
2010
|
if (type === PURCHASE_TYPE.NON_CONSUMABLE) {
|
|
1998
|
-
const owned = (await purchaseStore
|
|
1999
|
-
(p) => p.productId === productId
|
|
2000
|
-
);
|
|
2011
|
+
const owned = (await purchasesForProduct(purchaseStore, userId, productId))[0];
|
|
2001
2012
|
if (owned) {
|
|
2002
2013
|
const response2 = {
|
|
2003
2014
|
valid: true,
|
|
@@ -2127,10 +2138,7 @@ function createPurchaseRouter(config, purchaseStore) {
|
|
|
2127
2138
|
}
|
|
2128
2139
|
const { userId, productId } = query;
|
|
2129
2140
|
try {
|
|
2130
|
-
|
|
2131
|
-
if (productId !== void 0) {
|
|
2132
|
-
purchases = purchases.filter((p) => p.productId === productId);
|
|
2133
|
-
}
|
|
2141
|
+
const purchases = productId !== void 0 ? await purchasesForProduct(purchaseStore, userId, productId) : await purchaseStore.getPurchasesByUserId(userId);
|
|
2134
2142
|
const response = { purchases };
|
|
2135
2143
|
res.status(200).json(response);
|
|
2136
2144
|
} catch (err2) {
|
|
@@ -3106,6 +3114,26 @@ var PostgresPurchaseStore = class {
|
|
|
3106
3114
|
);
|
|
3107
3115
|
return result.rows.map(rowToPurchaseInfo);
|
|
3108
3116
|
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Purchases for one user + product, most-recent-first.
|
|
3119
|
+
*
|
|
3120
|
+
* Same shape and ordering as `getPurchasesByUserId`, with `product_id` pushed
|
|
3121
|
+
* into the WHERE clause instead of filtered in process. Non-consumables also
|
|
3122
|
+
* have a partial unique index on `(user_id, product_id)`, so the ownership
|
|
3123
|
+
* check this backs is a single index lookup rather than a full read of the
|
|
3124
|
+
* user's purchase history.
|
|
3125
|
+
*/
|
|
3126
|
+
async getPurchasesForProduct(userId, productId) {
|
|
3127
|
+
const pool = await this.getPool();
|
|
3128
|
+
const result = await pool.query(
|
|
3129
|
+
`SELECT *
|
|
3130
|
+
FROM onesub_purchases
|
|
3131
|
+
WHERE user_id = $1 AND product_id = $2
|
|
3132
|
+
ORDER BY purchased_at DESC`,
|
|
3133
|
+
[userId, productId]
|
|
3134
|
+
);
|
|
3135
|
+
return result.rows.map(rowToPurchaseInfo);
|
|
3136
|
+
}
|
|
3109
3137
|
async getPurchaseByTransactionId(txId) {
|
|
3110
3138
|
const pool = await this.getPool();
|
|
3111
3139
|
const result = await pool.query(
|
|
@@ -3316,6 +3344,23 @@ var RedisPurchaseStore = class {
|
|
|
3316
3344
|
const raws = await this.redis.mget(...ids.map((id) => PUR_TX_PREFIX + id));
|
|
3317
3345
|
return raws.filter((r) => r != null).map((r) => JSON.parse(r));
|
|
3318
3346
|
}
|
|
3347
|
+
/**
|
|
3348
|
+
* Purchases for one user + product, most-recent-first.
|
|
3349
|
+
*
|
|
3350
|
+
* Served from the `user_product` set that `savePurchase` already maintains for
|
|
3351
|
+
* `hasPurchased`, so this reads only the rows for this product rather than the
|
|
3352
|
+
* user's whole purchase history.
|
|
3353
|
+
*
|
|
3354
|
+
* That set is unordered — unlike the per-user sorted set — so the
|
|
3355
|
+
* most-recent-first contract is restored by an explicit sort here. The row
|
|
3356
|
+
* count is per-product, so this is small.
|
|
3357
|
+
*/
|
|
3358
|
+
async getPurchasesForProduct(userId, productId) {
|
|
3359
|
+
const ids = await this.redis.smembers(PUR_USER_PRODUCT_PREFIX + userId + ":" + productId);
|
|
3360
|
+
if (ids.length === 0) return [];
|
|
3361
|
+
const raws = await this.redis.mget(...ids.map((id) => PUR_TX_PREFIX + id));
|
|
3362
|
+
return raws.filter((r) => r != null).map((r) => JSON.parse(r)).sort((a, b) => Date.parse(b.purchasedAt) - Date.parse(a.purchasedAt));
|
|
3363
|
+
}
|
|
3319
3364
|
async getPurchaseByTransactionId(txId) {
|
|
3320
3365
|
const raw = await this.redis.get(PUR_TX_PREFIX + txId);
|
|
3321
3366
|
return raw ? JSON.parse(raw) : null;
|