Skip to content

Commit c1e8164

Browse files
thomson-trmi22186
andauthored
feat: Add Rokt purchaseFinalized method (#54)
- Add `purchaseFinalized` method to the Flutter Rokt API - Implement native calls for `purchaseFinalized` on Android and iOS - Add unit tests to verify the new functionality - Add a test helper to clear placeholders for test isolation Signed-off-by: Thomson Thomas <thomson.thomas@rokt.com> Co-authored-by: Robert Ing <rmi22186@gmail.com>
1 parent ba75ed6 commit c1e8164

4 files changed

Lines changed: 123 additions & 2 deletions

File tree

android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware
235235
result.success(true)
236236
}
237237
"roktSelectPlacements" -> this.roktSelectPlacements(call, result)
238+
"roktPurchaseFinalized" -> this.roktPurchaseFinalized(call, result)
238239
else -> {
239240
result.notImplemented()
240241
}
@@ -785,6 +786,26 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware
785786
return builder.build()
786787
}
787788

789+
private fun roktPurchaseFinalized(call: MethodCall, result: Result) {
790+
val placementId = call.argument<String>("placementId")
791+
val catalogItemId = call.argument<String>("catalogItemId")
792+
val success = call.argument<Boolean>("success") ?: true
793+
if (placementId != null && catalogItemId != null) {
794+
MParticle.getInstance()?.Rokt()?.purchaseFinalized(
795+
placementId = placementId,
796+
catalogItemId = catalogItemId,
797+
status = success,
798+
)
799+
result.success("Success")
800+
} else {
801+
result.error(
802+
"INVALID_PARAMS",
803+
"placementId and catalogItemId are required",
804+
null,
805+
)
806+
}
807+
}
808+
788809
private fun String.toColorMode(): RoktConfig.ColorMode =
789810
when (this) {
790811
"dark" -> RoktConfig.ColorMode.DARK

ios/Classes/SwiftMparticleFlutterSdkPlugin.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ public class SwiftMparticleFlutterSdkPlugin: NSObject, FlutterPlugin {
529529
}
530530
}
531531
}
532-
532+
533533
var roktConfig: MPRoktConfig?
534534
if let configMap = callArguments["config"] as? [String: Any] {
535535
roktConfig = buildRoktConfig(configMap: configMap)
@@ -540,12 +540,24 @@ public class SwiftMparticleFlutterSdkPlugin: NSObject, FlutterPlugin {
540540
}
541541

542542
roktEventHandler.subscribeToEvents(identifier: placementId)
543+
543544
MParticle.sharedInstance().rokt.selectPlacements(placementId, attributes: attributes, embeddedViews: placeholders, config: roktConfig, callbacks: callback)
544545
result(true)
545546
} else {
546547
print("Incorrect argument for \(call.method) iOS method")
547548
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing placementId", details: nil))
548549
}
550+
case "roktPurchaseFinalized":
551+
if let callArguments = call.arguments as? [String: Any],
552+
let placementId = callArguments["placementId"] as? String,
553+
let catalogItemId = callArguments["catalogItemId"] as? String,
554+
let success = callArguments["success"] as? Bool {
555+
MParticle.sharedInstance().rokt.purchaseFinalized(placementId, catalogItemId: catalogItemId, success: success)
556+
result(true)
557+
} else {
558+
print("Incorrect argument for \(call.method) iOS method")
559+
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing placementId or catalogItemId or success", details: nil))
560+
}
549561
default:
550562
print("mParticle flutter SDK for iOS does not support \(call.method)")
551563
}
@@ -569,7 +581,7 @@ public class SwiftMparticleFlutterSdkPlugin: NSObject, FlutterPlugin {
569581
private func buildRoktConfig(configMap: [String: Any]) -> MPRoktConfig? {
570582
let config = MPRoktConfig()
571583
var isConfigEmpty = true
572-
584+
573585
if let colorModeString = configMap["colorMode"] as? String {
574586
if #available(iOS 12.0, *) {
575587
isConfigEmpty = false

lib/mparticle_flutter_sdk.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ class MparticleFlutterSdk {
8989
_placeholders[id] = name;
9090
}
9191

92+
/// Clears all placeholders. Used for testing.
93+
@visibleForTesting
94+
void clearPlaceholders() {
95+
_placeholders.clear();
96+
}
97+
9298
/// Logs a product commerce event with an [productActionType], a promotion commerce event with a [eventType], and an impression commerce event if neither of the prior are implemented.
9399
Future<void> logCommerceEvent(CommerceEvent commerceEvent) async {
94100
var commerceEventMessage = {
@@ -325,6 +331,27 @@ class Rokt {
325331
return await _channel.invokeMethod('roktSelectPlacements', params);
326332
}
327333

334+
/// Notifies Rokt that a purchase has been finalized
335+
///
336+
/// Use this method to inform Rokt that a purchase has been completed or failed
337+
/// - Parameters:
338+
/// - placementId: The placement ID associated with the purchase
339+
/// - catalogItemId: The catalog item ID that was purchased
340+
/// - success: Whether the purchase was successful
341+
///
342+
/// Note: This method requires iOS 15+.
343+
Future<void> purchaseFinalized({
344+
required String placementId,
345+
required String catalogItemId,
346+
required bool success,
347+
}) async {
348+
return await _channel.invokeMethod('roktPurchaseFinalized', {
349+
'placementId': placementId,
350+
'catalogItemId': catalogItemId,
351+
'success': success,
352+
});
353+
}
354+
328355
Map<String, dynamic>? _roktConfigToMap({required RoktConfig? config}) {
329356
if (config == null) {
330357
return null;

test/mparticle_flutter_sdk_test.dart

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ void main() {
3636
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
3737
.setMockMethodCallHandler(channel, null);
3838
methodCall = null;
39+
mp.clearPlaceholders();
3940
});
4041

4142
group('mParticle Dart API Layer', () {
@@ -295,4 +296,64 @@ void main() {
295296
);
296297
});
297298
});
299+
300+
group('Rokt API', () {
301+
test('rokt select placements', () async {
302+
final roktConfig = RoktConfig(
303+
colorMode: ColorMode.dark,
304+
cacheConfig: CacheConfig(
305+
cacheDurationInSeconds: 100,
306+
cacheAttributes: {'key1': 'value1'}));
307+
await mp.rokt.selectPlacements(
308+
placementId: 'placement1',
309+
attributes: {'attr1': 'val1'},
310+
roktConfig: roktConfig,
311+
fontFilePathMap: {'font1': 'path1'});
312+
313+
expect(
314+
methodCall,
315+
isMethodCall('roktSelectPlacements', arguments: {
316+
'placementId': 'placement1',
317+
'attributes': {'attr1': 'val1'},
318+
'config': {
319+
'colorMode': 'dark',
320+
'cacheConfig': {
321+
'cacheDurationInSeconds': 100,
322+
'cacheAttributes': {'key1': 'value1'}
323+
}
324+
},
325+
'fontFilePathMap': {'font1': 'path1'},
326+
}));
327+
});
328+
329+
test('rokt select placements with placeholders', () async {
330+
mp.attachPlaceholder(id: 1, name: "placeholder1");
331+
await mp.rokt.selectPlacements(
332+
placementId: 'placement1',
333+
);
334+
335+
expect(
336+
methodCall,
337+
isMethodCall('roktSelectPlacements', arguments: {
338+
'placementId': 'placement1',
339+
'attributes': null,
340+
'config': null,
341+
'fontFilePathMap': null,
342+
'placeholders': {1: 'placeholder1'},
343+
}));
344+
});
345+
346+
test('rokt purchase finalized', () async {
347+
await mp.rokt.purchaseFinalized(
348+
placementId: 'placement1', catalogItemId: 'catalog1', success: true);
349+
350+
expect(
351+
methodCall,
352+
isMethodCall('roktPurchaseFinalized', arguments: {
353+
'placementId': 'placement1',
354+
'catalogItemId': 'catalog1',
355+
'success': true,
356+
}));
357+
});
358+
});
298359
}

0 commit comments

Comments
 (0)