payment-kit 1.18.9 → 1.18.11
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/api/src/libs/util.ts +52 -2
- package/api/src/libs/ws.ts +78 -12
- package/api/src/routes/checkout-sessions.ts +18 -1
- package/api/tests/libs/util.spec.ts +279 -1
- package/api/third.d.ts +1 -0
- package/blocklet.yml +42 -1
- package/package.json +13 -13
- package/src/global.css +0 -3
package/api/src/libs/util.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { joinURL, withQuery, withTrailingSlash } from 'ufo';
|
|
|
11
11
|
import axios from 'axios';
|
|
12
12
|
import dayjs from './dayjs';
|
|
13
13
|
import { blocklet, wallet } from './auth';
|
|
14
|
-
import type { Subscription } from '../store/models';
|
|
14
|
+
import type { PaymentMethod, Subscription } from '../store/models';
|
|
15
15
|
import logger from './logger';
|
|
16
16
|
|
|
17
17
|
export const OCAP_PAYMENT_TX_TYPE = 'fg:t:transfer_v2';
|
|
@@ -73,7 +73,7 @@ export const STRIPE_EVENTS: any[] = [
|
|
|
73
73
|
'refund.updated',
|
|
74
74
|
];
|
|
75
75
|
|
|
76
|
-
const api = axios.create({
|
|
76
|
+
export const api = axios.create({
|
|
77
77
|
timeout: 10 * 1000,
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -458,3 +458,53 @@ export function getAdminBillingSubscriptionUrl({
|
|
|
458
458
|
export function getCustomerIndexUrl({ locale, userDid }: { locale: string; userDid: string }) {
|
|
459
459
|
return getUrl(withQuery('customer', { locale, ...getConnectQueryParam({ userDid }) }));
|
|
460
460
|
}
|
|
461
|
+
|
|
462
|
+
// Check if user is in blocklist
|
|
463
|
+
export async function isUserInBlocklist(did: string, paymentMethod: PaymentMethod): Promise<boolean> {
|
|
464
|
+
try {
|
|
465
|
+
// Get API host from payment method settings
|
|
466
|
+
// @ts-ignore
|
|
467
|
+
const apiHost = paymentMethod?.settings?.[paymentMethod.type]?.api_host;
|
|
468
|
+
if (!apiHost) {
|
|
469
|
+
logger.warn('No API host found in payment method settings', { paymentMethodId: paymentMethod.id });
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Get user information
|
|
474
|
+
const { user }: { user: any } = await blocklet.getUser(did);
|
|
475
|
+
if (!user) {
|
|
476
|
+
logger.error('User not found', { did });
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
// Get all wallet DIDs associated with the user
|
|
480
|
+
const connectedAccounts = user.connectedAccounts || user.extraConfigs?.connectedAccounts || [];
|
|
481
|
+
const walletDids = connectedAccounts
|
|
482
|
+
.filter((account: any) => account.provider === 'wallet')
|
|
483
|
+
.map((account: any) => account.did);
|
|
484
|
+
|
|
485
|
+
if (!walletDids.length) {
|
|
486
|
+
logger.info('No wallet DIDs found for user', { did });
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const blockedUrl = joinURL(apiHost, '/blocked');
|
|
491
|
+
const blockedResponse = await api.get(blockedUrl);
|
|
492
|
+
const blockedDids = blockedResponse.data || [];
|
|
493
|
+
|
|
494
|
+
if (!Array.isArray(blockedDids) || blockedDids.length === 0) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Check if any of the user's DIDs are in the blocklist
|
|
499
|
+
const isBlocked = walletDids.some((walletDid: string) => blockedDids.includes(walletDid));
|
|
500
|
+
|
|
501
|
+
if (isBlocked) {
|
|
502
|
+
logger.info('User is in blocklist', { did, walletDids, blockedDids });
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return isBlocked;
|
|
506
|
+
} catch (error) {
|
|
507
|
+
logger.error('Error checking blocklist', { error: error.message, did });
|
|
508
|
+
return false; // Default to allowing payment on error
|
|
509
|
+
}
|
|
510
|
+
}
|
package/api/src/libs/ws.ts
CHANGED
|
@@ -1,25 +1,91 @@
|
|
|
1
1
|
import { sendToRelay } from '@blocklet/sdk/service/notification';
|
|
2
|
-
|
|
3
|
-
import type { CheckoutSession, Invoice, PaymentIntent } from '../store/models';
|
|
2
|
+
import { publish } from '@blocklet/sdk/service/eventbus';
|
|
3
|
+
import type { CheckoutSession, Invoice, PaymentIntent, Subscription, Refund } from '../store/models';
|
|
4
4
|
import { events } from './event';
|
|
5
5
|
|
|
6
|
-
export function broadcast(eventName: string, data: any) {
|
|
6
|
+
export function broadcast(eventName: string, data: any, extraParams?: Record<string, any>) {
|
|
7
7
|
sendToRelay('events', eventName, data).catch((err: any) => {
|
|
8
|
-
console.error(`Failed to broadcast event: ${eventName}`, err);
|
|
8
|
+
console.error(`Failed to broadcast event via relay: ${eventName}`, err);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
publish(eventName, {
|
|
12
|
+
id: data.id,
|
|
13
|
+
time: new Date().toISOString(),
|
|
14
|
+
data: {
|
|
15
|
+
object: data,
|
|
16
|
+
extraParams,
|
|
17
|
+
},
|
|
18
|
+
}).catch((err: any) => {
|
|
19
|
+
console.error(`Failed to publish event via EventBus: ${eventName}`, err);
|
|
9
20
|
});
|
|
10
21
|
}
|
|
11
22
|
|
|
12
23
|
export function initEventBroadcast() {
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
// Checkout Events
|
|
25
|
+
events.on('checkout.session.completed', (data: CheckoutSession, extraParams?: Record<string, any>) => {
|
|
26
|
+
broadcast('checkout.session.completed', data, extraParams);
|
|
27
|
+
});
|
|
28
|
+
events.on('checkout.session.nft_minted', (data: CheckoutSession, extraParams?: Record<string, any>) => {
|
|
29
|
+
broadcast('checkout.session.nft_minted', data, extraParams);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Payment Events
|
|
33
|
+
events.on('payment_intent.succeeded', (data: PaymentIntent, extraParams?: Record<string, any>) => {
|
|
34
|
+
broadcast('payment_intent.succeeded', data, extraParams);
|
|
35
|
+
});
|
|
36
|
+
events.on('payment_intent.payment_failed', (data: PaymentIntent, extraParams?: Record<string, any>) => {
|
|
37
|
+
broadcast('payment_intent.payment_failed', data, extraParams);
|
|
38
|
+
});
|
|
39
|
+
events.on('invoice.paid', (data: Invoice, extraParams?: Record<string, any>) => {
|
|
40
|
+
broadcast('invoice.paid', data, extraParams);
|
|
41
|
+
});
|
|
42
|
+
events.on('refund.succeeded', (data: Refund, extraParams?: Record<string, any>) => {
|
|
43
|
+
broadcast('refund.succeeded', data, extraParams);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Subscription Events
|
|
47
|
+
events.on('customer.subscription.created', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
48
|
+
broadcast('customer.subscription.created', data, extraParams);
|
|
15
49
|
});
|
|
16
|
-
events.on('
|
|
17
|
-
broadcast('
|
|
50
|
+
events.on('customer.subscription.started', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
51
|
+
broadcast('customer.subscription.started', data, extraParams);
|
|
52
|
+
});
|
|
53
|
+
events.on('customer.subscription.renewed', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
54
|
+
broadcast('customer.subscription.renewed', data, extraParams);
|
|
55
|
+
});
|
|
56
|
+
events.on('customer.subscription.upgraded', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
57
|
+
broadcast('customer.subscription.upgraded', data, extraParams);
|
|
58
|
+
});
|
|
59
|
+
events.on('customer.subscription.updated', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
60
|
+
broadcast('customer.subscription.updated', data, extraParams);
|
|
61
|
+
});
|
|
62
|
+
events.on('customer.subscription.deleted', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
63
|
+
broadcast('customer.subscription.deleted', data, extraParams);
|
|
64
|
+
});
|
|
65
|
+
events.on('customer.subscription.paused', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
66
|
+
broadcast('customer.subscription.paused', data, extraParams);
|
|
67
|
+
});
|
|
68
|
+
events.on('customer.subscription.resumed', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
69
|
+
broadcast('customer.subscription.resumed', data, extraParams);
|
|
70
|
+
});
|
|
71
|
+
events.on('customer.subscription.past_due', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
72
|
+
broadcast('customer.subscription.past_due', data, extraParams);
|
|
73
|
+
});
|
|
74
|
+
events.on('customer.subscription.recovered', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
75
|
+
broadcast('customer.subscription.recovered', data, extraParams);
|
|
76
|
+
});
|
|
77
|
+
events.on('customer.subscription.renew_failed', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
78
|
+
broadcast('customer.subscription.renew_failed', data, extraParams);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Trial Events
|
|
82
|
+
events.on('customer.subscription.trial_start', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
83
|
+
broadcast('customer.subscription.trial_start', data, extraParams);
|
|
18
84
|
});
|
|
19
|
-
events.on('
|
|
20
|
-
broadcast('
|
|
85
|
+
events.on('customer.subscription.trial_will_end', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
86
|
+
broadcast('customer.subscription.trial_will_end', data, extraParams);
|
|
21
87
|
});
|
|
22
|
-
events.on('
|
|
23
|
-
broadcast('
|
|
88
|
+
events.on('customer.subscription.trial_end', (data: Subscription, extraParams?: Record<string, any>) => {
|
|
89
|
+
broadcast('customer.subscription.trial_end', data, extraParams);
|
|
24
90
|
});
|
|
25
91
|
}
|
|
@@ -48,7 +48,13 @@ import {
|
|
|
48
48
|
getSubscriptionCreateSetup,
|
|
49
49
|
getSubscriptionTrialSetup,
|
|
50
50
|
} from '../libs/subscription';
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
CHECKOUT_SESSION_TTL,
|
|
53
|
+
formatAmountPrecisionLimit,
|
|
54
|
+
formatMetadata,
|
|
55
|
+
getDataObjectFromQuery,
|
|
56
|
+
isUserInBlocklist,
|
|
57
|
+
} from '../libs/util';
|
|
52
58
|
import { invoiceQueue } from '../queues/invoice';
|
|
53
59
|
import { paymentQueue } from '../queues/payment';
|
|
54
60
|
import {
|
|
@@ -70,6 +76,7 @@ import { SetupIntent } from '../store/models/setup-intent';
|
|
|
70
76
|
import { Subscription } from '../store/models/subscription';
|
|
71
77
|
import { SubscriptionItem } from '../store/models/subscription-item';
|
|
72
78
|
import { ensureInvoiceForCheckout } from './connect/shared';
|
|
79
|
+
import { CHARGE_SUPPORTED_CHAIN_TYPES } from '../libs/constants';
|
|
73
80
|
|
|
74
81
|
const router = Router();
|
|
75
82
|
|
|
@@ -692,6 +699,16 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
692
699
|
});
|
|
693
700
|
}
|
|
694
701
|
|
|
702
|
+
// check if user in block list
|
|
703
|
+
if (CHARGE_SUPPORTED_CHAIN_TYPES.includes(paymentMethod.type)) {
|
|
704
|
+
const inBlock = await isUserInBlocklist(req.user.did, paymentMethod);
|
|
705
|
+
if (inBlock) {
|
|
706
|
+
return res
|
|
707
|
+
.status(403)
|
|
708
|
+
.json({ code: 'PAYMENT_RESTRICTED', error: 'Unable to process your purchase at this time' });
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
695
712
|
await checkoutSession.update({ customer_id: customer.id, customer_did: req.user.did });
|
|
696
713
|
|
|
697
714
|
// payment intent is only created when checkout session is in payment mode
|
|
@@ -11,8 +11,13 @@ import {
|
|
|
11
11
|
getNextRetry,
|
|
12
12
|
tryWithTimeout,
|
|
13
13
|
getSubscriptionNotificationCustomActions,
|
|
14
|
+
isUserInBlocklist,
|
|
15
|
+
api,
|
|
14
16
|
} from '../../src/libs/util';
|
|
15
|
-
import type { Subscription } from '../../src/store/models';
|
|
17
|
+
import type { Subscription, PaymentMethod } from '../../src/store/models';
|
|
18
|
+
|
|
19
|
+
import { blocklet } from '../../src/libs/auth';
|
|
20
|
+
import logger from '../../src/libs/logger';
|
|
16
21
|
|
|
17
22
|
describe('createIdGenerator', () => {
|
|
18
23
|
it('should return a function that generates an ID with the specified prefix and size', () => {
|
|
@@ -375,3 +380,276 @@ describe('getSubscriptionNotificationCustomActions', () => {
|
|
|
375
380
|
});
|
|
376
381
|
});
|
|
377
382
|
});
|
|
383
|
+
|
|
384
|
+
describe('isUserInBlocklist', () => {
|
|
385
|
+
let mockAxios: any;
|
|
386
|
+
let mockBlocklet: any;
|
|
387
|
+
let mockLogger: any;
|
|
388
|
+
|
|
389
|
+
beforeEach(() => {
|
|
390
|
+
mockAxios = jest.spyOn(api, 'get').mockImplementation();
|
|
391
|
+
|
|
392
|
+
mockBlocklet = jest.spyOn(blocklet, 'getUser').mockImplementation();
|
|
393
|
+
|
|
394
|
+
mockLogger = {
|
|
395
|
+
warn: jest.spyOn(logger, 'warn').mockImplementation(),
|
|
396
|
+
info: jest.spyOn(logger, 'info').mockImplementation(),
|
|
397
|
+
error: jest.spyOn(logger, 'error').mockImplementation(),
|
|
398
|
+
};
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
afterEach(() => {
|
|
402
|
+
jest.clearAllMocks();
|
|
403
|
+
jest.resetAllMocks();
|
|
404
|
+
jest.restoreAllMocks();
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('should return true when user DID is in blocklist', async () => {
|
|
408
|
+
// Setup mocks
|
|
409
|
+
const userDid = 'did:user:123';
|
|
410
|
+
const paymentMethod = {
|
|
411
|
+
id: 'pm_123',
|
|
412
|
+
type: 'arcblock',
|
|
413
|
+
settings: {
|
|
414
|
+
arcblock: {
|
|
415
|
+
api_host: 'https://api.example.com',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
} as PaymentMethod;
|
|
419
|
+
|
|
420
|
+
mockAxios.mockResolvedValueOnce({ data: ['did:user:123', 'did:user:456'] });
|
|
421
|
+
mockBlocklet.mockResolvedValueOnce({
|
|
422
|
+
user: {
|
|
423
|
+
did: userDid,
|
|
424
|
+
connectedAccounts: [{ provider: 'wallet', did: userDid }],
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// Execute
|
|
429
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
430
|
+
|
|
431
|
+
// Verify
|
|
432
|
+
expect(result).toBe(true);
|
|
433
|
+
expect(mockAxios).toHaveBeenCalledWith('https://api.example.com/blocked');
|
|
434
|
+
expect(mockBlocklet).toHaveBeenCalledWith(userDid);
|
|
435
|
+
expect(mockLogger.info).toHaveBeenCalled();
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it('should return true when user wallet DID is in blocklist', async () => {
|
|
439
|
+
// Setup mocks
|
|
440
|
+
const userDid = 'did:user:123';
|
|
441
|
+
const walletDid = 'did:wallet:456';
|
|
442
|
+
const paymentMethod = {
|
|
443
|
+
id: 'pm_123',
|
|
444
|
+
type: 'arcblock',
|
|
445
|
+
settings: {
|
|
446
|
+
arcblock: {
|
|
447
|
+
api_host: 'https://api.example.com',
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
} as PaymentMethod;
|
|
451
|
+
|
|
452
|
+
mockAxios.mockResolvedValueOnce({ data: ['did:wallet:456', 'did:user:789'] });
|
|
453
|
+
mockBlocklet.mockResolvedValueOnce({
|
|
454
|
+
user: {
|
|
455
|
+
did: userDid,
|
|
456
|
+
connectedAccounts: [{ provider: 'wallet', did: walletDid }],
|
|
457
|
+
},
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Execute
|
|
461
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
462
|
+
|
|
463
|
+
// Verify
|
|
464
|
+
expect(result).toBe(true);
|
|
465
|
+
expect(mockAxios).toHaveBeenCalledWith('https://api.example.com/blocked');
|
|
466
|
+
expect(mockBlocklet).toHaveBeenCalledWith(userDid);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('should return false when user is not in blocklist', async () => {
|
|
470
|
+
// Setup mocks
|
|
471
|
+
const userDid = 'did:user:123';
|
|
472
|
+
const walletDid = 'did:wallet:456';
|
|
473
|
+
const paymentMethod = {
|
|
474
|
+
id: 'pm_123',
|
|
475
|
+
type: 'arcblock',
|
|
476
|
+
settings: {
|
|
477
|
+
arcblock: {
|
|
478
|
+
api_host: 'https://api.example.com',
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
} as PaymentMethod;
|
|
482
|
+
|
|
483
|
+
mockAxios.mockResolvedValueOnce({ data: ['did:user:789', 'did:wallet:789'] });
|
|
484
|
+
mockBlocklet.mockResolvedValueOnce({
|
|
485
|
+
user: {
|
|
486
|
+
did: userDid,
|
|
487
|
+
connectedAccounts: [{ provider: 'wallet', did: walletDid }],
|
|
488
|
+
},
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Execute
|
|
492
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
493
|
+
|
|
494
|
+
// Verify
|
|
495
|
+
expect(result).toBe(false);
|
|
496
|
+
expect(mockAxios).toHaveBeenCalledWith('https://api.example.com/blocked');
|
|
497
|
+
expect(mockBlocklet).toHaveBeenCalledWith(userDid);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('should handle API host with trailing slash', async () => {
|
|
501
|
+
// Setup mocks
|
|
502
|
+
const userDid = 'did:user:123';
|
|
503
|
+
const paymentMethod = {
|
|
504
|
+
id: 'pm_123',
|
|
505
|
+
type: 'arcblock',
|
|
506
|
+
settings: {
|
|
507
|
+
arcblock: {
|
|
508
|
+
api_host: 'https://api.example.com/',
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
} as PaymentMethod;
|
|
512
|
+
|
|
513
|
+
mockAxios.mockResolvedValueOnce({ data: ['did:user:789'] });
|
|
514
|
+
mockBlocklet.mockResolvedValueOnce({
|
|
515
|
+
user: {
|
|
516
|
+
did: userDid,
|
|
517
|
+
connectedAccounts: [{ provider: 'wallet', did: userDid }],
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// Execute
|
|
522
|
+
await isUserInBlocklist(userDid, paymentMethod);
|
|
523
|
+
|
|
524
|
+
// Verify - 确保调用了正确的 URL
|
|
525
|
+
expect(mockAxios).toHaveBeenCalledWith('https://api.example.com/blocked');
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('should return false when API host is not found', async () => {
|
|
529
|
+
// Setup mocks
|
|
530
|
+
const userDid = 'did:user:123';
|
|
531
|
+
const paymentMethod = {
|
|
532
|
+
id: 'pm_123',
|
|
533
|
+
type: 'arcblock',
|
|
534
|
+
settings: {},
|
|
535
|
+
} as PaymentMethod;
|
|
536
|
+
|
|
537
|
+
// Execute
|
|
538
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
539
|
+
|
|
540
|
+
// Verify
|
|
541
|
+
expect(result).toBe(false);
|
|
542
|
+
expect(mockLogger.warn).toHaveBeenCalled();
|
|
543
|
+
expect(mockAxios).not.toHaveBeenCalled();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('should return false when blocklist is empty', async () => {
|
|
547
|
+
// Setup mocks
|
|
548
|
+
const userDid = 'did:user:123';
|
|
549
|
+
const paymentMethod = {
|
|
550
|
+
id: 'pm_123',
|
|
551
|
+
type: 'arcblock',
|
|
552
|
+
settings: {
|
|
553
|
+
arcblock: {
|
|
554
|
+
api_host: 'https://api.example.com',
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
} as PaymentMethod;
|
|
558
|
+
|
|
559
|
+
// 先模拟用户信息获取
|
|
560
|
+
mockBlocklet.mockResolvedValueOnce({
|
|
561
|
+
user: {
|
|
562
|
+
did: userDid,
|
|
563
|
+
connectedAccounts: [{ provider: 'wallet', did: userDid }],
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// 再模拟空黑名单
|
|
568
|
+
mockAxios.mockResolvedValueOnce({ data: [] });
|
|
569
|
+
|
|
570
|
+
// Execute
|
|
571
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
572
|
+
|
|
573
|
+
// Verify
|
|
574
|
+
expect(result).toBe(false);
|
|
575
|
+
expect(mockBlocklet).toHaveBeenCalled(); // 现在应该调用了用户信息获取
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it('should return false when user is not found', async () => {
|
|
579
|
+
// Setup mocks
|
|
580
|
+
const userDid = 'did:user:123';
|
|
581
|
+
const paymentMethod = {
|
|
582
|
+
id: 'pm_123',
|
|
583
|
+
type: 'arcblock',
|
|
584
|
+
settings: {
|
|
585
|
+
arcblock: {
|
|
586
|
+
api_host: 'https://api.example.com',
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
} as PaymentMethod;
|
|
590
|
+
|
|
591
|
+
mockAxios.mockResolvedValueOnce({ data: ['did:user:123'] });
|
|
592
|
+
mockBlocklet.mockResolvedValueOnce({ user: null });
|
|
593
|
+
|
|
594
|
+
// Execute
|
|
595
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
596
|
+
|
|
597
|
+
// Verify
|
|
598
|
+
expect(result).toBe(false);
|
|
599
|
+
expect(mockLogger.error).toHaveBeenCalled();
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('should return false when API request fails', async () => {
|
|
603
|
+
// Setup mocks
|
|
604
|
+
const userDid = 'did:user:123';
|
|
605
|
+
const paymentMethod = {
|
|
606
|
+
id: 'pm_123',
|
|
607
|
+
type: 'arcblock',
|
|
608
|
+
settings: {
|
|
609
|
+
arcblock: {
|
|
610
|
+
api_host: 'https://api.example.com',
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
} as PaymentMethod;
|
|
614
|
+
|
|
615
|
+
mockAxios.mockRejectedValueOnce(new Error('Network error'));
|
|
616
|
+
|
|
617
|
+
// Execute
|
|
618
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
619
|
+
|
|
620
|
+
// Verify
|
|
621
|
+
expect(result).toBe(false);
|
|
622
|
+
expect(mockLogger.error).toHaveBeenCalled();
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it('should handle extraConfigs for connected accounts', async () => {
|
|
626
|
+
// Setup mocks
|
|
627
|
+
const userDid = 'did:user:123';
|
|
628
|
+
const walletDid = 'did:wallet:456';
|
|
629
|
+
const paymentMethod = {
|
|
630
|
+
id: 'pm_123',
|
|
631
|
+
type: 'arcblock',
|
|
632
|
+
settings: {
|
|
633
|
+
arcblock: {
|
|
634
|
+
api_host: 'https://api.example.com',
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
} as PaymentMethod;
|
|
638
|
+
|
|
639
|
+
mockAxios.mockResolvedValueOnce({ data: ['did:wallet:456'] });
|
|
640
|
+
mockBlocklet.mockResolvedValueOnce({
|
|
641
|
+
user: {
|
|
642
|
+
did: userDid,
|
|
643
|
+
extraConfigs: {
|
|
644
|
+
connectedAccounts: [{ provider: 'wallet', did: walletDid }],
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// Execute
|
|
650
|
+
const result = await isUserInBlocklist(userDid, paymentMethod);
|
|
651
|
+
|
|
652
|
+
// Verify
|
|
653
|
+
expect(result).toBe(true);
|
|
654
|
+
});
|
|
655
|
+
});
|
package/api/third.d.ts
CHANGED
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.18.
|
|
17
|
+
version: 1.18.11
|
|
18
18
|
logo: logo.png
|
|
19
19
|
files:
|
|
20
20
|
- dist
|
|
@@ -125,3 +125,44 @@ resource:
|
|
|
125
125
|
- type: paywall
|
|
126
126
|
description: Auto config paywall for your content
|
|
127
127
|
egress: true
|
|
128
|
+
events:
|
|
129
|
+
- type: checkout.session.completed
|
|
130
|
+
description: Payment checkout process has been successfully completed
|
|
131
|
+
- type: checkout.session.nft_minted
|
|
132
|
+
description: NFT has been successfully minted after payment
|
|
133
|
+
- type: customer.subscription.created
|
|
134
|
+
description: A new subscription has been successfully created
|
|
135
|
+
- type: customer.subscription.started
|
|
136
|
+
description: Subscription has been activated and billing cycle has begun
|
|
137
|
+
- type: customer.subscription.renewed
|
|
138
|
+
description: Subscription has been successfully renewed for next period
|
|
139
|
+
- type: customer.subscription.upgraded
|
|
140
|
+
description: Subscription plan has been changed to a different tier
|
|
141
|
+
- type: customer.subscription.updated
|
|
142
|
+
description: Subscription details or configuration has been modified
|
|
143
|
+
- type: customer.subscription.deleted
|
|
144
|
+
description: An existing subscription has been cancelled
|
|
145
|
+
- type: customer.subscription.renew_failed
|
|
146
|
+
description: Subscription renewal attempt has failed
|
|
147
|
+
- type: customer.subscription.trial_start
|
|
148
|
+
description: Subscription trial period has started
|
|
149
|
+
- type: customer.subscription.trial_will_end
|
|
150
|
+
description: Subscription trial period is about to end
|
|
151
|
+
- type: customer.subscription.trial_end
|
|
152
|
+
description: Subscription trial period has ended
|
|
153
|
+
- type: customer.subscription.paused
|
|
154
|
+
description: Subscription has been paused, payment collection suspended
|
|
155
|
+
- type: customer.subscription.resumed
|
|
156
|
+
description: Subscription has been resumed after being paused
|
|
157
|
+
- type: customer.subscription.past_due
|
|
158
|
+
description: Subscription payment is overdue
|
|
159
|
+
- type: customer.subscription.recovered
|
|
160
|
+
description: Subscription has been recovered from past due status
|
|
161
|
+
- type: payment_intent.succeeded
|
|
162
|
+
description: Payment has been successfully processed and confirmed
|
|
163
|
+
- type: payment_intent.payment_failed
|
|
164
|
+
description: Payment attempt has failed
|
|
165
|
+
- type: invoice.paid
|
|
166
|
+
description: Invoice has been fully paid and settled
|
|
167
|
+
- type: refund.succeeded
|
|
168
|
+
description: Refund has been successfully processed and completed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.11",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -43,19 +43,19 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@abtnode/cron": "^1.16.
|
|
46
|
+
"@abtnode/cron": "^1.16.39",
|
|
47
47
|
"@arcblock/did": "^1.19.10",
|
|
48
48
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
49
|
-
"@arcblock/did-connect": "^2.11.
|
|
49
|
+
"@arcblock/did-connect": "^2.11.48",
|
|
50
50
|
"@arcblock/did-util": "^1.19.10",
|
|
51
51
|
"@arcblock/jwt": "^1.19.10",
|
|
52
|
-
"@arcblock/ux": "^2.11.
|
|
52
|
+
"@arcblock/ux": "^2.11.48",
|
|
53
53
|
"@arcblock/validator": "^1.19.10",
|
|
54
|
-
"@blocklet/js-sdk": "^1.16.
|
|
55
|
-
"@blocklet/logger": "^1.16.
|
|
56
|
-
"@blocklet/payment-react": "1.18.
|
|
57
|
-
"@blocklet/sdk": "^1.16.
|
|
58
|
-
"@blocklet/ui-react": "^2.11.
|
|
54
|
+
"@blocklet/js-sdk": "^1.16.39",
|
|
55
|
+
"@blocklet/logger": "^1.16.39",
|
|
56
|
+
"@blocklet/payment-react": "1.18.11",
|
|
57
|
+
"@blocklet/sdk": "^1.16.39",
|
|
58
|
+
"@blocklet/ui-react": "^2.11.48",
|
|
59
59
|
"@blocklet/uploader": "^0.1.70",
|
|
60
60
|
"@blocklet/xss": "^0.1.25",
|
|
61
61
|
"@mui/icons-material": "^5.16.6",
|
|
@@ -119,9 +119,9 @@
|
|
|
119
119
|
"web3": "^4.16.0"
|
|
120
120
|
},
|
|
121
121
|
"devDependencies": {
|
|
122
|
-
"@abtnode/types": "^1.16.
|
|
122
|
+
"@abtnode/types": "^1.16.39",
|
|
123
123
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
124
|
-
"@blocklet/payment-types": "1.18.
|
|
124
|
+
"@blocklet/payment-types": "1.18.11",
|
|
125
125
|
"@types/cookie-parser": "^1.4.7",
|
|
126
126
|
"@types/cors": "^2.8.17",
|
|
127
127
|
"@types/debug": "^4.1.12",
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
"vite": "^5.3.5",
|
|
152
152
|
"vite-node": "^2.0.4",
|
|
153
153
|
"vite-plugin-babel-import": "^2.0.5",
|
|
154
|
-
"vite-plugin-blocklet": "^0.9.
|
|
154
|
+
"vite-plugin-blocklet": "^0.9.22",
|
|
155
155
|
"vite-plugin-node-polyfills": "^0.21.0",
|
|
156
156
|
"vite-plugin-svgr": "^4.2.0",
|
|
157
157
|
"vite-tsconfig-paths": "^4.3.2",
|
|
@@ -167,5 +167,5 @@
|
|
|
167
167
|
"parser": "typescript"
|
|
168
168
|
}
|
|
169
169
|
},
|
|
170
|
-
"gitHead": "
|
|
170
|
+
"gitHead": "370cb327400359ab5972ef60316ccd7fc86c174e"
|
|
171
171
|
}
|