@hayanmind/monetai-react-native 1.0.0-beta.0 → 1.0.0-beta.2

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 (43) hide show
  1. package/android/src/main/java/com/hayanmind/monetaireactnative/MonetaiReactNativeModule.kt +19 -0
  2. package/ios/MonetaiReactNative.mm +6 -4
  3. package/ios/MonetaiReactNative.swift +15 -3
  4. package/lib/commonjs/ApiRequest.js +42 -52
  5. package/lib/commonjs/ApiRequest.js.map +1 -1
  6. package/lib/commonjs/MonetaiSDK.js +28 -45
  7. package/lib/commonjs/MonetaiSDK.js.map +1 -1
  8. package/lib/commonjs/constants.js +14 -0
  9. package/lib/commonjs/constants.js.map +1 -0
  10. package/lib/commonjs/lib.js +2 -2
  11. package/lib/module/ApiRequest.js +36 -47
  12. package/lib/module/ApiRequest.js.map +1 -1
  13. package/lib/module/MonetaiSDK.js +29 -46
  14. package/lib/module/MonetaiSDK.js.map +1 -1
  15. package/lib/module/constants.js +10 -0
  16. package/lib/module/constants.js.map +1 -0
  17. package/lib/module/lib.js +2 -2
  18. package/lib/typescript/commonjs/src/ApiRequest.d.ts +17 -35
  19. package/lib/typescript/commonjs/src/ApiRequest.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/src/MonetaiSDK.d.ts +5 -14
  21. package/lib/typescript/commonjs/src/MonetaiSDK.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/src/constants.d.ts +8 -0
  23. package/lib/typescript/commonjs/src/constants.d.ts.map +1 -0
  24. package/lib/typescript/commonjs/src/index.d.ts +1 -1
  25. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/src/types.d.ts +10 -3
  27. package/lib/typescript/commonjs/src/types.d.ts.map +1 -1
  28. package/lib/typescript/module/src/ApiRequest.d.ts +17 -35
  29. package/lib/typescript/module/src/ApiRequest.d.ts.map +1 -1
  30. package/lib/typescript/module/src/MonetaiSDK.d.ts +5 -14
  31. package/lib/typescript/module/src/MonetaiSDK.d.ts.map +1 -1
  32. package/lib/typescript/module/src/constants.d.ts +8 -0
  33. package/lib/typescript/module/src/constants.d.ts.map +1 -0
  34. package/lib/typescript/module/src/index.d.ts +1 -1
  35. package/lib/typescript/module/src/index.d.ts.map +1 -1
  36. package/lib/typescript/module/src/types.d.ts +10 -3
  37. package/lib/typescript/module/src/types.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/src/ApiRequest.ts +43 -93
  40. package/src/MonetaiSDK.ts +50 -70
  41. package/src/constants.ts +7 -0
  42. package/src/index.tsx +2 -3
  43. package/src/types.ts +11 -3
@@ -1,8 +1,14 @@
1
1
  export type ABTestGroup = 'baseline' | 'monetai' | 'unknown';
2
2
  export type PredictResult = 'non-purchaser' | 'purchaser' | null;
3
- export interface PriceDecision {
4
- productId: string;
5
- discount: number;
3
+ export interface OfferProduct {
4
+ name: string;
5
+ sku: string;
6
+ discountRate: number;
7
+ }
8
+ export interface Offer {
9
+ agentId: number;
10
+ agentName: string;
11
+ products: OfferProduct[];
6
12
  }
7
13
  export type PaywallStyle = 'compact' | 'highlight-benefits' | 'key-feature-summary' | 'text-focused';
8
14
  export interface Feature {
@@ -22,6 +28,7 @@ export interface ViewProductItemParams {
22
28
  price: number;
23
29
  regularPrice: number;
24
30
  currencyCode: string;
31
+ promotionId: number;
25
32
  month?: number | null;
26
33
  }
27
34
  export interface DiscountInfo {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hayanmind/monetai-react-native",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "Unlock hidden app revenue with AI from non-converting users",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/commonjs/index.js",
package/src/ApiRequest.ts CHANGED
@@ -1,19 +1,20 @@
1
1
  import axios from 'axios';
2
2
  import type {
3
- ABTestGroup,
4
3
  AppUserDiscountResponse,
5
- PredictResult,
4
+ Offer,
5
+ OfferProduct,
6
6
  ViewProductItemParams,
7
7
  } from './types';
8
8
  import { getSDKVersion } from './lib';
9
9
  import { Platform } from 'react-native';
10
+ import { SDK_HEADERS } from './constants';
10
11
 
11
12
  const axiosInstance = axios.create({
12
13
  baseURL: 'https://monetai-api-414410537412.us-central1.run.app/sdk', // 필요에 따라 베이스 URL 설정
13
14
  headers: {
14
- 'X-SDK-Platform': 'react_native',
15
- 'X-SDK-Version': getSDKVersion(),
16
- 'X-Device-OS':
15
+ [SDK_HEADERS.SDK_PLATFORM]: 'react_native',
16
+ [SDK_HEADERS.SDK_VERSION]: getSDKVersion(),
17
+ [SDK_HEADERS.DEVICE_OS]:
17
18
  Platform.OS === 'ios'
18
19
  ? 'ios'
19
20
  : Platform.OS === 'android'
@@ -22,6 +23,26 @@ const axiosInstance = axios.create({
22
23
  },
23
24
  });
24
25
 
26
+ /**
27
+ * X-App-Version 헤더를 설정합니다.
28
+ * SDK 초기화 시점에 호출되어야 합니다.
29
+ */
30
+ export const setAppVersionHeader = (version: string) => {
31
+ axiosInstance.defaults.headers.common[SDK_HEADERS.APP_VERSION] = version;
32
+ };
33
+
34
+ /**
35
+ * X-App-Bundle-Id 헤더를 설정합니다.
36
+ * SDK 초기화 시점에 호출되어야 합니다.
37
+ */
38
+ export const setBundleIdHeader = (bundleId: string) => {
39
+ axiosInstance.defaults.headers.common[SDK_HEADERS.BUNDLE_ID] = bundleId;
40
+ };
41
+
42
+ export const setUserIdHeader = (userId: string) => {
43
+ axiosInstance.defaults.headers.common['X-User-Id'] = userId;
44
+ };
45
+
25
46
  export const initialize = async ({
26
47
  sdkKey,
27
48
  userId,
@@ -30,6 +51,9 @@ export const initialize = async ({
30
51
  userId: string;
31
52
  }) => {
32
53
  try {
54
+ // X-User-Id 헤더 설정 (user property 수집용)
55
+ setUserIdHeader(userId);
56
+
33
57
  const response = await axiosInstance.post<{
34
58
  organization_id: number;
35
59
  platform: 'react_native';
@@ -40,38 +64,10 @@ export const initialize = async ({
40
64
  version: getSDKVersion(),
41
65
  });
42
66
 
43
- const abTestGroupResponse = await axiosInstance.post<{
44
- group: ABTestGroup | null;
45
- campaign: {
46
- id: number;
47
- created_at: Date | null;
48
- organization_id: number;
49
- campaign_name: string;
50
- started_at: Date | null;
51
- ended_at: Date | null;
52
- traffic_ratio: number;
53
- allocation_ratio: number;
54
- discount_ratio: number;
55
- exposure_time_sec: number;
56
- model_accuracy: number | null;
57
- } | null;
58
- }>('/ab-test', {
59
- sdkKey,
60
- userId,
61
- platform:
62
- Platform.OS === 'ios'
63
- ? 'ios'
64
- : Platform.OS === 'android'
65
- ? 'android'
66
- : Platform.OS,
67
- });
68
-
69
67
  return {
70
68
  organizationId: response.data.organization_id,
71
69
  platform: response.data.platform,
72
70
  version: response.data.version,
73
- group: abTestGroupResponse.data.group,
74
- campaign: abTestGroupResponse.data.campaign,
75
71
  };
76
72
  } catch (error: any) {
77
73
  console.error('Failed to initialize integration:', error.message);
@@ -133,6 +129,7 @@ export const createViewProductItemEvent = async ({
133
129
  price: params.price,
134
130
  regularPrice: params.regularPrice,
135
131
  currencyCode: params.currencyCode,
132
+ promotionId: params.promotionId,
136
133
  month: params.month,
137
134
  createdAt: createdAt ?? new Date(),
138
135
  platform:
@@ -147,55 +144,6 @@ export const createViewProductItemEvent = async ({
147
144
  }
148
145
  };
149
146
 
150
- export const predict = async ({
151
- userId,
152
- sdkKey,
153
- }: {
154
- userId: string;
155
- sdkKey: string;
156
- }) => {
157
- try {
158
- const response = await axiosInstance.post<{
159
- prediction: PredictResult;
160
- testGroup: ABTestGroup | null;
161
- }>('/predict', {
162
- sdkKey,
163
- userId,
164
- });
165
-
166
- return response.data;
167
- } catch (error: any) {
168
- console.error('Failed to get A/B test group:', error.message);
169
- throw new Error(
170
- error.response?.data?.message || 'Failed to get A/B test group'
171
- );
172
- }
173
- };
174
-
175
- export const createAppUserDiscount = async (discountInfo: {
176
- startedAt: Date;
177
- endedAt: Date;
178
- userId: string;
179
- sdkKey: string;
180
- }) => {
181
- try {
182
- const response = await axiosInstance.post<{
183
- discount: AppUserDiscountResponse;
184
- }>('/app-user-discounts', {
185
- sdkKey: discountInfo.sdkKey,
186
- appUserId: discountInfo.userId,
187
- startedAt: discountInfo.startedAt,
188
- endedAt: discountInfo.endedAt,
189
- });
190
- return response.data.discount;
191
- } catch (error: any) {
192
- console.error('Failed to set discount info:', error.message);
193
- throw new Error(
194
- error.response?.data?.message || 'Failed to set discount info'
195
- );
196
- }
197
- };
198
-
199
147
  /**
200
148
  * sdkKey와 app_user_id에 해당하는 가장 최근 할인 정보를 조회합니다.
201
149
  * API GET /app-user-discounts/latest 엔드포인트를 호출합니다.
@@ -223,23 +171,27 @@ export const getAppUserDiscount = async ({
223
171
  };
224
172
 
225
173
  /**
226
- * Thompson Sampling을 사용한 최적 가격 결정
227
- * API POST /dynamic-pricing/decision 엔드포인트를 호출합니다.
174
+ * 오퍼 조회
175
+ * API POST /offers/get-offer 엔드포인트를 호출합니다.
228
176
  */
229
- export const getPriceDecision = async ({
177
+ export const getOffer = async ({
230
178
  sdkKey,
231
179
  userId,
180
+ promotionId,
232
181
  }: {
233
182
  sdkKey: string;
234
183
  userId: string;
235
- }) => {
184
+ promotionId: number;
185
+ }): Promise<Offer | null> => {
236
186
  try {
237
187
  const response = await axiosInstance.post<{
238
- productId: string;
239
- discount: number;
240
- }>('/dynamic-pricing/decision', {
188
+ agentId: number;
189
+ agentName: string;
190
+ products: OfferProduct[];
191
+ } | null>('/offers/get-offer', {
241
192
  sdkKey,
242
193
  userId,
194
+ promotionId,
243
195
  platform:
244
196
  Platform.OS === 'ios'
245
197
  ? 'ios'
@@ -249,9 +201,7 @@ export const getPriceDecision = async ({
249
201
  });
250
202
  return response.data;
251
203
  } catch (error: any) {
252
- console.error('Failed to get price decision:', error.message);
253
- throw new Error(
254
- error.response?.data?.message || 'Failed to get price decision'
255
- );
204
+ console.error('Failed to get offer:', error.message);
205
+ throw new Error(error.response?.data?.message || 'Failed to get offer');
256
206
  }
257
207
  };
package/src/MonetaiSDK.ts CHANGED
@@ -1,14 +1,12 @@
1
1
  import { NativeModules, Platform } from 'react-native';
2
2
  import mitt from 'mitt';
3
- import dayjs from 'dayjs';
4
3
  import * as ApiRequest from './ApiRequest';
5
4
  import {
6
- type ABTestGroup,
7
- type PredictResult,
5
+ type Offer,
8
6
  type LogEventOptions,
9
7
  type ViewProductItemParams,
10
8
  } from './types';
11
- import { createAppUserDiscount } from './ApiRequest';
9
+ import { setAppVersionHeader, setBundleIdHeader } from './ApiRequest';
12
10
 
13
11
  const LINKING_ERROR =
14
12
  `The package '@hayanmind/monetai-react-native' doesn't seem to be linked. Make sure: \n\n` +
@@ -16,16 +14,29 @@ const LINKING_ERROR =
16
14
  '- You rebuilt the app after installing the package\n' +
17
15
  '- You are not using Expo Go\n';
18
16
 
19
- const MonetaiReactNative = NativeModules.MonetaiReactNative
20
- ? NativeModules.MonetaiReactNative
21
- : new Proxy(
22
- {},
23
- {
24
- get() {
25
- throw new Error(LINKING_ERROR);
26
- },
27
- }
28
- );
17
+ interface MonetaiReactNativeModule {
18
+ setUserId(userId: string): Promise<void>;
19
+ getUserId(): Promise<string | null>;
20
+ setSdkKey(sdkKey: string): Promise<void>;
21
+ getSdkKey(): Promise<string | null>;
22
+ startObserving(useStoreKit2: boolean): Promise<void>;
23
+ sendReceipt(): Promise<void>;
24
+ reset(): Promise<void>;
25
+ getAppVersion(): Promise<string | null>;
26
+ getBundleId(): Promise<string | null>;
27
+ }
28
+
29
+ const MonetaiReactNative: MonetaiReactNativeModule =
30
+ NativeModules.MonetaiReactNative
31
+ ? NativeModules.MonetaiReactNative
32
+ : new Proxy(
33
+ {},
34
+ {
35
+ get() {
36
+ throw new Error(LINKING_ERROR);
37
+ },
38
+ }
39
+ );
29
40
 
30
41
  // Emitter로 사용할 이벤트 타입 정의
31
42
  type MonetaiEvents = {
@@ -33,7 +44,6 @@ type MonetaiEvents = {
33
44
  };
34
45
 
35
46
  class MonetaiSDK {
36
- private exposureTimeSec: number | null = null;
37
47
  private initialized = false;
38
48
 
39
49
  // SDK 초기화 전에 로깅된 이벤트들을 저장할 큐
@@ -51,10 +61,6 @@ class MonetaiSDK {
51
61
  return this.emitter;
52
62
  }
53
63
 
54
- public getExposureTimeSec() {
55
- return this.exposureTimeSec;
56
- }
57
-
58
64
  /**
59
65
  * 모넷AI SDK를 초기화합니다
60
66
  * @param userId 사용자 고유 ID (필수)
@@ -76,7 +82,6 @@ class MonetaiSDK {
76
82
  platform: 'react_native';
77
83
  version: string;
78
84
  userId: string;
79
- group: ABTestGroup | null;
80
85
  }> {
81
86
  if (!userId) {
82
87
  throw new Error('userId is required');
@@ -91,6 +96,26 @@ class MonetaiSDK {
91
96
  // userId 설정
92
97
  await MonetaiReactNative.setUserId(userId);
93
98
 
99
+ // 앱 버전을 가져와서 API 헤더에 설정
100
+ try {
101
+ const appVersion = await MonetaiReactNative.getAppVersion();
102
+ if (appVersion) {
103
+ setAppVersionHeader(appVersion);
104
+ }
105
+ } catch (error) {
106
+ console.warn('[MonetaiSDK] Failed to get app version:', error);
107
+ }
108
+
109
+ // Bundle ID / Package Name을 가져와서 API 헤더에 설정
110
+ try {
111
+ const bundleId = await MonetaiReactNative.getBundleId();
112
+ if (bundleId) {
113
+ setBundleIdHeader(bundleId);
114
+ }
115
+ } catch (error) {
116
+ console.warn('[MonetaiSDK] Failed to get bundle id:', error);
117
+ }
118
+
94
119
  // 트랜잭션 관찰 시작
95
120
  await MonetaiReactNative.startObserving(useStoreKit2);
96
121
 
@@ -100,7 +125,6 @@ class MonetaiSDK {
100
125
  });
101
126
 
102
127
  const result = await ApiRequest.initialize({ sdkKey, userId });
103
- this.exposureTimeSec = result.campaign?.exposure_time_sec ?? null;
104
128
  this.initialized = true;
105
129
 
106
130
  // SDK 초기화 후, 초기화 전에 저장된 pendingEvents를 전송
@@ -128,7 +152,6 @@ class MonetaiSDK {
128
152
  platform: 'react_native',
129
153
  version: result.version,
130
154
  userId: userId,
131
- group: result.group,
132
155
  };
133
156
  }
134
157
 
@@ -186,55 +209,12 @@ class MonetaiSDK {
186
209
  });
187
210
  }
188
211
 
189
- // Non-Purchaser 예측
190
- public async predict(): Promise<{
191
- prediction: PredictResult;
192
- testGroup: ABTestGroup | null;
193
- }> {
194
- const sdkKey = await this.getSdkKey();
195
- const userId = await this.getUserId();
196
-
197
- if (!sdkKey || !userId || !this.exposureTimeSec) {
198
- throw new Error(
199
- 'MonetaiSDK is not initialized. Call initialize() first.'
200
- );
201
- }
202
- const result = await ApiRequest.predict({ userId, sdkKey });
203
-
204
- if (result.prediction === 'non-purchaser') {
205
- const discountInfo = await ApiRequest.getAppUserDiscount({
206
- sdkKey,
207
- userId,
208
- });
209
- const hasActiveDiscount =
210
- discountInfo != null &&
211
- discountInfo.app_user_id === userId &&
212
- dayjs(discountInfo.ended_at).isAfter(dayjs());
213
-
214
- if (!hasActiveDiscount) {
215
- const startedAt = dayjs();
216
- const endedAt = startedAt.clone().add(this.exposureTimeSec, 'second');
217
- await createAppUserDiscount({
218
- startedAt: startedAt.toDate(),
219
- endedAt: endedAt.toDate(),
220
- userId,
221
- sdkKey,
222
- });
223
- this.emitter.emit('LOAD_DISCOUNT_INFO');
224
- }
225
- }
226
-
227
- return result;
228
- }
229
-
230
212
  /**
231
- * Thompson Sampling을 사용한 최적 가격 결정
232
- * @returns productId와 할인율
213
+ * 오퍼 조회
214
+ * @param promotionId 프로모션 ID
215
+ * @returns 에이전트 매칭 결과 및 상품 목록, 없으면 null
233
216
  */
234
- public async getPriceDecision(): Promise<{
235
- productId: string;
236
- discount: number;
237
- }> {
217
+ public async getOffer(promotionId: number): Promise<Offer | null> {
238
218
  const sdkKey = await this.getSdkKey();
239
219
  const userId = await this.getUserId();
240
220
 
@@ -244,7 +224,7 @@ class MonetaiSDK {
244
224
  );
245
225
  }
246
226
 
247
- return await ApiRequest.getPriceDecision({ sdkKey, userId });
227
+ return await ApiRequest.getOffer({ sdkKey, userId, promotionId });
248
228
  }
249
229
 
250
230
  public getInitialized() {
@@ -0,0 +1,7 @@
1
+ export const SDK_HEADERS = {
2
+ BUNDLE_ID: 'X-App-Bundle-Id',
3
+ DEVICE_OS: 'X-Device-OS',
4
+ SDK_PLATFORM: 'X-SDK-Platform',
5
+ SDK_VERSION: 'X-SDK-Version',
6
+ APP_VERSION: 'X-App-Version',
7
+ } as const;
package/src/index.tsx CHANGED
@@ -26,8 +26,6 @@ export {
26
26
 
27
27
  // Export types
28
28
  export type {
29
- ABTestGroup,
30
- PredictResult,
31
29
  EventParams,
32
30
  LogEventOptions,
33
31
  DiscountInfo,
@@ -37,5 +35,6 @@ export type {
37
35
  BannerParams,
38
36
  PaywallParams,
39
37
  PaywallStyle,
40
- PriceDecision,
38
+ Offer,
39
+ OfferProduct,
41
40
  } from './types';
package/src/types.ts CHANGED
@@ -1,9 +1,16 @@
1
1
  export type ABTestGroup = 'baseline' | 'monetai' | 'unknown';
2
2
  export type PredictResult = 'non-purchaser' | 'purchaser' | null;
3
3
 
4
- export interface PriceDecision {
5
- productId: string; // Product ID
6
- discount: number; // 할인율 (0.5 = 50%)
4
+ export interface OfferProduct {
5
+ name: string;
6
+ sku: string;
7
+ discountRate: number;
8
+ }
9
+
10
+ export interface Offer {
11
+ agentId: number;
12
+ agentName: string;
13
+ products: OfferProduct[];
7
14
  }
8
15
 
9
16
  // Banner and Paywall style types
@@ -34,6 +41,7 @@ export interface ViewProductItemParams {
34
41
  price: number;
35
42
  regularPrice: number;
36
43
  currencyCode: string;
44
+ promotionId: number;
37
45
  month?: number | null;
38
46
  }
39
47