Skip to content

Commit 745e066

Browse files
authored
feat(analytics): add logTransaction method (#967)
* feat(analytics): add `logTransaction` method * style: format code * fix(analytics): validate transaction ID before lookup * docs(analytics): clarify transactionId property description
1 parent efa5b6f commit 745e066

File tree

8 files changed

+114
-0
lines changed

8 files changed

+114
-0
lines changed

.changeset/add-log-transaction.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@capacitor-firebase/analytics': minor
3+
---
4+
5+
feat(ios): add `logTransaction` method

packages/analytics/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ const initiateOnDeviceConversionMeasurementWithHashedPhoneNumber = async () => {
214214
* [`setEnabled(...)`](#setenabled)
215215
* [`isEnabled()`](#isenabled)
216216
* [`resetAnalyticsData()`](#resetanalyticsdata)
217+
* [`logTransaction(...)`](#logtransaction)
217218
* [`initiateOnDeviceConversionMeasurementWithEmailAddress(...)`](#initiateondeviceconversionmeasurementwithemailaddress)
218219
* [`initiateOnDeviceConversionMeasurementWithPhoneNumber(...)`](#initiateondeviceconversionmeasurementwithphonenumber)
219220
* [`initiateOnDeviceConversionMeasurementWithHashedEmailAddress(...)`](#initiateondeviceconversionmeasurementwithhashedemailaddress)
@@ -398,6 +399,25 @@ Only available for Android and iOS.
398399
--------------------
399400

400401

402+
### logTransaction(...)
403+
404+
```typescript
405+
logTransaction(options: LogTransactionOptions) => Promise<void>
406+
```
407+
408+
Logs a StoreKit 2 transaction.
409+
410+
Only available for iOS (15.0+).
411+
412+
| Param | Type |
413+
| ------------- | ----------------------------------------------------------------------- |
414+
| **`options`** | <code><a href="#logtransactionoptions">LogTransactionOptions</a></code> |
415+
416+
**Since:** 8.2.0
417+
418+
--------------------
419+
420+
401421
### initiateOnDeviceConversionMeasurementWithEmailAddress(...)
402422

403423
```typescript
@@ -544,6 +564,13 @@ Only available for iOS.
544564
| **`enabled`** | <code>boolean</code> | 0.1.0 |
545565

546566

567+
#### LogTransactionOptions
568+
569+
| Prop | Type | Description | Since |
570+
| ------------------- | ------------------- | ---------------------------------------------------------- | ----- |
571+
| **`transactionId`** | <code>string</code> | The StoreKit 2 `Transaction.id` value as a numeric string. | 8.2.0 |
572+
573+
547574
#### InitiateOnDeviceConversionMeasurementWithEmailAddressOptions
548575

549576
| Prop | Type | Description | Since |

packages/analytics/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/analytics/FirebaseAnalyticsPlugin.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ public void resetAnalyticsData(PluginCall call) {
178178
}
179179
}
180180

181+
@PluginMethod
182+
public void logTransaction(PluginCall call) {
183+
call.unimplemented("Not implemented on Android.");
184+
}
185+
181186
@PluginMethod
182187
public void initiateOnDeviceConversionMeasurementWithEmailAddress(PluginCall call) {
183188
call.unimplemented("Not implemented on Android.");

packages/analytics/ios/Plugin/FirebaseAnalytics.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import StoreKit
23

34
import Capacitor
45
import FirebaseCore
@@ -60,6 +61,25 @@ import FirebaseAnalytics
6061
Analytics.resetAnalyticsData()
6162
}
6263

64+
@available(iOS 15.0, *)
65+
public func logTransaction(transactionId: String) async throws {
66+
guard let id = UInt64(transactionId) else {
67+
throw NSError(domain: "FirebaseAnalytics", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid transaction identifier."])
68+
}
69+
var matchedTransaction: Transaction?
70+
for await result in Transaction.all {
71+
let transaction = FirebaseAnalyticsHelper.getTransaction(from: result)
72+
if transaction.id == id {
73+
matchedTransaction = transaction
74+
break
75+
}
76+
}
77+
guard let transaction = matchedTransaction else {
78+
throw NSError(domain: "FirebaseAnalytics", code: 0, userInfo: [NSLocalizedDescriptionKey: "Transaction not found."])
79+
}
80+
Analytics.logTransaction(transaction)
81+
}
82+
6383
@objc public func initiateOnDeviceConversionMeasurement(email: String) {
6484
Analytics.initiateOnDeviceConversionMeasurement(emailAddress: email)
6585
}

packages/analytics/ios/Plugin/FirebaseAnalyticsHelper.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import StoreKit
23
import FirebaseAnalytics
34
import CommonCrypto
45

@@ -96,6 +97,16 @@ public class FirebaseAnalyticsHelper {
9697
return regex?.firstMatch(in: phoneNumber, range: range) != nil
9798
}
9899

100+
@available(iOS 15.0, *)
101+
public static func getTransaction(from verificationResult: VerificationResult<Transaction>) -> Transaction {
102+
switch verificationResult {
103+
case .verified(let transaction):
104+
return transaction
105+
case .unverified(let transaction, _):
106+
return transaction
107+
}
108+
}
109+
99110
public static func sha256(_ string: String) -> Data {
100111
let data = string.data(using: .utf8)!
101112
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))

packages/analytics/ios/Plugin/FirebaseAnalyticsPlugin.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class FirebaseAnalyticsPlugin: CAPPlugin, CAPBridgedPlugin {
2222
CAPPluginMethod(name: "setEnabled", returnType: CAPPluginReturnPromise),
2323
CAPPluginMethod(name: "isEnabled", returnType: CAPPluginReturnPromise),
2424
CAPPluginMethod(name: "resetAnalyticsData", returnType: CAPPluginReturnPromise),
25+
CAPPluginMethod(name: "logTransaction", returnType: CAPPluginReturnPromise),
2526
CAPPluginMethod(name: "initiateOnDeviceConversionMeasurementWithEmailAddress", returnType: CAPPluginReturnPromise),
2627
CAPPluginMethod(name: "initiateOnDeviceConversionMeasurementWithPhoneNumber", returnType: CAPPluginReturnPromise),
2728
CAPPluginMethod(name: "initiateOnDeviceConversionMeasurementWithHashedEmailAddress", returnType: CAPPluginReturnPromise),
@@ -34,6 +35,7 @@ public class FirebaseAnalyticsPlugin: CAPPlugin, CAPBridgedPlugin {
3435
public let errorEnabledMissing = "enabled must be provided."
3536
public let errorConsentTypeMissing = "consentType must be provided."
3637
public let errorConsentStatusMissing = "consentStatus must be provided."
38+
public let errorTransactionIdMissing = "transactionId must be provided."
3739
public let errorEmailAddressMissing = "emailAddress must be provided."
3840
public let errorInvalidEmailFormat = "Invalid email format. Please provide a valid email address."
3941
public let errorPhoneNumberMissing = "phoneNumber must be provided."
@@ -125,6 +127,25 @@ public class FirebaseAnalyticsPlugin: CAPPlugin, CAPBridgedPlugin {
125127
call.resolve()
126128
}
127129

130+
@objc func logTransaction(_ call: CAPPluginCall) {
131+
guard let transactionId = call.getString("transactionId") else {
132+
call.reject(errorTransactionIdMissing)
133+
return
134+
}
135+
if #available(iOS 15.0, *) {
136+
Task {
137+
do {
138+
try await implementation?.logTransaction(transactionId: transactionId)
139+
call.resolve()
140+
} catch {
141+
call.reject(error.localizedDescription)
142+
}
143+
}
144+
} else {
145+
call.unimplemented("Not implemented on iOS < 15.0.")
146+
}
147+
}
148+
128149
@objc func initiateOnDeviceConversionMeasurementWithEmailAddress(_ call: CAPPluginCall) {
129150
guard let emailAddress = call.getString("emailAddress") else {
130151
call.reject(errorEmailAddressMissing)

packages/analytics/src/definitions.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ export interface FirebaseAnalyticsPlugin {
7171
* @since 0.1.0
7272
*/
7373
resetAnalyticsData(): Promise<void>;
74+
/**
75+
* Logs a StoreKit 2 transaction.
76+
*
77+
* Only available for iOS (15.0+).
78+
*
79+
* @since 8.2.0
80+
*/
81+
logTransaction(options: LogTransactionOptions): Promise<void>;
7482
/**
7583
* Initiates on-device conversion measurement with an email address.
7684
*
@@ -281,6 +289,18 @@ export enum ConsentStatus {
281289
Denied = 'DENIED',
282290
}
283291

292+
/**
293+
* @since 8.2.0
294+
*/
295+
export interface LogTransactionOptions {
296+
/**
297+
* The StoreKit 2 `Transaction.id` value as a numeric string.
298+
*
299+
* @since 8.2.0
300+
*/
301+
transactionId: string;
302+
}
303+
284304
/**
285305
* @since 7.2.0
286306
*/

packages/analytics/src/web.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
InitiateOnDeviceConversionMeasurementWithHashedPhoneNumberOptions,
1919
IsEnabledResult,
2020
LogEventOptions,
21+
LogTransactionOptions,
2122
SetConsentOptions,
2223
SetCurrentScreenOptions,
2324
SetEnabledOptions,
@@ -89,6 +90,10 @@ export class FirebaseAnalyticsWeb
8990
logEvent(analytics, options.name, options.params);
9091
}
9192

93+
public async logTransaction(_options: LogTransactionOptions): Promise<void> {
94+
throw this.unimplemented('Not implemented on web.');
95+
}
96+
9297
public async setSessionTimeoutDuration(
9398
_options: SetSessionTimeoutDurationOptions,
9499
): Promise<void> {

0 commit comments

Comments
 (0)