Enterprise

Learn how to integrate your CRM with FlexPay’s Enterprise, optimizing transaction retries and improving recovery outcomes.

Introduction

The purpose of this document is to guide you through the necessary steps for integrating your CRM with FlexPay's failed payment recovery technology. With the Enterprise Integration, FlexPay serves as an intermediary layer between your system of record and the payment gateway, enhancing your retry attempts and providing real-time analysis to optimize transaction recovery.

Recognizing the complexity of integrations, we want to highlight two key adjustments to your CRM that will be essential for making this process successful.

  • A scheduler: FlexPay will provide a retry date and time for recoverable transactions in our response. Your system needs to schedule and send the transaction at the recommended time.
  • A modified routing logic: Direct retries through FlexPay rather than the gateway from your system of record. This change shouldn't impact rebills, only retries on failed payments.

Requirements

To benefit from FlexPay’s Enterprise API, you will need the following:

  • FlexPay Production account
  • At least one Company is configured in the account (There is a default company)
  • Payment gateways are configured for each Company
  • Production API keys for each Company

Process Flow

  1. A subscription rebill is sent directly to the gateway. It declines.
  2. It is then rerouted & scheduled to be sent through FlexPay.
  3. 24 hours after the initial rebill failure, a Charge is sent to FlexPay
  4. FlexPay reviews the transaction details and performs any data cleanup if it determines it will increase the likelihood of success. The transaction is sent directly from FlexPay to the Gateway.
  5. FlexPay analyses the response from the gateway and responds to the System or Record.
  6. The System of Record schedules the transaction and reattempts on the date and time provided by FlexPay.

The retry loop continues until one of the following events takes place:

  • The transaction approves.
  • The transaction hard declines.
  • A configuration limit is reached. Maximum 15 retries or 30 days, whichever is reached first.

Building your Integration

Before sending a transaction to FlexPay, a payment gateway must be configured under a Company. It must include the merchant account’s credentials so FlexPay can connect on your behalf. Depending on the complexity of your payment ecosystem, you could leverage the Gateway Management API. Learn more here.

Gateways can also be created manually in the FlexPay client portal. If you only have a small number of merchant accounts, this may be the easiest way to create the payment gateways in FlexPay. Learn more here.

The most common way of interacting with FlexPay's Enterprise API is through the Charge endpoint, this is how you provide details about a failed payment and kickstart the recovery process. FlexPay's AI-powered engine utilizes this information to recommend the optimal timing for retrying the failed payment. This not only enhances the chances of approval but also assists in preserving a healthy merchant account. Refund and VOID endpoints are also available to you. Let's explore these items in more detail:

Charge

API Reference: Charge Request

As previously mentioned, your primary interaction with FlexPay will be through the charge endpoint. Once a rebill fails, all subsequent requests should be processed via FlexPay. When the FlexPay API receives a Charge request, the transaction is processed directly at the gateway. The response code and message we receive, along with the details you provided for the failed payment are analyzed, and a response is provided back to your CRM. You can expect this to take only a few hundred milliseconds more than your usual gateway transaction time. If we return a retry date, we expect you to schedule it on your CRM and send a new attempt at the provided date and time. If there is no retry date, the response code and message will indicate the result.

Method: POST

URL: https://api.flexpay.io/v1/gateways/charge

Refer to our API reference document for detailed instructions on structuring the payload and understanding all the fields. While there are over 50 fields available, only a few are mandatory (7). Some fields are particularly important due to the insights our machine learning models can derive from them, while others may assist with improving the approval rate. The remaining fields are optional but may aid in reconciliation on your end. Below is a summary to help you prioritize the most critical fields.

MandatoryHighly Recommended
merchantTransactionIdpaymentMethod > expiryMonth
orderIdpaymentMethod > expiryYear
amountpaymentMethod > firstSix
currencyCodepaymentMethod > fullName
retryCountpaymentMethod > address1
creditCardNumber OR gatewayPaymentMethodIdpaymentMethod > postalCode
paymentMethod > merchantAccountReferenceId OR gatewayTokenpaymentMethod > city
referenceData (except on the initial request where it must be null)paymentMethod > state
paymentMethod > country
paymentMethod > email OR customerId
dateFirstAttempt
customerIp
mitStoredTransactionId

Example Requests:

{
  "transaction": {
    "merchantTransactionId": "Trx-632541",
    "orderId": "ORD-00094535",
    "amount": 1100,
    "currencyCode": "USD",
    "retryCount": 1,
    "customerId": "46209790217",
    "paymentMethod": {
      "creditCardNumber": "4111111111111111",
      "lastFourDigits": "1111",
      "firstSixDigits": "411111",
      "expiryMonth": "05",
      "expiryYear": "2025",
      "fullName": "Carl Johson",
      "address1": "560 Rue Peel",
      "postalCode": "J7Y3U9",
      "city": "Montreal",
      "state": "QC",
      "country": "CA",
      "email": "[email protected]",
      "merchantAccountReferenceId": "SUPPaysafeSandbox"
    },
    "dateFirstAttempt": "2024-09-12T19:20:30Z",
    "referenceData": null,
    "customerIp": "10.10.10.10",
    "mitStoredTransactionId": "6459276079792"
  }
}
{
    "transaction": {
        "response": {
            "avsCode": null,
            "avsMessage": null,
            "cvvCode": null,
            "cvvMessage": null,
            "errorCode": "3022",
            "errorDetail": "The card has been declined due to insufficient funds."
        },
        "engagedRecoveryState": 0,
        "paymentMethod": {
            "paymentMethodId": "FFWYTQ6IUITU3FOOAGI6SCBFRM",
            "creditCardNumber": "411111******1111",
            "expiryMonth": "05",
            "expiryYear": "2025",
            "cvv": "",
            "firstName": null,
            "lastName": null,
            "fullName": "Carl Johson",
            "customerId": "46209790217",
            "address1": "560 Rue Peel",
            "address2": null,
            "postalCode": "J7Y3U9",
            "city": "Montreal",
            "state": "QC",
            "country": "CA",
            "email": "[email protected]",
            "phoneNumber": null,
            "paymentMethodType": "CreditCard",
            "fingerprint": null,
            "lastFourDigits": "1111",
            "firstSixDigits": "411111",
            "cardType": "VISA",
            "dateCreated": "2024-09-13T01:38:30.923Z",
            "storageState": "Cached",
            "merchantAccountReferenceId": "SUPPaysafeSandbox"
        },
        "transactionId": "068YJ2155M0000F1082C0DG0B39S4",
        "transactionDate": "2024-09-13T01:38:30.829Z",
        "transactionStatus": 2,
        "message": "The card has been declined due to insufficient funds.",
        "responseCode": "20023",
        "transactionType": "Charge",
        "merchantTransactionId": "Trx-632541",
        "customerId": "46209790217",
        "currencyCode": "USD",
        "amount": 1100,
        "gatewayToken": "ZFLC6BRZR7EUDA25AGIZJNXMUI",
        "gatewayType": "paysafe",
        "gatewayTransactionId": "452a5311-903b-418e-b036-9dc4d19f7007",
        "merchantAccountReferenceId": "SUPPaysafeSandbox",
        "assignedGatewayToken": "ZFLC6BRZR7EUDA25AGIZJNXMUI",
        "orderId": "ORD-00094535",
        "retryDate": "2024-09-14T22:00:00Z",
        "retryCount": 1,
        "dateFirstAttempt": "2024-09-12T19:20:30Z",
        "description": null,
        "productSku": null,
        "subscriptionId": null,
        "productCategory": null,
        "billingPlan": null,
        "customerIp": "10.10.10.10",
        "billingCycle": 1,
        "shippingAddress": {
            "address1": null,
            "address2": null,
            "postalCode": null,
            "city": null,
            "state": null,
            "country": null
        },
        "referenceData": "SAABAADTDfdf09wIyVYvBjmPyUGDXQGRlLbsosA2AFjTkuEBAAABkekIJS0=",
        "disableCustomerRecovery": false,
        "customVariable1": null,
        "customVariable2": null,
        "customVariable3": null,
        "customVariable4": null,
        "customVariable5": null
    }
}
{
  "transaction": {
    "merchantTransactionId": "Trx-632970",
    "orderId": "ORD-00094535",
    "amount": 1100,
    "currencyCode": "USD",
    "retryCount": 2,
    "customerId": "46209790217",
    "paymentMethod": {
      "creditCardNumber": "4111111111111111",
      "lastFourDigits": "1111",
      "firstSixDigits": "411111",
      "expiryMonth": "05",
      "expiryYear": "2025",
      "fullName": "Carl Johson",
      "address1": "560 Rue Peel",
      "postalCode": "J7Y3U9",
      "city": "Montreal",
      "state": "QC",
      "country": "CA",
      "email": "[email protected]",
      "merchantAccountReferenceId": "SUPPaysafeSandbox"
    },
    "dateFirstAttempt": "2024-09-12T19:20:30Z",
    "referenceData": "SAABAADTDfdf09wIyVYvBjmPyUGDXQGRlLbsosA2AFjTkuEBAAABkekIJS0=",
    "customerIp": "10.10.10.10",
    "mitStoredTransactionId": "6459276079792"
  }
}
{
    "transaction": {
        "response": {
            "avsCode": null,
            "avsMessage": null,
            "cvvCode": null,
            "cvvMessage": null,
            "errorCode": "3022",
            "errorDetail": "The card has been declined due to insufficient funds."
        },
        "engagedRecoveryState": 0,
        "paymentMethod": {
            "paymentMethodId": "CMRIWALYYGVUNIM4AGI6SCJXNU",
            "creditCardNumber": "411111******1111",
            "expiryMonth": "05",
            "expiryYear": "2025",
            "cvv": "",
            "firstName": null,
            "lastName": null,
            "fullName": "Carl Johson",
            "customerId": "46209790217",
            "address1": "560 Rue Peel",
            "address2": null,
            "postalCode": "J7Y3U9",
            "city": "Montreal",
            "state": "QC",
            "country": "CA",
            "email": "[email protected]",
            "phoneNumber": null,
            "paymentMethodType": "CreditCard",
            "fingerprint": null,
            "lastFourDigits": "1111",
            "firstSixDigits": "411111",
            "cardType": "VISA",
            "dateCreated": "2024-09-13T01:39:41.037Z",
            "storageState": "Cached",
            "merchantAccountReferenceId": "SUPPaysafeSandbox"
        },
        "transactionId": "068YJ29Q3W0000F10820X4K6Z1ADT",
        "transactionDate": "2024-09-13T01:39:40.959Z",
        "transactionStatus": 2,
        "message": "The card has been declined due to insufficient funds.",
        "responseCode": "20023",
        "transactionType": "Charge",
        "merchantTransactionId": "Trx-632970",
        "customerId": "46209790217",
        "currencyCode": "USD",
        "amount": 1100,
        "gatewayToken": "ZFLC6BRZR7EUDA25AGIZJNXMUI",
        "gatewayType": "paysafe",
        "gatewayTransactionId": "657f9f01-1a09-4dfa-8757-9127bf979fb6",
        "merchantAccountReferenceId": "SUPPaysafeSandbox",
        "assignedGatewayToken": "ZFLC6BRZR7EUDA25AGIZJNXMUI",
        "orderId": "ORD-00094535",
        "retryDate": "2024-09-18T22:00:00Z",
        "retryCount": 2,
        "dateFirstAttempt": "2024-09-12T19:20:30Z",
        "description": null,
        "productSku": null,
        "subscriptionId": null,
        "productCategory": null,
        "billingPlan": null,
        "customerIp": "10.10.10.10",
        "billingCycle": 1,
        "shippingAddress": {
            "address1": null,
            "address2": null,
            "postalCode": null,
            "city": null,
            "state": null,
            "country": null
        },
        "referenceData": "AAACAADTDfdf09wIAAAAAAAAAAAAAAAAAAAAAMA2AFjTkuEBAAABkekIJS0=",
        "disableCustomerRecovery": false,
        "customVariable1": null,
        "customVariable2": null,
        "customVariable3": null,
        "customVariable4": null,
        "customVariable5": null
    }
}

The CRM must capture and process the response based on the responseCode.

Approved Transaction (Response Code: 10000): If the transaction is approved, the subscription should be reinstated to good standing.

Soft Decline (Response Code: 20000-29999):

  • Review the retryDate provided by FlexPay and schedule a subsequent attempt.
  • Increment the retryCount (e.g., from 1 to 2).
  • Include the referenceData value from the FlexPay response in the next request. Each subsequent transaction should include the most recent referenceData from FlexPay.

Hard Decline (Response Code: 30000-49999): Continue with your established process for handling unrecovered payments.

API Error (Response Code: 50000-59999): Review the request you sent to FlexPay. A response in this range indicates an issue with your API request.

📘

While the Refund and Void endpoints are optional, we recommend including them in your integration to create a more comprehensive solution.

Refund

API Reference: Refund Request

It can process partial and full refunds. You need to specify the amount in the body of the request. If there are time limitations imposed by a particular gateway (i.e. refunds only available for 120 days), they need to be accounted for by the CRM. Any refund request received by FlexPay will be sent directly to the payment gateway.

When refunds are processed through FlexPay, the refunded amount will be credited on your next invoice. This ensures that you are not billed for any recovery amounts that have been refunded after being previously invoiced.

Example:

Method: POST

URL: https://api.flexpay.io/v1/transactions/066TAFW3A00000F10829KM8QVSPDM/refund

{
    "transaction": {
        "merchantTransactionId": "Ref-4592798278",
        "disableCustomerRecovery": false,
        "amount": 1995
    }
}

Void

API Reference: Void Request

It cancels an authorized transaction before it settles with the cardholder's bank.

Example:

Method: POST

URL: https://api.flexpay.io/v1/transactions/066T7STFGW0000F1082ESKR7A0BJ4/void

{
    "transaction": {
        "merchantTransactionId": "V-56417687",
        "disableCustomerRecovery": false
    }
}

Sandbox Testing

The FlexPay enablement team typically creates a 'Sandbox' account for testing, which mirrors the functionality of a production account. It’s important to exercise caution when configuring the account, as it can replicate real-world scenarios. The Sandbox environment allows you to test various use cases and payment scenarios.

The sandbox environment applies to Payment Gateways and API keys. Companies can have both Production and Sandbox API keys associated with them. When you create a Sandbox API Key, you will only be able to create & send requests to payment gateways in the Sandbox environment. Not all gateways are supported in the Sandbox. In the Sandbox Environment, there is a gateway called “FlexPay Sandbox”. This gateway must be used for the testing scenarios described below.

Refer to the Sandbox Guide for detailed instructions on how to complete your testing.

Requirements

The requirements for Sandbox testing are similar to those outlined previously in this guide, with two main differences. Instead of a regular FlexPay account, you need a FlexPay Test account, and instead of a regular payment gateway, you need a FlexPay Sandbox gateway. Please work closely with your integration manager to get these provisioned for you.

Use Cases

Be sure to create at least one test transaction for each scenario outlined below. The transactions will be reviewed by the integrations team and are required for the approval of an integration. Please record the transaction tokens for each of the corresponding scenarios.

Important Points

  • There are 2 important fields required for the FlexPay payment logic to function as intended: retryCount and referenceData.
    • When the initial transaction is processed directly on the gateway (i.e. not through FlexPay), the retryCount begins at 0. If it declines, the transaction is sent to FlexPay the next day with a retry count of 1. Another way of thinking about it is that the initial attempt is not a retry, and retry 1 = the first retry. The retry count increases with each subsequent transaction until it is approved or receives a hard decline, at which point the count is reset.
    • The reference data is provided in the response from FlexPay. Reference data is stored and then returned (daisy-chained) in the next transaction until it is approved or receives a hard decline, at which point the field is emptied (null). NOTE: Reference data is stored at the transaction level and not the customer level.
  • The merchant Transaction ID is unique: This value is coming from the Integration Point and is used to reconcile each transaction.
  • Multiple currencies are supported: If a Client is using different currencies, please test at least 2 of them.
  • The Retry Date (for both soft & hard declines) must be handled properly: a transaction without a retry date should NOT be retried.
  • Do not retry a transaction before the given date & time.

Use Case 1: Schedule a Soft Decline

Sending a transaction with an expected response code in the 20K range will return a retry date & time as well as the reference data field. The retry date must be used to schedule the next transaction at the given Date and Time (UTC).

Use Case 2: Do Not Schedule Hard Decline

Sending a transaction with an expected response code in the 30K, 40K, and 50K range will not return a retry date. These transactions should not be scheduled to retry automatically later.

Use Case 3: Recover a Transaction

Use the appropriate test cards & amount to send an approved charge with a retryCount equal or higher than 1.

Use Case 4: Validate the Payment Logic Using Reference Data & Retry Count

Simulate a string of transactions attempting to salvage a customer’s order/invoice.

  • Transaction 1: Send a request that will produce a soft decline, where retryCount = 0, and referenceData is empty
  • Transaction 2: Send a request that will produce a soft decline, where retryCount = 1, and referenceData is equal to the output referenceData in the response from Transaction 1
  • Transaction 3: Send a request that will produce a soft decline, where retryCount = 2, and referenceData is equal to the output referenceData in the response from Transaction 2

Use Case 5: Refund a Recovered Transaction

If FlexPay recovers a transaction that is subsequently refunded, we will credit any fees related to that transaction on your FlexPay invoice. In order to properly track refunded recoveries, you will need to process those refunds through FlexPay.

  • Transaction 1: Send an approved charge with a retryCount equal or higher than 1
  • Transaction 2: Send an approved refund using the FlexPay transaction token from Transaction 1