Importing Billing and Payment data

Summary

The iMIS API supports high speed import of billing and payment data for membership and non-membership subscriptions. The API can be used to import current billing data and to backfill iMIS with historical billing records. Payment information may optionally be provided in the import. This offers organizations the flexibility to manage billing and payment entirely outside of iMIS, or to generate billing data externally and use iMIS for payment collection.

Features

There are no restrictions on term begin date, end date, or length. A term may begin and end on any day of the month and consist of any number of days. This provides support for terms such as weekly, bi-weekly (fortnightly), or twice monthly.

Supplying payment data is optional. Where this is not present, appropriate invoices will be projected in iMIS for members to pay as normal.

Billing data is imported using the Cash accounting system. In this accounting system physical invoice records are not generated. Instead, invoices are projected in iMIS from subscription data.

Processing Summary

Billing data is uploaded to iMIS via the API. To manage system resources, data must be uploaded in batches known as packages. Your system will be responsible for composing, uploading, and monitoring packages.

Package upload is a high-speed operation. After receiving a package upload request, the API will perform some basic validation, store the package, add it to the processing queue, and return its ID. You may continue uploading packages whilst others are being processed.

Packages are processed sequentially on a separate thread in the order they were received. Your system will call the iMIS API to query the processing state of a package and to retrieve any associated warnings or errors. If any corrections are required, an appropriate package can be composed and uploaded.

Package Structure

Each package may contain a maximum of 100 party records. For example, importing billing data for 3000 parties would require uploading 30 packages. There are no limits on the number of packages that can be uploaded.

Each party record is composed of a PartyId, term dates, a list of subscription items with billed and paid amounts, and optional details of a payment. The package may contain multiple records with the same PartyId, where each record would typically supply different term dates or subscription items.

A single party record within the package might look like this:

{
    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyData, Asi.Contracts",
    "PartyId": "10956",
    "BillToId": "10205",
    "ExternalId": "idFromYourSystem",
    "BillBeginDate": "2023-07-01",
    "BillThruDate": "2023-07-31",
    "PaidThruDate": "2023-07-31",
    "TransactionDate": "2023-07-26",
    "Items": {
        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemDataCollection, Asi.Contracts",
        "$values": [
            {
                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemData, Asi.Contracts",
                "ProductCode": "REG",
                "Copies": 1,
                "BilledAmount": 200,
                "PaidAmount": 200
            },
            {
                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemData, Asi.Contracts",
                "ProductCode": "JOURNAL",
                "Copies": 1,
                "BilledAmount": 34.95,
                "PaidAmount": 34.95
            }
        ]
    },
    "Payment": {
        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyPaymentData, Asi.Contracts",
        "Amount": 234.95,
        "BatchId": "20562-4",
        "PaymentMethodId": "CASH",
        "PaymentReference": "vf6qks8"
    }
}

In the above example two subscription items were included for this party record. The supplied term dates would apply to both those products. There is no limit to the number of subscription items per party record.

To upload billing data for the same PartyId but with different term dates, simply include another party record with appropriate dates and items. The same PartyId can appear in the package multiple times.

Processing Behavior

Processing of an import package is an atomic operation. If any fatal exceptions occur whilst processing a package, then none of the records in the package will be imported.

Party records are processed sequentially in the order they appear in the package. Each party record is processed in an atomic manner. If there are issues with any of the data in a given party record, then none of the data from that record will be imported. Instead, an error will be written, and processing will move to the next party record in the package.

The following rules apply when processing a party record.

If a subscription does not exist for the supplied ProductCode then one will be created. The subscription dates and balance will be set based on the supplied data.

If a subscription does exist for the supplied ProductCode, the supplied BillThruDate will be compared with the existing subscription BillThru date. If the supplied date is newer, the subscription will be updated, otherwise it will be skipped.

Where the subscription is updated - The existing subscription record will be updated with the supplied data. Its status will always be reset to Active. The balance is always recalculated based on the supplied BilledAmount and PaidAmount. The previous balance will not be maintained irrespective of system settings.

Where the subscription is skipped - The vast majority of the subscription properties will remain unchanged. This includes all primary dates and the balance. Some background tracking properties including Life To Date payments will be updated. A warning message will be written to the processing log indicating the subscription was skipped.

Irrespective of whether the subscription was inserted, updated or skipped, appropriate Accounting, Payment and Activity records will be written to ensure the historical record is intact. If the subscription is for a fundraising product, Gift records will also be updated.

Supplying a PaidThruDate is optional. It can be considered an override. Where a PaidThruDate is supplied, the subscription PaidThru date will be set to the supplied PaidThruDate unconditionally. Where a PaidThruDate was not supplied, the subscription PaidThru date will be set to the supplied BillThru date provided the supplied PaidAmount is equal to the supplied BilledAmount (i.e. the subscription is paid in full). Otherwise, the subscription PaidThru date will not be modified. If the subscription was skipped, its PaidThru date will not be modified under any circumstances.

Complimentary subscriptions should be uploaded with a BilledAmount and PaidAmount of 0.

Supplying a BillToId is optional. If not supplied, it will be established using the normal system rules.

If any of the imported subscriptions match the primary billing product for the contact’s current customer type (wildcard matching supported), then the import is considered to be a membership import. Otherwise, it is considered a non-membership import.

Membership import - If a PaidThruDate was supplied, the contacts PaidThru date will be set to the supplied value unconditionally. Where a PaidThruDate was not supplied, the contacts PaidThru date will be set to the supplied BillThruDate provided the supplied date is more recent. The contacts RenewedThru date will be set to the supplied BillThruDate provided the supplied date is more recent.

Non-membership import - The contacts PaidThru and RenewedThru dates will not be modified.

A batch is only required if payment data is supplied. Supplying a BatchId is optional in all cases. If a BatchId is supplied, the batch it identifies must be in the Open or Ready state. If a batch is required and a BatchId is not supplied, an appropriate batch will be identified or created based on the supplied TransactionDate.

Uploading a package

Packages are uploaded to the following endpoint with a POST request

https://{{URL}}/api/DuesImportPackage/_execute

Following is an example of a request containing two party records

{
    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePostRequest, Asi.Contracts",
    "DuesImportPackage": {
        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackageData, Asi.Contracts",
        "DuesImportJobId": "job_2023-7-26",
        "DuesImportPackageParties": {
            "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyDataCollection, Asi.Contracts",
            "$values": [
                {
                    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyData, Asi.Contracts",
                    "PartyId": "10956",
                    "BillToId": "10205",
                    "ExternalId": "idFromYourSystem",
                    "BillBeginDate": "2023-07-01",
                    "BillThruDate": "2023-07-31",
                    "PaidThruDate": "2023-07-31",
                    "TransactionDate": "2023-07-26",
                    "Items": {
                        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemDataCollection, Asi.Contracts",
                        "$values": [
                            {
                                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemData, Asi.Contracts",
                                "ProductCode": "REG",
                                "Copies": 1,
                                "BilledAmount": 200,
                                "PaidAmount": 200
                            },
                            {
                                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemData, Asi.Contracts",
                                "ProductCode": "JOURNAL",
                                "Copies": 1,
                                "BilledAmount": 34.95,
                                "PaidAmount": 34.95
                            }
                        ]
                    },
                    "Payment": {
                        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyPaymentData, Asi.Contracts",
                        "Amount": 234.95,
                        "BatchId": "20562-4",
                        "PaymentMethodId": "CASH",
                        "PaymentReference": "vf6qks8"
                    }
                },
                {
                    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyData, Asi.Contracts",
                    "PartyId": "26843",
                    "BillToId": "",
                    "ExternalId": "",
                    "BillBeginDate": "2023-07-01",
                    "BillThruDate": "2023-07-31",
                    "PaidThruDate": "2023-07-31",
                    "TransactionDate": "2023-07-26",
                    "Items": {
                        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemDataCollection, Asi.Contracts",
                        "$values": [
                            {
                                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemData, Asi.Contracts",
                                "ProductCode": "STU",
                                "Copies": 1,
                                "BilledAmount": 150,
                                "PaidAmount": 0
                            }
                        ]
                    },
                    "Payment": {
                        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyPaymentData, Asi.Contracts",
                        "Amount": 0,
                        "BatchId": "20562-4",
                        "PaymentMethodId": "CASH",
                        "PaymentReference": null
                    }
                }
            ]
        }
    }
}

A successful request will result in a 200 OK response. This indicates the package has been stored in the database and scheduled for processing.

The response body will contain a single integer. This is the DuesImportPackageId of the stored package. You will need this ID to query the processing state of the package and retrieve its results.

Retrieving package status

The processing status of a package can be queried with a POST request to the following endpoint

https://{{URL}}/api/DuesImportPackage/_execute
{
    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackageGetPackageStatusRequest, Asi.Contracts",
    "DuesImportPackageId": 54
}

A successful request will result in a 200 OK response.

The response body will contain a single integer. This represents one of the following processing statuses:

0NotFoundThe supplied DuesImportPackageId does not exist.
1AwaitProcessingThe package is queued and awaiting processing.
2InProcessThe package is currently being processed.
3CompletedProcessing completed without any errors or warnings.
4CompletedWithWarningsProcessing completed with warnings.
5CompletedWithErrorsProcessing completed with errors.
6FailedProcessing failed.
7CanceledReserved for future use.

Retrieving package results

Where a package has Completed processing (status 3,4,5) or has Failed (status 6), any associated errors and warnings can be retrieved with a POST request to the following endpoint

https://{{URL}}/api/DuesImportPackage/_execute

{
    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackageGetPackageResultsRequest, Asi.Contracts",
    "DuesImportPackageId": 54
}

A successful request will result in a 200 OK response. The response body will contain an object with the following notable properties:

TaskSummaryData - Contains details of the package including a StatusMessage which summarizes the number of errors and warnings that were encountered. If processing of the package Failed completely then StatusMessage will contain a reason.

PartyResults - Contains a $values results array with one entry for each party record that generated a result or warning. Each result entry will contain the full party record from the package including all item and payment details. The Message property will contain details of the error/warning along with the PartyId, ExternalId (if supplied) and the Index of the party record within the uploaded package. The MessageType property indicates if this is an Error (0) or Warning (1).

Example of a PartyResults property containing a single warning entry in the $values results array.

{
    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackageResultData, Asi.Contracts",
    "DuesImportPackageId": 54,
    "DuesImportJobId": "job_2023-7-26",
    "TaskSummaryData": {
        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackageTaskSummaryData, Asi.Contracts",
        "DuesImportPackageId": 54,
        "DuesImportJobId": "job_2023-7-26",
        "DuesImportPackageStatus": 5,
        "StatusMessage": "2 attempted\r\n1 succeeded\r\n1 succeeded with warnings",
        "CreatedBy": "CBAKER",
        "CreatedOn": "2023-07-26T19:50:12.92"
    },
    "PartyResults": {
        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyResultDataCollection, Asi.Contracts",
        "$values": [
            {
                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyResultData, Asi.Contracts",
                "DuesImportPackageErrorId": 27,
                "DuesImportPackageId": 54,
                "DuesImportJobId": "job_2023-7-26",
                "PartyId": "10956",
                "DuesImportPackageParty": {
                    "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyData, Asi.Contracts",
                    "PartyId": "10956",
                    "BillToId": "10205",
                    "ExternalId": "idFromYourSystem",
                    "BillBeginDate": "2023-07-01",
                    "BillThruDate": "2023-07-31",
                    "PaidThruDate": "2023-07-31",
                    "TransactionDate": "2023-07-26",
                    "Items": {
                        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemDataCollection, Asi.Contracts",
                        "$values": [
                            {
                                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemData, Asi.Contracts",
                                "ProductCode": "REG",
                                "Copies": 1,
                                "BilledAmount": 200,
                                "PaidAmount": 200
                            },
                            {
                                "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyItemData, Asi.Contracts",
                                "ProductCode": "JOURNAL",
                                "Copies": 1,
                                "BilledAmount": 34.95,
                                "PaidAmount": 34.95
                            }
                        ]
                    },
                    "Payment": {
                        "$type": "Asi.Soa.Commerce.DataContracts.DuesImportPackagePartyPaymentData, Asi.Contracts",
                        "Amount": 234.95,
                        "BatchId": "20562-4",
                        "PaymentMethodId": "CASH",
                        "PaymentReference": "vf6qks8"
                    }
                },
                "Message": "Bypassing update of subscription REG due to earlier BillThruDate.\r\nPartyId: 10956; ExternalId: idFromYourSystem; Index: 0;",
                "OccurredOn": "2023-07-26T19:50:13.113",
                "MessageType": 1
            }
        ]
    }
}

Error conditions

The following scenarios will result in a party record generating an error. Details of the error will be written to the database and may be retrieved as described above. Where an error is generated, the associated party record will not be imported, and processing will move to the next party record.

  • PartyId does not identify an existing contact.
  • BillToId does not identify an existing contact.
  • Item.ProductCode does not identify an existing product.
  • Item.ProductCode is not a membership type product.
  • Item.BilledAmount is negative.
  • Item.PaidAmount is negative.
  • Item.PaidAmount is greater than BilledAmount.
  • Payment.PaymentMethodId does not identify an existing payment method.
  • Payment.PaymentMethodId does not identify a payment method of type Cash.
  • Payment.BatchId does not identify an existing batch.
  • Payment.BatchId does not identify a batch in the Open or Ready state.
  • Payment.Amount doesn't match the sum of all Item.PaidAmount