Skip to content

Commit 34d9c69

Browse files
committed
feat: Add Rokt purchaseFinalized method
- 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>
1 parent e8b4bbb commit 34d9c69

4 files changed

Lines changed: 123 additions & 3 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
@@ -226,6 +226,7 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler {
226226
result.success(true)
227227
}
228228
"roktSelectPlacements" -> this.roktSelectPlacements(call, result)
229+
"roktPurchaseFinalized" -> this.roktPurchaseFinalized(call, result)
229230
else -> {
230231
result.notImplemented()
231232
}
@@ -750,6 +751,26 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler {
750751
return builder.build()
751752
}
752753

754+
private fun roktPurchaseFinalized(call: MethodCall, result: Result) {
755+
val placementId = call.argument<String>("placementId")
756+
val catalogItemId = call.argument<String>("catalogItemId")
757+
val success = call.argument<Boolean>("success") ?: true
758+
if (placementId != null && catalogItemId != null) {
759+
MParticle.getInstance()?.Rokt()?.purchaseFinalized(
760+
placementId = placementId,
761+
catalogItemId = catalogItemId,
762+
status = success,
763+
)
764+
result.success("Success")
765+
} else {
766+
result.error(
767+
"INVALID_PARAMS",
768+
"placementId and catalogItemId are required",
769+
null,
770+
)
771+
}
772+
}
773+
753774
private fun String.toColorMode(): RoktConfig.ColorMode =
754775
when (this) {
755776
"dark" -> RoktConfig.ColorMode.DARK

ios/Classes/SwiftMparticleFlutterSdkPlugin.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ public class SwiftMparticleFlutterSdkPlugin: NSObject, FlutterPlugin {
527527
}
528528
}
529529
}
530-
530+
531531
var roktConfig: MPRoktConfig?
532532
if let configMap = callArguments["config"] as? [String: Any] {
533533
roktConfig = buildRoktConfig(configMap: configMap)
@@ -537,12 +537,23 @@ public class SwiftMparticleFlutterSdkPlugin: NSObject, FlutterPlugin {
537537
registerPartnerFonts(typefaces)
538538
}
539539

540-
MParticle.sharedInstance().rokt.selectPlacements(placementId, attributes: attributes, placements: placeholders, config: roktConfig, callbacks: callback)
540+
MParticle.sharedInstance().rokt.selectPlacements(placementId, attributes: attributes, embeddedViews: placeholders, config: roktConfig, callbacks: callback)
541541
result(true)
542542
} else {
543543
print("Incorrect argument for \(call.method) iOS method")
544544
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing placementId", details: nil))
545545
}
546+
case "roktPurchaseFinalized":
547+
if let callArguments = call.arguments as? [String: Any],
548+
let placementId = callArguments["placementId"] as? String,
549+
let catalogItemId = callArguments["catalogItemId"] as? String,
550+
let success = callArguments["success"] as? Bool {
551+
MParticle.sharedInstance().rokt.purchaseFinalized(placementId, catalogItemId: catalogItemId, success: success)
552+
result(true)
553+
} else {
554+
print("Incorrect argument for \(call.method) iOS method")
555+
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing placementId or catalogItemId or success", details: nil))
556+
}
546557
default:
547558
print("mParticle flutter SDK for iOS does not support \(call.method)")
548559
}
@@ -566,7 +577,7 @@ public class SwiftMparticleFlutterSdkPlugin: NSObject, FlutterPlugin {
566577
private func buildRoktConfig(configMap: [String: Any]) -> MPRoktConfig? {
567578
let config = MPRoktConfig()
568579
var isConfigEmpty = true
569-
580+
570581
if let colorModeString = configMap["colorMode"] as? String {
571582
if #available(iOS 12.0, *) {
572583
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)