Procare Pay Integration Service

Complete API Documentation for Payment Processing

Get Saved Payment Methods List

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

Description: Retrieves all saved payment methods (credit cards and bank accounts) for a specific payor (customer payment profile).

What is this endpoint used for?

This endpoint lists all payment methods a customer has on file. Think of it like viewing all the cards in someone's wallet:

  • Checkout Display: Show customers their saved cards/accounts during payment
  • Payment Method Management: Let customers view all their saved payment options
  • Default Selection: Identify which payment method is set as default
  • Account Dashboard: Display payment methods in customer account settings
  • Subscription Management: Show which card is being used for recurring payments

Path Parameters

merchantId (required)
Type: string
Pattern: Must be an 11-digit number
Description: The unique 11-digit merchant identifier
payorId (required)
Type: string
Description: The unique payor (payment profile) identifier

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.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class SavedPaymentMethodResponse
{
    [JsonProperty("savedPaymentMethodId")]
    public string SavedPaymentMethodId { get; set; }

    [JsonProperty("accountType")]
    public string AccountType { get; set; }

    [JsonProperty("maskedAccountNumber")]
    public string MaskedAccountNumber { get; set; }

    [JsonProperty("lastFour")]
    public string LastFour { get; set; }

    [JsonProperty("cardExpiry")]
    public string CardExpiry { get; set; }

    [JsonProperty("abaRoutingNumber")]
    public string AbaRoutingNumber { get; set; }

    [JsonProperty("accountHolderFirstName")]
    public string AccountHolderFirstName { get; set; }

    [JsonProperty("accountHolderLastName")]
    public string AccountHolderLastName { get; set; }

    [JsonProperty("accountHolderEmail")]
    public string AccountHolderEmail { get; set; }

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

    [JsonProperty("isDefaultAccount")]
    public bool? IsDefaultAccount { get; set; }

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

    [JsonProperty("sponsorKey")]
    public string SponsorKey { get; set; }
}

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

    public SavedPaymentMethodsClient(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"));
    }

    public async Task> GetSavedPaymentMethodsAsync(
        string merchantId,
        string payorId)
    {
        try
        {
            string endpoint = $"/v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods";

            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;
        }
    }

    // Helper: Get only active (non-deleted) payment methods
    public async Task> GetActivePaymentMethodsAsync(
        string merchantId,
        string payorId)
    {
        var allMethods = await GetSavedPaymentMethodsAsync(merchantId, payorId);
        return allMethods.Where(m => !m.IsDeleted).ToList();
    }

    // Helper: Get default payment method
    public async Task GetDefaultPaymentMethodAsync(
        string merchantId,
        string payorId)
    {
        var allMethods = await GetSavedPaymentMethodsAsync(merchantId, payorId);
        return allMethods.FirstOrDefault(m => m.IsDefaultAccount == true && !m.IsDeleted);
    }
}

// Example usage 1: Get all payment methods
var client = new SavedPaymentMethodsClient("your-bearer-token-here");

List paymentMethods =
    await client.GetSavedPaymentMethodsAsync("12345678901", "payor123");

foreach (var method in paymentMethods)
{
    if (!method.IsDeleted)
    {
        Console.WriteLine($"{method.AccountType} ending in {method.LastFour}");
        if (method.IsDefaultAccount == true)
        {
            Console.WriteLine("  [Default]");
        }
    }
}

// Example usage 2: Get default payment method
SavedPaymentMethodResponse defaultMethod =
    await client.GetDefaultPaymentMethodAsync("12345678901", "payor123");

if (defaultMethod != null)
{
    Console.WriteLine($"Default: {defaultMethod.AccountType} ****{defaultMethod.LastFour}");
}
Imports System.Collections.Generic
Imports System.Linq
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks
Imports Newtonsoft.Json

Public Class SavedPaymentMethodResponse
    <JsonProperty("savedPaymentMethodId")>
    Public Property SavedPaymentMethodId As String

    <JsonProperty("accountType")>
    Public Property AccountType As String

    <JsonProperty("maskedAccountNumber")>
    Public Property MaskedAccountNumber As String

    <JsonProperty("lastFour")>
    Public Property LastFour As String

    <JsonProperty("cardExpiry")>
    Public Property CardExpiry As String

    <JsonProperty("isDeleted")>
    Public Property IsDeleted As Boolean

    <JsonProperty("isDefaultAccount")>
    Public Property IsDefaultAccount As Boolean?
End Class

Public Class SavedPaymentMethodsClient
    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)
    End Sub

    Public Async Function GetSavedPaymentMethodsAsync(
        merchantId As String,
        payorId As String) As Task(Of List(Of SavedPaymentMethodResponse))

        Try
            Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods"

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

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

    ' Helper: Get only active payment methods
    Public Async Function GetActivePaymentMethodsAsync(
        merchantId As String,
        payorId As String) As Task(Of List(Of SavedPaymentMethodResponse))

        Dim allMethods = Await GetSavedPaymentMethodsAsync(merchantId, payorId)
        Return allMethods.Where(Function(m) Not m.IsDeleted).ToList()
    End Function
End Class

' Example usage
Dim client As New SavedPaymentMethodsClient("your-bearer-token-here")

Dim paymentMethods As List(Of SavedPaymentMethodResponse) = _
    Await client.GetSavedPaymentMethodsAsync("12345678901", "payor123")

For Each method In paymentMethods
    If Not method.IsDeleted Then
        Console.WriteLine($"{method.AccountType} ending in {method.LastFour}")
        If method.IsDefaultAccount Then
            Console.WriteLine("  [Default]")
        End If
    End If
Next
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.stream.Collectors;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;

class SavedPaymentMethodResponse {
    @SerializedName("savedPaymentMethodId")
    private String savedPaymentMethodId;

    @SerializedName("accountType")
    private String accountType;

    @SerializedName("maskedAccountNumber")
    private String maskedAccountNumber;

    @SerializedName("lastFour")
    private String lastFour;

    @SerializedName("cardExpiry")
    private String cardExpiry;

    @SerializedName("isDeleted")
    private boolean isDeleted;

    @SerializedName("isDefaultAccount")
    private Boolean isDefaultAccount;

    // Getters
    public String getSavedPaymentMethodId() { return savedPaymentMethodId; }
    public String getAccountType() { return accountType; }
    public String getMaskedAccountNumber() { return maskedAccountNumber; }
    public String getLastFour() { return lastFour; }
    public String getCardExpiry() { return cardExpiry; }
    public boolean isDeleted() { return isDeleted; }
    public Boolean getIsDefaultAccount() { return isDefaultAccount; }
}

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

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

    public List getSavedPaymentMethods(
            String merchantId,
            String payorId) throws Exception {

        String endpoint = baseUrl + "/v1/rest/merchants/" + merchantId +
                         "/payors/" + payorId + "/savedPaymentMethods";

        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 if (response.statusCode() == 404) {
            throw new Exception("Payor not found");
        } else {
            throw new Exception("Request failed with status: " + response.statusCode());
        }
    }

    // Helper: Get only active payment methods
    public List getActivePaymentMethods(
            String merchantId,
            String payorId) throws Exception {

        List allMethods =
            getSavedPaymentMethods(merchantId, payorId);

        return allMethods.stream()
            .filter(m -> !m.isDeleted())
            .collect(Collectors.toList());
    }

    // Helper: Get default payment method
    public SavedPaymentMethodResponse getDefaultPaymentMethod(
            String merchantId,
            String payorId) throws Exception {

        List allMethods =
            getSavedPaymentMethods(merchantId, payorId);

        return allMethods.stream()
            .filter(m -> !m.isDeleted() && Boolean.TRUE.equals(m.getIsDefaultAccount()))
            .findFirst()
            .orElse(null);
    }

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

            List methods =
                client.getSavedPaymentMethods("12345678901", "payor123");

            for (SavedPaymentMethodResponse method : methods) {
                if (!method.isDeleted()) {
                    System.out.println(method.getAccountType() +
                                     " ending in " + method.getLastFour());
                    if (Boolean.TRUE.equals(method.getIsDefaultAccount())) {
                        System.out.println("  [Default]");
                    }
                }
            }

            // Get default method
            SavedPaymentMethodResponse defaultMethod =
                client.getDefaultPaymentMethod("12345678901", "payor123");

            if (defaultMethod != null) {
                System.out.println("\nDefault: " + defaultMethod.getAccountType() +
                                 " ****" + defaultMethod.getLastFour());
            }
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
require 'net/http'
require 'uri'
require 'json'

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

  def get_saved_payment_methods(merchant_id, payor_id)
    uri = URI("#{@base_url}/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}/savedPaymentMethods")

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

    request = Net::HTTP::Get.new(uri.path)
    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 "Payor not found: #{payor_id}"
    else
      raise "Request failed with status: #{response.code} - #{response.message}"
    end
  rescue StandardError => e
    puts "Request error: #{e.message}"
    raise
  end

  # Helper: Get only active payment methods
  def get_active_payment_methods(merchant_id, payor_id)
    all_methods = get_saved_payment_methods(merchant_id, payor_id)
    all_methods.reject { |m| m['isDeleted'] }
  end

  # Helper: Get default payment method
  def get_default_payment_method(merchant_id, payor_id)
    all_methods = get_saved_payment_methods(merchant_id, payor_id)
    all_methods.find { |m| m['isDefaultAccount'] && !m['isDeleted'] }
  end
end

# Example usage
client = SavedPaymentMethodsClient.new('your-bearer-token-here')

payment_methods = client.get_saved_payment_methods('12345678901', 'payor123')

payment_methods.each do |method|
  unless method['isDeleted']
    puts "#{method['accountType']} ending in #{method['lastFour']}"
    puts '  [Default]' if method['isDefaultAccount']
  end
end

# Get default method
default_method = client.get_default_payment_method('12345678901', 'payor123')

if default_method
  puts "\nDefault: #{default_method['accountType']} ****#{default_method['lastFour']}"
end
  rescue StandardError => e
    puts "Request error: #{e.message}"
    raise
  end

  # Helper: Get only active payment methods
  def get_active_payment_methods(merchant_id, payor_id)
    all_methods = get_saved_payment_methods(merchant_id, payor_id)
    all_methods.reject { |m| m['isDeleted'] }
  end

  # Helper: Get default payment method
  def get_default_payment_method(merchant_id, payor_id)
    all_methods = get_saved_payment_methods(merchant_id, payor_id)
    all_methods.find { |m| m['isDefaultAccount'] && !m['isDeleted'] }
  end
end

# Example usage
client = SavedPaymentMethodsClient.new('your-bearer-token-here')

payment_methods = client.get_saved_payment_methods('12345678901', 'payor123')

payment_methods.each do |method|
  unless method['isDeleted']
    puts "#{method['accountType']} ending in #{method['lastFour']}"
    puts '  [Default]' if method['isDefaultAccount']
  end
end

# Get default method
default_method = client.get_default_payment_method('12345678901', 'payor123')

if default_method
  puts "\nDefault: #{default_method['accountType']} ****#{default_method['lastFour']}"
end

Response Structure

The response is an array of saved payment method objects:

Field Type Description
savedPaymentMethodId string Unique identifier for this payment method
accountType string Type of account: VISA, MC, AMEX, DISC, ECHK (checking), SAV (savings)
maskedAccountNumber string Masked card/account number (e.g., "************1234")
lastFour string Last 4 digits of the card/account number
cardExpiry string Card expiration date (for credit cards only)
abaRoutingNumber string Bank routing number (for ACH accounts only)
accountHolderFirstName string First name on the account
accountHolderLastName string Last name on the account
accountHolderEmail string Email address associated with the payment method
isDeleted boolean Whether this payment method has been deleted
isDefaultAccount boolean Whether this is the default payment method
createdDate string (date-time) When the payment method was added
deletedDate string (date-time) When the payment method was deleted (null if active)
sponsorKey string Sponsor key associated with this payment method

Example Response

[
  {
    "savedPaymentMethodId": "5513027774438108365",
    "accountType": "VISA",
    "maskedAccountNumber": "************1234",
    "lastFour": "1234",
    "cardExpiry": "1225",
    "accountHolderFirstName": "John",
    "accountHolderLastName": "Doe",
    "accountHolderEmail": "john.doe@example.com",
    "accountHolderStreetLine1": "123 Main St",
    "accountHolderCity": "Springfield",
    "accountHolderRegion": "IL",
    "accountHolderPostalCode": "62701",
    "isDeleted": false,
    "isDefaultAccount": true,
    "createdDate": "2024-01-15T10:30:00Z",
    "deletedDate": null,
    "sponsorKey": "SPONS123"
  },
  {
    "savedPaymentMethodId": "5513027774438108366",
    "accountType": "ECHK",
    "maskedAccountNumber": "******7890",
    "lastFour": "7890",
    "abaRoutingNumber": "123456789",
    "accountHolderFirstName": "John",
    "accountHolderLastName": "Doe",
    "isDeleted": false,
    "isDefaultAccount": false,
    "createdDate": "2024-02-01T14:20:00Z",
    "deletedDate": null
  },
  {
    "savedPaymentMethodId": "5513027774438108367",
    "accountType": "MC",
    "maskedAccountNumber": "************5678",
    "lastFour": "5678",
    "cardExpiry": "0626",
    "accountHolderFirstName": "John",
    "accountHolderLastName": "Doe",
    "isDeleted": true,
    "isDefaultAccount": false,
    "createdDate": "2023-12-10T09:15:00Z",
    "deletedDate": "2024-03-01T11:00:00Z"
  }
]

HTTP Status Codes

Status Code Description
200 Success - Payment methods retrieved (may be empty array if none exist)
400 Bad Request - Invalid merchant ID or payor ID format
401 Unauthorized - Missing or invalid authentication token
404 Not Found - Payor not found for this merchant
500 Internal Server Error - Server encountered an unexpected error

Common Use Cases

1. Display Payment Methods at Checkout

// Show customer their saved cards
List methods =
    await client.GetActivePaymentMethodsAsync(merchantId, payorId);

if (methods.Any())
{
    Console.WriteLine("Select a payment method:");

    foreach (var method in methods)
    {
        string displayName = $"{method.AccountType} ending in {method.LastFour}";

        if (!string.IsNullOrEmpty(method.CardExpiry))
        {
            displayName += $" (Exp: {method.CardExpiry})";
        }

        if (method.IsDefaultAccount == true)
        {
            displayName += " [Default]";
        }

        Console.WriteLine(displayName);
    }
}
else
{
    Console.WriteLine("No saved payment methods. Add one?");
}

2. Show Account Management Page

// Display all payment methods in customer's account settings
List allMethods =
    await client.GetSavedPaymentMethodsAsync(merchantId, payorId);

Console.WriteLine("Your Payment Methods:");
Console.WriteLine("====================");

foreach (var method in allMethods)
{
    if (!method.IsDeleted)
    {
        Console.WriteLine($"\n{method.AccountType} ****{method.LastFour}");
        Console.WriteLine($"Added: {method.CreatedDate:MM/dd/yyyy}");

        if (method.IsDefaultAccount == true)
        {
            Console.WriteLine("Status: Default Payment Method");
        }

        Console.WriteLine($"ID: {method.SavedPaymentMethodId}");
        Console.WriteLine("[Edit] [Delete] [Set as Default]");
    }
}

3. Find Default Payment Method for Recurring Billing

// Get default card for subscription charge
SavedPaymentMethodResponse defaultMethod =
    await client.GetDefaultPaymentMethodAsync(merchantId, payorId);

if (defaultMethod != null)
{
    Console.WriteLine("Charging subscription to:");
    Console.WriteLine($"{defaultMethod.AccountType} ****{defaultMethod.LastFour}");

    // Process recurring payment
    await ProcessRecurringPayment(
        merchantId,
        payorId,
        defaultMethod.SavedPaymentMethodId,
        subscriptionAmount
    );
}
else
{
    Console.WriteLine("No default payment method set. Please select one.");
}

4. Check if Customer Has ACH Account

// See if customer has bank account on file
List methods =
    await client.GetActivePaymentMethodsAsync(merchantId, payorId);

var achAccounts = methods.Where(m =>
    m.AccountType == "ECHK" || m.AccountType == "SAV"
).ToList();

if (achAccounts.Any())
{
    Console.WriteLine("ACH payment available:");
    foreach (var account in achAccounts)
    {
        Console.WriteLine($"{account.AccountType} ****{account.LastFour}");
    }
}

5. Validate Payment Method Before Transaction

// Before processing, check if payment method exists and is active
string selectedPaymentMethodId = GetUserSelection();

List methods =
    await client.GetSavedPaymentMethodsAsync(merchantId, payorId);

var selectedMethod = methods.FirstOrDefault(m =>
    m.SavedPaymentMethodId == selectedPaymentMethodId
);

if (selectedMethod == null)
{
    Console.WriteLine("Payment method not found");
}
else if (selectedMethod.IsDeleted)
{
    Console.WriteLine("This payment method has been deleted");
}
else
{
    // Check if card is expired
    if (!string.IsNullOrEmpty(selectedMethod.CardExpiry))
    {
        if (IsCardExpired(selectedMethod.CardExpiry))
        {
            Console.WriteLine("This card has expired. Please update it.");
            return;
        }
    }

    // Process transaction
    await ProcessTransaction(merchantId, payorId, selectedPaymentMethodId, amount);
}

Best Practices

  • Filter out deleted methods: By default, hide deleted payment methods from customers
  • Highlight default method: Clearly indicate which payment method is the default
  • Check expiration dates: Warn customers if their credit card is expired or expiring soon
  • Cache appropriately: Cache for the duration of a session, but refresh before processing payments
  • Handle empty results: Some payors may have no payment methods (all deleted)
  • Display card brands: Use accountType to show appropriate card logos (VISA, MC, etc.)
  • Security consideration: Only show last 4 digits - never display full card numbers

Understanding Deleted Payment Methods

Why are deleted methods still returned?

Deleted payment methods appear in the response for historical and auditing purposes. They allow you to:

  • Track which payment methods were previously used
  • Maintain transaction history linkage
  • Comply with record-keeping requirements
  • Provide customer support with complete payment history

Always filter by isDeleted: false when displaying payment options to customers.

Account Type Reference

Account Type Description Additional Fields
VISA Visa credit card cardExpiry, maskedAccountNumber
MC Mastercard credit card cardExpiry, maskedAccountNumber
AMEX American Express card cardExpiry, maskedAccountNumber
DISC Discover card cardExpiry, maskedAccountNumber
ECHK Checking account (ACH) abaRoutingNumber, maskedAccountNumber
SAV Savings account (ACH) abaRoutingNumber, maskedAccountNumber

Troubleshooting

404 Not Found - "Payor not found"

  • The payor ID doesn't exist for this merchant
  • Verify the payor ID is correct
  • Confirm the merchant ID matches the payor's merchant
  • Check if payor was deleted

Empty Array Returned

  • Payor exists but has no saved payment methods
  • All payment methods may have been deleted
  • This is normal for newly created payors
  • Prompt user to add a payment method

All Methods Show isDeleted: true

  • Customer has deleted all their payment methods
  • Filter active methods returns empty list
  • Prompt customer to add a new payment method

Related Endpoints

  • GET /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{id} - Get single payment method details
  • POST /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods - Add a new payment method
  • PUT /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{id} - Update a payment method
  • DELETE /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{id} - Delete a payment method

Additional Notes

  • The response includes both active and deleted payment methods by default
  • Only one payment method can be marked as default at a time
  • The maskedAccountNumber is safe to display - it contains no sensitive information
  • For credit cards, check the cardExpiry field to warn about expired cards
  • For ACH, the abaRoutingNumber is not masked (it's not considered sensitive)
  • The sponsorKey can be used to track which sponsor/program the payment method belongs to
  • Billing address fields (street, city, region, postal code) are included if they were provided during creation
  • The response order is not guaranteed - sort by isDefaultAccount and createdDate as needed