Procare Pay Integration Service

Complete API Documentation for Payment Processing

Get Payors List by Merchant

GET /v1/rest/merchants/{merchantId}/payors

Description: Retrieves a list of up to 200 payors (customer payment profiles) for a specific merchant. Results can be filtered by customer account ID or specific payor IDs.

What is this endpoint used for?

This endpoint helps you find and retrieve customer payment profiles (called "payors"). Think of it like looking up customer records in a filing system:

  • Customer Search: Find a customer's payment profile using their account ID
  • List Management: Retrieve multiple customer profiles at once
  • Account Reconciliation: Match your system's customer IDs with payment profiles
  • Billing Dashboard: Display customers who have saved payment methods
  • Support Lookup: Help customer service locate a customer's payment information

What is a Payor?

A Payor is a customer payment profile that contains:

  • Customer identification information (your internal customer ID)
  • One or more saved payment methods (credit cards or bank accounts)
  • Payment preferences (default payment method, custom data)
  • Transaction history linkage

Think of a payor as a "customer wallet" that holds all their payment cards and bank accounts.

Path Parameters

merchantId (required)
Type: string
Pattern: Must be an 11-digit number (e.g., "12345678901")
Description: The unique 11-digit merchant identifier

Query Parameters

customerAccountId (optional)
Type: string
Description: Filter results by your system's customer account ID
Use Case: Find the payment profile for a specific customer
payorIds (optional)
Type: string (comma-delimited list)
Description: Retrieve specific payor profiles by their IDs
Format: payorId1,payorId2,payorId3
Use Case: Fetch multiple known payor profiles in one request
Important: You must provide either customerAccountId OR payorIds - at least one is required. These two parameters are mutually exclusive and cannot be used together.

Authentication

This endpoint requires bearer token authentication using the Authorization header.

Note: The bearer token must be a valid Cognito token obtained through the OAuth2 authentication flow.

Code Examples

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class PayorResponse
{
    [JsonProperty("payorId")]
    public string PayorId { get; set; }

    [JsonProperty("payorIdLimitedBitFlag")]
    public bool PayorIdLimitedBitFlag { get; set; }

    [JsonProperty("customerAccountId")]
    public string CustomerAccountId { get; set; }

    [JsonProperty("applicationId")]
    public string ApplicationId { get; set; }

    [JsonProperty("createdDate")]
    public DateTime? CreatedDate { get; set; }

    [JsonProperty("isDeleted")]
    public bool IsDeleted { get; set; }

    [JsonProperty("accountStatus")]
    public string AccountStatus { get; set; }

    [JsonProperty("defaultSavedPaymentMethodId")]
    public string DefaultSavedPaymentMethodId { get; set; }

    [JsonProperty("savedPaymentMethodIds")]
    public List SavedPaymentMethodIds { get; set; }

    [JsonProperty("savedPaymentMethods")]
    public List SavedPaymentMethods { get; set; }

    [JsonProperty("customerData")]
    public Dictionary CustomerData { get; set; }
}

public class PayorsClient
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl = "https://your-api-domain.com";
    private readonly string _bearerToken;

    public PayorsClient(string bearerToken)
    {
        _bearerToken = bearerToken;
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri(_baseUrl);
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", _bearerToken);
        _httpClient.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
    }

    // Search by customer account ID
    public async Task> GetPayorsByCustomerIdAsync(
        string merchantId,
        string customerAccountId)
    {
        try
        {
            string endpoint = $"/v1/rest/merchants/{merchantId}/payors?customerAccountId={Uri.EscapeDataString(customerAccountId)}";

            HttpResponseMessage response = await _httpClient.GetAsync(endpoint);
            response.EnsureSuccessStatusCode();

            string responseBody = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject>(responseBody);
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"Request error: {e.Message}");
            throw;
        }
    }

    // Get specific payors by IDs
    public async Task> GetPayorsByIdsAsync(
        string merchantId,
        List payorIds)
    {
        try
        {
            string payorIdsParam = string.Join(",", payorIds);
            string endpoint = $"/v1/rest/merchants/{merchantId}/payors?payorIds={Uri.EscapeDataString(payorIdsParam)}";

            HttpResponseMessage response = await _httpClient.GetAsync(endpoint);
            response.EnsureSuccessStatusCode();

            string responseBody = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject>(responseBody);
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"Request error: {e.Message}");
            throw;
        }
    }
}

// Example usage 1: Find payor by customer ID
var client = new PayorsClient("your-bearer-token-here");
List payors = await client.GetPayorsByCustomerIdAsync(
    "12345678901",
    "CUST-12345"
);

foreach (var payor in payors)
{
    Console.WriteLine($"Payor ID: {payor.PayorId}");
    Console.WriteLine($"Customer: {payor.CustomerAccountId}");
    Console.WriteLine($"Payment Methods: {payor.SavedPaymentMethodIds?.Count ?? 0}");
}

// Example usage 2: Get specific payors
var payorIds = new List { "payor123", "payor456", "payor789" };
List specificPayors = await client.GetPayorsByIdsAsync(
    "12345678901",
    payorIds
);
Imports System.Collections.Generic
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks
Imports Newtonsoft.Json

Public Class PayorResponse
    <JsonProperty("payorId")>
    Public Property PayorId As String

    <JsonProperty("customerAccountId")>
    Public Property CustomerAccountId As String

    <JsonProperty("createdDate")>
    Public Property CreatedDate As DateTime?

    <JsonProperty("savedPaymentMethodIds")>
    Public Property SavedPaymentMethodIds As List(Of String)

    <JsonProperty("customerData")>
    Public Property CustomerData As Dictionary(Of String, String)
End Class

Public Class PayorsClient
    Private ReadOnly _httpClient As HttpClient
    Private ReadOnly _baseUrl As String = "https://your-api-domain.com"
    Private ReadOnly _bearerToken As String

    Public Sub New(bearerToken As String)
        _bearerToken = bearerToken
        _httpClient = New HttpClient()
        _httpClient.BaseAddress = New Uri(_baseUrl)
        _httpClient.DefaultRequestHeaders.Authorization = _
            New AuthenticationHeaderValue("Bearer", _bearerToken)
        _httpClient.DefaultRequestHeaders.Accept.Add( _
            New MediaTypeWithQualityHeaderValue("application/json"))
    End Sub

    ' Search by customer account ID
    Public Async Function GetPayorsByCustomerIdAsync(
        merchantId As String,
        customerAccountId As String) As Task(Of List(Of PayorResponse))
        Try
            Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/payors?customerAccountId={Uri.EscapeDataString(customerAccountId)}"

            Dim response As HttpResponseMessage = Await _httpClient.GetAsync(endpoint)
            response.EnsureSuccessStatusCode()

            Dim responseBody As String = Await response.Content.ReadAsStringAsync()
            Return JsonConvert.DeserializeObject(Of List(Of PayorResponse))(responseBody)
        Catch ex As HttpRequestException
            Console.WriteLine($"Request error: {ex.Message}")
            Throw
        End Try
    End Function

    ' Get specific payors by IDs
    Public Async Function GetPayorsByIdsAsync(
        merchantId As String,
        payorIds As List(Of String)) As Task(Of List(Of PayorResponse))
        Try
            Dim payorIdsParam As String = String.Join(",", payorIds)
            Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/payors?payorIds={Uri.EscapeDataString(payorIdsParam)}"

            Dim response As HttpResponseMessage = Await _httpClient.GetAsync(endpoint)
            response.EnsureSuccessStatusCode()

            Dim responseBody As String = Await response.Content.ReadAsStringAsync()
            Return JsonConvert.DeserializeObject(Of List(Of PayorResponse))(responseBody)
        Catch ex As HttpRequestException
            Console.WriteLine($"Request error: {ex.Message}")
            Throw
        End Try
    End Function
End Class

' Example usage: Find payor by customer ID
Dim client As New PayorsClient("your-bearer-token-here")
Dim payors As List(Of PayorResponse) = Await client.GetPayorsByCustomerIdAsync(
    "12345678901",
    "CUST-12345"
)

For Each payor In payors
    Console.WriteLine($"Payor ID: {payor.PayorId}")
    Console.WriteLine($"Customer: {payor.CustomerAccountId}")
Next
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;

class PayorResponse {
    @SerializedName("payorId")
    private String payorId;

    @SerializedName("customerAccountId")
    private String customerAccountId;

    @SerializedName("savedPaymentMethodIds")
    private List savedPaymentMethodIds;

    // Getters
    public String getPayorId() { return payorId; }
    public String getCustomerAccountId() { return customerAccountId; }
    public List getSavedPaymentMethodIds() { return savedPaymentMethodIds; }
}

public class PayorsClient {
    private final HttpClient httpClient;
    private final String baseUrl;
    private final String bearerToken;
    private final Gson gson;

    public PayorsClient(String bearerToken) {
        this.bearerToken = bearerToken;
        this.baseUrl = "https://your-api-domain.com";
        this.httpClient = HttpClient.newHttpClient();
        this.gson = new Gson();
    }

    // Search by customer account ID
    public List getPayorsByCustomerId(
            String merchantId,
            String customerAccountId) throws Exception {

        String encodedCustomerId = URLEncoder.encode(customerAccountId, StandardCharsets.UTF_8);
        String endpoint = baseUrl + "/v1/rest/merchants/" + merchantId +
                         "/payors?customerAccountId=" + encodedCustomerId;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(endpoint))
            .header("Authorization", "Bearer " + bearerToken)
            .header("Accept", "application/json")
            .GET()
            .build();

        HttpResponse response = httpClient.send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

        if (response.statusCode() == 200) {
            return gson.fromJson(response.body(), new TypeToken>(){}.getType());
        } else {
            throw new Exception("Request failed with status: " + response.statusCode());
        }
    }

    // Get specific payors by IDs
    public List getPayorsByIds(
            String merchantId,
            List payorIds) throws Exception {

        String payorIdsParam = String.join(",", payorIds);
        String encodedPayorIds = URLEncoder.encode(payorIdsParam, StandardCharsets.UTF_8);
        String endpoint = baseUrl + "/v1/rest/merchants/" + merchantId +
                         "/payors?payorIds=" + encodedPayorIds;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(endpoint))
            .header("Authorization", "Bearer " + bearerToken)
            .header("Accept", "application/json")
            .GET()
            .build();

        HttpResponse response = httpClient.send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

        if (response.statusCode() == 200) {
            return gson.fromJson(response.body(), new TypeToken>(){}.getType());
        } else {
            throw new Exception("Request failed with status: " + response.statusCode());
        }
    }

    public static void main(String[] args) {
        try {
            PayorsClient client = new PayorsClient("your-bearer-token-here");

            // Find payor by customer ID
            List payors = client.getPayorsByCustomerId(
                "12345678901",
                "CUST-12345"
            );

            for (PayorResponse payor : payors) {
                System.out.println("Payor ID: " + payor.getPayorId());
                System.out.println("Customer: " + payor.getCustomerAccountId());
                System.out.println("Payment Methods: " +
                    (payor.getSavedPaymentMethodIds() != null ?
                     payor.getSavedPaymentMethodIds().size() : 0));
            }
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
require 'net/http'
require 'uri'
require 'json'
require 'cgi'

class PayorsClient
  def initialize(bearer_token, base_url = 'https://your-api-domain.com')
    @bearer_token = bearer_token
    @base_url = base_url
  end

  # Search by customer account ID
  def get_payors_by_customer_id(merchant_id, customer_account_id)
    encoded_customer_id = CGI.escape(customer_account_id)
    uri = URI("#{@base_url}/v1/rest/merchants/#{merchant_id}/payors?customerAccountId=#{encoded_customer_id}")

    make_request(uri)
  end

  # Get specific payors by IDs
  def get_payors_by_ids(merchant_id, payor_ids)
    payor_ids_param = payor_ids.join(',')
    encoded_payor_ids = CGI.escape(payor_ids_param)
    uri = URI("#{@base_url}/v1/rest/merchants/#{merchant_id}/payors?payorIds=#{encoded_payor_ids}")

    make_request(uri)
  end

  private

  def make_request(uri)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true if uri.scheme == 'https'

    request = Net::HTTP::Get.new(uri.request_uri)
    request['Authorization'] = "Bearer #{@bearer_token}"
    request['Accept'] = 'application/json'

    response = http.request(request)

    case response.code.to_i
    when 200
      JSON.parse(response.body)
    when 404
      raise "Payors not found"
    else
      raise "Request failed with status: #{response.code} - #{response.message}"
    end
  rescue StandardError => e
    puts "Request error: #{e.message}"
    raise
  end
end

# Example usage 1: Find payor by customer ID
client = PayorsClient.new('your-bearer-token-here')

payors = client.get_payors_by_customer_id('12345678901', 'CUST-12345')

payors.each do |payor|
  puts "Payor ID: #{payor['payorId']}"
  puts "Customer: #{payor['customerAccountId']}"
  puts "Payment Methods: #{payor['savedPaymentMethodIds']&.length || 0}"
  puts
end

# Example usage 2: Get specific payors
payor_ids = ['payor123', 'payor456', 'payor789']
specific_payors = client.get_payors_by_ids('12345678901', payor_ids)

puts "Retrieved #{specific_payors.length} payors"

Response Structure

The response is an array of payor objects. Each payor contains:

Field Type Description
payorId string Unique identifier for the payor profile
payorIdLimitedBitFlag boolean If true, payor ID uses 64-bit integer format; if false, uses GUID format
customerAccountId string Your external customer ID - links to your system
applicationId string The application that created this payor profile
createdDate string (date-time) When the payor profile was created
isDeleted boolean Whether the payor profile is marked as deleted
accountStatus string Current status of the payor account
deletedDate string (date-time) When the payor was deleted (null if active)
defaultSavedPaymentMethodId string ID of the default payment method for this payor
savedPaymentMethodIds array of strings List of all saved payment method IDs
savedPaymentMethods array of objects Full details of all saved payment methods
customerData object Custom key-value pairs for storing additional information

Example Response

[
  {
    "responseStatus": "Accepted",
    "responseCode": "200",
    "responseResult": "success",
    "payorId": "5513027774438108364",
    "payorIdLimitedBitFlag": true,
    "customerAccountId": "CUST-12345",
    "applicationId": "14",
    "createdDate": "2024-01-15T10:30:00Z",
    "isDeleted": false,
    "accountStatus": "Active",
    "deletedDate": null,
    "defaultSavedPaymentMethodId": "5513027774438108365",
    "savedPaymentMethodIds": [
      "5513027774438108365",
      "5513027774438108366"
    ],
    "savedPaymentMethods": [
      {
        "savedPaymentMethodId": "5513027774438108365",
        "accountType": "VISA",
        "maskedAccountNumber": "************1234",
        "lastFour": "1234",
        "cardExpiry": "1225",
        "isDefaultAccount": true
      },
      {
        "savedPaymentMethodId": "5513027774438108366",
        "accountType": "ECHK",
        "maskedAccountNumber": "******7890",
        "lastFour": "7890",
        "abaRoutingNumber": "123456789",
        "isDefaultAccount": false
      }
    ],
    "customerData": {
      "accountType": "premium",
      "billingCycle": "monthly"
    }
  }
]

HTTP Status Codes

Status Code Description
200 Success - Payors retrieved successfully (may be empty array if none found)
400 Bad Request - Invalid merchant ID, missing required parameters, or both customerAccountId and payorIds provided
401 Unauthorized - Missing or invalid authentication token
404 Not Found - Merchant not found
500 Internal Server Error - Server encountered an unexpected error

Common Use Cases

1. Find Customer's Payment Profile

// User logs in with customer ID "CUST-12345"
List payors = await client.GetPayorsByCustomerIdAsync(
    merchantId,
    "CUST-12345"
);

if (payors.Any())
{
    var payor = payors.First();
    Console.WriteLine($"Found payment profile: {payor.PayorId}");
    Console.WriteLine($"Saved cards: {payor.SavedPaymentMethodIds.Count}");

    // Display saved payment methods for checkout
    foreach (var method in payor.SavedPaymentMethods)
    {
        DisplayPaymentMethod(method);
    }
}
else
{
    Console.WriteLine("No payment profile found. Create one?");
}

2. Bulk Retrieve Payors for Reporting

// Get multiple specific payors for a report
var payorIds = new List
{
    "5513027774438108364",
    "5513027774438108365",
    "5513027774438108366"
};

List payors = await client.GetPayorsByIdsAsync(merchantId, payorIds);

// Generate report
foreach (var payor in payors)
{
    GeneratePayorReport(payor);
}

3. Customer Service Lookup

// Support agent searches for customer
string customerEmail = "john.doe@example.com";
string customerId = LookupCustomerIdByEmail(customerEmail);

List payors = await client.GetPayorsByCustomerIdAsync(
    merchantId,
    customerId
);

if (payors.Any())
{
    var payor = payors.First();
    Console.WriteLine($"Customer has {payor.SavedPaymentMethodIds.Count} saved payment methods");
    Console.WriteLine($"Default payment method: {payor.DefaultSavedPaymentMethodId}");
    Console.WriteLine($"Account created: {payor.CreatedDate}");
}

4. Account Reconciliation

// Match your customer records with payment profiles
List yourCustomerIds = GetAllCustomerIds();

foreach (var customerId in yourCustomerIds)
{
    try
    {
        var payors = await client.GetPayorsByCustomerIdAsync(merchantId, customerId);

        if (!payors.Any())
        {
            Console.WriteLine($"Customer {customerId} has no payment profile");
            // Maybe create one or flag for follow-up
        }
        else
        {
            Console.WriteLine($"Customer {customerId} -> Payor {payors.First().PayorId}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error for customer {customerId}: {ex.Message}");
    }
}

5. Display All Payment Methods for Customer

// Checkout page: show all saved cards
List payors = await client.GetPayorsByCustomerIdAsync(
    merchantId,
    currentCustomerId
);

if (payors.Any() && payors.First().SavedPaymentMethods != null)
{
    foreach (var method in payors.First().SavedPaymentMethods)
    {
        if (!method.IsDeleted)
        {
            Console.WriteLine($"{method.AccountType} ending in {method.LastFour}");

            if (method.IsDefaultAccount == true)
            {
                Console.WriteLine("  [Default]");
            }
        }
    }
}

Best Practices

  • Use customerAccountId for single customer lookup: Most efficient way to find one customer's profile
  • Use payorIds for bulk operations: When you need multiple specific payors (up to 200)
  • Cache payor data: Store payor ID in your database after first lookup to avoid repeated searches
  • Check isDeleted flag: Filter out deleted payors unless you need historical data
  • Handle empty results gracefully: A 200 response with empty array means no payors found (not an error)
  • Use savedPaymentMethods for display: The full payment method details are included in the response
  • Respect the 200-payor limit: If you need more, implement pagination or filter by date

Understanding the Response Limit

200 Payor Maximum: This endpoint returns up to 200 payors per request. If you have more than 200 payors matching your criteria, only the first 200 will be returned.

Strategies for Large Datasets:

  • Search by customer ID: Usually returns 1 payor (most common use case)
  • Batch by payor IDs: Split large lists into groups of 200 or less
  • Use more specific filters: The API may add date-based filtering in future versions
  • Store payor IDs locally: Cache the mapping between customer IDs and payor IDs

Troubleshooting

400 Bad Request - "At least one of customerAccountId or payorIds must be provided"

  • You must include either customerAccountId or payorIds query parameter
  • Cannot call endpoint with no query parameters
  • Add one of the required parameters to your request

400 Bad Request - "Query parameters are mutually exclusive"

  • You cannot use both customerAccountId and payorIds together
  • Choose one search method: by customer ID or by payor IDs
  • Remove one of the parameters from your request

Empty Array Returned (200 OK)

  • No payors found matching your search criteria
  • Customer may not have a payment profile yet
  • Check if customer ID is correct (case-sensitive)
  • Verify payor IDs exist and belong to this merchant

404 Not Found

  • Merchant ID does not exist or is invalid
  • Check that merchant ID is exactly 11 digits
  • Verify merchant account is active

Related Endpoints

  • GET /v1/rest/merchants/{merchantId}/payors/{payorId} - Get a single payor by ID
  • POST /v1/rest/merchants/{merchantId}/payors - Create a new payor profile
  • DELETE /v1/rest/merchants/{merchantId}/payors/{payorId} - Delete a payor profile

Additional Notes

  • The savedPaymentMethods array includes full details, so you don't need to make additional API calls to get payment method information
  • Deleted payors (where isDeleted is true) are still returned in results for historical purposes
  • The customerAccountId is YOUR system's customer ID - it's how you link payment profiles to your customers
  • Payor IDs can be either GUID format or 64-bit integers depending on the payorIdLimitedBitFlag setting
  • The applicationId indicates which application created the payor (useful in multi-application scenarios)
  • Custom data stored in customerData is preserved and returned with the payor
  • If a payor has no saved payment methods, the savedPaymentMethods array will be empty or null