@jolibox/implement 1.2.8 → 1.3.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.
Files changed (65) hide show
  1. package/.rush/temp/package-deps_build.json +37 -27
  2. package/dist/common/rewards/registers/use-gem-only.d.ts +2 -1
  3. package/dist/common/rewards/registers/use-gem.d.ts +2 -1
  4. package/dist/common/rewards/registers/use-jolicoin-only.d.ts +2 -1
  5. package/dist/common/rewards/registers/use-jolicoin.d.ts +2 -1
  6. package/dist/common/rewards/registers/use-subscription.d.ts +2 -1
  7. package/dist/common/rewards/registers/utils/coins/jolicoin/jolicoin-handler.d.ts +1 -0
  8. package/dist/common/rewards/registers/utils/coins/joligem/gem-handler.d.ts +2 -1
  9. package/dist/common/rewards/registers/utils/common.d.ts +1 -0
  10. package/dist/common/rewards/registers/utils/subscription/sub-handler.d.ts +2 -1
  11. package/dist/common/rewards/reward-emitter.d.ts +8 -0
  12. package/dist/common/rewards/type.d.ts +1 -1
  13. package/dist/common/utils/index.d.ts +1 -1
  14. package/dist/index.js +25 -25
  15. package/dist/index.native.js +54 -54
  16. package/dist/native/payment/utils/__tests__/cache-with-storage.test.d.ts +1 -0
  17. package/dist/native/payment/utils/cache-with-storage.d.ts +7 -0
  18. package/dist/native/rewards/check-frequency.d.ts +8 -0
  19. package/dist/native/rewards/index.d.ts +1 -0
  20. package/dist/native/rewards/ui/subscription-modal.d.ts +1 -0
  21. package/dist/native/subscription/index.d.ts +3 -0
  22. package/dist/native/subscription/registers/base.d.ts +22 -0
  23. package/dist/native/subscription/registers/sub-app.d.ts +21 -0
  24. package/dist/native/subscription/registers/type.d.ts +10 -0
  25. package/dist/native/subscription/subscription-helper.d.ts +22 -0
  26. package/dist/native/subscription/subscription-service.d.ts +43 -0
  27. package/dist/native/subscription/type.d.ts +18 -0
  28. package/implement.build.log +2 -2
  29. package/package.json +5 -5
  30. package/src/common/context/index.ts +4 -1
  31. package/src/common/rewards/registers/use-gem-only.ts +5 -2
  32. package/src/common/rewards/registers/use-gem.ts +5 -2
  33. package/src/common/rewards/registers/use-jolicoin-only.ts +5 -2
  34. package/src/common/rewards/registers/use-jolicoin.ts +5 -2
  35. package/src/common/rewards/registers/use-subscription.ts +5 -2
  36. package/src/common/rewards/registers/utils/coins/jolicoin/jolicoin-handler.ts +33 -11
  37. package/src/common/rewards/registers/utils/coins/joligem/gem-handler.ts +34 -13
  38. package/src/common/rewards/registers/utils/common.ts +9 -0
  39. package/src/common/rewards/registers/utils/subscription/commands/use-subscription.ts +16 -0
  40. package/src/common/rewards/registers/utils/subscription/sub-handler.ts +21 -6
  41. package/src/common/rewards/reward-emitter.ts +8 -0
  42. package/src/common/rewards/type.ts +1 -1
  43. package/src/common/utils/index.ts +1 -1
  44. package/src/h5/api/ads.ts +6 -3
  45. package/src/h5/bootstrap/auth/__tests__/auth.test.ts +15 -9
  46. package/src/h5/bootstrap/auth/sub.ts +1 -1
  47. package/src/native/api/ads.ts +43 -6
  48. package/src/native/api/call-host-method.ts +5 -61
  49. package/src/native/api/login.ts +22 -7
  50. package/src/native/api/payment.ts +79 -5
  51. package/src/native/payment/__tests__/payment-service-simple.test.ts +14 -1
  52. package/src/native/payment/payment-service.ts +26 -27
  53. package/src/native/payment/utils/__tests__/cache-with-storage.test.ts +414 -0
  54. package/src/native/payment/utils/cache-with-storage.ts +112 -0
  55. package/src/native/rewards/check-frequency.ts +6 -0
  56. package/src/native/rewards/index.ts +1 -0
  57. package/src/native/rewards/ui/payment-modal.ts +2 -2
  58. package/src/native/rewards/ui/subscription-modal.ts +81 -0
  59. package/src/native/subscription/index.ts +12 -0
  60. package/src/native/subscription/registers/base.ts +88 -0
  61. package/src/native/subscription/registers/sub-app.ts +258 -0
  62. package/src/native/subscription/registers/type.ts +13 -0
  63. package/src/native/subscription/subscription-helper.ts +53 -0
  64. package/src/native/subscription/subscription-service.ts +339 -0
  65. package/src/native/subscription/type.ts +18 -0
@@ -0,0 +1,7 @@
1
+ export declare function executeAndCache<T>(cacheKey: string, executor: () => Promise<T>, options?: {
2
+ storageKeyPrefix?: string;
3
+ }): Promise<T>;
4
+ export declare function executeWithPersistentCache<T>(cacheKey: string, executor: () => Promise<T>, options?: {
5
+ cacheDuration?: number;
6
+ storageKeyPrefix?: string;
7
+ }): Promise<T>;
@@ -40,9 +40,17 @@ export declare const checkUnloginModalFrequencyGem: (config: {
40
40
  canShow: boolean;
41
41
  isFirst: boolean;
42
42
  }>;
43
+ export declare const checkSubscriptionFrequency: (config: {
44
+ dailyMaxPopUps?: number;
45
+ minInterval?: number;
46
+ }) => Promise<{
47
+ canShow: boolean;
48
+ isFirst: boolean;
49
+ }>;
43
50
  export declare const updateUseModalFrequency: () => Promise<void>;
44
51
  export declare const updatePaymentFrequency: () => Promise<void>;
45
52
  export declare const updateUnloginModalFrequency: () => Promise<void>;
46
53
  export declare const updateUseModalFrequencyGem: () => Promise<void>;
47
54
  export declare const updatePaymentFrequencyGem: () => Promise<void>;
48
55
  export declare const updateUnloginModalFrequencyGem: () => Promise<void>;
56
+ export declare const updateSubscriptionFrequency: () => Promise<void>;
@@ -4,3 +4,4 @@
4
4
  import './ui/use-modal';
5
5
  import './ui/payment-modal';
6
6
  import './ui/unlogin-modal';
7
+ import './ui/subscription-modal';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from './subscription-service';
2
+ export * from './type';
3
+ export declare const subscriptionService: import("./subscription-service").SubscriptionService;
@@ -0,0 +1,22 @@
1
+ import { StandardResponse } from '@jolibox/types';
2
+ import type { IPlaceSubscriptionOrderResponse } from './type';
3
+ import { UserPaymentError, InternalPaymentError } from '@jolibox/common';
4
+ type SubscriptionStatus = 'INIT' | 'PENDING' | 'SUCCESS' | 'FAILED';
5
+ declare const createSubscriptionError: (errMsg: string, errNo: number, extra?: Record<string, unknown>) => UserPaymentError | InternalPaymentError, createSubscriptionInternalError: (errMsg: string, errNo: number, extra?: Record<string, unknown>) => UserPaymentError | InternalPaymentError;
6
+ export { createSubscriptionError, createSubscriptionInternalError };
7
+ export declare abstract class BaseSubscriptionRegister<T extends Record<string, any>, E extends Record<string, any>, R = void> {
8
+ private _orderId;
9
+ private _orderStatus;
10
+ startSubscription: (params: T) => Promise<StandardResponse<R>>;
11
+ protected generateCallbackUrl: () => string;
12
+ protected setOrderId(orderId: string): void;
13
+ protected setOrderStatus(status: SubscriptionStatus): void;
14
+ protected createSubscriptionError: (errMsg: string, errNo: number, extra?: Record<string, unknown>) => UserPaymentError | InternalPaymentError;
15
+ protected createSubscriptionInternalError: (errMsg: string, errNo: number, extra?: Record<string, unknown>) => UserPaymentError | InternalPaymentError;
16
+ abstract placeOrder(params: T): Promise<StandardResponse<IPlaceSubscriptionOrderResponse<E>>>;
17
+ abstract cancelOrder(orderId: string): Promise<StandardResponse<void>>;
18
+ abstract generateSubscriptionContext(): Record<string, any>;
19
+ abstract subscribe(params: T): Promise<StandardResponse<R>>;
20
+ get orderId(): string | null;
21
+ get status(): SubscriptionStatus;
22
+ }
@@ -0,0 +1,21 @@
1
+ import { StandardResponse } from '@jolibox/types';
2
+ export interface IPlaceOrderSubAppParams {
3
+ basePlanId: string;
4
+ appStoreProductId: string;
5
+ productId: string;
6
+ }
7
+ export interface ISubAppProductInfo {
8
+ subPlanId: string;
9
+ }
10
+ export interface ISubAppSubscriptionContext {
11
+ gameId: string;
12
+ productId: string;
13
+ planId: string;
14
+ }
15
+ export declare function createSubAppSubscriptionHandler(): (params: {
16
+ productId: string;
17
+ appStoreProductId: string;
18
+ planId: string;
19
+ }) => Promise<StandardResponse<{
20
+ subPlanId: string;
21
+ }, unknown>>;
@@ -0,0 +1,10 @@
1
+ export interface IPlaceSubscriptionOrderResponse<T = any> {
2
+ orderId: string;
3
+ productInfo: T;
4
+ }
5
+ export declare const SubscriptionErrorCodeMap: {
6
+ readonly PlaceOrderFailed: 1001;
7
+ readonly CancelOrderFailed: 1002;
8
+ readonly SubscriptionFailed: 1003;
9
+ readonly InvalidPlan: 1004;
10
+ };
@@ -0,0 +1,22 @@
1
+ export type SubscriptionType = 'SUB_APP';
2
+ import { StandardResponse } from '@jolibox/types';
3
+ export type SubscriptionResult<T> = StandardResponse<T>;
4
+ export interface SubscriptionHandlerMap {
5
+ SUB_APP: (params: {
6
+ productId: string;
7
+ appStoreProductId: string;
8
+ planId: string;
9
+ }) => Promise<SubscriptionResult<{
10
+ subPlanId: string;
11
+ }>>;
12
+ }
13
+ export type SubscriptionHandler<T extends SubscriptionType> = SubscriptionHandlerMap[T];
14
+ export declare function createSubscriptionHelper(): {
15
+ registerSubscriptionHandler<T extends SubscriptionType>(type: T, handler: SubscriptionHandler<T>): void;
16
+ invokeSubscription<T extends SubscriptionType>(type: T, ...args: Parameters<SubscriptionHandler<T>>): Promise<any>;
17
+ };
18
+ export type SubscriptionHelper = ReturnType<typeof createSubscriptionHelper>;
19
+ export declare const subscriptionHelper: {
20
+ registerSubscriptionHandler<T extends SubscriptionType>(type: T, handler: SubscriptionHandler<T>): void;
21
+ invokeSubscription<T extends SubscriptionType>(type: T, ...args: Parameters<SubscriptionHandler<T>>): Promise<any>;
22
+ };
@@ -0,0 +1,43 @@
1
+ import { ISubscriptionTierData } from '@jolibox/types';
2
+ import { StandardResponse } from '@jolibox/types';
3
+ import { RequestCacheService } from '@jolibox/common';
4
+ type SubscriptionRequest = Record<string, never>;
5
+ type SubscriptionResponse = ISubscriptionTierData[];
6
+ interface SubscriptionCacheData {
7
+ subscriptionTiers: ISubscriptionTierData[];
8
+ }
9
+ declare class BaseSubscriptionService extends RequestCacheService<SubscriptionRequest, SubscriptionResponse, SubscriptionCacheData, never> {
10
+ private apiEndpoint;
11
+ private pendingSubscriptions;
12
+ private subInfoCacheKeys;
13
+ constructor(apiEndpoint: string);
14
+ getSubInfo(): Promise<SubscriptionResponse | undefined>;
15
+ refreshSubInfo(): Promise<SubscriptionResponse | undefined>;
16
+ updateNativePriceCache(appStoreProductIds: string[]): Promise<void>;
17
+ updateAllNativePriceCache(): Promise<void>;
18
+ subscribe(params: {
19
+ basePlanId: string;
20
+ nativeProductId: string;
21
+ appStoreProductId: string;
22
+ }): Promise<{
23
+ subPlanId: string;
24
+ message: string;
25
+ result: 'SUCCESS' | 'FAILED' | 'FAILURE_SUBSCRIPTED';
26
+ }>;
27
+ invokeSubscriptionPanel(): Promise<StandardResponse<{
28
+ result: 'SUCCESS' | 'FAILED';
29
+ subPlanId?: string;
30
+ }>>;
31
+ getTierByProductId(productId: string): Promise<{
32
+ nativeProductId: string;
33
+ appStoreProductId: string;
34
+ basePlanId: string;
35
+ } | undefined>;
36
+ private listenSubscriptionPanelStateChange;
37
+ }
38
+ export declare class SubscriptionService extends BaseSubscriptionService {
39
+ constructor();
40
+ }
41
+ export declare const createSubscriptionService: () => SubscriptionService;
42
+ export declare const clearSubscriptionNativePriceCache: () => void;
43
+ export {};
@@ -0,0 +1,18 @@
1
+ export interface ISubscriptionPlanData {
2
+ basePlanId: string;
3
+ productId: string;
4
+ pricing: {
5
+ formattedPrice: string;
6
+ originalPrice?: string;
7
+ discountPercentage?: number;
8
+ };
9
+ trial?: {
10
+ duration: number;
11
+ description?: string;
12
+ };
13
+ promotion?: {
14
+ type: 'new_user' | 'limited_time' | 'winback';
15
+ badge: string;
16
+ description: string;
17
+ };
18
+ }
@@ -1,9 +1,9 @@
1
1
  Invoking: npm run clean && npm run build:esm && tsc
2
2
 
3
- > @jolibox/implement@1.2.8 clean
3
+ > @jolibox/implement@1.3.0 clean
4
4
  > rimraf ./dist
5
5
 
6
6
 
7
- > @jolibox/implement@1.2.8 build:esm
7
+ > @jolibox/implement@1.3.0 build:esm
8
8
  > BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=esm
9
9
 
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@jolibox/implement",
3
3
  "description": "This project is Jolibox JS-SDk implement for Native && H5",
4
- "version": "1.2.8",
4
+ "version": "1.3.0",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
- "@jolibox/common": "1.2.8",
10
- "@jolibox/types": "1.2.8",
11
- "@jolibox/native-bridge": "1.2.8",
12
- "@jolibox/ads": "1.2.8",
9
+ "@jolibox/common": "1.3.0",
10
+ "@jolibox/types": "1.3.0",
11
+ "@jolibox/native-bridge": "1.3.0",
12
+ "@jolibox/ads": "1.3.0",
13
13
  "localforage": "1.10.0",
14
14
  "@jolibox/ui": "1.0.0",
15
15
  "web-vitals": "4.2.4"
@@ -76,6 +76,9 @@ const wrapContext = () => {
76
76
  const testMode = joliboxEnv === 'staging';
77
77
  const channel = headerJson?.channel;
78
78
 
79
+ const originUrl = new URL(window.location.href);
80
+ const originUrlParams = originUrl.searchParams;
81
+
79
82
  // default should show stay recommend panel
80
83
  let shouldInterupt = payloadJson?.__shouldInterupt;
81
84
 
@@ -93,7 +96,7 @@ const wrapContext = () => {
93
96
  return joliboxEnv;
94
97
  },
95
98
  get joliSource(): string | null {
96
- return urlParams.get('joliSource') ?? null;
99
+ return originUrlParams.get('joliSource') ?? urlParams.get('joliSource') ?? null;
97
100
  },
98
101
  get mpId(): string {
99
102
  return defaultGameID ?? payloadJson?.id ?? '';
@@ -13,10 +13,12 @@ export const createGemOnlyRewardHandler = (
13
13
  httpClient: IHttpClient,
14
14
  {
15
15
  onUnlockSuccess,
16
- onUnlockFailed
16
+ onUnlockFailed,
17
+ hasSubscription
17
18
  }: {
18
19
  onUnlockSuccess?: (data: { quantity: number; balance: number }) => void;
19
20
  onUnlockFailed?: () => void;
21
+ hasSubscription: () => boolean;
20
22
  }
21
23
  ): GemOnlyRewardsHandler => {
22
24
  const handleUnlockFailed = (() => (params: IAdBreakParams) => {
@@ -50,7 +52,8 @@ export const createGemOnlyRewardHandler = (
50
52
  unlockOptionsHandler,
51
53
  initiateAndAwaitPayment,
52
54
  showUnlockWithCurrencyModal: showUnlockWithGemModal,
53
- showUnloginModal
55
+ showUnloginModal,
56
+ hasSubscription
54
57
  }
55
58
  });
56
59
 
@@ -13,10 +13,12 @@ export const createGemRewardHandler = (
13
13
  httpClient: IHttpClient,
14
14
  {
15
15
  onUnlockSuccess,
16
- onUnlockFailed
16
+ onUnlockFailed,
17
+ hasSubscription
17
18
  }: {
18
19
  onUnlockSuccess?: (data: { quantity: number; balance: number }) => void;
19
20
  onUnlockFailed?: () => void;
21
+ hasSubscription: () => boolean;
20
22
  }
21
23
  ): GemRewardsHandler => {
22
24
  const handleUnlockFailed = () => {
@@ -45,7 +47,8 @@ export const createGemRewardHandler = (
45
47
  unlockOptionsHandler,
46
48
  initiateAndAwaitPayment,
47
49
  showUnlockWithCurrencyModal: showUnlockWithGemModal,
48
- showUnloginModal
50
+ showUnloginModal,
51
+ hasSubscription
49
52
  }
50
53
  });
51
54
 
@@ -13,10 +13,12 @@ export const createJolicoinOnlyRewardHandler = (
13
13
  httpClient: IHttpClient,
14
14
  {
15
15
  onUnlockSuccess,
16
- onUnlockFailed
16
+ onUnlockFailed,
17
+ hasSubscription
17
18
  }: {
18
19
  onUnlockSuccess?: (data: { quantity: number; balance: number }) => void;
19
20
  onUnlockFailed?: () => void;
21
+ hasSubscription: () => boolean;
20
22
  }
21
23
  ): JolicoinOnlyRewardsHandler => {
22
24
  const handleUnlockFailed = (() => (params: IAdBreakParams) => {
@@ -50,7 +52,8 @@ export const createJolicoinOnlyRewardHandler = (
50
52
  unlockOptionsHandler,
51
53
  initiateAndAwaitPayment,
52
54
  showUnlockWithCurrencyModal: showUnlockWithJolicoinModal,
53
- showUnloginModal
55
+ showUnloginModal,
56
+ hasSubscription
54
57
  }
55
58
  });
56
59
 
@@ -13,10 +13,12 @@ export const createJolicoinRewardHandler = (
13
13
  httpClient: IHttpClient,
14
14
  {
15
15
  onUnlockSuccess,
16
- onUnlockFailed
16
+ onUnlockFailed,
17
+ hasSubscription
17
18
  }: {
18
19
  onUnlockSuccess?: (data: { quantity: number; balance: number }) => void;
19
20
  onUnlockFailed?: () => void;
21
+ hasSubscription: () => boolean;
20
22
  }
21
23
  ): JolicoinRewardsHandler => {
22
24
  const handleUnlockFailed = () => {
@@ -45,7 +47,8 @@ export const createJolicoinRewardHandler = (
45
47
  unlockOptionsHandler,
46
48
  initiateAndAwaitPayment,
47
49
  showUnlockWithCurrencyModal: showUnlockWithJolicoinModal,
48
- showUnloginModal
50
+ showUnloginModal,
51
+ hasSubscription
49
52
  }
50
53
  });
51
54
 
@@ -9,10 +9,12 @@ export const createSubscriptionRewardHandler = (
9
9
  httpClient: IHttpClient,
10
10
  {
11
11
  onSubSuccess,
12
- onSubFailed
12
+ onSubFailed,
13
+ hasSubscription
13
14
  }: {
14
15
  onSubSuccess?: () => void;
15
16
  onSubFailed?: () => void;
17
+ hasSubscription: () => boolean;
16
18
  }
17
19
  ): SubscriptionRewardsHandler => {
18
20
  const handleUnlockFailed = () => {
@@ -26,7 +28,8 @@ export const createSubscriptionRewardHandler = (
26
28
  handleSubSuccess: onSubSuccess,
27
29
  handleSubFailed: handleUnlockFailed,
28
30
  unlockOptionsHandler,
29
- showSubscriptionModal
31
+ showSubscriptionModal,
32
+ hasSubscription
30
33
  }
31
34
  });
32
35
 
@@ -17,6 +17,8 @@ import {
17
17
  import { rewardsCommands } from '../rewards-command';
18
18
  import { RewardsCommandType } from '@jolibox/types';
19
19
  import { refreshBalanceCache, warmupBalanceCache } from './cached-fetch-balance';
20
+ import { CURRENCY_HANDLERS } from '../commands/currency-handlers';
21
+ import { checkIfSupportSubscription } from '../../common';
20
22
 
21
23
  interface ICurrencyUnlockRes {
22
24
  code: 'SUCCESS' | 'BALANCE_NOT_ENOUGH' | 'EPISODE_LOCK_JUMP' | 'EPISODE_UNLOCK_ALREADY';
@@ -39,7 +41,8 @@ const createCommonCurrencyRewardHandler = (
39
41
  unlockOptionsHandler,
40
42
  initiateAndAwaitPayment,
41
43
  showUnlockWithCurrencyModal,
42
- showUnloginModal
44
+ showUnloginModal,
45
+ hasSubscription
43
46
  }
44
47
  }: {
45
48
  handlers: {
@@ -49,6 +52,7 @@ const createCommonCurrencyRewardHandler = (
49
52
  initiateAndAwaitPayment: ReturnType<typeof createInitiateAndAwaitPayment>;
50
53
  showUnlockWithCurrencyModal: ReturnType<typeof createShowUnlockWithCurrencyModal>;
51
54
  showUnloginModal: ReturnType<typeof createShowUnloginModal>;
55
+ hasSubscription?: () => boolean;
52
56
  };
53
57
  }
54
58
  ) => {
@@ -67,19 +71,36 @@ const createCommonCurrencyRewardHandler = (
67
71
  const commands = [`Rewards.${type}.usePayment`, `Rewards.${type}.useJolicoin`];
68
72
  return async (params: IAdBreakParams) => {
69
73
  try {
70
- let result = true;
71
- for (const command of commands) {
72
- const commandResult = await rewardsCommands.executeCommand(command as RewardsCommandType);
73
- result = result && commandResult.result !== 'FAILED';
74
- if (commandResult.result !== 'CONTINUE') {
75
- break;
74
+ const unlockOptions = await unlockOptionsHandler.getData();
75
+
76
+ const supportSubscription = checkIfSupportSubscription(unlockOptions, hasSubscription);
77
+ if (supportSubscription) {
78
+ // Use currency handler for balance and quantity calculation
79
+ const handler = CURRENCY_HANDLERS['JOLI_COIN'];
80
+ const newBalance = await handler.fetchBalance(httpClient);
81
+ const currentUser = handler.getCurrentUser(unlockOptions);
82
+ const newUser = handler.createUpdatedUser(newBalance?.balance ?? 0, currentUser.enableAutoDeduct);
83
+
84
+ if (!handler.isEnough(unlockOptions, newUser)) {
85
+ handleUnlockFailed?.(params);
86
+ return false;
87
+ }
88
+ } else {
89
+ let result = true;
90
+ for (const command of commands) {
91
+ const commandResult = await rewardsCommands.executeCommand(command as RewardsCommandType);
92
+ result = result && commandResult.result !== 'FAILED';
93
+ if (commandResult.result !== 'CONTINUE') {
94
+ break;
95
+ }
96
+ }
97
+ if (!result) {
98
+ handleUnlockFailed?.(params);
99
+ return false;
76
100
  }
77
101
  }
78
102
 
79
- if (!result) {
80
- handleUnlockFailed?.(params);
81
- return false;
82
- }
103
+ // success
83
104
 
84
105
  const config = CURRENCY_CONFIG[currencyType];
85
106
  const unlockWithCurrency = await httpClient.post<ICurrencyUnlockRes>(config.unlockEndpoint, {
@@ -143,6 +164,7 @@ export const createCommonJolicoinRewardHandler = (
143
164
  initiateAndAwaitPayment: ReturnType<typeof createInitiateAndAwaitPayment>;
144
165
  showUnlockWithCurrencyModal: ReturnType<typeof createShowUnlockWithCurrencyModal>;
145
166
  showUnloginModal: ReturnType<typeof createShowUnloginModal>;
167
+ hasSubscription?: () => boolean;
146
168
  };
147
169
  }
148
170
  ) => {
@@ -4,7 +4,7 @@ import { uuidv4 } from '@jolibox/common';
4
4
  import { UnlockOptionsEventName, IUnlockOptionsEvent } from '@/common/rewards/reward-emitter';
5
5
  import { IAdBreakParams } from '@/common/ads';
6
6
  import { EventPromiseHandler } from '../../event-listener';
7
- import { CURRENCY_CONFIG } from '../currency-config';
7
+ import { CURRENCY_CONFIG, CurrencyType } from '../currency-config';
8
8
 
9
9
  import {
10
10
  createInitiateAndAwaitPayment,
@@ -16,6 +16,8 @@ import { registerUseJolicoinCommand, createShowUnlockWithCurrencyModal } from '.
16
16
  import { rewardsCommands } from '../rewards-command';
17
17
  import { RewardsCommandType } from '@jolibox/types';
18
18
  import { refreshGemBalanceCache, warmupGemBalanceCache } from './cached-fetch-gem-balance';
19
+ import { CURRENCY_HANDLERS } from '../commands/currency-handlers';
20
+ import { checkIfSupportSubscription } from '../../common';
19
21
 
20
22
  interface IGemUnlockRes {
21
23
  code: 'SUCCESS' | 'BALANCE_NOT_ENOUGH' | 'EPISODE_LOCK_JUMP' | 'EPISODE_UNLOCK_ALREADY';
@@ -37,7 +39,8 @@ export const createCommonGemRewardHandler = (
37
39
  unlockOptionsHandler,
38
40
  initiateAndAwaitPayment,
39
41
  showUnlockWithCurrencyModal,
40
- showUnloginModal
42
+ showUnloginModal,
43
+ hasSubscription
41
44
  }
42
45
  }: {
43
46
  handlers: {
@@ -47,6 +50,7 @@ export const createCommonGemRewardHandler = (
47
50
  initiateAndAwaitPayment: ReturnType<typeof createInitiateAndAwaitPayment>;
48
51
  showUnlockWithCurrencyModal: ReturnType<typeof createShowUnlockWithCurrencyModal>;
49
52
  showUnloginModal: ReturnType<typeof createShowUnloginModal>;
53
+ hasSubscription?: () => boolean;
50
54
  };
51
55
  }
52
56
  ) => {
@@ -67,18 +71,35 @@ export const createCommonGemRewardHandler = (
67
71
 
68
72
  return async (params: IAdBreakParams) => {
69
73
  try {
70
- let result = true;
71
- for (const command of commands) {
72
- const commandResult = await rewardsCommands.executeCommand(command as RewardsCommandType);
73
- result = result && commandResult.result !== 'FAILED';
74
- if (commandResult.result !== 'CONTINUE') {
75
- break;
76
- }
77
- }
74
+ const unlockOptions = await unlockOptionsHandler.getData();
75
+ const supportSubscription = checkIfSupportSubscription(unlockOptions, hasSubscription);
78
76
 
79
- if (!result) {
80
- handleUnlockFailed?.(params);
81
- return false;
77
+ console.log('[GemHandler] supportSubscription', supportSubscription, unlockOptions);
78
+ if (supportSubscription) {
79
+ // Use currency handler for balance and quantity calculation
80
+ const handler = CURRENCY_HANDLERS['JOLI_GEM'];
81
+ const newBalance = await handler.fetchBalance(httpClient);
82
+ const currentUser = handler.getCurrentUser(unlockOptions);
83
+ const newUser = handler.createUpdatedUser(newBalance?.balance ?? 0, currentUser.enableAutoDeduct);
84
+
85
+ if (!handler.isEnough(unlockOptions, newUser)) {
86
+ handleUnlockFailed?.(params);
87
+ return false;
88
+ }
89
+ } else {
90
+ let result = true;
91
+ for (const command of commands) {
92
+ const commandResult = await rewardsCommands.executeCommand(command as RewardsCommandType);
93
+ console.log('[GemHandler] command result', commandResult, command);
94
+ result = result && commandResult.result !== 'FAILED';
95
+ if (commandResult.result !== 'CONTINUE') {
96
+ break;
97
+ }
98
+ }
99
+ if (!result) {
100
+ handleUnlockFailed?.(params);
101
+ return false;
102
+ }
82
103
  }
83
104
 
84
105
  const config = CURRENCY_CONFIG['GEM'];
@@ -6,3 +6,12 @@ export const unlockOptionsHandler = createEventPromiseHandler<
6
6
  IUnlockOptionsEvent,
7
7
  typeof UnlockOptionsEventName
8
8
  >(rewardsEmitter, UnlockOptionsEventName);
9
+
10
+ export const checkIfSupportSubscription = (
11
+ unlockOptions: IUnlockOptionsEvent,
12
+ extraCheck?: () => boolean
13
+ ) => {
14
+ const hasSubscription = unlockOptions.options?.some((option) => option.type === 'SUBSCRIPTION');
15
+ console.log('typeof extraCheck', typeof extraCheck, extraCheck?.());
16
+ return hasSubscription && (!extraCheck || extraCheck?.());
17
+ };
@@ -1,4 +1,6 @@
1
+ import { CURRENCY_HANDLERS, CurrencyType } from '../../coins/commands/currency-handlers';
1
2
  import { rewardsCommands } from '../../coins/rewards-command';
3
+ import { unlockOptionsHandler } from '../../common';
2
4
  import { createEventPromiseHandler } from '../../event-listener';
3
5
  import { IUseSubscriptionResultEvent, UseSubscriptionResultEventName } from '@/common/rewards/reward-emitter';
4
6
  import { rewardsEmitter, InvokeSubscriptionEventName } from '@/common/rewards/reward-emitter';
@@ -8,6 +10,20 @@ export const registerUseSubscriptionCommand = (params: {
8
10
  }) => {
9
11
  const { showSubscriptionModal } = params;
10
12
  rewardsCommands.registerCommand(`Rewards.subscription`, async () => {
13
+ const unlockOptions = await unlockOptionsHandler.getData();
14
+
15
+ const type = unlockOptions?.options?.find((option) => option.type === 'JOLI_GEM')?.type;
16
+ // Use currency handler for balance and quantity calculation
17
+ const handler = CURRENCY_HANDLERS[type ? 'JOLI_GEM' : 'JOLI_COIN'];
18
+ const currentUser = handler.getCurrentUser(unlockOptions);
19
+
20
+ // if enough, pass this command into JOLI_GEM or JOLI_COIN command
21
+ if (handler.isEnough(unlockOptions, currentUser)) {
22
+ return {
23
+ result: 'FAILED'
24
+ };
25
+ }
26
+
11
27
  const subscriptionResult = await showSubscriptionModal();
12
28
  return {
13
29
  result: subscriptionResult === 'SUCCESS' ? 'SUCCESS' : 'FAILED'
@@ -7,18 +7,28 @@ import { RewardsCommandType } from '@jolibox/types';
7
7
  import { createShowSubscriptionModal } from './commands/use-subscription';
8
8
  import { registerUseSubscriptionCommand } from './commands';
9
9
  import { context } from '@/common/context';
10
+ import { checkIfSupportSubscription } from '../common';
11
+ import { refreshBalanceCache } from '../coins/jolicoin/cached-fetch-balance';
12
+ import { refreshGemBalanceCache } from '../coins/joligem/cached-fetch-gem-balance';
10
13
 
11
14
  export const createCommonSubscriptionHandler = (
12
15
  type: 'SUBSCRIPTION',
13
16
  httpClient: IHttpClient,
14
17
  {
15
- handlers: { handleSubSuccess, handleSubFailed, unlockOptionsHandler, showSubscriptionModal }
18
+ handlers: {
19
+ handleSubSuccess,
20
+ handleSubFailed,
21
+ unlockOptionsHandler,
22
+ showSubscriptionModal,
23
+ hasSubscription
24
+ }
16
25
  }: {
17
26
  handlers: {
18
27
  handleSubSuccess?: () => void;
19
28
  handleSubFailed?: (params: IAdBreakParams) => void;
20
29
  unlockOptionsHandler: EventPromiseHandler<IUnlockOptionsEvent, typeof UnlockOptionsEventName>;
21
30
  showSubscriptionModal: ReturnType<typeof createShowSubscriptionModal>;
31
+ hasSubscription?: () => boolean;
22
32
  };
23
33
  }
24
34
  ) => {
@@ -31,6 +41,12 @@ export const createCommonSubscriptionHandler = (
31
41
 
32
42
  return async (params: IAdBreakParams) => {
33
43
  try {
44
+ const unlockOptions = await unlockOptionsHandler.getData();
45
+ const supportSubscription = checkIfSupportSubscription(unlockOptions, hasSubscription);
46
+ if (!supportSubscription) {
47
+ // pass to next handler
48
+ return false;
49
+ }
34
50
  let result = true;
35
51
  for (const command of commands) {
36
52
  const commandResult = await rewardsCommands.executeCommand(command as RewardsCommandType);
@@ -40,12 +56,11 @@ export const createCommonSubscriptionHandler = (
40
56
  }
41
57
  }
42
58
 
43
- if (!result) {
44
- handleSubFailed?.(params);
45
- return false;
46
- }
59
+ // refresh balance and gem balance cache.in case of user login, the balance and gem balance is not updated.
60
+ refreshBalanceCache(httpClient);
61
+ refreshGemBalanceCache(httpClient);
47
62
 
48
- console.log('-----unlockWithSubscription result-----');
63
+ console.log('-----unlockWithSubscription result-----', result);
49
64
  if (result) {
50
65
  try {
51
66
  params.adBreakDone?.({
@@ -84,6 +84,10 @@ export interface IUseModalFrequencyConfig {
84
84
  dailyMaxPopUps?: number;
85
85
  minInterval?: number;
86
86
  };
87
+ joinMember: {
88
+ dailyMaxPopUps?: number;
89
+ minInterval?: number;
90
+ };
87
91
  };
88
92
  loginGuide: {
89
93
  show: boolean;
@@ -146,6 +150,10 @@ export const DefaltJoliCoinUseAndCharge = {
146
150
  useJolicoin: {
147
151
  dailyMaxPopUps: 2,
148
152
  minInterval: 8 * 60 * 60 * 1000
153
+ },
154
+ joinMember: {
155
+ dailyMaxPopUps: 2,
156
+ minInterval: 8 * 60 * 60 * 1000
149
157
  }
150
158
  };
151
159