Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Table of Contents
stylenone


...


1 Summary

The goal of this paper is to provide tips and good practices on how to perform a multi-marketplace integration with Maestrano, i.e be able to handle multiple Maestrano-powered marketplaces including Maestrano.com.

...

The diagram below provides a general overview of the Maestrano Enterprise Delivery Network:

2 Principles

When doing multi-marketplace integrations, three concepts need to be considered and properly scoped: Configuration, Routes and Models.

...

When storing records related to Maestrano and/or Maestrano Enterprise Marketplaces, your application should be able to keep track of which marketplace and which marketplace customer account this record is related to. This is usually done by storing a marketplace key - and marketplace customer account id - on the record or in an identity map table (see section 4).


3 Example of multi-marketplace integration (pseudo code)

The example below gives a high level overview of what your code may look like in the context of a multi-marketplace integration. If you have already done the integration to maestrano.com the code below will look familiar and essentially intends to show how you can "extend" your current integration to make it multi-marketplace.

The code samples show you the general steps involved in getting a basic multi-marketplace setup for Single Sign-On, Single Billing and Connec data sharing. Note that for this example, we assume the use of one of Maestrano's SDKs - available in Ruby, Java, PHP and .NET.

3.1 Configuration

The configuration step involves declaring multiple marketplace configurations, parameterizing the routes that will be used by our controllers (SSO, Connec etc.) and adding a metadata endpoint to expose our marketplace specific configuration.

...

Code Block
languageruby
titleMetadataController
linenumberstrue
# The metadata controller exposes my configuration to the requesting marketplace
# Thanks to this metadata controller, the marketplace will be able to discover my configuration
# and send webhook notifications to the right endpoint.
class MetadataController
  
	# The show action responds to the following route
	# GET /mno-enterprise/:marketplace_key/metadata
  	function show
    	# Because the URL was parameterized, we can retrieve the marketplace key
    	# from the URL parameters
    	marketplace_key = params['marketplace_key']
 
    	# Next step to make sure we authenticate the marketplace. Authentication is
    	# marketplace specific
    	unless Maestrano.with(marketplace_key).authenticate(http_basic['login'],http_basic['password'])
      		render_json("Unauthorized, code: '401')
    	end     
    
    	# Eventually, we render our configuration manifest for this specific marketplace
    	render_json(Maestrano.with(marketplace_key).to_metadata)
  	end
  
end


3.2 Single Sign-On

Info
titleOpenID also available!

The example below assumes you are using our SDK for Single Sign-On which is based on SAML 2.0.

Code Block
languageruby
titleSamlSSOController
linenumberstrue
#
# This controller handles the Single Sign-On handshake
#
class SamlSsoController
  
  	# The 'initialize' controller action responds the following route
  	# GET /mno-enterprise/:marketplace_key/saml/initialize
  	#
  	# The goal of this action is to trigger the Single Sign-On handshake
  	# between the marketplace platform and your application
  	function initialize
		# Retrieve the marketplace key from the URL parameters
    	marketplace_key = params['marketplace_key']
	
		redirect_to MaestranoSamlRequest.with(marketplace_key).new(params).redirect_url
  	end
  
  	# The 'consume' controller action responds to the following route
  	# POST /mno-enterprise/:marketplace_key/saml/consume
  	function consume
    	# Retrieve the marketplace key from the URL parameters
    	marketplace_key = params['marketplace_key']
    
		# Process the response
		saml_response = Maestrano::Saml::Response.with(marketplace_key).new(params[:SAMLResponse])
    
    	# Reject if invalid
    	unless saml_response.is_valid?
      		redirect_to "/some/error/path"
    	end
 
    	# Extract information from the response
  		user_attributes = Maestrano::SSO::BaseUser.new(saml_response).to_hash_or_associative_array
  		group_attributes = Maestrano::SSO::BaseGroup.new(saml_response).to_hash_or_associative_array

  		# Find/create the user and the organization
    	# The creation or retrieval of records should be scoped to a specific provider (marketplace_key)
  		user = User.find_or_create_for_maestrano_marketplace(user_attributes,marketplace_key)
  		organization = Organization.find_or_create_for_maestrano_marketplace(group_attributes,marketplace_key)

  		# Add user to the organization if not there already
  		unless organization.has_member?(user)
    		organization.add_member(user)
  		end

		# Sign the user in and redirect to application root
		# To be customised depending on how you handle user
  		# sign in and 
  		sign_user_in(user)
  		redirect_to "/some/post-login/path"
	end
 
end


3.3 Account Webhooks

Code Block
languageruby
titleWebhookAccountController
linenumberstrue
#
# This controller handles notification of people leaving a group (remove_user action) or companies
# cancelling their subscription to your service (destroy_group)
class WebhookAccountController
 
  	# The 'destroy_group' controller action responds the following route
  	# DESTROY /mno-enterprise/:marketplace_key/account/group/:id
  	function destroy_group
    	# Authenticate request as usual
    	unless Maestrano.with(marketplace_key).authenticate(http_basic['login'],http_basic['password'])
      		render json: "Unauthorized, code: '401'
    	end
 
	    # Retrieve the request parameters
	    marketplace_key = params[:marketplace_key]
    	group_uid = params[:id]
 
	    # Retrieve the group/company
	    organization = Organization.find_by_marketplace_and_uid(marketplace_key,group_uid)
    
	    # Destroy it
	    organization.destroy
	end
  
  	# The 'destroy_group' controller action responds the following route
  	# DESTROY /mno-enterprise/:marketplace_key/account/group/:group_id/user/:id
  	function remove_user
    	# Authenticate request as usual
    	unless Maestrano.with(marketplace_key).authenticate(http_basic['login'],http_basic['password'])
	      render json: "Unauthorized, code: '401'
    	end
    
	    # Retrieve the request parameters
    	marketplace_key = params[:marketplace_key]
	    group_uid = params[:group_id]
	    user_uid = params[:id]
    
	    # Retrieve the group/company as well as the user
	    organization = Organization.find_by_marketplace_and_uid(marketplace_key,group_uid)
	    user = User.find_by_marketplace_and_uid(marketplace_key,user_uid)
 
	    # Remove the user
	    organization.remove_user(user)
	end
 
end

3.4 Billing

The code below shows how to bill an organization which has been tagged with a marketplace key. All Maestrano SDKs offer the ability to scope REST calls with a marketplace key. See the documentation of the relevant SDK for more details.

Code Block
languageruby
titleMonthlyBillingJob
linenumberstrue
class MonthlyBillingJob
  
	# Run the billing job
  	function run
    	foreach organization in Organization.all()
 
      		if organization.maestrano_marketplace_key != null
  				# Use Maestrano's billing API
		        Bill.with(organization.maestrano_marketplace_key).create(amount: $100, group_id: organization.maestrano_uid)
      		else
		        # For your own customers, just charge as usual
        		organization.charge_credit_card($100)
		    end
	    end
	end
  
end

3.5 Connec Data Sharing

The code sample below shows an example of model automatically forwarding a notification to Connec! upon save, in a multi-marketplace way.

Code Block
languageruby
titleInvoiceModel
linenumberstrue
class InvoiceModel
  
	function save
    	return false unless this.save_to_db
	    if this.maestrano_uid
			client = MaestranoConnecClient.with(this.maestrano_marketplace_key).new(this.maestrano_group_uid)
      		client.post('/invoices', this.to_maestrano_json)
    	end
	end
end


3.6 Connec Webhooks

Code Block
languageruby
titleConnecWebhookController
linenumberstrue
# This controller processes any data sharing notifications sent by marketplaces via
# Connec!
# E.g.: I receive a new invoice from Connec!™ that was created in another application
class WebhookConnecController
  
	# The 'receive' controller action responds to the following route
  	# POST /mno-enterprise/:marketplace/connec/receive
  	function receive
  		# Retrieve the marketplace key from the URL parameters
	    marketplace_key = params['marketplace_key']

    	# Authenticate request as usual
	    unless Maestrano.with(marketplace_key).authenticate(http_basic['login'],http_basic['password'])
    		render json: "Unauthorized, code: '401'
    	end
    
    	# Finally, process the request for a specific marketplace
    	MyConnecWrapperClass.process_invoice_updates(params['invoices'],marketplace_key)
  	end
  
end



4 Good practice: handling objects connected to third parties

You may have - or anticipate to have - a couple of integrations with platforms like Maestrano, QuickBooks, Xero, SalesForce etc. which will require you to synchronize objects using their APIs. For each of these objects, you will need to keep track of the foreign ids associated with the objects you've remotely created and/or received ("connected objects").

...