react-native-purchases-admob 0.0.0-stage → 10.15.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.
- package/LICENSE +20 -0
- package/README.md +91 -2
- package/lib/commonjs/index.js +446 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/state.js +92 -0
- package/lib/commonjs/state.js.map +1 -0
- package/lib/commonjs/tracker.js +161 -0
- package/lib/commonjs/tracker.js.map +1 -0
- package/lib/module/index.js +439 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/state.js +82 -0
- package/lib/module/state.js.map +1 -0
- package/lib/module/tracker.js +150 -0
- package/lib/module/tracker.js.map +1 -0
- package/lib/typescript/src/index.d.ts +230 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/state.d.ts +31 -0
- package/lib/typescript/src/state.d.ts.map +1 -0
- package/lib/typescript/src/tracker.d.ts +17 -0
- package/lib/typescript/src/tracker.d.ts.map +1 -0
- package/package.json +109 -4
- package/src/index.ts +623 -0
- package/src/state.ts +109 -0
- package/src/tracker.ts +200 -0
package/src/state.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { AdCaptureMethod, AdMediatorName } from "react-native-purchases";
|
|
2
|
+
import type {
|
|
3
|
+
AdFormat,
|
|
4
|
+
RewardedAdTrackingMetadata,
|
|
5
|
+
} from "react-native-purchases";
|
|
6
|
+
import type { ResponseInfo } from "react-native-google-mobile-ads";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Per-ad bookkeeping, kept in a weak side table so the adapter never changes
|
|
10
|
+
* the lifetime of the `react-native-google-mobile-ads` objects it tracks.
|
|
11
|
+
*/
|
|
12
|
+
export class TrackedAdState {
|
|
13
|
+
loadPlacement: string | null;
|
|
14
|
+
showPlacement: string | null = null;
|
|
15
|
+
/**
|
|
16
|
+
* Either AdMob's response id (read once the ad loads) or, for ads created
|
|
17
|
+
* with reward verification, the id generated before the request so the SSV
|
|
18
|
+
* token and every tracking event share one impression id.
|
|
19
|
+
*/
|
|
20
|
+
impressionId: string = "";
|
|
21
|
+
networkName: string | null = null;
|
|
22
|
+
clientTransactionId: string | null = null;
|
|
23
|
+
rewardVerificationDelivered = false;
|
|
24
|
+
loadedOnce = false;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
readonly adFormat: AdFormat,
|
|
28
|
+
readonly adUnitId: string,
|
|
29
|
+
placement?: string | null,
|
|
30
|
+
public pinnedImpressionId: string | null = null
|
|
31
|
+
) {
|
|
32
|
+
this.loadPlacement = placement ?? null;
|
|
33
|
+
if (pinnedImpressionId) {
|
|
34
|
+
this.impressionId = pinnedImpressionId;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get placement(): string | null {
|
|
39
|
+
return this.showPlacement ?? this.loadPlacement;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
updateFromResponse(info: ResponseInfo | null | undefined): void {
|
|
43
|
+
if (!this.pinnedImpressionId) {
|
|
44
|
+
this.impressionId = info?.responseId ?? "";
|
|
45
|
+
}
|
|
46
|
+
this.networkName =
|
|
47
|
+
info?.loadedAdapterResponse?.adapterClassName ??
|
|
48
|
+
info?.adapterClassName ??
|
|
49
|
+
null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get trackingMetadata(): RewardedAdTrackingMetadata {
|
|
53
|
+
return {
|
|
54
|
+
networkName: this.networkName,
|
|
55
|
+
mediatorName: AdMediatorName.adMob,
|
|
56
|
+
adFormat: this.adFormat,
|
|
57
|
+
placement: this.placement,
|
|
58
|
+
adUnitId: this.adUnitId,
|
|
59
|
+
impressionId: this.impressionId,
|
|
60
|
+
captureMethod: AdCaptureMethod.reactNativeAdMobAdapter,
|
|
61
|
+
} as RewardedAdTrackingMetadata;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const states = new WeakMap<object, TrackedAdState>();
|
|
66
|
+
|
|
67
|
+
export function stateFor(ad: object): TrackedAdState | undefined {
|
|
68
|
+
return states.get(ad);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function setState(ad: object, state: TrackedAdState): TrackedAdState {
|
|
72
|
+
states.set(ad, state);
|
|
73
|
+
return state;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* UUID v4. Uses `crypto` when the runtime or a polyfill such as
|
|
78
|
+
* `react-native-get-random-values` provides it; Hermes has none by default,
|
|
79
|
+
* so it falls back to `Math.random`, which is enough for a correlation key.
|
|
80
|
+
*/
|
|
81
|
+
interface CryptoLike {
|
|
82
|
+
randomUUID?: () => string;
|
|
83
|
+
getRandomValues?: (array: Uint8Array) => Uint8Array;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function generateImpressionId(): string {
|
|
87
|
+
const cryptoObj = (globalThis as { crypto?: CryptoLike }).crypto;
|
|
88
|
+
if (cryptoObj?.randomUUID) {
|
|
89
|
+
return cryptoObj.randomUUID();
|
|
90
|
+
}
|
|
91
|
+
const bytes = new Uint8Array(16);
|
|
92
|
+
if (cryptoObj?.getRandomValues) {
|
|
93
|
+
cryptoObj.getRandomValues(bytes);
|
|
94
|
+
} else {
|
|
95
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
96
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;
|
|
100
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
|
|
101
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
102
|
+
return [
|
|
103
|
+
hex.slice(0, 4).join(""),
|
|
104
|
+
hex.slice(4, 6).join(""),
|
|
105
|
+
hex.slice(6, 8).join(""),
|
|
106
|
+
hex.slice(8, 10).join(""),
|
|
107
|
+
hex.slice(10, 16).join(""),
|
|
108
|
+
].join("-");
|
|
109
|
+
}
|
package/src/tracker.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import Purchases, {
|
|
2
|
+
AdCaptureMethod,
|
|
3
|
+
AdMediatorName,
|
|
4
|
+
AdRevenuePrecision,
|
|
5
|
+
} from "react-native-purchases";
|
|
6
|
+
import type {
|
|
7
|
+
AdDisplayedData,
|
|
8
|
+
AdFailedToLoadData,
|
|
9
|
+
AdFormat,
|
|
10
|
+
AdLoadedData,
|
|
11
|
+
AdOpenedData,
|
|
12
|
+
AdRevenueData,
|
|
13
|
+
AdRewardEarnedUnverifiedData,
|
|
14
|
+
} from "react-native-purchases";
|
|
15
|
+
import type { PaidEvent } from "react-native-google-mobile-ads";
|
|
16
|
+
import { Platform } from "react-native";
|
|
17
|
+
|
|
18
|
+
import { TrackedAdState } from "./state";
|
|
19
|
+
|
|
20
|
+
export function logWarning(message: string): void {
|
|
21
|
+
// tslint:disable-next-line:no-console
|
|
22
|
+
console.warn(`[react-native-purchases-admob] ${message}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The data types mark this field @internal, hence the assertions below.
|
|
26
|
+
const captureMethod = AdCaptureMethod.reactNativeAdMobAdapter;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* `react-native-google-mobile-ads` reports load failures with a string reason
|
|
30
|
+
* rather than the platform SDK's numeric error code, and the two SDKs number
|
|
31
|
+
* their codes differently. Each table inverts the library's own native mapping
|
|
32
|
+
* for that platform (ReactNativeGoogleMobileAdsCommon.java and
|
|
33
|
+
* RNGoogleMobileAdsOwnedMappers.m), so the emitted number matches what the
|
|
34
|
+
* native integration on the same platform reports.
|
|
35
|
+
*/
|
|
36
|
+
const androidErrorCodes: Record<string, number> = {
|
|
37
|
+
"internal-error": 0,
|
|
38
|
+
"invalid-request": 1,
|
|
39
|
+
"network-error": 2,
|
|
40
|
+
"no-fill": 3,
|
|
41
|
+
"app-id-missing": 8,
|
|
42
|
+
"mediation-no-fill": 9,
|
|
43
|
+
"request-id-mismatch": 10,
|
|
44
|
+
"invalid-ad-string": 11,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const iosErrorCodes: Record<string, number> = {
|
|
48
|
+
"invalid-request": 0,
|
|
49
|
+
"no-fill": 1,
|
|
50
|
+
"network-error": 2,
|
|
51
|
+
"server-error": 3,
|
|
52
|
+
timeout: 5,
|
|
53
|
+
"mediation-data-error": 7,
|
|
54
|
+
"mediation-adapter-error": 8,
|
|
55
|
+
"mediation-invalid-ad-size": 10,
|
|
56
|
+
"internal-error": 11,
|
|
57
|
+
"invalid-argument": 12,
|
|
58
|
+
"ad-already-used": 19,
|
|
59
|
+
"app-id-missing": 20,
|
|
60
|
+
"invalid-ad-string": 21,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function mediatorErrorCode(reason: string | undefined): number | null {
|
|
64
|
+
if (!reason) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const table = Platform.OS === "ios" ? iosErrorCodes : androidErrorCodes;
|
|
68
|
+
const mapped = table[reason];
|
|
69
|
+
if (mapped !== undefined) {
|
|
70
|
+
return mapped;
|
|
71
|
+
}
|
|
72
|
+
const numeric = Number(reason);
|
|
73
|
+
return Number.isInteger(numeric) ? numeric : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function revenueMicrosFrom(paid: PaidEvent): number {
|
|
77
|
+
if (paid.valueMicros != null) {
|
|
78
|
+
const micros = Number(paid.valueMicros);
|
|
79
|
+
if (Number.isFinite(micros)) {
|
|
80
|
+
return Math.round(micros);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return Math.round(paid.value * 1_000_000);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function precisionFrom(precision: number): AdRevenuePrecision {
|
|
87
|
+
switch (precision) {
|
|
88
|
+
case 1:
|
|
89
|
+
return AdRevenuePrecision.estimated;
|
|
90
|
+
case 2:
|
|
91
|
+
return AdRevenuePrecision.publisherDefined;
|
|
92
|
+
case 3:
|
|
93
|
+
return AdRevenuePrecision.exact;
|
|
94
|
+
default:
|
|
95
|
+
return AdRevenuePrecision.unknown;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function swallow(event: string, promise: Promise<void>): void {
|
|
100
|
+
promise.catch((e) => logWarning(`Could not track ${event}: ${e}`));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const AdMobTracker = {
|
|
104
|
+
trackLoaded(state: TrackedAdState): void {
|
|
105
|
+
swallow(
|
|
106
|
+
"ad loaded",
|
|
107
|
+
Purchases.adTracker.trackAdLoaded({
|
|
108
|
+
networkName: state.networkName,
|
|
109
|
+
mediatorName: AdMediatorName.adMob,
|
|
110
|
+
adFormat: state.adFormat,
|
|
111
|
+
placement: state.loadPlacement,
|
|
112
|
+
adUnitId: state.adUnitId,
|
|
113
|
+
impressionId: state.impressionId,
|
|
114
|
+
captureMethod,
|
|
115
|
+
} as AdLoadedData)
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
trackFailedToLoad(
|
|
120
|
+
adUnitId: string,
|
|
121
|
+
adFormat: AdFormat,
|
|
122
|
+
placement: string | null,
|
|
123
|
+
reason: string | undefined
|
|
124
|
+
): void {
|
|
125
|
+
swallow(
|
|
126
|
+
"ad failed to load",
|
|
127
|
+
Purchases.adTracker.trackAdFailedToLoad({
|
|
128
|
+
mediatorName: AdMediatorName.adMob,
|
|
129
|
+
adFormat,
|
|
130
|
+
placement,
|
|
131
|
+
adUnitId,
|
|
132
|
+
mediatorErrorCode: mediatorErrorCode(reason),
|
|
133
|
+
captureMethod,
|
|
134
|
+
} as AdFailedToLoadData)
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
trackDisplayed(state: TrackedAdState): void {
|
|
139
|
+
swallow(
|
|
140
|
+
"ad displayed",
|
|
141
|
+
Purchases.adTracker.trackAdDisplayed({
|
|
142
|
+
networkName: state.networkName,
|
|
143
|
+
mediatorName: AdMediatorName.adMob,
|
|
144
|
+
adFormat: state.adFormat,
|
|
145
|
+
placement: state.placement,
|
|
146
|
+
adUnitId: state.adUnitId,
|
|
147
|
+
impressionId: state.impressionId,
|
|
148
|
+
captureMethod,
|
|
149
|
+
} as AdDisplayedData)
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
trackOpened(state: TrackedAdState): void {
|
|
154
|
+
swallow(
|
|
155
|
+
"ad opened",
|
|
156
|
+
Purchases.adTracker.trackAdOpened({
|
|
157
|
+
networkName: state.networkName,
|
|
158
|
+
mediatorName: AdMediatorName.adMob,
|
|
159
|
+
adFormat: state.adFormat,
|
|
160
|
+
placement: state.placement,
|
|
161
|
+
adUnitId: state.adUnitId,
|
|
162
|
+
impressionId: state.impressionId,
|
|
163
|
+
captureMethod,
|
|
164
|
+
} as AdOpenedData)
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
trackRewardEarnedUnverified(state: TrackedAdState): void {
|
|
169
|
+
swallow(
|
|
170
|
+
"reward earned",
|
|
171
|
+
Purchases.adTracker.trackAdRewardEarnedUnverified({
|
|
172
|
+
networkName: state.networkName,
|
|
173
|
+
mediatorName: AdMediatorName.adMob,
|
|
174
|
+
adFormat: state.adFormat,
|
|
175
|
+
placement: state.placement,
|
|
176
|
+
adUnitId: state.adUnitId,
|
|
177
|
+
impressionId: state.impressionId,
|
|
178
|
+
captureMethod,
|
|
179
|
+
} as AdRewardEarnedUnverifiedData)
|
|
180
|
+
);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
trackRevenue(state: TrackedAdState, paid: PaidEvent): void {
|
|
184
|
+
swallow(
|
|
185
|
+
"ad revenue",
|
|
186
|
+
Purchases.adTracker.trackAdRevenue({
|
|
187
|
+
networkName: state.networkName,
|
|
188
|
+
mediatorName: AdMediatorName.adMob,
|
|
189
|
+
adFormat: state.adFormat,
|
|
190
|
+
placement: state.placement,
|
|
191
|
+
adUnitId: state.adUnitId,
|
|
192
|
+
impressionId: state.impressionId,
|
|
193
|
+
revenueMicros: revenueMicrosFrom(paid),
|
|
194
|
+
currency: paid.currency,
|
|
195
|
+
precision: precisionFrom(paid.precision),
|
|
196
|
+
captureMethod,
|
|
197
|
+
} as AdRevenueData)
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
};
|