@jolibox/implement 1.2.4 → 1.2.5
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/.rush/temp/package-deps_build.json +24 -18
- package/dist/common/cache/__tests__/request-cache-service.test.d.ts +1 -0
- package/dist/common/cache/request-cache-service.d.ts +111 -0
- package/dist/common/report/base-tracker.d.ts +2 -1
- package/dist/common/rewards/cached-fetch-reward.d.ts +46 -0
- package/dist/common/rewards/cached-reward-service.d.ts +24 -0
- package/dist/common/rewards/fetch-reward.d.ts +2 -3
- package/dist/common/rewards/index.d.ts +2 -0
- package/dist/common/rewards/registers/utils/coins/jolicoin/cached-fetch-balance.d.ts +34 -0
- package/dist/common/rewards/registers/utils/coins/jolicoin/fetch-balance.d.ts +2 -1
- package/dist/common/rewards/registers/utils/coins/joligem/cached-fetch-gem-balance.d.ts +34 -0
- package/dist/common/rewards/registers/utils/coins/joligem/fetch-gem-balance.d.ts +2 -1
- package/dist/index.js +9 -9
- package/dist/index.native.js +33 -33
- package/dist/native/payment/payment-service.d.ts +36 -30
- package/implement.build.log +2 -2
- package/package.json +5 -5
- package/src/common/cache/__tests__/request-cache-service.test.ts +686 -0
- package/src/common/cache/request-cache-service.ts +393 -0
- package/src/common/report/base-tracker.ts +2 -2
- package/src/common/rewards/cached-fetch-reward.ts +241 -0
- package/src/common/rewards/cached-reward-service.ts +255 -0
- package/src/common/rewards/fetch-reward.ts +17 -93
- package/src/common/rewards/index.ts +3 -0
- package/src/common/rewards/registers/utils/coins/commands/use-payment.ts +8 -0
- package/src/common/rewards/registers/utils/coins/jolicoin/cached-fetch-balance.ts +177 -0
- package/src/common/rewards/registers/utils/coins/jolicoin/fetch-balance.ts +13 -1
- package/src/common/rewards/registers/utils/coins/jolicoin/jolicoin-handler.ts +2 -0
- package/src/common/rewards/registers/utils/coins/joligem/cached-fetch-gem-balance.ts +181 -0
- package/src/common/rewards/registers/utils/coins/joligem/fetch-gem-balance.ts +13 -1
- package/src/common/rewards/registers/utils/coins/joligem/gem-handler.ts +2 -0
- package/src/h5/api/ads.ts +5 -0
- package/src/h5/api/storage.ts +2 -2
- package/src/h5/http/index.ts +2 -2
- package/src/h5/report/event-tracker.ts +2 -2
- package/src/native/api/ads.ts +7 -1
- package/src/native/api/payment.ts +4 -4
- package/src/native/payment/__tests__/payment-service-simple.test.ts +97 -31
- package/src/native/payment/payment-service.ts +224 -210
- package/src/native/rewards/ui/payment-modal.ts +14 -7
|
@@ -5,149 +5,81 @@ import { StandardResponse } from '@jolibox/types';
|
|
|
5
5
|
import { applyNative } from '@jolibox/native-bridge';
|
|
6
6
|
import { innerFetch as fetch } from '@/native/network';
|
|
7
7
|
import type { PaymentResult } from './payment-helper';
|
|
8
|
+
import { RequestCacheService, RequestAdapter } from '@/common/cache/request-cache-service';
|
|
8
9
|
|
|
9
10
|
type PaymentPurchaseType = 'JOLI_COIN' | 'JOLI_GEM';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
JOLI_COIN: '/api/joli-coin/balance-detail',
|
|
13
|
-
JOLI_GEM: '/api/joli-gem/balance-detail'
|
|
14
|
-
};
|
|
12
|
+
// Request/Response interfaces for RequestCacheService
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
export interface CachedPaymentChoices {
|
|
18
|
-
choices: IPaymentChoice[];
|
|
19
|
-
timestamp: number;
|
|
20
|
-
expiresAt: number;
|
|
21
|
-
productIds: string[];
|
|
22
|
-
}
|
|
14
|
+
type PaymentRequest = Record<string, never>;
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
interface PaymentResponse {
|
|
17
|
+
balance: number;
|
|
18
|
+
enableAutoDeduct: boolean;
|
|
19
|
+
paymentChoices: IPaymentChoice[];
|
|
20
|
+
}
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
interface PaymentCacheData {
|
|
23
|
+
paymentChoices: IPaymentChoice[];
|
|
24
|
+
}
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const productsInfo = await PaymentService.getProductsInfo(CoinFetchUrlMap[type]);
|
|
39
|
-
this.productInfoCache[type] = productsInfo?.paymentChoices ?? [];
|
|
40
|
-
return this.productInfoCache[type];
|
|
41
|
-
}
|
|
26
|
+
interface PaymentRealTimeData {
|
|
27
|
+
balance: number;
|
|
28
|
+
enableAutoDeduct: boolean;
|
|
29
|
+
}
|
|
42
30
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
// Utility function for merging response data
|
|
32
|
+
function mergeResponseData(
|
|
33
|
+
responseData: { paymentChoices: IPaymentChoice[] },
|
|
34
|
+
data: { [appStoreProductId: string]: { price: string } }
|
|
35
|
+
) {
|
|
36
|
+
Object.keys(data).forEach((key) => {
|
|
37
|
+
const choice = responseData.paymentChoices.find((choice) => choice.appStoreProductId === key);
|
|
38
|
+
if (choice) {
|
|
39
|
+
choice.totalAmountStr = data[key].price;
|
|
50
40
|
}
|
|
41
|
+
});
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
appStoreProductId
|
|
57
|
-
}
|
|
58
|
-
)) as PaymentResult<{ totalAmount: string }>;
|
|
59
|
-
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private static mergeResponseData(
|
|
64
|
-
responseData: { paymentChoices: IPaymentChoice[] },
|
|
65
|
-
data: { [appStoreProductId: string]: { price: string } }
|
|
66
|
-
) {
|
|
67
|
-
Object.keys(data).forEach((key) => {
|
|
68
|
-
const choice = responseData.paymentChoices.find((choice) => choice.appStoreProductId === key);
|
|
69
|
-
if (choice) {
|
|
70
|
-
choice.totalAmountStr = data[key].price;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
responseData.paymentChoices = responseData.paymentChoices.filter(
|
|
75
|
-
(choice) => !isUndefinedOrNull(choice.totalAmountStr)
|
|
76
|
-
) as IPaymentChoice[];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
static async getProductsInfo(apiEndpoint: string): Promise<
|
|
80
|
-
| {
|
|
81
|
-
balance: number;
|
|
82
|
-
enableAutoDeduct: boolean;
|
|
83
|
-
paymentChoices: IPaymentChoice[];
|
|
84
|
-
}
|
|
85
|
-
| undefined
|
|
86
|
-
> {
|
|
87
|
-
return Promise.race([
|
|
88
|
-
this.getProductsInfoInternal(apiEndpoint),
|
|
89
|
-
new Promise<never>((_, reject) => {
|
|
90
|
-
setTimeout(() => {
|
|
91
|
-
reject(new Error('[PaymentService] Request timeout after 3 seconds'));
|
|
92
|
-
}, 3000);
|
|
93
|
-
})
|
|
94
|
-
]);
|
|
95
|
-
}
|
|
43
|
+
responseData.paymentChoices = responseData.paymentChoices.filter(
|
|
44
|
+
(choice) => !isUndefinedOrNull(choice.totalAmountStr)
|
|
45
|
+
) as IPaymentChoice[];
|
|
46
|
+
}
|
|
96
47
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
enableAutoDeduct: boolean;
|
|
110
|
-
paymentChoices: IPaymentChoice[];
|
|
111
|
-
}>
|
|
112
|
-
>(apiEndpoint, {
|
|
48
|
+
// Payment Request Adapter for RequestCacheService
|
|
49
|
+
class PaymentRequestAdapter
|
|
50
|
+
implements RequestAdapter<PaymentRequest, PaymentResponse, PaymentCacheData, PaymentRealTimeData>
|
|
51
|
+
{
|
|
52
|
+
// applyNative 的缓存
|
|
53
|
+
private static nativePriceCache = new Map<string, { [appStoreProductId: string]: { price: string } }>();
|
|
54
|
+
private static readonly NATIVE_PRICE_CACHE_DURATION = 30 * 60 * 1000; // 30分钟缓存
|
|
55
|
+
private static nativePriceCacheTimestamp = new Map<string, number>();
|
|
56
|
+
|
|
57
|
+
async execute(endpoint: string, _options?: PaymentRequest): Promise<PaymentResponse> {
|
|
58
|
+
// 获取服务端数据(余额等实时数据)
|
|
59
|
+
const { response } = await fetch<StandardResponse<PaymentResponse>>(endpoint, {
|
|
113
60
|
method: 'GET',
|
|
114
61
|
appendHostCookie: true,
|
|
115
62
|
responseType: 'json'
|
|
116
63
|
});
|
|
117
64
|
|
|
65
|
+
console.log('[PaymentService] get products info from server', endpoint, response.data);
|
|
66
|
+
|
|
118
67
|
if (!response.data?.data) {
|
|
119
68
|
throw new Error('get products info failed');
|
|
120
69
|
}
|
|
121
70
|
|
|
122
71
|
const serverData = response.data.data;
|
|
123
72
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
console.log('[PaymentService] Using cached paymentChoices');
|
|
129
|
-
|
|
130
|
-
// 使用缓存的 paymentChoices,但保持服务端的余额等实时数据
|
|
131
|
-
return {
|
|
132
|
-
balance: serverData.balance,
|
|
133
|
-
enableAutoDeduct: serverData.enableAutoDeduct,
|
|
134
|
-
paymentChoices: cachedChoices
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
console.log('[PaymentService] Fetching fresh paymentChoices from native');
|
|
139
|
-
|
|
140
|
-
// 缓存未命中,请求原生数据
|
|
141
|
-
const { data } = await applyNative('requestProductDetailsAsync', {
|
|
142
|
-
appStoreProductIds:
|
|
143
|
-
serverData.paymentChoices
|
|
144
|
-
?.filter((choice) => typeof choice.appStoreProductId === 'string')
|
|
145
|
-
.map((choice) => choice.appStoreProductId as string) ?? []
|
|
146
|
-
});
|
|
73
|
+
const appStoreProductIds =
|
|
74
|
+
serverData.paymentChoices
|
|
75
|
+
?.filter((choice) => typeof choice.appStoreProductId === 'string')
|
|
76
|
+
.map((choice) => choice.appStoreProductId as string) ?? [];
|
|
147
77
|
|
|
148
|
-
if (
|
|
149
|
-
this.
|
|
150
|
-
|
|
78
|
+
if (appStoreProductIds.length > 0) {
|
|
79
|
+
const nativeData = await this.getNativePriceData(appStoreProductIds);
|
|
80
|
+
if (nativeData) {
|
|
81
|
+
mergeResponseData(serverData, nativeData);
|
|
82
|
+
}
|
|
151
83
|
}
|
|
152
84
|
|
|
153
85
|
if (serverData.paymentChoices.length === 0) {
|
|
@@ -157,123 +89,205 @@ export class PaymentService {
|
|
|
157
89
|
return serverData;
|
|
158
90
|
}
|
|
159
91
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const cached =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
92
|
+
private async getNativePriceData(
|
|
93
|
+
appStoreProductIds: string[]
|
|
94
|
+
): Promise<{ [appStoreProductId: string]: { price: string } } | null> {
|
|
95
|
+
// 检查缓存
|
|
96
|
+
const cacheKey = appStoreProductIds.sort().join(',');
|
|
97
|
+
const cached = PaymentRequestAdapter.nativePriceCache.get(cacheKey);
|
|
98
|
+
const cacheTime = PaymentRequestAdapter.nativePriceCacheTimestamp.get(cacheKey) || 0;
|
|
99
|
+
|
|
100
|
+
if (cached && Date.now() - cacheTime < PaymentRequestAdapter.NATIVE_PRICE_CACHE_DURATION) {
|
|
101
|
+
console.log('[PaymentService] Using cached native price data');
|
|
102
|
+
return cached;
|
|
169
103
|
}
|
|
170
104
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
105
|
+
console.log('[PaymentService] Fetching fresh native price data');
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const { data } = await applyNative('requestProductDetailsAsync', {
|
|
109
|
+
appStoreProductIds
|
|
110
|
+
});
|
|
175
111
|
|
|
176
|
-
|
|
177
|
-
|
|
112
|
+
if (data) {
|
|
113
|
+
// cache
|
|
114
|
+
PaymentRequestAdapter.nativePriceCache.set(cacheKey, data);
|
|
115
|
+
PaymentRequestAdapter.nativePriceCacheTimestamp.set(cacheKey, Date.now());
|
|
116
|
+
}
|
|
178
117
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
118
|
+
return data;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.warn('[PaymentService] Failed to fetch native product details:', error);
|
|
121
|
+
throw error; // rethrow error, let upper layer handle it
|
|
183
122
|
}
|
|
123
|
+
}
|
|
184
124
|
|
|
185
|
-
|
|
125
|
+
extractCacheableData(response: PaymentResponse): PaymentCacheData {
|
|
126
|
+
return {
|
|
127
|
+
paymentChoices: response.paymentChoices
|
|
128
|
+
};
|
|
129
|
+
}
|
|
186
130
|
|
|
187
|
-
|
|
131
|
+
extractRealTimeData?: undefined;
|
|
132
|
+
mergeData(cached: PaymentCacheData, realTime: PaymentRealTimeData): PaymentResponse {
|
|
133
|
+
return {
|
|
134
|
+
balance: realTime.balance,
|
|
135
|
+
enableAutoDeduct: realTime.enableAutoDeduct,
|
|
136
|
+
paymentChoices: cached.paymentChoices
|
|
137
|
+
};
|
|
188
138
|
}
|
|
189
139
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
140
|
+
processCachedData(cached: PaymentCacheData): PaymentResponse {
|
|
141
|
+
// when only cached data is available, use default real-time data values
|
|
142
|
+
return {
|
|
143
|
+
balance: 0, // default balance
|
|
144
|
+
enableAutoDeduct: false, // default not enable auto deduct
|
|
145
|
+
paymentChoices: cached.paymentChoices
|
|
146
|
+
};
|
|
147
|
+
}
|
|
197
148
|
|
|
198
|
-
|
|
149
|
+
// clear native price cache static method
|
|
150
|
+
static clearNativePriceCache(): void {
|
|
151
|
+
this.nativePriceCache.clear();
|
|
152
|
+
this.nativePriceCacheTimestamp.clear();
|
|
153
|
+
console.log('[PaymentService] Cleared native price cache');
|
|
199
154
|
}
|
|
155
|
+
}
|
|
200
156
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
157
|
+
// Base Payment Service class
|
|
158
|
+
class BasePaymentService extends RequestCacheService<
|
|
159
|
+
PaymentRequest,
|
|
160
|
+
PaymentResponse,
|
|
161
|
+
PaymentCacheData,
|
|
162
|
+
PaymentRealTimeData
|
|
163
|
+
> {
|
|
164
|
+
// 失败计数器,按 endpoint 统计
|
|
165
|
+
private static failureCounters = new Map<string, number>();
|
|
166
|
+
private static readonly MAX_FAILURE_COUNT = 2;
|
|
167
|
+
|
|
168
|
+
constructor(private apiEndpoint: string, private paymentType: PaymentPurchaseType) {
|
|
169
|
+
super(new PaymentRequestAdapter(), {
|
|
170
|
+
duration: 10 * 60 * 1000, // 10分钟缓存 paymentChoices
|
|
171
|
+
timeout: 1000 // 1秒超时
|
|
172
|
+
});
|
|
206
173
|
}
|
|
207
174
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
175
|
+
async getProductsInfo(): Promise<PaymentResponse | undefined> {
|
|
176
|
+
// 检查失败计数,如果达到最大失败次数则直接抛出错误
|
|
177
|
+
const currentFailureCount = BasePaymentService.failureCounters.get(this.apiEndpoint) || 0;
|
|
178
|
+
if (currentFailureCount >= BasePaymentService.MAX_FAILURE_COUNT) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`getProductsInfo has failed more than ${BasePaymentService.MAX_FAILURE_COUNT} times for ${this.apiEndpoint}`
|
|
181
|
+
);
|
|
211
182
|
}
|
|
212
183
|
|
|
213
|
-
|
|
184
|
+
try {
|
|
185
|
+
const result = await this.request(this.apiEndpoint);
|
|
186
|
+
// 成功时重置失败计数器
|
|
187
|
+
BasePaymentService.failureCounters.delete(this.apiEndpoint);
|
|
188
|
+
return result;
|
|
189
|
+
} catch (error) {
|
|
190
|
+
// 失败时增加计数器
|
|
191
|
+
const newCount = currentFailureCount + 1;
|
|
192
|
+
BasePaymentService.failureCounters.set(this.apiEndpoint, newCount);
|
|
193
|
+
console.warn(
|
|
194
|
+
`[PaymentService] getProductsInfo failed (${newCount}/${
|
|
195
|
+
BasePaymentService.MAX_FAILURE_COUNT + 1
|
|
196
|
+
}) for ${this.apiEndpoint}:`,
|
|
197
|
+
error
|
|
198
|
+
);
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
214
201
|
}
|
|
215
202
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
203
|
+
async purchase(productId: string): Promise<PaymentResult<{ totalAmount: string }>> {
|
|
204
|
+
const productsInfo = await this.getProductsInfo();
|
|
205
|
+
const appStoreProductId = productsInfo?.paymentChoices.find(
|
|
206
|
+
(choice) => choice.productId === productId
|
|
207
|
+
)?.appStoreProductId;
|
|
208
|
+
if (!appStoreProductId) {
|
|
209
|
+
throw new Error('appStoreProductId not found');
|
|
210
|
+
}
|
|
224
211
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
212
|
+
const result = (await paymentHelper.invokePayment(
|
|
213
|
+
this.paymentType === 'JOLI_COIN' ? 'JOLI_COIN_IAP' : 'JOLI_GEM_IAP',
|
|
214
|
+
{
|
|
215
|
+
productId,
|
|
216
|
+
appStoreProductId
|
|
230
217
|
}
|
|
218
|
+
)) as PaymentResult<{ totalAmount: string }>;
|
|
231
219
|
|
|
232
|
-
|
|
233
|
-
});
|
|
220
|
+
return result;
|
|
234
221
|
}
|
|
235
222
|
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
this.paymentChoicesCache.delete(apiEndpoint);
|
|
240
|
-
console.log(`[PaymentService] Cleared cache for ${apiEndpoint}`);
|
|
241
|
-
} else {
|
|
242
|
-
this.paymentChoicesCache.clear();
|
|
243
|
-
console.log('[PaymentService] Cleared all payment choices cache');
|
|
244
|
-
}
|
|
223
|
+
// 获取产品信息(使用 RequestCacheService)
|
|
224
|
+
async getProductsInfoWithBalance(): Promise<PaymentResponse | undefined> {
|
|
225
|
+
return this.request(this.apiEndpoint);
|
|
245
226
|
}
|
|
246
227
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
} {
|
|
252
|
-
const now = Date.now();
|
|
253
|
-
let validCount = 0;
|
|
254
|
-
let expiredCount = 0;
|
|
255
|
-
|
|
256
|
-
for (const cached of this.paymentChoicesCache.values()) {
|
|
257
|
-
if (now > cached.expiresAt) {
|
|
258
|
-
expiredCount++;
|
|
259
|
-
} else {
|
|
260
|
-
validCount++;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
228
|
+
// 强制刷新产品信息
|
|
229
|
+
async refreshProductsInfo(): Promise<PaymentResponse | undefined> {
|
|
230
|
+
return this.forceRequest(this.apiEndpoint);
|
|
231
|
+
}
|
|
263
232
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
expiredCount
|
|
268
|
-
};
|
|
233
|
+
// 重置失败计数器(供测试使用)
|
|
234
|
+
static resetFailureCounters(): void {
|
|
235
|
+
this.failureCounters.clear();
|
|
269
236
|
}
|
|
270
237
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
238
|
+
// 获取失败计数器状态(供测试使用)
|
|
239
|
+
static getFailureCount(endpoint: string): number {
|
|
240
|
+
return this.failureCounters.get(endpoint) || 0;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// JOLI_COIN payment service
|
|
245
|
+
export class JoliCoinPaymentService extends BasePaymentService {
|
|
246
|
+
constructor() {
|
|
247
|
+
super('/api/joli-coin/balance-detail', 'JOLI_COIN');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// JOLI_GEM payment service
|
|
252
|
+
export class JoliGemPaymentService extends BasePaymentService {
|
|
253
|
+
constructor() {
|
|
254
|
+
super('/api/joli-gem/balance-detail', 'JOLI_GEM');
|
|
278
255
|
}
|
|
279
256
|
}
|
|
257
|
+
|
|
258
|
+
// service instance management
|
|
259
|
+
let joliCoinServiceInstance: JoliCoinPaymentService | null = null;
|
|
260
|
+
let joliGemServiceInstance: JoliGemPaymentService | null = null;
|
|
261
|
+
|
|
262
|
+
// create JOLI_COIN payment service instance
|
|
263
|
+
export const createJoliCoinPaymentService = (): JoliCoinPaymentService => {
|
|
264
|
+
if (joliCoinServiceInstance) {
|
|
265
|
+
return joliCoinServiceInstance;
|
|
266
|
+
}
|
|
267
|
+
joliCoinServiceInstance = new JoliCoinPaymentService();
|
|
268
|
+
return joliCoinServiceInstance;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// create JOLI_GEM payment service instance
|
|
272
|
+
export const createJoliGemPaymentService = (): JoliGemPaymentService => {
|
|
273
|
+
if (joliGemServiceInstance) {
|
|
274
|
+
return joliGemServiceInstance;
|
|
275
|
+
}
|
|
276
|
+
joliGemServiceInstance = new JoliGemPaymentService();
|
|
277
|
+
return joliGemServiceInstance;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// 导出原生价格缓存清理方法供测试使用
|
|
281
|
+
export const clearNativePriceCache = (): void => {
|
|
282
|
+
PaymentRequestAdapter.clearNativePriceCache();
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// 导出失败计数器重置方法供测试使用
|
|
286
|
+
export const resetFailureCounters = (): void => {
|
|
287
|
+
BasePaymentService.resetFailureCounters();
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// 导出获取失败计数方法供测试使用
|
|
291
|
+
export const getFailureCount = (endpoint: string): number => {
|
|
292
|
+
return BasePaymentService.getFailureCount(endpoint);
|
|
293
|
+
};
|
|
@@ -28,7 +28,7 @@ import { track } from '@/native/report';
|
|
|
28
28
|
import { updateAutoDeductConfig } from './utils';
|
|
29
29
|
import { createEventPromiseHandler } from '@/common/rewards/registers/utils/event-listener';
|
|
30
30
|
import { TrackEvent } from '@jolibox/types';
|
|
31
|
-
import {
|
|
31
|
+
import { createJoliCoinPaymentService, createJoliGemPaymentService } from '@/native/payment/payment-service';
|
|
32
32
|
|
|
33
33
|
// 货币配置映射
|
|
34
34
|
interface CurrencyPaymentConfig {
|
|
@@ -85,6 +85,9 @@ const CURRENCY_PAYMENT_CONFIG: Record<'JOLI_COIN' | 'JOLI_GEM', CurrencyPaymentC
|
|
|
85
85
|
|
|
86
86
|
const loading = createLoading();
|
|
87
87
|
|
|
88
|
+
const JoliCoinPaymentService = createJoliCoinPaymentService();
|
|
89
|
+
const JoliGemPaymentService = createJoliGemPaymentService();
|
|
90
|
+
|
|
88
91
|
const modalUseFrequencyConfig = createEventPromiseHandler<
|
|
89
92
|
IUseModalFrequencyConfig,
|
|
90
93
|
typeof UseModalFrequencyEventName
|
|
@@ -114,6 +117,7 @@ rewardsEmitter.on(
|
|
|
114
117
|
await loading.show({
|
|
115
118
|
duration: 3000
|
|
116
119
|
});
|
|
120
|
+
|
|
117
121
|
const config = await modalUseFrequencyConfig.getData();
|
|
118
122
|
const { canShow: canShowPaymentModal } = await paymentConfig.frequencyChecker(
|
|
119
123
|
config.joliCoinUseAndCharge[paymentConfig.frequencyConfigKey]
|
|
@@ -134,7 +138,9 @@ rewardsEmitter.on(
|
|
|
134
138
|
await loading.show({
|
|
135
139
|
duration: 3000
|
|
136
140
|
});
|
|
137
|
-
const
|
|
141
|
+
const paymentService = currencyType === 'JOLI_COIN' ? JoliCoinPaymentService : JoliGemPaymentService;
|
|
142
|
+
// get product info from cache, if not found, get from server and cache it
|
|
143
|
+
const balenceDetails = await paymentService.getProductsInfo();
|
|
138
144
|
loading.hide();
|
|
139
145
|
|
|
140
146
|
if (!balenceDetails) {
|
|
@@ -158,7 +164,7 @@ rewardsEmitter.on(
|
|
|
158
164
|
...choice,
|
|
159
165
|
totalAmountStr: choice.totalAmountStr ?? ''
|
|
160
166
|
})) ?? [],
|
|
161
|
-
enableAutoDeduct:
|
|
167
|
+
enableAutoDeduct: params.enableAutoDeduct
|
|
162
168
|
},
|
|
163
169
|
buttons: {
|
|
164
170
|
confirm: {
|
|
@@ -183,8 +189,8 @@ rewardsEmitter.on(
|
|
|
183
189
|
return;
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
const
|
|
187
|
-
if ((
|
|
192
|
+
const newBalence = await paymentService.refreshProductsInfo();
|
|
193
|
+
if ((newBalence?.balance ?? 0) >= params.quantity) {
|
|
188
194
|
rewardsEmitter.emit(PaymentResultEventName, {
|
|
189
195
|
paymentResult: 'SUCCESS',
|
|
190
196
|
currency: currencyType
|
|
@@ -193,6 +199,7 @@ rewardsEmitter.on(
|
|
|
193
199
|
return;
|
|
194
200
|
}
|
|
195
201
|
}
|
|
202
|
+
// invoke native payment
|
|
196
203
|
console.log('invokeNativePayment', productId);
|
|
197
204
|
const appStoreProductId = balenceDetails?.paymentChoices?.find(
|
|
198
205
|
(choice) => choice.productId === productId
|
|
@@ -280,9 +287,9 @@ rewardsEmitter.on(
|
|
|
280
287
|
|
|
281
288
|
/** preload payment details */
|
|
282
289
|
|
|
283
|
-
|
|
290
|
+
JoliCoinPaymentService.getProductsInfo().catch((e) => {
|
|
284
291
|
console.error('preload joli coin payment details failed', e);
|
|
285
292
|
});
|
|
286
|
-
|
|
293
|
+
JoliGemPaymentService.getProductsInfo().catch((e) => {
|
|
287
294
|
console.error('preload joli gem payment details failed', e);
|
|
288
295
|
});
|