Pay Central SDK

The Pay Central SDK allows iMIS clients to take payments through Pay Central from pages outside of an iMIS RiSE website.

To use this feature, the iMIS system must be licensed for the "Pay Central SDK" feature. Current licensed features can be viewed from Staff Site > Settings > About iMIS.

Payment Flow

The basic flow for taking a payment using the SDK is:

  1. Call the iMIS API to acquire an SDK AuthToken.
  2. Include a script tag in the page to load the SDK script.
  3. Initialize an instance of the PayCentral manager object.
  4. Create a payment request.
  5. Show a payment control for the payment request.
  6. Submit the payment request to retrieve a payment token.
  7. Submit a ComboOrder to the iMIS API that includes the payment token.

Development Environment

You will be writing both server side and client side code.

Requests to the iMIS API to acquire an SDK AuthToken and submit a ComboOrder must originate from server side code. The iMIS API access control policy will deny these requests if made from the browser.

The Pay Central SDK is an external script that you will include in your page using a <script> tag. You will work with the Pay Central SDK on your page by calling functions it exposes. You will be responsible for developing the page into which the Pay Central SDK script is embedded.

The Pay Central SDK adds elements to the DOM to render an appropriate UI for taking payments. You control when this happens, and where on the page this UI is rendered.

To get a successful development environment running there are some rules that must be followed.

  • The iMIS instance you are developing against must be licensed for the "Pay Central SDK" feature.
  • You must whitelist the domain of your page in iMIS and you must supply that domain when generating an SDK AuthToken. An attempt to generate an AuthToken for a domain that has not been whitelisted will fail. Any attempt to show a payment UI on a page whose domain does not match that associated with the AuthToken will fail.
  • If you are developing against localhost, you will need to whitelist localhost in iMIS and supply localhost as the domain when requesting a token. There are no exceptions to the whitelisting rule. If you prefer not to whitelist localhost, then you will need to use another technique such as a hosts file or a reverse proxy to allow your page to be served from localhost whilst using a different domain name in the browser.
  • Do not specify a port number when whitelisting a domain.
  • Your page must be served over https. It is not essential that the certificate is valid or trusted.

Whitelisting domains

To allow payments from non-iMIS websites, the domains of those websites must first be whitelisted in iMIS. This is done in iMIS through Staff Site > Settings > Pay Central > SDK tab

Enter the domain names of the websites you wish to take payments from, one per line. Do not include the http:// or https:// protocol prefixes or port numbers.

After saving changes, it may take a couple of minutes for them to take effect.

During development, you will need to ensure that the domain you are developing against is whitelisted in iMIS. This is true for all domains including localhost.

SDK AuthToken

A new SDK AuthToken is required each time the payment process is initiated from a page.

A token remains valid for a limited period of time. When a token expires, an attempt to show a payment control or submit a payment request for that token will result in an exception.

Once payment has been successfully taken, the token will be invalidated and cannot be reused.

Tokens should be generated on demand when the payment process is initiated. Do not maintain a pool of pre-generated tokens.

The AuthToken will be required client side (in the browser). There are multiple strategies for achieving this. Common options are:

  • Include the token somewhere on the page when it is composed server-side e.g. in a hidden input control.
  • Generate tokens on demand by using AJAX to call your own server-side code.

SDK Tokens are generated by making a request to the iMIS API. Normal rules for making the API request apply i.e. a bearer token must be provided in the Authorization header. The request must be made server side. The iMIS API access control policy will restrict this request from being made in the browser unless it comes from a page inside an iMIS RiSE website.

Make a POST request to the paycentralsdk service using the GenericExecuteRequest format targeting the "GetSdkToken" operation.

Two parameter values must be provided within the body.

ParameterDetails
domainThe domain of the page into which the payment control will be loaded e.g. "donate.myassociation.com". This must be one of the whitelisted domains.
partyIdSomething to identify the customer making the payment. In iMIS this would be the PartyId of the logged in user. When called from outside of iMIS, this would ideally be something to uniquely identify the user of that external system. This identifier is written to Pay Central telemetry and is used by Technical Support to locate appropriate telemetry entries when a customer is experiencing payment issues. If it is not possible to provide a useful identifying value, then any string may be provided e.g. "donor". Max length 36 characters.

URL: {imisurl}/api/paycentralsdk/_execute

Method: POST

{
    "$type": "Asi.Soa.Core.DataContracts.GenericExecuteRequest, Asi.Contracts",
    "EntityTypeName": "PayCentralSdk",
    "OperationName": "GetSdkToken",
    "Parameters": {
        "$type": "System.Collections.ObjectModel.Collection`1[[System.Object, mscorlib]], mscorlib",
        "$values": [
            "{domain}",
            "{partyId}"
        ]
    },
    "ParameterTypeNames": {
        "$type": "System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib",
        "$values": [
            "System.String",
            "System.String"
        ]
    },
    "UseJson": false
}

There is also support for supplying an options parameter object which controls additional behavior through property values. You only need to use this request format if you wish to override any of the default options property values.

options PropertyDetails
IsMerchantedInitiatedIndicates a payment initiated by the seller on behalf of a customer without the need for additional cardholder authentication. A value of true will only be observed if the bearer token supplied in the Authorization header identifies a staff user.
Defaults to false.
StyleSheetUrlsTo override the default styling of the payment control, supply an array of style sheet urls.
Defaults to empty.

Where the options parameter is supplied, you only need to include properties for which you wish to modify the default value. For example, to supply StyleSheetUrls but leave IsMerchantInitiated as its default of false, you only need to include the StyleSheetUrls property in the supplied options object.

Note that when supplying the options parameter, the ParameterTypeNames object is also different.

URL : {imisurl}/api/paycentralsdk/_execute

Method: POST

{
    "$type": "Asi.Soa.Core.DataContracts.GenericExecuteRequest, Asi.Contracts",
    "EntityTypeName": "PayCentralSdk",
    "OperationName": "GetSdkToken",
    "Parameters": {
        "$type": "System.Collections.ObjectModel.Collection`1[[System.Object, mscorlib]], mscorlib",
        "$values": [
            "{domain}",
            "{partyId}",
            {
                "$type": "Asi.Soa.Commerce.DataContracts.PayCentralSdkTokenOptionsData, Asi.Contracts",
                "IsMerchantInitiated": false,
                "StyleSheetUrls": {
                    "$type": "System.Collections.Generic.List`1[[System.String, mscorlib]], mscorlib",
                    "$values": [
                        "https://yourdomain.com/style1.css",
                        "https://yourdomain.com/style2.css"
                    ]
                }
            }
        ]
    },
    "ParameterTypeNames": {
        "$type": "System.Collections.ObjectModel.Collection`1[[System.String, mscorlib]], mscorlib",
        "$values": [
            "System.String",
            "System.String",
            "Asi.Soa.Commerce.DataContracts.PayCentralSdkTokenOptionsData, Asi.Contracts"
        ]
    },
    "UseJson": false
}

For all variants, a successful request will result in a 200 OK response. Any other response status codes indicate a problem.

A 200 OK response indicates the targeted service and operation were successfully reached. The response body is in JSON format and contains an object of type Asi.Soa.Core.DataContracts.ServiceResponse. This object must be inspected to verify the operation was successful.

If the operation succeeded, IsSuccessStatusCode will be true, and the Result property contains the AuthToken.

{
    "$type": "Asi.Soa.Core.DataContracts.ServiceResponse`1[[Asi.Soa.Commerce.DataContracts.PayCentralSdkTokenData, Asi.Contracts]], Asi.Contracts",
    "IsCachedResult": false,
    "IsSuccessStatusCode": true,
    "Message": null,
    "ReasonPhrase": null,
    "Result": {
        "$type": "Asi.Soa.Commerce.DataContracts.PayCentralSdkTokenData, Asi.Contracts",
        "AuthToken": "1ayqdCaRGQdV19H-jFTRJWCbyZ0M7NxRftn3i3AdNMI",
        "SdkScriptUrl": "https://paycentral-ui.imis.com/js/sdk/paycentral.js"
    },
    "StatusCode": 0,
    "ValidationResults": {
        "$type": "Asi.Soa.Core.DataContracts.ValidationResultsData, Asi.Contracts",
        "Errors": {
            "$type": "Asi.Soa.Core.DataContracts.ValidationResultDataCollection, Asi.Contracts",
            "$values": [
            ]
        },
        "Warnings": {
            "$type": "Asi.Soa.Core.DataContracts.ValidationResultDataCollection, Asi.Contracts",
            "$values": [
            ]
        }
    }
}

If the operation failed, IsSuccessStatusCode will be false. The Message property will contain a summary of all errors. Full details will be available in the ValidationResults property.

{
    "$type": "Asi.Soa.Core.DataContracts.ServiceResponse`1[[Asi.Soa.Commerce.DataContracts.PayCentralSdkTokenData, Asi.Contracts]], Asi.Contracts",
    "IsCachedResult": false,
    "IsSuccessStatusCode": false,
    "Message": "Error: domain 'worldofwidgets.com' is not whitelisted in iMIS.\n",
    "ReasonPhrase": null,
    "Result": null,
    "StatusCode": 9,
    "ValidationResults": {
        "$type": "Asi.Soa.Core.DataContracts.ValidationResultsData, Asi.Contracts",
        "Errors": {
            "$type": "Asi.Soa.Core.DataContracts.ValidationResultDataCollection, Asi.Contracts",
            "$values": [
                {
                    "$type": "Asi.Soa.Core.DataContracts.ValidationResultData, Asi.Contracts",
                    "Message": "domain 'worldofwidgets.com' is not whitelisted in iMIS."
                }
            ]
        },
        "Warnings": {
            "$type": "Asi.Soa.Core.DataContracts.ValidationResultDataCollection, Asi.Contracts",
            "$values": [
            ]
        }
    }
}

SDK Script

The SDK script must be loaded into the page that will be taking payments. Include the following script tag in the page:

<script async src="https://paycentral-ui.imis.com/js/sdk/paycentral.js"></script>

If you are located outside the United States, the script URL may be different. ASI support will provide you with the appropriate URL.

Once loaded and processed, a PayCentral function will be added to the window making it globally available.

Attempts to access PayCentral before the script has been executed will result in a "PayCentral is not defined" error. You must design your code in such a way that this does not happen.

If you wish to access PayCentral as part of the page load, then you should wait for an appropriate event such as document.DOMContentLoaded or window.load. These events do not fire until after script files have been loaded and processed.

window.addEventListener("load", () => paycentralReady());

function paycentralReady() {

	// sanity check, this should not fail 
	if (typeof PayCentral !== "function") {
		console.error("PayCentral function does not exist !");
		return;
	}

	// if we get here, PayCentral is ready to use 

}

Create a PayCentral instance

The first step in taking a payment is to initialize a new instance of PayCentral using the SDK AuthToken returned by the iMIS API.

let paycentralInstance;
try {
	paycentralInstance = PayCentral(sdkAuthToken);
} catch (e) {
	// Handle error  
}

You must ensure the SDK script has been loaded and processed before accessing PayCentral. If this has not happened, a "PayCentral is not defined" error will be thrown by the browser.

There can only be one active PayCentral instance per page. To create a new instance, you must first destroy any existing ones. See Destroy further on in this document.

Error

If a PayCentral instance cannot be created, an error will be thrown containing appropriate details. The error will automatically be written to the console.

Create a PaymentRequest

Before an appropriate UI control can be loaded, a PaymentRequest must be created. This encapsulates all the relevant information about the payment to be taken including; method, amount, billing address, cardholder name, etc. Portions of this can be modified later without requiring a new PaymentRequest to be created e.g. the amount and billing address.

There can only be one active PaymentRequest per page. To create a new PaymentRequest against an existing PayCentral instance, you must first destroy the existing PaymentRequest. See Destroy further on in this document.

let paymentRequest;
try {
	paymentRequest = paycentralInstance.createPaymentRequest(method, options);
} catch (e) {
	// Handle error  
}

method

An object defining the payment method to be used for taking the payment.

{
    "paymentType": "CreditCard",
    "gateway": {
        "id": "c06e37e0-abec-492c-ae4d-28df64a9d20d",
        "provider": "iMIS Pay"
    }
}

The gateway id is unique to each gateway registration for each iMIS customer.

The JSON for this object, including the correct gateway id, can be copied directly from the Payment method page in iMIS.

Staff Site > Settings > Finance > Pay Central > Payment methods > open an iMIS Pay payment method > SDK Options

options

An object defining properties of the payment request that may be changed before submitting the request.

{
    "amount": {
        "amount": 10.63,
        "currency": "USD"
    },
    "billingDetails": {
        "name": {
            "firstName": "Jerry",
            "lastName": "Baugh"
        },
        "address": {
            "addressLines": [
                "21403 Chagrin Blvd"
            ],
            "cityName": "Beachwood",
            "countryCode": "US",
            "countryName": "United States",
            "countrySubEntityCode": "OH",
            "countrySubEntityName": "Ohio",
            "postalCode": "44122"
        }
    },
    "card": {
        "cardholderName": "Jerry Baugh"
    }
}

Error

If a PaymentRequest cannot be created, an error will be thrown containing appropriate details. The error will automatically be written to the console.

Show control

Load and display an appropriate payment control for the payment request.

let container = document.getElementById("paymentControlContainer");
try {
	await paymentRequest.show(container);
} catch (e) {
	// Handle error  
}

This is an asynchronous operation and must be awaited or handled with .then().catch()

container

A DIV in the DOM into which the payment control with be loaded and displayed.

Supplied as either:

  1. An HTMLElement
  2. A query selector string

The container DIV can be positioned on the page as required.

Error

If a control cannot be loaded, an error will be thrown containing appropriate details. The error will automatically be written to the console.

In some cases, the resulting error may be generic and mask the root cause. Inspecting all underlying messages in the console will provide more insight. An example of this is a CSP error which would happen in the case where the page domain does not match that supplied when generating the AuthToken.

Updating PaymentRequest options

An existing PaymentRequest may be updated with a new options object at any time before it is submitted. This can happen even when a payment control is being displayed. For example, the customer may change the amount, billing address or cardholder name.

try {
	await paymentRequest.update(options);
} catch (e) {
	// Handle error  
}

This is an asynchronous operation and must be awaited or handled with .then().catch()

Error

If a PaymentRequest cannot be updated, an error will be thrown containing appropriate details. The error will automatically be written to the console.

Submit PaymentRequest

To submit the payment to Pay Central for validation and capturing.

try {
	let paymentResult = await paymentRequest.submit();
} catch (e) {
	// Handle error  
}

This is an asynchronous operation and must be awaited or handled with .then().catch()

If submission completes, an object will be returned. This will contain a "succeeded" property indicating if payment capture was successful.

Succeeded

If payment processing succeeded, the result object will contain a "data" property holding a payment token.

{
    "succeeded": true,
    "capture": "Immediate",
    "data": "c023f3c9ad0e48688c4a4fa7d322221f"
}

The payment token must be submitted to the iMIS API as part of a ComboOrder to finalize and take the payment.

Failed

If payment processing failed, the result object will contain an "error" property containing an array of errors.

{
    "succeeded": false,
    "capture": "Immediate",
    "errors": [
        "Invalid card number.",
        "Invalid expiration date (expired)."
    ]
}

Error

If submit() fails at a lower level, an error will be thrown. This will contain all available information. The error will be automatically written to the console.

Submit a ComboOrder

To finalize and take the payment, a ComboOrder must be submitted to the iMIS API. This will result in

  1. Validating and finalizing the payment in Pay Central.
  2. Creating appropriate iMIS records (Donations, Orders, Registrations, Memberships, Invoice payments, etc.).
  3. Registering the payment in iMIS and applying it to appropriate records.

The payment must be accounted for by the creation of appropriate iMIS records. You cannot take a payment without allocating it within iMIS. This all happens via the ComboOrder as normal by populating the ComboOrder.Order and/or ComboOrder.Invoices objects appropriately. Refer to exisiting documentation for populating and submitting a ComboOrder.

To take and allocate the payment, the ComboOrder.Payments collection must contain a RemittanceData object formed of the known payment details and the values returned from the successful SDK PaymentRequest submission.

The majority of the RemittanceData object will be composed of values that were passed to the SDK PaymentRequest. This includes amount, billing address and card holder name. Other properties will need to be populated from values registered in iMIS.

RemittanceData.DataVaultPaymentIntentId should be set to the payment token returned from the SDK in PaymentResult.data.

The following is an example of a fully formed RemittanceData object for a credit card payment.

{
    "Amount": {
        "Amount": 10.63,
        "Currency": {
            "CurrencyCode": "USD",
            "DecimalPositions": 2
        },
        "IsAmountDefined": true
    },
    "CreditCardInformation": {
        "HoldersName": "Jerry Baugh",
        "Address": {
            "AddressLines": ["21403 Chagrin Blvd"],
            "CityName": "Beachwood",
            "CountryCode": "US",
            "CountryName": "United States",
            "CountrySubEntityCode": "OH",
            "PostalCode": "44122"
        }
    },
    "PaymentToken": {
        "PaymentMethodIsReadOnly": false
    },
    "PaymentMethod": {
        "Name": "Visa",
        "PaymentMethodId": "IP_VISA",
        "PaymentType": "CreditCard",
        "DataVaultGatewayAccountId": "c06e37e0-abec-492c-ae4d-28df64a9d20d",
        "DataVaultAuthorizationGateway": "IP_CC"
    },
    "PayorParty": {
        "PartyId": "23281"
    },
    "DataVaultPaymentIntentId": "dd3b980906a14bdda3de19861ad0810f"
}

PaymentMethod

NameDisplay Name as configured in the relevant Payment method set.
Staff Site > Settings > Finance > Pay Central > Payment method sets
PaymentMethodIdPayment method code.
Staff Site > Settings > Finance > Pay Central > Payment methods
PaymentTypeCreditCard
DataVaultGatewayAccountIdSame as the gateway.id value supplied when creating a PaymentRequest.
Staff Site > Settings > Finance > Pay Central > Payment methods > open a payment method > SDK Options
DataVaultAuthorizationGatewayGateway Account Code.
Staff Site > Settings > Finance > Pay Central > Gateways

PayorParty

PartyIdThe payment must be associated with a Party in iMIS. If you do not have a direct association between users of your system and an iMIS Party record, then you will need to develop an appropriate strategy to either identify or create a Party record. In some cases, it may be acceptable to associate multiple payments with a well-known Party record, for example, a Party specifically created to associate anonymous donations with. Each system is different. You must consider and develop your own strategy to meet your needs and observe appropriate regulations.

DataVaultPaymentIntentId

The string value returned from the SDK in the PaymentResult.data property.

Destroy

At any time, entities created by the SDK can be destroyed. Any UI created by these entities, or their children, will be torn down.

Current

Destroy the current active PayCentral instance if any.

PayCentral.destroy()

If there is no current active instance, this has no effect. No error will be generated.

Instance

To destroy a specific instance, call destroy() on that instance. If there is a PaymentRequest associated with the instance it will be destroyed and any controls displayed by it will be torn down.

paycentralInstance.destroy()

Once a PayCentral instance has been destroyed, it will throw errors if any further operations are attempted on it. A new PayCentral instance must be created.

If the instance has already been destroyed, this has no effect. No error will be generated.

PaymentRequest

To destroy a specific PaymentRequest, call destroy on that instance. Any associated UI will be torn down.

paymentRequest.destroy()

Once a PaymentRequest has been destroyed, it will throw errors if any further operations are attempted on it. A new PaymentRequest may be created from the current PayCentral instance.

If the PaymentRequest has already been destroyed, this has no effect. No error will be generated.