Skip to content

Objective-C iOS wrapper for Mastercard's web SRC Initiator

License

Notifications You must be signed in to change notification settings

hangingwater/MCSCommerceWeb

 
 

Repository files navigation

MCSCommerceWeb

  1. Overview
  2. Installation
  3. Configuration
  4. Checkout Button
  5. Checkout
  6. Migrating from MCCMerchant

MCSCommerceWeb is a lightweight framework used to integrate Merchants with EMV Secure Remote Commerce and Mastercard's web-based SRC-Initiator with backward compatibility for existing Masterpass integrations. MCSCommerceWeb facilitates the initiation of the checkout experience and returns the transaction result to the Merchant after completion.

Note: currently, this framework is only recommended for existing U.S. Masterpass merchants. Merchants using version 2.8 must follow the steps in the migration section. Merchants using version older than 2.8 should follow all steps wihtout migration from this integration guide.

CocoaPods

To include MCSCommerceWeb in your Xcode project, include the following in your project's Podfile:

pod 'MCSCommerceWeb', '1.0.1'

When instantiating MCSCommerceWeb, an MCSConfiguration object needs to be provided.

MCSConfiguration requires the following parameters:

  • locale: This is the locale in which the transaction is processing
  • checkoutId: The unique identifier assigned to the merchant during onboarding
  • checkoutUrl: The URL used to load the checkout experience. Note: when testing in the Sandbox environment, use https://sandbox.masterpass.com/routing/v2/mobileapi/web-checkout . For Production, use https://masterpass.com/routing/v2/mobileapi/web-checkout .
  • callbackScheme: This must match the scheme component of the callbackUrl configured for this merchant. This value is required to redirect back from the WKWebView
  • allowedCardTypes : The payment networks supported by this merchant (e.g. master, visa, amex).
// Swift
let locale = Locale(identifier: "en_us")
let checkoutId = "1d45705100044e14b52e71730e71cc5a"
let checkoutUrl = "https://masterpass.com/routing/v2/mobileapi/web-checkout";
let callbackScheme = "fancyshop";
let allowedCardTypes = [.master, .visa]
    
let commerceConfig = MCSConfiguration(
    locale: locale,
    checkoutId: checkoutId,
    checkoutUrl: checkoutUrl,
    callbackScheme: callbackScheme,
    allowedCardTypes: allowedCardTypes)
    
let commerceWeb = MCSCommerceWeb.sharedManager()
commerceWeb.setConfiguration(withConfiguration: commerceConfig)
// Objective-C
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_us"];
NSString *checkoutId = @"1d45705100044e14b52e71730e71cc5a";
NSString *checkoutUrl = @"https://masterpass.com/routing/v2/mobileapi/web-checkout";
NSString *callbackScheme = @"fancyshop";
NSSet *allowedCardTypes = [NSSet setWithObject:MCSCardTypeMaster, MCSCardTypeVisa];
MCSConfiguration *config = [[MCSConfiguration alloc] initWithLocale:locale
                                                         checkoutId:checkoutId
                                                        checkoutUrl:checkoutUrl
                                                     callbackScheme:callbackScheme
                                                   allowedCardTypes:allowedCardTypes];

MCSCommerceWeb *commerceWeb = [MCSCommerceWeb sharedManager];
[commerceWeb initWithConfiguration:config];

Transactions are initiated by adding MCSCheckoutButton to the view.

// Swift
// SomeViewController.swift
let button = commerceWeb.getCheckoutButton(withDelegate:checkoutDelegate)
button.addToSuperview(superview: buttonContainer)
// Objective-C
// SomeViewController.m
MCSCheckoutButton *button = [commerceWeb checkoutButtonWithDelegate:checkoutDelegate];
[button addToSuperview:buttonContainer];

withDelegate:

The MCSCheckoutDelegate provided has the following methods:

// MCSCheckoutDelegate
// Swift
// Fetches the checkout request object required to initiate this transaction.
func getCheckoutRequest(withHandler: @escaping (MCSCheckoutRequest) -> Void)

// Notifies the delegate when the transaction has completed
func checkoutCompleted(withRequest request: MCSCheckoutRequest, status: MCSCheckoutStatus, transactionId: String?)
// MCSCheckoutDelegate
// Objective-C
// Fetches the checkout request object required to initiate this transaction.
- (void)checkoutRequestForTransaction:(nonnull void(^)(MCSCheckoutRequest * _Nonnull checkoutRequest))handler;

//Notifies the delegate when the transaction has completed
- (void)checkoutRequest:(MCSCheckoutRequest *)request didCompleteWithStatus:(MCSCheckoutStatus)status forTransaction:(NSString * _Nullable)transactionId;

When the user touches up on MCSCheckoutButton, MCSCheckoutRequest for this transaction is retrieved.

Checkout Request

// Swift
func getCheckoutRequest(withHandler: @escaping (MCSCheckoutRequest) -> Void)
// Objective-C
- (void)checkoutRequestForTransaction:(nonnull void(^)(MCSCheckoutRequest * _Nonnull checkoutRequest))handler;

checkoutRequest: Data object with transaction-specific parameters needed to complete checkout. This request can also override existing merchant configurations.

Here are the required and optional fields:

Parameter Type Required Description
allowedCardTypes Array Yes Set of all card types accepted for this transaction
amount Decimal Yes The transaction total to be authorized
cartId String Yes Merchant's unique identifier for this transaction
callbackUrl String No URL used to communicate back to the merchant application
cryptoOptions Array No Cryptogram formats accepted by this merchant
cvc2Support Boolean No Enable or disable support for CVC2 card security
shippingLocationProfile String No Shipping locations available for this merchant
suppress3Ds Boolean No Enable or disable 3DS verification
suppressShippingAddress Boolean No Enable or disable shipping options. Typically for digital goods or services, this will be set to true
unpredictableNumber String No For tokenized transactions, unpredictableNumber is required for cryptogram generation
validityPeriodMinutes Integer No The expiration time of a generated cryptogram, in minutes

The implementation of the checkout with these parameters:

// Swift
func getCheckoutRequest(withHandler: @escaping (MCSCheckoutRequest) -> Void) {
	let checkoutRequest = MCSCheckoutRequest()
	checkoutRequest.amount = NSDecimalNumber(string: String(shoppingCart.total))
	checkoutRequest.currency = sdkConfig.currency
	checkoutRequest.cartId = shoppingCart.cartId
	checkoutRequest.allowedCardTypes = [.master,.visa]
	checkoutRequest.suppressShippingAddress = sdkConfig.suppressShipping
	checkoutRequest.callbackUrl = "fancyshop://"
	checkoutRequest.unpredictableNumber = "12345678"
	    
	let cryptoOptionVisa = MCSCryptoOptions()
	cryptoOptionVisa.cardType = "visa"
	cryptoOptionVisa.format = ["TVV"]
	
	let cryptoOptionMaster = MCSCryptoOptions()
	cryptoOptionMaster.cardType = "master"
	cryptoOptionMaster.format = ["ICC,UCAF"]
	
	checkoutRequest.cryptoOptions = [cryptoOptionMaster,cryptoOptionVisa]
	    
	withHandler(checkoutRequest)
}
// Objective-C
- (void)checkoutRequestForTransaction:(nonnull void(^)(MCSCheckoutRequest * _Nonnull checkoutRequest))handler {
	MCSCheckoutRequest *checkoutRequest = [[MCSCheckoutRequest alloc] init];
	checkoutRequest.amount = [[NSDecimalNumber alloc] initWithString:shoppingCart.total];
	checkoutRequest.currency = sdkConfig.currency
	checkoutRequest.cartId = shoppingCart.cartId
	checkoutRequest.allowedCardTypes = [NSSet setWithObjects:MCSCardTypeMaster, MCSCardTypeVisa, nil];
	checkoutRequest.suppressShippingAddress = sdkConfig.suppressShipping;
	checkoutRequest.callbackUrl = @"fancyshop://";
	checkoutRequest.unpredictableNumber = @"12345678";
	
	MCSCryptoOptions *cryptoOptionMaster = [[MCSCryptoOptions alloc] init];
	cryptoOptionMaster.cardType = MCSCardTypeMaster;
	cryptoOptionMaster.format = @[MCSCryptoFormatICC, MCSCryptoFormatUCAF];
	
	checkoutRequest.cryptoOptions = @[cryptoOptionMaster];
	
	handler(checkoutRequest);
}

Transaction Result

// Swift
func checkoutCompleted(withRequest request: MCSCheckoutRequest, status: MCSCheckoutStatus, transactionId: String?)
// Objective-C
- (void)checkoutRequest:(MCSCheckoutRequest *)request didCompleteWithStatus:(MCSCheckoutStatus)status forTransaction:(NSString * _Nullable)transactionId;

The result of a transaction is returned to the application via the MCSCheckoutDelegate provided when retrieving the MCSCheckoutButton.

// Swift
func checkoutCompleted(withRequest request: MCSCheckoutRequest!, status: MCSCheckoutStatus, transactionId: String?) {
	if (transactionId != nil) {
		//comlpete transaction
	}
}
// Objective-C
- (void)checkoutRequest:(MCSCheckoutRequest *)request didCompleteWithStatus:(MCSCheckoutStatus)status forTransaction:(NSString * _Nullable)transactionId {
	if (transactionId != nil) {
		//complete transaction
	}
}

Note: MCCMerchant APIs are deprecated in MCSCommerceWeb and will be removed in subsequent versions. It is encouraged to migrate to the APIs above.

MCSCommerceWeb provides API compatibility for MCCMerchant. Existing applications using MCCmerchant can easily migrate to MCSCommerceWeb with minimal changes. Consider the following when migrating from MCCMerchant.

Interfaces

The MCCMerchant interface is included in this SDK, however import statements must update to use the MCSCommerceWeb module.

// Swift
// previous import statement
import MCCMerchant

// current import statement
import MCSCommerceWeb
// Objective-C
// previous
#import <MCCMerchant/MCCMerchant.h>

// current
#import <MCSCommerceWeb/MCSCommerceWeb.h>
MCCConfiguration

MCCConfiguration has 4 new required properties

  • checkoutId : The merchant identifier generated when created a merchant developer profile. Note: this is moved from MCCCheckoutRequest
  • allowedCardTypes : The payment networks supported by this merchant (e.g. master, visa, amex). Note: this is moved from MCCCheckoutRequest
  • callbackScheme : The scheme used to return data to this application. This is the value configured in URL Schemes in the info.plist
  • checkoutUrl : The URL used to load the checkout experience. Note: if you are migrating to MCSCommerceWeb, but still plan to checkout with Masterpass, you will still need to provide this URL.
Environment URL
Masterpass Sandbox https://sandbox.masterpass.com/routing/v2/mobileapi/web-checkout
Masterpass Production https://masterpass.com/routing/v2/mobileapi/web-checkout
Handle checkout response
// MCCMerchant.h
+ (BOOL)handleMasterpassResponse:(NSString *_Nonnull)url delegate:(id<MCCMerchantDelegate> _Nonnull)merchantDelegate;

handleMasterpassResponse:delegate no longer has any effect. MCSCommerceWeb implements WKWebView instead of SFSafariViewController and the checkout response is handled using MCCDelegate directly. The MCCDelegate provided in any checkout calls will be the delegate that receives the checkout response.

Add Payment Method
// MCSCommerceWeb.h
- (MCCPaymentMethod *_Nonnull)addPaymentMethod;

addMasterpassPaymentMethod:withCompletionBlock: should no longer be called. MCSCommerceWeb now provides the function addPaymentMethod: to support payment method. It always returns the same payment method with the following properties:

  • paymentMethodName : Click to Pay
  • paymentMethodLogo : The SRC logo as UIImage

This payment method, or any other, can be used with paymentMethodCheckout: to initiate a standard checkout flow (see next).

Payment Method Checkout
// MCSCommerceWeb.h
- (void)paymentMethodCheckout:(id<MCSCheckoutDelegate> _Nonnull)delegate request:(MCSCheckoutRequest *_Nonnull)request;

paymentMethodCheckout: should no longer be called. MCSCommerceWeb provides paymentMethodCheckout:delegate:request: which will initiate the standard SRC checkout flow with the MCSCheckoutDelegate handling the response.

Old Function New Function
+ (void)addMasterpassPaymentMethod:(id<MCCMerchantDelegate> _Nonnull)merchantDelegate withCompletionBlock:(void(^ __nonnull) (MCCPaymentMethod* _Nullable mccPayment, NSError * _Nullable error))completionHandler; - (MCCPaymentMethod *_Nonnull)addPaymentMethod;
+ (void)paymentMethodCheckout:(id<MCCMerchantDelegate> _Nonnull) merchantDelegate; - (void)paymentMethodCheckout:(id<MCSCheckoutDelegate> _Nonnull)delegate request:(MCSCheckoutRequest *_Nonnull)request

Integrating with the web checkout experience is possible without this SDK. Using WebKit, the checkout experience can be embedded into any application.

Refer to the Apple developer documentation for UIWebView here.

For the UIWebView, we need to build a url with required and optional parameters to load the webView itself. The checkout URL sample and parameters can be found below:

https://stage.src.mastercard.com/srci/?checkoutId=asdfghjk123456&cartId=111111-2222-3333-aaaa-qwerqwerqwer&amount=11.22&currency=USD&allowedCardTypes=master%2Camex%2Cvisa&suppressShippingAddress=false&locale=en_US&channel=mobile&masterCryptoFormat=UCAF%2CICC

Parameter Description
checkoutID This value is provided from the merchant onboarding
cartId Randomly generated UUID
amount The amount to be charged
currency The currency of the amount
allowedCardTypes The cards the merchant supports (Mastercard/Visa/Amex)
suppressShippingAddress If set to true, Masterpass will not ask for a shipping address
locale The language Masterpass should load
channel Default should be set to mobile
masterCryptoFormat Default should be set to UCAF%2CICC

###WKWebView

In the UIViewController, configure the WebView and initialize the following:

// Swift
func viewDidLoad() {
    super.viewDidLoad()

    let preferences = WKPreferences()
    preferences.javaScriptCanOpenWindowsAutomatically = true

    let configuration = WKWebViewConfiguration()
    configuration.preferences = preferences
    configuration.setURLSchemeHandler(self, forURLScheme: scheme)

    let webview = WKWebView(frame: CGRect.zero, configuration: configuration)
    webview.uiDelegate = self
    webview.navigationDelegate = self

    let request = URLRequest(url: url)

    webview.load(request)
    webview.allowsBackForwardNavigationGestures = true
    view.backgroundColor = UIColor.white
    view.addSubview(webview)
}
// Objective-C
- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKPreferences *preferences = [[WKPreferences alloc] init];
    preferences.javaScriptCanOpenWindowsAutomatically = true;
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    configuration.preferences = preferences;
    [configuration setURLSchemeHandler:self forURLScheme:self.scheme];
    
    WKWebView *webview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
    webview.UIDelegate = self;
    webview.navigationDelegate = self;
    
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:_url];
    
    [webview loadRequest:request];
    [webview setAllowsBackForwardNavigationGestures:YES];
    [self.view setBackgroundColor:[UIColor whiteColor]];
    [self.view addSubview:webview];
}

webView requires implementing the WKWebView delegate function webView:startURLSchemeTask: for handling the callbackUrl in order to parse the transaction response data:

// Swift
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
    let callbackUrl = urlSchemeTask.request.url
    var urlComponents: URLComponents? = nil
    if let callbackUrl = callbackUrl {
        urlComponents = URLComponents(url: callbackUrl, resolvingAgainstBaseURL: true)
    }
    
    let urlResponse = URLResponse()
    urlSchemeTask.didReceive(urlResponse)
    urlSchemeTask.didFinish()
    
    var transactionID;
    var status;
    
    for item in urlComponents?.queryItems ?? [] {
        if (item.name == "oauth_token") {
            transactionId = item.value
        } else if (item.name == "mpstatus") {
            status = item.value
        }
    }
    
    // handle checkout response here
    if (status == 'success') {
    	// handle successful transaction
    } else {
    	// handle canceled transaction
    }
}
// Objective-C
- (void) webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
    NSURL *callbackUrl = urlSchemeTask.request.URL;
    NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:callbackUrl resolvingAgainstBaseURL:YES];
        
    NSURLResponse *urlResponse = [[NSURLResponse alloc] init];
    [urlSchemeTask didReceiveResponse:urlResponse];
    [urlSchemeTask didFinish];
    
    NSString *transactionId;
    NSString *status;
    
    for (NSURLQueryItem *item in [urlComponents queryItems]) {
        if ([item.name isEqualToString:@"oauth_token"]) {
            transactionId = item.value;
        } else if ([item.name isEqualToString:@"mpstatus"]) {
            status = item.value;
        }
    }
        
    // handle checkout response here
    if ([status isEqualToString:@"success"]) {
    	// handle successful transaction
    } else {
    	// handle canceled transaction 
    }
}

WebView requires implementing the WKWebView delegate function webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures: to enable popup windows from the host web view:

// Swift
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
	let popup = WKWebView(frame: view.bounds, configuration: configuration)
    popup.uiDelegate = self
    popup.navigationDelegate = self
        
    view.addSubview(popup)
        
    return popup
}
// Objective-C
- (WKWebView *) webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    WebView *popup = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    popup.UIDelegate = self;
    popup.navigationDelegate = self;
    
    [self.view addSubview:popup];
        
    return popup;
}

WebView requires implementing the WKWebView delegate function webView:decidePolicyForNavigationAction:decisionHandler: to decide whether to allow or cancel a navigation:

// Swift
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
   if (navigationAction.navigationType == .linkActivated) {
        let url = navigationAction.request.url
        if let url = url {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }
        decisionHandler(.cancel)
    } else {
        decisionHandler(.allow)
    }
}
// Objective-C
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
        NSURL *url = navigationAction.request.URL;
        [UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil];
        
        decisionHandler(WKNavigationActionPolicyCancel);
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

About

Objective-C iOS wrapper for Mastercard's web SRC Initiator

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Swift 66.1%
  • Objective-C 33.7%
  • Ruby 0.2%