@jolibox/implement 1.2.7 → 1.2.9-beta.9
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 +38 -28
- package/dist/common/rewards/registers/use-gem-only.d.ts +2 -1
- package/dist/common/rewards/registers/use-gem.d.ts +2 -1
- package/dist/common/rewards/registers/use-jolicoin-only.d.ts +2 -1
- package/dist/common/rewards/registers/use-jolicoin.d.ts +2 -1
- package/dist/common/rewards/registers/use-subscription.d.ts +2 -1
- package/dist/common/rewards/registers/utils/coins/jolicoin/jolicoin-handler.d.ts +1 -0
- package/dist/common/rewards/registers/utils/coins/joligem/gem-handler.d.ts +2 -1
- package/dist/common/rewards/registers/utils/common.d.ts +1 -0
- package/dist/common/rewards/registers/utils/subscription/sub-handler.d.ts +2 -1
- package/dist/common/rewards/reward-emitter.d.ts +8 -0
- package/dist/common/rewards/type.d.ts +1 -1
- package/dist/common/utils/index.d.ts +1 -1
- package/dist/index.js +25 -25
- package/dist/index.native.js +54 -54
- package/dist/native/payment/utils/__tests__/cache-with-storage.test.d.ts +1 -0
- package/dist/native/payment/utils/cache-with-storage.d.ts +7 -0
- package/dist/native/rewards/check-frequency.d.ts +8 -0
- package/dist/native/rewards/index.d.ts +1 -0
- package/dist/native/rewards/ui/subscription-modal.d.ts +1 -0
- package/dist/native/subscription/index.d.ts +3 -0
- package/dist/native/subscription/registers/base.d.ts +22 -0
- package/dist/native/subscription/registers/sub-app.d.ts +21 -0
- package/dist/native/subscription/registers/type.d.ts +10 -0
- package/dist/native/subscription/subscription-helper.d.ts +22 -0
- package/dist/native/subscription/subscription-service.d.ts +43 -0
- package/dist/native/subscription/type.d.ts +18 -0
- package/implement.build.log +2 -2
- package/package.json +5 -5
- package/src/common/context/index.ts +4 -1
- package/src/common/rewards/cached-fetch-reward.ts +16 -1
- package/src/common/rewards/registers/use-gem-only.ts +5 -2
- package/src/common/rewards/registers/use-gem.ts +5 -2
- package/src/common/rewards/registers/use-jolicoin-only.ts +5 -2
- package/src/common/rewards/registers/use-jolicoin.ts +5 -2
- package/src/common/rewards/registers/use-subscription.ts +5 -2
- package/src/common/rewards/registers/utils/coins/jolicoin/jolicoin-handler.ts +33 -11
- package/src/common/rewards/registers/utils/coins/joligem/gem-handler.ts +34 -13
- package/src/common/rewards/registers/utils/common.ts +9 -0
- package/src/common/rewards/registers/utils/subscription/commands/use-subscription.ts +16 -0
- package/src/common/rewards/registers/utils/subscription/sub-handler.ts +23 -7
- package/src/common/rewards/reward-emitter.ts +8 -0
- package/src/common/rewards/type.ts +1 -1
- package/src/common/utils/index.ts +1 -1
- package/src/h5/api/ads.ts +6 -3
- package/src/h5/bootstrap/auth/__tests__/auth.test.ts +15 -9
- package/src/h5/bootstrap/auth/sub.ts +1 -1
- package/src/native/api/ads.ts +43 -6
- package/src/native/api/call-host-method.ts +5 -61
- package/src/native/api/login.ts +22 -7
- package/src/native/api/payment.ts +78 -3
- package/src/native/payment/__tests__/payment-service-simple.test.ts +14 -1
- package/src/native/payment/payment-service.ts +26 -27
- package/src/native/payment/utils/__tests__/cache-with-storage.test.ts +414 -0
- package/src/native/payment/utils/cache-with-storage.ts +112 -0
- package/src/native/rewards/check-frequency.ts +6 -0
- package/src/native/rewards/index.ts +1 -0
- package/src/native/rewards/ui/payment-modal.ts +2 -2
- package/src/native/rewards/ui/subscription-modal.ts +81 -0
- package/src/native/subscription/index.ts +12 -0
- package/src/native/subscription/registers/base.ts +88 -0
- package/src/native/subscription/registers/sub-app.ts +258 -0
- package/src/native/subscription/registers/type.ts +13 -0
- package/src/native/subscription/subscription-helper.ts +53 -0
- package/src/native/subscription/subscription-service.ts +339 -0
- package/src/native/subscription/type.ts +18 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { checkSubscriptionFrequency, updateSubscriptionFrequency } from '../check-frequency';
|
|
2
|
+
import {
|
|
3
|
+
IUseModalFrequencyConfig,
|
|
4
|
+
rewardsEmitter,
|
|
5
|
+
UseModalFrequencyEventName,
|
|
6
|
+
InvokeSubscriptionEventName,
|
|
7
|
+
UseSubscriptionResultEventName
|
|
8
|
+
} from '@/common/rewards/reward-emitter';
|
|
9
|
+
import { createEventPromiseHandler } from '@/common/rewards/registers/utils/event-listener';
|
|
10
|
+
import { canIUseNative } from '@/native/api/base';
|
|
11
|
+
import { subscriptionService } from '@/native/subscription';
|
|
12
|
+
import { createLoading } from '@jolibox/ui';
|
|
13
|
+
|
|
14
|
+
interface CurrencyModalConfig {
|
|
15
|
+
frequencyConfigKey: keyof IUseModalFrequencyConfig['joliCoinUseAndCharge'];
|
|
16
|
+
frequencyChecker: (config: {
|
|
17
|
+
dailyMaxPopUps?: number;
|
|
18
|
+
minInterval?: number;
|
|
19
|
+
}) => Promise<{ canShow: boolean; isFirst: boolean }>;
|
|
20
|
+
frequencyUpdater: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const SUBSCRIPTION_MODAL_CONFIG: Record<'SUBSCRIPTION', CurrencyModalConfig> = {
|
|
24
|
+
SUBSCRIPTION: {
|
|
25
|
+
frequencyConfigKey: 'joinMember',
|
|
26
|
+
frequencyChecker: checkSubscriptionFrequency,
|
|
27
|
+
frequencyUpdater: updateSubscriptionFrequency
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const loading = createLoading();
|
|
32
|
+
|
|
33
|
+
const modalUseFrequencyConfig = createEventPromiseHandler<
|
|
34
|
+
IUseModalFrequencyConfig,
|
|
35
|
+
typeof UseModalFrequencyEventName
|
|
36
|
+
>(rewardsEmitter, UseModalFrequencyEventName);
|
|
37
|
+
|
|
38
|
+
modalUseFrequencyConfig.getData();
|
|
39
|
+
/**
|
|
40
|
+
* confirm currency modal - 支持 jolicoin 和 gem
|
|
41
|
+
*/
|
|
42
|
+
rewardsEmitter.on(InvokeSubscriptionEventName, async (type: 'SUBSCRIPTION') => {
|
|
43
|
+
// 获取货币类型配置
|
|
44
|
+
const modalConfig = SUBSCRIPTION_MODAL_CONFIG['SUBSCRIPTION'];
|
|
45
|
+
|
|
46
|
+
if (!canIUseNative('invokeSubscriptionPanelSync')) {
|
|
47
|
+
//TODO: show Toast
|
|
48
|
+
console.info('invokeSubscriptionPanelSync not supported');
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
rewardsEmitter.emit(UseSubscriptionResultEventName, { result: 'FAILED' });
|
|
51
|
+
}, 0);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await loading.show({
|
|
56
|
+
duration: 3000
|
|
57
|
+
});
|
|
58
|
+
const config = await modalUseFrequencyConfig.getData();
|
|
59
|
+
const { canShow: canShowUseModal, isFirst: isFirstUseModal } = await modalConfig.frequencyChecker(
|
|
60
|
+
config.joliCoinUseAndCharge[modalConfig.frequencyConfigKey]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
loading.hide();
|
|
64
|
+
|
|
65
|
+
// If not used directly, then check frequency control.
|
|
66
|
+
if (!canShowUseModal) {
|
|
67
|
+
// return by frequency control
|
|
68
|
+
rewardsEmitter.emit(UseSubscriptionResultEventName, { result: 'FAILED' });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { data, code } = await subscriptionService.invokeSubscriptionPanel();
|
|
73
|
+
if (code == 'SUCCESS' && data?.subPlanId) {
|
|
74
|
+
rewardsEmitter.emit(UseSubscriptionResultEventName, { result: 'SUCCESS' });
|
|
75
|
+
} else {
|
|
76
|
+
rewardsEmitter.emit(UseSubscriptionResultEventName, { result: 'FAILED' });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 根据货币类型更新使用频次
|
|
80
|
+
await modalConfig.frequencyUpdater();
|
|
81
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './subscription-service';
|
|
2
|
+
export * from './type';
|
|
3
|
+
|
|
4
|
+
// Export subscription helper
|
|
5
|
+
import { subscriptionHelper } from './subscription-helper';
|
|
6
|
+
import { createSubAppSubscriptionHandler } from './registers/sub-app';
|
|
7
|
+
import { createSubscriptionService } from './subscription-service';
|
|
8
|
+
|
|
9
|
+
export const subscriptionService = createSubscriptionService();
|
|
10
|
+
|
|
11
|
+
// Register sub-app subscription handler
|
|
12
|
+
subscriptionHelper.registerSubscriptionHandler('SUB_APP', createSubAppSubscriptionHandler());
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { StandardResponse } from '@jolibox/types';
|
|
2
|
+
import type { IPlaceSubscriptionOrderResponse } from './type';
|
|
3
|
+
import { SubscriptionErrorCodeMap } from './type';
|
|
4
|
+
import { UserPaymentError, InternalPaymentError } from '@jolibox/common';
|
|
5
|
+
import { context } from '@/common/context';
|
|
6
|
+
import { innerFetch as fetch } from '@/native/network';
|
|
7
|
+
|
|
8
|
+
type SubscriptionStatus = 'INIT' | 'PENDING' | 'SUCCESS' | 'FAILED';
|
|
9
|
+
|
|
10
|
+
const createSubscriptionErrorFactory = () => {
|
|
11
|
+
const [createSubscriptionError, createSubscriptionInternalError] = [
|
|
12
|
+
UserPaymentError,
|
|
13
|
+
InternalPaymentError
|
|
14
|
+
].map((ctor) => {
|
|
15
|
+
return (errMsg: string, errNo: number, extra?: Record<string, unknown>) => {
|
|
16
|
+
return new ctor(errMsg, errNo, extra);
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
createSubscriptionError,
|
|
21
|
+
createSubscriptionInternalError
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const { createSubscriptionError, createSubscriptionInternalError } = createSubscriptionErrorFactory();
|
|
26
|
+
export { createSubscriptionError, createSubscriptionInternalError };
|
|
27
|
+
|
|
28
|
+
const CALLBACK_HOST = context.testMode ? 'https://stg-game.jolibox.com' : 'https://game.jolibox.com';
|
|
29
|
+
|
|
30
|
+
export abstract class BaseSubscriptionRegister<
|
|
31
|
+
T extends Record<string, any>,
|
|
32
|
+
E extends Record<string, any>,
|
|
33
|
+
R = void
|
|
34
|
+
> {
|
|
35
|
+
private _orderId: string | null = null;
|
|
36
|
+
private _orderStatus: SubscriptionStatus = 'INIT';
|
|
37
|
+
|
|
38
|
+
startSubscription = async (params: T): Promise<StandardResponse<R>> => {
|
|
39
|
+
if (this._orderId) {
|
|
40
|
+
await this.cancelOrder(this.orderId as string);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { code, message } = await this.placeOrder(params);
|
|
44
|
+
if (code !== 'SUCCESS') {
|
|
45
|
+
throw createSubscriptionError(message, SubscriptionErrorCodeMap.PlaceOrderFailed);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// invoke subscription flow
|
|
49
|
+
return await this.subscribe(params);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
protected generateCallbackUrl = () => {
|
|
53
|
+
// Set subscription callback parameters
|
|
54
|
+
const originalSearch = new URLSearchParams('');
|
|
55
|
+
originalSearch.set('utm_source', context.deviceInfo.platform);
|
|
56
|
+
originalSearch.set(
|
|
57
|
+
'joliSource',
|
|
58
|
+
context.encodeJoliSourceQuery({
|
|
59
|
+
...this.generateSubscriptionContext()
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
return `${CALLBACK_HOST}/subscription/callback?${originalSearch.toString()}`;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
protected setOrderId(orderId: string) {
|
|
66
|
+
this._orderId = orderId;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected setOrderStatus(status: SubscriptionStatus) {
|
|
70
|
+
this._orderStatus = status;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected createSubscriptionError = createSubscriptionError;
|
|
74
|
+
protected createSubscriptionInternalError = createSubscriptionInternalError;
|
|
75
|
+
|
|
76
|
+
abstract placeOrder(params: T): Promise<StandardResponse<IPlaceSubscriptionOrderResponse<E>>>;
|
|
77
|
+
abstract cancelOrder(orderId: string): Promise<StandardResponse<void>>;
|
|
78
|
+
abstract generateSubscriptionContext(): Record<string, any>;
|
|
79
|
+
abstract subscribe(params: T): Promise<StandardResponse<R>>;
|
|
80
|
+
|
|
81
|
+
get orderId(): string | null {
|
|
82
|
+
return this._orderId;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get status(): SubscriptionStatus {
|
|
86
|
+
return this._orderStatus;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { StandardResponse } from '@jolibox/types';
|
|
2
|
+
import { context } from '@/common/context';
|
|
3
|
+
import { innerFetch as fetch } from '@/native/network';
|
|
4
|
+
import { BaseSubscriptionRegister } from './base';
|
|
5
|
+
import type { IPlaceSubscriptionOrderResponse } from './type';
|
|
6
|
+
import { PaymentErrorCodeMap } from '@/native/payment/registers/type';
|
|
7
|
+
import { createPaymentInternalError } from '@/native/payment/registers/base';
|
|
8
|
+
import { ResponseType } from '@jolibox/types';
|
|
9
|
+
import { Deferred } from '@jolibox/common';
|
|
10
|
+
import { invokeNative, onNative } from '@jolibox/native-bridge';
|
|
11
|
+
import { isNumber } from '@jolibox/common';
|
|
12
|
+
import { SubscriptionErrorCodeMap } from './type';
|
|
13
|
+
|
|
14
|
+
export interface IPlaceOrderSubAppParams {
|
|
15
|
+
basePlanId: string;
|
|
16
|
+
appStoreProductId: string;
|
|
17
|
+
productId: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ISubAppProductInfo {
|
|
21
|
+
subPlanId: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ISubAppSubscriptionContext {
|
|
25
|
+
gameId: string;
|
|
26
|
+
productId: string;
|
|
27
|
+
planId: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** global subscription */
|
|
31
|
+
|
|
32
|
+
onNative('onGlobalSubscriptionStateChange', (data) => {
|
|
33
|
+
const { status, orderResponse } = data;
|
|
34
|
+
console.log('[onGlobalSubscriptionStateChange] data', data);
|
|
35
|
+
if (status === 'SUCCESS' && (orderResponse?.subPlanId || orderResponse?.tickedId)) {
|
|
36
|
+
// user must be logined
|
|
37
|
+
context.onEnvConfigChanged({
|
|
38
|
+
hostUserInfo: {
|
|
39
|
+
isLogin: true,
|
|
40
|
+
isSubUser: true
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const pendingSubscriptions = new Map<string, Deferred<StandardResponse<{ subPlanId: string }>>>();
|
|
47
|
+
|
|
48
|
+
onNative('onSubscritptionStateChange', (data) => {
|
|
49
|
+
const { subUUID, status, orderResponse } = data;
|
|
50
|
+
|
|
51
|
+
console.log('[onSubscritptionStateChange] data', data);
|
|
52
|
+
const deferred = pendingSubscriptions.get(subUUID);
|
|
53
|
+
if (!deferred) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (status === 'SUCCESS') {
|
|
58
|
+
deferred.resolve({
|
|
59
|
+
code: 'SUCCESS' as ResponseType,
|
|
60
|
+
message: 'jolicoin payment success',
|
|
61
|
+
data: { subPlanId: orderResponse?.subPlanId ?? orderResponse.tickedId }
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
if (status === 'FAILED') {
|
|
65
|
+
if (isNumber(orderResponse?.errNo) && orderResponse?.errNo !== 0) {
|
|
66
|
+
throw createPaymentInternalError(
|
|
67
|
+
`subscription failed: ${orderResponse?.errMsg}`,
|
|
68
|
+
SubscriptionErrorCodeMap.SubscriptionFailed
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
deferred.resolve({
|
|
73
|
+
code: status as ResponseType,
|
|
74
|
+
message: orderResponse?.errMsg ?? 'UNKNOWN_ERROR'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Clean up map
|
|
79
|
+
pendingSubscriptions.delete(subUUID);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const internalSubscribe = async (params: {
|
|
83
|
+
appStoreProductId: string;
|
|
84
|
+
basePlanId: string;
|
|
85
|
+
productId: string;
|
|
86
|
+
appAccountToken?: string;
|
|
87
|
+
}) => {
|
|
88
|
+
const deferred = new Deferred<StandardResponse<{ subPlanId: string }>>();
|
|
89
|
+
let targetOrderUUID: string | undefined;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
console.log('[internalSubscribe] requestSubscriptionSync params', params);
|
|
93
|
+
const response = invokeNative('requestSubscriptionSync', {
|
|
94
|
+
params: {
|
|
95
|
+
basePlanId: params.basePlanId,
|
|
96
|
+
appStoreProductId: params.appStoreProductId,
|
|
97
|
+
productId: params.productId
|
|
98
|
+
},
|
|
99
|
+
...(params.appAccountToken
|
|
100
|
+
? {
|
|
101
|
+
paymentBody: {
|
|
102
|
+
appAccountToken: params.appAccountToken,
|
|
103
|
+
appStoreProductId: params.appStoreProductId
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
: {})
|
|
107
|
+
});
|
|
108
|
+
targetOrderUUID = response.data?.subUUID;
|
|
109
|
+
|
|
110
|
+
if (!targetOrderUUID) {
|
|
111
|
+
throw createPaymentInternalError(
|
|
112
|
+
'subUUID is null',
|
|
113
|
+
PaymentErrorCodeMap.JolicoinPlaceOrderRequestFailed
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
pendingSubscriptions.set(targetOrderUUID, deferred);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
throw createPaymentInternalError(
|
|
120
|
+
JSON.stringify(e) ?? 'jolicoin payment failed',
|
|
121
|
+
PaymentErrorCodeMap.JolicoinPlaceOrderRequestFailed
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return deferred.promise;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Android 实现 - 不需要 placeOrder
|
|
129
|
+
class SubAppAndroidSubscriptionRegister extends BaseSubscriptionRegister<
|
|
130
|
+
IPlaceOrderSubAppParams,
|
|
131
|
+
ISubAppProductInfo,
|
|
132
|
+
{ subPlanId: string }
|
|
133
|
+
> {
|
|
134
|
+
constructor(private readonly productId: string, private readonly planId: string) {
|
|
135
|
+
super();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async placeOrder(
|
|
139
|
+
_params: IPlaceOrderSubAppParams
|
|
140
|
+
): Promise<StandardResponse<IPlaceSubscriptionOrderResponse<ISubAppProductInfo>>> {
|
|
141
|
+
// Android 不需要实际下单,直接返回成功
|
|
142
|
+
return {
|
|
143
|
+
code: 'SUCCESS',
|
|
144
|
+
message: 'Android subscription order placed locally'
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
cancelOrder(_orderId: string): Promise<StandardResponse<void>> {
|
|
149
|
+
// Android 本地取消
|
|
150
|
+
return Promise.resolve({
|
|
151
|
+
code: 'SUCCESS',
|
|
152
|
+
message: 'Android order cancelled locally',
|
|
153
|
+
data: undefined
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
generateSubscriptionContext(): ISubAppSubscriptionContext {
|
|
158
|
+
return {
|
|
159
|
+
gameId: context.mpId,
|
|
160
|
+
productId: this.productId,
|
|
161
|
+
planId: this.planId
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async subscribe(params: IPlaceOrderSubAppParams): Promise<StandardResponse<{ subPlanId: string }>> {
|
|
166
|
+
const response = await internalSubscribe(params);
|
|
167
|
+
return response;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// iOS 实现 - 需要 placeOrder
|
|
172
|
+
class SubAppIOSSubscriptionRegister extends BaseSubscriptionRegister<
|
|
173
|
+
IPlaceOrderSubAppParams,
|
|
174
|
+
ISubAppProductInfo,
|
|
175
|
+
{ subPlanId: string }
|
|
176
|
+
> {
|
|
177
|
+
private subscriptionToken: string | undefined;
|
|
178
|
+
|
|
179
|
+
constructor(private readonly productId: string, private readonly planId: string) {
|
|
180
|
+
super();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async placeOrder(
|
|
184
|
+
params: IPlaceOrderSubAppParams
|
|
185
|
+
): Promise<StandardResponse<IPlaceSubscriptionOrderResponse<ISubAppProductInfo>>> {
|
|
186
|
+
console.log('----start place order for iOS subscription----');
|
|
187
|
+
|
|
188
|
+
const env = invokeNative('envSync');
|
|
189
|
+
const accountToken = env?.data?.hostUserInfo?.appAccountToken;
|
|
190
|
+
if (!accountToken) {
|
|
191
|
+
throw createPaymentInternalError(
|
|
192
|
+
'appAccountToken is null',
|
|
193
|
+
PaymentErrorCodeMap.JolicoinPlaceOrderRequestFailed
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
this.subscriptionToken = accountToken;
|
|
197
|
+
return {
|
|
198
|
+
code: 'SUCCESS' as ResponseType,
|
|
199
|
+
message: 'placeorder sub-app success in native'
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async cancelOrder(orderId: string): Promise<StandardResponse<void>> {
|
|
204
|
+
// Android 本地取消
|
|
205
|
+
return Promise.resolve({
|
|
206
|
+
code: 'SUCCESS',
|
|
207
|
+
message: 'Android order cancelled locally',
|
|
208
|
+
data: undefined
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
generateSubscriptionContext(): ISubAppSubscriptionContext {
|
|
213
|
+
return {
|
|
214
|
+
gameId: context.mpId,
|
|
215
|
+
productId: this.productId,
|
|
216
|
+
planId: this.planId
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async subscribe(params: IPlaceOrderSubAppParams): Promise<StandardResponse<{ subPlanId: string }>> {
|
|
221
|
+
return await internalSubscribe({ ...params, appAccountToken: this.subscriptionToken });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const SubAppSubscriptionRegisterMap = {
|
|
226
|
+
android: SubAppAndroidSubscriptionRegister,
|
|
227
|
+
ios: SubAppIOSSubscriptionRegister
|
|
228
|
+
} as const;
|
|
229
|
+
|
|
230
|
+
type SubAppSubscriptionRegister = SubAppAndroidSubscriptionRegister | SubAppIOSSubscriptionRegister;
|
|
231
|
+
|
|
232
|
+
export function createSubAppSubscriptionHandler() {
|
|
233
|
+
const productSubscriptionMap = new Map<string, SubAppSubscriptionRegister>();
|
|
234
|
+
|
|
235
|
+
return async (params: { productId: string; appStoreProductId: string; planId: string }) => {
|
|
236
|
+
console.log('[SubAppSubscriptionHandler] createSubAppSubscriptionHandler params', params);
|
|
237
|
+
const { productId, planId } = params;
|
|
238
|
+
const cacheKey = `${productId}_${planId}`;
|
|
239
|
+
|
|
240
|
+
let instance = productSubscriptionMap.get(cacheKey);
|
|
241
|
+
if (!instance) {
|
|
242
|
+
console.log('[SubAppSubscriptionHandler] createSubAppSubscriptionHandler instance not found', cacheKey);
|
|
243
|
+
const Register =
|
|
244
|
+
SubAppSubscriptionRegisterMap[context.platform as keyof typeof SubAppSubscriptionRegisterMap];
|
|
245
|
+
if (!Register) {
|
|
246
|
+
throw new Error(`Platform ${context.platform} not supported for sub-app subscription`);
|
|
247
|
+
}
|
|
248
|
+
instance = new Register(productId, planId);
|
|
249
|
+
productSubscriptionMap.set(cacheKey, instance);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return instance.startSubscription({
|
|
253
|
+
basePlanId: planId,
|
|
254
|
+
appStoreProductId: params.appStoreProductId,
|
|
255
|
+
productId
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StandardResponse } from '@jolibox/types';
|
|
2
|
+
|
|
3
|
+
export interface IPlaceSubscriptionOrderResponse<T = any> {
|
|
4
|
+
orderId: string;
|
|
5
|
+
productInfo: T;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const SubscriptionErrorCodeMap = {
|
|
9
|
+
PlaceOrderFailed: 1001,
|
|
10
|
+
CancelOrderFailed: 1002,
|
|
11
|
+
SubscriptionFailed: 1003,
|
|
12
|
+
InvalidPlan: 1004
|
|
13
|
+
} as const;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type SubscriptionType = 'SUB_APP'; // 目前支持sub-app平台
|
|
2
|
+
|
|
3
|
+
import { StandardResponse } from '@jolibox/types';
|
|
4
|
+
import { reportError } from '@/common/report/errors/report';
|
|
5
|
+
import { BaseError } from '@jolibox/common';
|
|
6
|
+
|
|
7
|
+
export type SubscriptionResult<T> = StandardResponse<T>;
|
|
8
|
+
|
|
9
|
+
export interface SubscriptionHandlerMap {
|
|
10
|
+
SUB_APP: (params: {
|
|
11
|
+
productId: string;
|
|
12
|
+
appStoreProductId: string;
|
|
13
|
+
planId: string;
|
|
14
|
+
}) => Promise<SubscriptionResult<{ subPlanId: string }>>; // sub-app subscription
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type SubscriptionHandler<T extends SubscriptionType> = SubscriptionHandlerMap[T];
|
|
18
|
+
|
|
19
|
+
export function createSubscriptionHelper() {
|
|
20
|
+
const subscriptionHandlers = new Map<SubscriptionType, SubscriptionHandler<any>>();
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
registerSubscriptionHandler<T extends SubscriptionType>(type: T, handler: SubscriptionHandler<T>) {
|
|
24
|
+
subscriptionHandlers.set(type, handler);
|
|
25
|
+
},
|
|
26
|
+
async invokeSubscription<T extends SubscriptionType>(
|
|
27
|
+
type: T,
|
|
28
|
+
...args: Parameters<SubscriptionHandler<T>>
|
|
29
|
+
) {
|
|
30
|
+
const subscriptionHandler = subscriptionHandlers.get(type);
|
|
31
|
+
if (!subscriptionHandler) {
|
|
32
|
+
return {
|
|
33
|
+
code: 'FAILED',
|
|
34
|
+
message: `[joliboxSDK]: ${type} subscription not supported in this platform`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const result = await subscriptionHandler(...args);
|
|
39
|
+
return result;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
reportError(e as BaseError);
|
|
42
|
+
return {
|
|
43
|
+
code: 'FAILED',
|
|
44
|
+
message: `[joliboxSDK]: subscription error ${e}`
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type SubscriptionHelper = ReturnType<typeof createSubscriptionHelper>;
|
|
52
|
+
|
|
53
|
+
export const subscriptionHelper = createSubscriptionHelper();
|