Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Kits/RoktStripePayment/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@mparticle/cordova-rokt-stripe-payment-kit",
"version": "3.0.0",
"description": "Adds Rokt Stripe payment extension (Apple Pay) support to your Cordova project",
"homepage": "https://www.mparticle.com",
"repository": {
"type": "git",
"url": "https://github.com/mParticle/cordova-plugin-mparticle",
"directory": "Kits/RoktStripePayment"
},
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"provenance": true,
"registry": "https://registry.npmjs.org/"
},
"cordova": {
"id": "@mparticle/cordova-rokt-stripe-payment-kit",
"platforms": [
"ios"
]
}
}
17 changes: 17 additions & 0 deletions Kits/RoktStripePayment/plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
id="@mparticle/cordova-rokt-stripe-payment-kit"
version="3.0.0">
<name>MParticle Rokt Stripe Payment Kit</name>
<description>Adds Rokt Stripe payment extension (Apple Pay) support to your Cordova project</description>
<platform name="ios">
<podspec>
<config>
<source url="https://cdn.cocoapods.org/"/>
</config>
<pods use-frameworks="true">
<pod name="RoktStripePaymentExtension" spec="~> 0.1" />
</pods>
</podspec>
</platform>
</plugin>
85 changes: 84 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ cordova plugin add @mparticle/cordova-sdk
**Install the SDK** using CocoaPods:

```bash
$ # Update your Podfile to depend on 'mParticle-Apple-SDK' version 8.5.2 or later
$ # Update your Podfile to depend on 'mParticle-Apple-SDK' version 9.0 or later
$ pod install
```

Expand Down Expand Up @@ -264,6 +264,89 @@ identity.modify(request, function (userId) => {
});
```

## Rokt

### Installation

Add the Rokt kit to your `config.xml`:

```xml
<plugin name="@mparticle/cordova-rokt-kit" spec="~> 3.0" />
```

### Select Placements

Display a Rokt overlay placement on your confirmation or thank-you page:

```js
var attributes = {
'email': 'j.smith@example.com',
'firstname': 'Jenny',
'lastname': 'Smith',
'confirmationref': '54321',
'country': 'US'
};

mparticle.Rokt.selectPlacements('YourPlacementIdentifier', attributes);
```

You can optionally pass a configuration object:

```js
var config = {
colorMode: mparticle.Rokt.ColorMode.SYSTEM, // LIGHT, DARK, or SYSTEM
cacheConfig: {
cacheDurationInSeconds: 5400,
cacheAttributes: {}
}
};

mparticle.Rokt.selectPlacements('YourPlacementIdentifier', attributes, config);
```

### Shoppable Ads

Shoppable Ads enable post-purchase upsell offers with instant checkout via Apple Pay or Stripe. Currently supported on **iOS only**.

#### 1. Add the Stripe payment kit

```xml
<plugin name="@mparticle/cordova-rokt-stripe-payment-kit" spec="~> 3.0" />
```

#### 2. Register the payment extension

The `RoktStripePaymentExtension` must be registered in your native iOS code after SDK initialization and before calling `selectShoppableAds`.

Since `RoktStripePaymentExtension` is Swift-only, ObjC apps need a bridging class. See `example/platform_overrides/ios/RoktPaymentSetup.swift` for the wrapper, then call from your AppDelegate:

```objc
#import "YourApp-Swift.h"

[RoktPaymentSetup registerPaymentExtensionWithMerchantId:@"merchant.com.yourapp.rokt"];
```

#### 3. Display Shoppable Ads

```js
var attributes = {
'email': 'j.smith@example.com',
'firstname': 'Jenny',
'lastname': 'Smith',
'confirmationref': 'ORD-8829-XK2',
'amount': '52.25',
'currency': 'USD',
'paymenttype': 'visa',
'shippingaddress1': '123 Main St',
'shippingcity': 'Brooklyn',
'shippingstate': 'NY',
'shippingzipcode': '11201',
'shippingcountry': 'US'
};

mparticle.Rokt.selectShoppableAds('YourPlacementIdentifier', attributes);
```

# License

Apache 2.0
1 change: 1 addition & 0 deletions example/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@
<hook type="after_platform_add" src="hooks/after_platform_add/010_copy_platform_specific_files.js" />
<plugin name="@mparticle/cordova-sdk" spec="../plugin" />
<plugin name="@mparticle/cordova-rokt-kit" spec="../Kits/Rokt" />
<plugin name="@mparticle/cordova-rokt-stripe-payment-kit" spec="../Kits/RoktStripePayment" />
</widget>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var shell = require('child_process');
module.exports = function(context) {
// Get platform from context
var platform = context.opts.platforms[0];

function ensureDirectoryExistence(filePath) {
var dirname = path.dirname(filePath);
if (fs.existsSync(dirname)) {
Expand All @@ -16,35 +16,64 @@ module.exports = function(context) {
fs.mkdirSync(dirname, { recursive: true });
}

if (platform === 'android') {
// Copy MainActivity.java
var srcFile = path.join(context.opts.projectRoot, 'platform_overrides', 'android', 'MainActivity.java');
var destFile = path.join(context.opts.projectRoot, 'platforms', 'android', 'app', 'src', 'main', 'java', 'com', 'mparticle', 'example', 'MainActivity.java');

if (fs.existsSync(srcFile)) {
function copyFile(srcRelative, destRelative, label) {
var src = path.join(context.opts.projectRoot, srcRelative);
var dest = path.join(context.opts.projectRoot, destRelative);
if (fs.existsSync(src)) {
try {
ensureDirectoryExistence(destFile);
fs.copyFileSync(srcFile, destFile);
console.log('Copied custom MainActivity.java');
ensureDirectoryExistence(dest);
fs.copyFileSync(src, dest);
console.log('Copied ' + label);
} catch (err) {
console.error('Error copying MainActivity.java:', err);
console.error('Error copying ' + label + ':', err);
process.exitCode = 1;
}
}
}

if (platform === 'android') {
copyFile(
path.join('platform_overrides', 'android', 'MainActivity.java'),
path.join('platforms', 'android', 'app', 'src', 'main', 'java', 'com', 'mparticle', 'example', 'MainActivity.java'),
'custom MainActivity.java'
);
} else if (platform === 'ios') {
// Copy AppDelegate.m
var srcFile = path.join(context.opts.projectRoot, 'platform_overrides', 'ios', 'AppDelegate.m');
var destFile = path.join(context.opts.projectRoot, 'platforms', 'ios', 'MParticleExample', 'AppDelegate.m');

if (fs.existsSync(srcFile)) {
copyFile(
path.join('platform_overrides', 'ios', 'AppDelegate.m'),
path.join('platforms', 'ios', 'MParticleExample', 'AppDelegate.m'),
'custom AppDelegate.m'
);

copyFile(
path.join('platform_overrides', 'ios', 'RoktPaymentSetup.swift'),
path.join('platforms', 'ios', 'MParticleExample', 'RoktPaymentSetup.swift'),
'RoktPaymentSetup.swift'
);

// Create bridging header for Swift/ObjC interop
var bridgingHeaderPath = path.join(context.opts.projectRoot, 'platforms', 'ios', 'MParticleExample', 'MParticleExample-Bridging-Header.h');
if (!fs.existsSync(bridgingHeaderPath)) {
try {
ensureDirectoryExistence(destFile);
fs.copyFileSync(srcFile, destFile);
console.log('Copied custom AppDelegate.m');
fs.writeFileSync(bridgingHeaderPath, '// Bridging header for Swift/ObjC interop\n');
console.log('Created bridging header');
} catch (err) {
console.error('Error copying AppDelegate.m:', err);
console.error('Error creating bridging header:', err);
process.exitCode = 1;
}
}

// NOTE: RoktStripePaymentExtension pod is added automatically by the
// @mparticle/cordova-rokt-stripe-payment-kit declared in config.xml.
// If using a kit, no manual Podfile manipulation is needed here.

// Add Swift file to Xcode project and configure Swift settings
var iosPath = path.join(context.opts.projectRoot, 'platforms', 'ios');
var configureXcodeScript = path.join(context.opts.projectRoot, 'hooks', 'after_platform_add', 'configure_xcode_swift.rb');
try {
shell.execSync('cd ' + iosPath + ' && ruby ' + configureXcodeScript, { stdio: 'inherit' });
} catch (err) {
console.error('Error configuring Xcode project for Swift:', err);
process.exitCode = 1;
}
}
};
16 changes: 16 additions & 0 deletions example/hooks/after_platform_add/configure_xcode_swift.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'xcodeproj'

proj = Xcodeproj::Project.open('MParticleExample.xcodeproj')
target = proj.targets.find { |t| t.name == 'MParticleExample' }
group = proj.main_group.find_subpath('MParticleExample', false)

swift_ref = group.new_file('RoktPaymentSetup.swift')
target.source_build_phase.add_file_reference(swift_ref)

target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '5.0'
config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] = 'MParticleExample/MParticleExample-Bridging-Header.h'
end

proj.save
puts 'Configured Xcode project for Swift/Shoppable Ads'
6 changes: 5 additions & 1 deletion example/platform_overrides/ios/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Licensed to the Apache Software Foundation (ASF) under one
#import "AppDelegate.h"
#import "MainViewController.h"
#import "mParticle.h"
#import "MParticleExample-Swift.h"

@implementation AppDelegate

Expand All @@ -41,8 +42,11 @@ - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(N
};

[[MParticle sharedInstance] startWithOptions:mParticleOptions];

[MParticle sharedInstance].logLevel = MPILogLevelVerbose;

// Register Rokt Stripe payment extension for Shoppable Ads
[RoktPaymentSetup registerPaymentExtensionWithMerchantId:@"merchant.com.mparticle.example"];

self.viewController = [[MainViewController alloc] init];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
Expand Down
11 changes: 11 additions & 0 deletions example/platform_overrides/ios/RoktPaymentSetup.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation
import RoktStripePaymentExtension
import mParticle_Apple_SDK_ObjC

@objc public class RoktPaymentSetup: NSObject {
@objc public static func registerPaymentExtension(merchantId: String) {
if let stripeExt = RoktStripePaymentExtension(applePayMerchantId: merchantId) {
MParticle.sharedInstance().rokt.register(stripeExt)
}
}
}
2 changes: 2 additions & 0 deletions example/www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ <h2>mParticle</h2>
<hr>
<h2>Rokt</h2>
<button id="selectPlacementsBtn" class="button">Select Placements</button>
<button id="selectShoppableAdsBtn" class="button">Select Shoppable Ads</button>
<button id="purchaseFinalizedBtn" class="button">Purchase Finalized</button>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
Expand Down
35 changes: 35 additions & 0 deletions example/www/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ document.getElementById("setATTStatusBtn").addEventListener('click', setATTStatu
document.getElementById("loginBtn").addEventListener('click', login, false);
document.getElementById("logoutBtn").addEventListener('click', logout, false);
document.getElementById("trackConversionBtn").addEventListener('click', trackConversion, false);
document.getElementById("selectShoppableAdsBtn").addEventListener('click', selectShoppableAds, false);
document.getElementById("purchaseFinalizedBtn").addEventListener('click', purchaseFinalized, false);

var app = {
// Application Constructor
Expand Down Expand Up @@ -244,4 +246,37 @@ function trackConversion() {
);
}

function selectShoppableAds() {
console.log('MParticleCordova Plugin Example: Selecting Shoppable Ads');

var attributes = {
'email': 'j.smith@example.com',
'firstname': 'Jenny',
'lastname': 'Smith',
'country': 'US',
'shippingstate': 'NY',
'shippingzipcode': '10001',
'confirmationref': '54321'
};

var config = {
colorMode: mparticle.Rokt.ColorMode.SYSTEM,
cacheConfig: {
cacheDurationInSeconds: 5400,
cacheAttributes: {}
}
};

mparticle.Rokt.selectShoppableAds(
'StgRoktShoppableAds',
attributes,
config
);
}

function purchaseFinalized() {
console.log('MParticleCordova Plugin Example: Purchase Finalized');
mparticle.Rokt.purchaseFinalized('StgRoktShoppableAds', 'catalog-item-123', true);
}

app.initialize();
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ public boolean execute(String action, final JSONArray args, final CallbackContex
} else if (action.equals("selectPlacements")) {
selectPlacements(args);
return true;
} else if (action.equals("selectShoppableAds")) {
selectShoppableAds(args);
return true;
} else if (action.equals("purchaseFinalized")) {
purchaseFinalized(args);
return true;
} else {
return false;
}
Expand Down Expand Up @@ -442,6 +448,18 @@ public void selectPlacements(final JSONArray args) throws JSONException {
);
}

public void selectShoppableAds(final JSONArray args) throws JSONException {
Logger.warning("selectShoppableAds is not yet supported on Android");
}

public void purchaseFinalized(final JSONArray args) throws JSONException {
final String placementId = args.getString(0);
final String catalogItemId = args.getString(1);
final boolean success = args.getBoolean(2);

MParticle.getInstance().Rokt().purchaseFinalized(placementId, catalogItemId, success);
}

private static IdentityApiRequest ConvertIdentityAPIRequest(JSONObject map) throws JSONException {
IdentityApiRequest.Builder identityRequest = IdentityApiRequest.withEmptyUser();

Expand Down
Loading
Loading