payment-kit 1.23.9 → 1.23.10

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.
@@ -7,6 +7,7 @@ import {
7
7
  CreditGrant,
8
8
  Customer,
9
9
  Invoice,
10
+ Meter,
10
11
  PaymentCurrency,
11
12
  PaymentMethod,
12
13
  Price,
@@ -51,6 +52,22 @@ export async function processAutoRecharge(job: AutoRechargeJobData) {
51
52
  return;
52
53
  }
53
54
 
55
+ // Check if the associated meter is inactive
56
+ if (currency.type === 'credit') {
57
+ // Find meter by currency_id (meter.currency_id -> PaymentCurrency) or by metadata.meter_id
58
+ const meter = await Meter.findOne({
59
+ where: { currency_id: currencyId },
60
+ });
61
+ if (meter && meter.status === 'inactive') {
62
+ logger.info('Meter is inactive, skipping auto recharge', {
63
+ customerId,
64
+ currencyId,
65
+ meterId: meter.id,
66
+ });
67
+ return;
68
+ }
69
+ }
70
+
54
71
  // 1. find auto recharge config
55
72
  const config = (await AutoRechargeConfig.findOne({
56
73
  where: {
@@ -302,6 +319,24 @@ export async function checkAndTriggerAutoRecharge(
302
319
  currencyId,
303
320
  currentBalance,
304
321
  });
322
+
323
+ // Check if the associated meter is inactive
324
+ const currency = await PaymentCurrency.findByPk(currencyId);
325
+ if (currency?.type === 'credit') {
326
+ // Find meter by currency_id (meter.currency_id -> PaymentCurrency)
327
+ const meter = await Meter.findOne({
328
+ where: { currency_id: currencyId },
329
+ });
330
+ if (meter && meter.status === 'inactive') {
331
+ logger.info('Meter is inactive, skipping auto recharge check', {
332
+ customerId: customer.id,
333
+ currencyId,
334
+ meterId: meter.id,
335
+ });
336
+ return;
337
+ }
338
+ }
339
+
305
340
  const config = await AutoRechargeConfig.findOne({
306
341
  where: {
307
342
  customer_id: customer.id,
@@ -10,6 +10,7 @@ import {
10
10
  AutoRechargeConfig,
11
11
  Customer,
12
12
  EVMChainType,
13
+ Meter,
13
14
  PaymentCurrency,
14
15
  PaymentMethod,
15
16
  Price,
@@ -339,6 +340,16 @@ router.post('/submit', async (req, res) => {
339
340
  throw new CustomError(400, `Currency not found: ${value.currency_id}`);
340
341
  }
341
342
 
343
+ // Check if the associated meter is active when enabling auto-recharge
344
+ if (configData.enabled && currency.type === 'credit') {
345
+ const meter = await Meter.findOne({
346
+ where: { currency_id: value.currency_id },
347
+ });
348
+ if (meter && meter.status === 'inactive') {
349
+ throw new CustomError(400, 'Cannot enable auto top-up: the associated meter is inactive');
350
+ }
351
+ }
352
+
342
353
  if (currency.recharge_config?.base_price_id && currency.recharge_config?.base_price_id !== value.price_id) {
343
354
  throw new CustomError(400, 'Price is not the base price');
344
355
  }
@@ -2,7 +2,7 @@ import { Router } from 'express';
2
2
  import Joi from 'joi';
3
3
  import { BN, fromTokenToUnit } from '@ocap/util';
4
4
 
5
- import { literal, OrderItem } from 'sequelize';
5
+ import { literal, OrderItem, fn, col, Op } from 'sequelize';
6
6
  import pick from 'lodash/pick';
7
7
  import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '../libs/api';
8
8
  import logger from '../libs/logger';
@@ -165,6 +165,89 @@ router.get('/summary', authMine, async (req, res) => {
165
165
  }
166
166
  });
167
167
 
168
+ const holdersSchema = Joi.object({
169
+ currency_id: Joi.string().required(),
170
+ page: Joi.number().integer().min(1).default(1),
171
+ pageSize: Joi.number().integer().min(0).optional(), // 0 or undefined = return all
172
+ livemode: Joi.boolean().optional(),
173
+ });
174
+
175
+ // Get all holders (customers with balance) for a specific credit currency
176
+ router.get('/holders', auth, async (req, res) => {
177
+ try {
178
+ const { error, value } = holdersSchema.validate(req.query, { stripUnknown: true });
179
+ if (error) {
180
+ return res.status(400).json({ error: error.message });
181
+ }
182
+
183
+ const { currency_id: currencyId, page, pageSize, livemode } = value;
184
+
185
+ const currency = await PaymentCurrency.findByPk(currencyId);
186
+ if (!currency) {
187
+ return res.status(404).json({ error: `PaymentCurrency ${currencyId} not found` });
188
+ }
189
+
190
+ if (currency.type !== 'credit') {
191
+ return res.status(400).json({ error: 'Currency must be of type credit' });
192
+ }
193
+
194
+ // Build where clause for credit grants
195
+ const grantWhere: any = {
196
+ currency_id: currencyId,
197
+ status: { [Op.in]: ['granted', 'pending'] }, // Only active grants
198
+ };
199
+ if (typeof livemode === 'boolean') {
200
+ grantWhere.livemode = livemode;
201
+ }
202
+
203
+ // Use database aggregation - only customer_id, no JOIN needed
204
+ const aggregatedData = (await CreditGrant.findAll({
205
+ where: grantWhere,
206
+ attributes: [
207
+ 'customer_id',
208
+ [fn('COUNT', col('id')), 'grantCount'],
209
+ [fn('SUM', literal('CAST(remaining_amount AS DECIMAL(40,0))')), 'totalBalance'],
210
+ ],
211
+ group: ['customer_id'], // Only group by customer_id - much faster!
212
+ order: [[literal('totalBalance'), 'DESC']],
213
+ raw: true,
214
+ })) as any[];
215
+
216
+ const totalCount = aggregatedData.length;
217
+
218
+ // Paginate (pageSize = 0 or undefined means return all)
219
+ const shouldPaginate = pageSize && pageSize > 0;
220
+ const paginatedData = shouldPaginate
221
+ ? aggregatedData.slice((page - 1) * pageSize, page * pageSize)
222
+ : aggregatedData;
223
+
224
+ const holders = paginatedData.map((item) => ({
225
+ customer_id: item.customer_id,
226
+ balance: (item.totalBalance || '0').toString(),
227
+ grantCount: parseInt(item.grantCount || '0', 10),
228
+ }));
229
+
230
+ return res.json({
231
+ holders,
232
+ currency: {
233
+ id: currency.id,
234
+ name: currency.name,
235
+ symbol: currency.symbol,
236
+ decimal: currency.decimal,
237
+ },
238
+ paging: {
239
+ page: shouldPaginate ? page : 1,
240
+ pageSize: shouldPaginate ? pageSize : totalCount,
241
+ total: totalCount,
242
+ totalPages: shouldPaginate ? Math.ceil(totalCount / pageSize) : 1,
243
+ },
244
+ });
245
+ } catch (err: any) {
246
+ logger.error('Error getting credit holders', { error: err.message });
247
+ return res.status(400).json({ error: err.message });
248
+ }
249
+ });
250
+
168
251
  const checkAutoRechargeSchema = Joi.object({
169
252
  customer_id: Joi.string().required(),
170
253
  currency_id: Joi.string().required(),
package/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.23.9
17
+ version: 1.23.10
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.23.9",
3
+ "version": "1.23.10",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "prelint": "npm run types",
@@ -59,9 +59,9 @@
59
59
  "@blocklet/error": "^0.3.5",
60
60
  "@blocklet/js-sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
61
61
  "@blocklet/logger": "^1.17.8-beta-20260104-120132-cb5b1914",
62
- "@blocklet/payment-broker-client": "1.23.9",
63
- "@blocklet/payment-react": "1.23.9",
64
- "@blocklet/payment-vendor": "1.23.9",
62
+ "@blocklet/payment-broker-client": "1.23.10",
63
+ "@blocklet/payment-react": "1.23.10",
64
+ "@blocklet/payment-vendor": "1.23.10",
65
65
  "@blocklet/sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
66
66
  "@blocklet/ui-react": "^3.3.10",
67
67
  "@blocklet/uploader": "^0.3.19",
@@ -131,7 +131,7 @@
131
131
  "devDependencies": {
132
132
  "@abtnode/types": "^1.17.8-beta-20260104-120132-cb5b1914",
133
133
  "@arcblock/eslint-config-ts": "^0.3.3",
134
- "@blocklet/payment-types": "1.23.9",
134
+ "@blocklet/payment-types": "1.23.10",
135
135
  "@types/cookie-parser": "^1.4.9",
136
136
  "@types/cors": "^2.8.19",
137
137
  "@types/debug": "^4.1.12",
@@ -178,5 +178,5 @@
178
178
  "parser": "typescript"
179
179
  }
180
180
  },
181
- "gitHead": "98f0267cd0769183f21ea954a8fe0b4573f7d684"
181
+ "gitHead": "9d058650e81ae43ffafb3e1253b50b6245d510ac"
182
182
  }