Create Payor Profile
POST
/v1/rest/merchants/{merchantId}/payors
Description: Creates a new payor profile (customer payment profile) with at least one saved payment method for a specific merchant. This is typically the first step in setting up a customer for recurring payments or storing their payment information for future use.
What is this endpoint used for?
This endpoint creates a "customer wallet" that can store one or more payment methods. Think of it like opening a tab at a restaurant:
- Account Setup: Create a payment profile when a customer registers or adds their first payment method
- Recurring Billing: Set up customers for subscription or automatic payments
- Save for Later: Allow customers to save payment info for faster checkout
- One-Click Payments: Enable quick payment without re-entering card details
- Multi-Card Support: Let customers save multiple payment options
Important Security Note: Payment card numbers and bank account numbers must be encrypted using the public encryption keys before sending to this API. Use
GET /v1/rest/keys/pan to retrieve the encryption key. Never send unencrypted account numbers.
Important Note: It is strongly recommended that integrators use the Procare Pay Hosted Payments Page to manage payor profiles and payment methods. This API is intended for advanced use cases where direct API integration is necessary. Using the hosted page ensures PCI compliance and reduces the scope of sensitive data handling. Calling this endpoint directly from your integrated application will include your application in PCI scope!
Path Parameters
merchantId (required)
Type: string
Pattern: Must be an 11-digit number
Description: The unique 11-digit merchant identifier
Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
| customerAccountId | string | Optional | YOUR system's customer identifier - links this payor to your customer |
| encryptedAccountNumber | string | Required | Encrypted credit card or bank account number (use PAN key to encrypt) |
| paymentMethodType | string | Required | Account type: Checking, Saving, or CreditCard |
| accountHolderFirstName | string | Required | First name on the account |
| accountHolderLastName | string | Required | Last name on the account |
| cardExpiry | string | Conditional | Card expiration (MMYY, MMYYYY, or YYYYMMDD) - required for credit cards |
| abaRoutingNumber | string | Conditional | Bank routing number - required for ACH (checking/savings) |
| accountHolderStreetLine1 | string | Optional | Billing street address |
| accountHolderStreetLine2 | string | Optional | Billing address line 2 (apt, suite, etc.) |
| accountHolderCity | string | Optional | Billing city |
| accountHolderRegion | string | Recommended | State/province (recommended for ACH, improves fraud prevention) |
| accountHolderPostalCode | string | Recommended | ZIP/postal code (recommended for fraud prevention) |
| accountHolderPhoneNumber | string | Optional | Phone number |
| accountHolderEmail | string | Optional | Email address |
| customerAccountType | string | Optional | Your custom account type classification |
| setAsDefaultPaymentMethod | boolean | Optional | Whether this is the default payment method (default: false) |
| idLimitedBitFlag | boolean | Optional | Use 64-bit integer IDs (true) or GUIDs (false). Default: false |
| customerData | object | Optional | Custom key-value pairs for additional data |
| sponsorKey | string | Optional | Sponsor key (1-8 characters) for tracking/reporting |
| allowTransactionalEmails | boolean | Optional | Allow sending transaction receipts by email (requires email field) |
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.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class SavedPaymentMethodRequest
{
[JsonProperty("customerAccountId")]
public string CustomerAccountId { get; set; }
[JsonProperty("encryptedAccountNumber")]
public string EncryptedAccountNumber { get; set; }
[JsonProperty("paymentMethodType")]
public string PaymentMethodType { get; set; }
[JsonProperty("accountHolderFirstName")]
public string AccountHolderFirstName { get; set; }
[JsonProperty("accountHolderLastName")]
public string AccountHolderLastName { get; set; }
[JsonProperty("cardExpiry")]
public string CardExpiry { get; set; }
[JsonProperty("abaRoutingNumber")]
public string AbaRoutingNumber { get; set; }
[JsonProperty("accountHolderStreetLine1")]
public string AccountHolderStreetLine1 { get; set; }
[JsonProperty("accountHolderCity")]
public string AccountHolderCity { get; set; }
[JsonProperty("accountHolderRegion")]
public string AccountHolderRegion { get; set; }
[JsonProperty("accountHolderPostalCode")]
public string AccountHolderPostalCode { get; set; }
[JsonProperty("accountHolderEmail")]
public string AccountHolderEmail { get; set; }
[JsonProperty("setAsDefaultPaymentMethod")]
public bool SetAsDefaultPaymentMethod { get; set; }
[JsonProperty("customerData")]
public Dictionary CustomerData { get; set; }
[JsonProperty("sponsorKey")]
public string SponsorKey { get; set; }
}
public class PayorResponse
{
[JsonProperty("payorId")]
public string PayorId { get; set; }
[JsonProperty("savedPaymentMethodId")]
public string SavedPaymentMethodId { get; set; }
[JsonProperty("responseStatus")]
public string ResponseStatus { get; set; }
[JsonProperty("responseMessage")]
public string ResponseMessage { get; set; }
}
public class CreatePayorClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl = "https://your-api-domain.com";
private readonly string _bearerToken;
public CreatePayorClient(string bearerToken)
{
_bearerToken = bearerToken;
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(_baseUrl);
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", _bearerToken);
}
public async Task CreatePayorWithCreditCardAsync(
string merchantId,
string customerAccountId,
string encryptedCardNumber,
string cardExpiry,
string firstName,
string lastName,
string address,
string city,
string state,
string zip,
string email = null)
{
var request = new SavedPaymentMethodRequest
{
CustomerAccountId = customerAccountId,
EncryptedAccountNumber = encryptedCardNumber,
PaymentMethodType = "CreditCard",
CardExpiry = cardExpiry,
AccountHolderFirstName = firstName,
AccountHolderLastName = lastName,
AccountHolderStreetLine1 = address,
AccountHolderCity = city,
AccountHolderRegion = state,
AccountHolderPostalCode = zip,
AccountHolderEmail = email,
SetAsDefaultPaymentMethod = true
};
return await CreatePayorAsync(merchantId, request);
}
public async Task CreatePayorWithBankAccountAsync(
string merchantId,
string customerAccountId,
string encryptedAccountNumber,
string routingNumber,
string accountType, // "Checking" or "Saving"
string firstName,
string lastName,
string email = null)
{
var request = new SavedPaymentMethodRequest
{
CustomerAccountId = customerAccountId,
EncryptedAccountNumber = encryptedAccountNumber,
AbaRoutingNumber = routingNumber,
PaymentMethodType = accountType,
AccountHolderFirstName = firstName,
AccountHolderLastName = lastName,
AccountHolderEmail = email,
SetAsDefaultPaymentMethod = true
};
return await CreatePayorAsync(merchantId, request);
}
private async Task CreatePayorAsync(
string merchantId,
SavedPaymentMethodRequest request)
{
try
{
string endpoint = $"/v1/rest/merchants/{merchantId}/payors";
string jsonContent = JsonConvert.SerializeObject(request);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.PostAsync(endpoint, content);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject(responseBody);
}
catch (HttpRequestException e)
{
Console.WriteLine($"Request error: {e.Message}");
throw;
}
}
}
// Example usage: Create payor with credit card
var client = new CreatePayorClient("your-bearer-token-here");
// First, encrypt the card number
string cardNumber = "4111111111111111";
string encryptedCard = await EncryptWithPanKey(cardNumber); // Use PAN key
PayorResponse result = await client.CreatePayorWithCreditCardAsync(
merchantId: "12345678901",
customerAccountId: "CUST-12345",
encryptedCardNumber: encryptedCard,
cardExpiry: "1225",
firstName: "John",
lastName: "Doe",
address: "123 Main St",
city: "Springfield",
state: "IL",
zip: "62701",
email: "john.doe@example.com"
);
Console.WriteLine($"Payor created: {result.PayorId}");
Console.WriteLine($"Payment method ID: {result.SavedPaymentMethodId}");
Imports System.Collections.Generic
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Text
Imports System.Threading.Tasks
Imports Newtonsoft.Json
Public Class SavedPaymentMethodRequest
<JsonProperty("customerAccountId")>
Public Property CustomerAccountId As String
<JsonProperty("encryptedAccountNumber")>
Public Property EncryptedAccountNumber As String
<JsonProperty("paymentMethodType")>
Public Property PaymentMethodType As String
<JsonProperty("accountHolderFirstName")>
Public Property AccountHolderFirstName As String
<JsonProperty("accountHolderLastName")>
Public Property AccountHolderLastName As String
<JsonProperty("cardExpiry")>
Public Property CardExpiry As String
<JsonProperty("abaRoutingNumber")>
Public Property AbaRoutingNumber As String
<JsonProperty("accountHolderStreetLine1")>
Public Property AccountHolderStreetLine1 As String
<JsonProperty("accountHolderCity")>
Public Property AccountHolderCity As String
<JsonProperty("accountHolderRegion")>
Public Property AccountHolderRegion As String
<JsonProperty("accountHolderPostalCode")>
Public Property AccountHolderPostalCode As String
<JsonProperty("accountHolderEmail")>
Public Property AccountHolderEmail As String
<JsonProperty("setAsDefaultPaymentMethod")>
Public Property SetAsDefaultPaymentMethod As Boolean
End Class
Public Class PayorResponse
<JsonProperty("payorId")>
Public Property PayorId As String
<JsonProperty("savedPaymentMethodId")>
Public Property SavedPaymentMethodId As String
<JsonProperty("responseStatus")>
Public Property ResponseStatus As String
End Class
Public Class CreatePayorClient
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 CreatePayorWithCreditCardAsync(
merchantId As String,
customerAccountId As String,
encryptedCardNumber As String,
cardExpiry As String,
firstName As String,
lastName As String,
address As String,
city As String,
state As String,
zip As String,
Optional email As String = Nothing) As Task(Of PayorResponse)
Dim request As New SavedPaymentMethodRequest With {
.CustomerAccountId = customerAccountId,
.EncryptedAccountNumber = encryptedCardNumber,
.PaymentMethodType = "CreditCard",
.CardExpiry = cardExpiry,
.AccountHolderFirstName = firstName,
.AccountHolderLastName = lastName,
.AccountHolderStreetLine1 = address,
.AccountHolderCity = city,
.AccountHolderRegion = state,
.AccountHolderPostalCode = zip,
.AccountHolderEmail = email,
.SetAsDefaultPaymentMethod = True
}
Return Await CreatePayorAsync(merchantId, request)
End Function
Private Async Function CreatePayorAsync(
merchantId As String,
request As SavedPaymentMethodRequest) As Task(Of PayorResponse)
Try
Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/payors"
Dim jsonContent As String = JsonConvert.SerializeObject(request)
Dim content As New StringContent(jsonContent, Encoding.UTF8, "application/json")
Dim response As HttpResponseMessage = Await _httpClient.PostAsync(endpoint, content)
response.EnsureSuccessStatusCode()
Dim responseBody As String = Await response.Content.ReadAsStringAsync()
Return JsonConvert.DeserializeObject(Of PayorResponse)(responseBody)
Catch ex As HttpRequestException
Console.WriteLine($"Request error: {ex.Message}")
Throw
End Try
End Function
End Class
' Example usage
Dim client As New CreatePayorClient("your-bearer-token-here")
' Encrypt card number first
Dim cardNumber As String = "4111111111111111"
Dim encryptedCard As String = Await EncryptWithPanKey(cardNumber)
Dim result As PayorResponse = Await client.CreatePayorWithCreditCardAsync(
"12345678901",
"CUST-12345",
encryptedCard,
"1225",
"John",
"Doe",
"123 Main St",
"Springfield",
"IL",
"62701",
"john.doe@example.com"
)
Console.WriteLine($"Payor created: {result.PayorId}")
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
class SavedPaymentMethodRequest {
@SerializedName("customerAccountId")
private String customerAccountId;
@SerializedName("encryptedAccountNumber")
private String encryptedAccountNumber;
@SerializedName("paymentMethodType")
private String paymentMethodType;
@SerializedName("accountHolderFirstName")
private String accountHolderFirstName;
@SerializedName("accountHolderLastName")
private String accountHolderLastName;
@SerializedName("cardExpiry")
private String cardExpiry;
@SerializedName("abaRoutingNumber")
private String abaRoutingNumber;
@SerializedName("accountHolderStreetLine1")
private String accountHolderStreetLine1;
@SerializedName("accountHolderCity")
private String accountHolderCity;
@SerializedName("accountHolderRegion")
private String accountHolderRegion;
@SerializedName("accountHolderPostalCode")
private String accountHolderPostalCode;
@SerializedName("accountHolderEmail")
private String accountHolderEmail;
@SerializedName("setAsDefaultPaymentMethod")
private boolean setAsDefaultPaymentMethod;
// Setters for builder pattern
public void setCustomerAccountId(String id) { this.customerAccountId = id; }
public void setEncryptedAccountNumber(String num) { this.encryptedAccountNumber = num; }
public void setPaymentMethodType(String type) { this.paymentMethodType = type; }
public void setAccountHolderFirstName(String name) { this.accountHolderFirstName = name; }
public void setAccountHolderLastName(String name) { this.accountHolderLastName = name; }
public void setCardExpiry(String expiry) { this.cardExpiry = expiry; }
public void setAbaRoutingNumber(String routing) { this.abaRoutingNumber = routing; }
public void setAccountHolderStreetLine1(String address) { this.accountHolderStreetLine1 = address; }
public void setAccountHolderCity(String city) { this.accountHolderCity = city; }
public void setAccountHolderRegion(String region) { this.accountHolderRegion = region; }
public void setAccountHolderPostalCode(String postal) { this.accountHolderPostalCode = postal; }
public void setAccountHolderEmail(String email) { this.accountHolderEmail = email; }
public void setSetAsDefaultPaymentMethod(boolean isDefault) { this.setAsDefaultPaymentMethod = isDefault; }
}
class PayorResponse {
@SerializedName("payorId")
private String payorId;
@SerializedName("savedPaymentMethodId")
private String savedPaymentMethodId;
@SerializedName("responseStatus")
private String responseStatus;
public String getPayorId() { return payorId; }
public String getSavedPaymentMethodId() { return savedPaymentMethodId; }
public String getResponseStatus() { return responseStatus; }
}
public class CreatePayorClient {
private final HttpClient httpClient;
private final String baseUrl;
private final String bearerToken;
private final Gson gson;
public CreatePayorClient(String bearerToken) {
this.bearerToken = bearerToken;
this.baseUrl = "https://your-api-domain.com";
this.httpClient = HttpClient.newHttpClient();
this.gson = new Gson();
}
public PayorResponse createPayorWithCreditCard(
String merchantId,
String customerAccountId,
String encryptedCardNumber,
String cardExpiry,
String firstName,
String lastName,
String address,
String city,
String state,
String zip,
String email) throws Exception {
SavedPaymentMethodRequest request = new SavedPaymentMethodRequest();
request.setCustomerAccountId(customerAccountId);
request.setEncryptedAccountNumber(encryptedCardNumber);
request.setPaymentMethodType("CreditCard");
request.setCardExpiry(cardExpiry);
request.setAccountHolderFirstName(firstName);
request.setAccountHolderLastName(lastName);
request.setAccountHolderStreetLine1(address);
request.setAccountHolderCity(city);
request.setAccountHolderRegion(state);
request.setAccountHolderPostalCode(zip);
request.setAccountHolderEmail(email);
request.setSetAsDefaultPaymentMethod(true);
return createPayor(merchantId, request);
}
private PayorResponse createPayor(
String merchantId,
SavedPaymentMethodRequest request) throws Exception {
String endpoint = baseUrl + "/v1/rest/merchants/" + merchantId + "/payors";
String jsonBody = gson.toJson(request);
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(endpoint))
.header("Authorization", "Bearer " + bearerToken)
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse response = httpClient.send(
httpRequest,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() == 201 || response.statusCode() == 200) {
return gson.fromJson(response.body(), PayorResponse.class);
} else {
throw new Exception("Request failed with status: " + response.statusCode());
}
}
public static void main(String[] args) {
try {
CreatePayorClient client = new CreatePayorClient("your-bearer-token-here");
// Encrypt card number first
String cardNumber = "4111111111111111";
String encryptedCard = encryptWithPanKey(cardNumber);
PayorResponse result = client.createPayorWithCreditCard(
"12345678901",
"CUST-12345",
encryptedCard,
"1225",
"John",
"Doe",
"123 Main St",
"Springfield",
"IL",
"62701",
"john.doe@example.com"
);
System.out.println("Payor created: " + result.getPayorId());
System.out.println("Payment method ID: " + result.getSavedPaymentMethodId());
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
}
require 'net/http'
require 'uri'
require 'json'
class CreatePayorClient
def initialize(bearer_token, base_url = 'https://your-api-domain.com')
@bearer_token = bearer_token
@base_url = base_url
end
def create_payor_with_credit_card(merchant_id, customer_account_id,
encrypted_card_number, card_expiry,
first_name, last_name,
address, city, state, zip, email = nil)
request_body = {
customerAccountId: customer_account_id,
encryptedAccountNumber: encrypted_card_number,
paymentMethodType: 'CreditCard',
cardExpiry: card_expiry,
accountHolderFirstName: first_name,
accountHolderLastName: last_name,
accountHolderStreetLine1: address,
accountHolderCity: city,
accountHolderRegion: state,
accountHolderPostalCode: zip,
accountHolderEmail: email,
setAsDefaultPaymentMethod: true
}
create_payor(merchant_id, request_body)
end
def create_payor_with_bank_account(merchant_id, customer_account_id,
encrypted_account_number, routing_number,
account_type, first_name, last_name, email = nil)
request_body = {
customerAccountId: customer_account_id,
encryptedAccountNumber: encrypted_account_number,
abaRoutingNumber: routing_number,
paymentMethodType: account_type, # 'Checking' or 'Saving'
accountHolderFirstName: first_name,
accountHolderLastName: last_name,
accountHolderEmail: email,
setAsDefaultPaymentMethod: true
}
create_payor(merchant_id, request_body)
end
private
def create_payor(merchant_id, request_body)
uri = URI("#{@base_url}/v1/rest/merchants/#{merchant_id}/payors")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'
request = Net::HTTP::Post.new(uri.path)
request['Authorization'] = "Bearer #{@bearer_token}"
request['Content-Type'] = 'application/json'
request['Accept'] = 'application/json'
request.body = JSON.generate(request_body)
response = http.request(request)
case response.code.to_i
when 200, 201
JSON.parse(response.body)
when 400
raise "Validation error: #{response.body}"
when 404
raise 'Merchant 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: Create payor with credit card
client = CreatePayorClient.new('your-bearer-token-here')
# Encrypt card number first
card_number = '4111111111111111'
encrypted_card = encrypt_with_pan_key(card_number)
result = client.create_payor_with_credit_card(
'12345678901',
'CUST-12345',
encrypted_card,
'1225',
'John',
'Doe',
'123 Main St',
'Springfield',
'IL',
'62701',
'john.doe@example.com'
)
puts "Payor created: #{result['payorId']}"
puts "Payment method ID: #{result['savedPaymentMethodId']}"
puts "Status: #{result['responseStatus']}"
Example Request Body (Credit Card)
{
"customerAccountId": "CUST-12345",
"encryptedAccountNumber": "iJKL...mnop=",
"paymentMethodType": "CreditCard",
"cardExpiry": "1225",
"accountHolderFirstName": "John",
"accountHolderLastName": "Doe",
"accountHolderStreetLine1": "123 Main St",
"accountHolderCity": "Springfield",
"accountHolderRegion": "IL",
"accountHolderPostalCode": "62701",
"accountHolderEmail": "john.doe@example.com",
"setAsDefaultPaymentMethod": true,
"customerData": {
"accountType": "premium",
"notes": "VIP customer"
},
"sponsorKey": "SPONS123",
"allowTransactionalEmails": true
}
Example Request Body (Bank Account/ACH)
{
"customerAccountId": "CUST-12345",
"encryptedAccountNumber": "aBCD...efgh=",
"abaRoutingNumber": "123456789",
"paymentMethodType": "Checking",
"accountHolderFirstName": "Jane",
"accountHolderLastName": "Smith",
"accountHolderEmail": "jane.smith@example.com",
"setAsDefaultPaymentMethod": true
}
Example Response
{
"responseStatus": "SavedPaymentMethodCreated",
"responseCode": "201",
"responseResult": "success",
"responseMessage": "Payment profile created successfully",
"correlationId": "abc123-def456-ghi789",
"payorId": "5513027774438108364",
"savedPaymentMethodId": "5513027774438108365",
"customerAccountId": "CUST-12345",
"createdDate": "2024-01-15T10:30:00Z",
"accountType": "VISA",
"maskedAccountNumber": "************1234",
"lastFour": "1234",
"cardExpiry": "1225",
"isDefaultAccount": true,
"sponsorKey": "SPONS123"
}
HTTP Status Codes
| Status Code | Description |
|---|---|
| 201 | Created - Payor profile created successfully |
| 400 | Bad Request - Validation error (missing required fields, invalid format, etc.) |
| 401 | Unauthorized - Missing or invalid authentication token |
| 404 | Not Found - Merchant not found or inactive |
| 408 | Request Timeout - Operation took too long (retry) |
| 500 | Internal Server Error - Server encountered an unexpected error |
Common Validation Errors
| Error | Cause | Solution |
|---|---|---|
| Encrypted Account Number is required | Missing encryptedAccountNumber | Encrypt card/account number with PAN key |
| Card Expiry is required | Missing cardExpiry for credit card | Provide expiry in MMYY, MMYYYY, or YYYYMMDD |
| ABA Routing Number is required | Missing abaRoutingNumber for ACH | Provide 9-digit routing number |
| Invalid encrypted data | Account number not properly encrypted | Use PAN key from /v1/rest/keys/pan to encrypt |
| SponsorKey validation error | SponsorKey too long or empty string | Must be 1-8 characters or null |
Best Practices
- Always encrypt account numbers: Use the PAN public key from
GET /v1/rest/keys/pan - Store payor ID: Save the returned payorId in your database linked to your customer
- Store payment method ID: Save savedPaymentMethodId for future transactions
- Validate before encrypting: Check card number format and expiry date before encryption
- Provide complete address: Include address, city, state, and ZIP for better fraud prevention
- Set default wisely: First payment method should usually be default (setAsDefaultPaymentMethod: true)
- Use customerAccountId consistently: This links your customer to their payment profile
- Clear sensitive data: Immediately clear card numbers from memory after encryption
- Handle duplicates: Check if customer already has a payor before creating
Security Considerations
Critical Security Requirements:
- Never send unencrypted account numbers to this API
- Never log or store unencrypted card numbers
- Always use HTTPS for API requests
- Fetch fresh encryption keys before encrypting (don't cache long-term)
- Clear card numbers from memory immediately after use
- Don't expose encrypted values to end users
Complete Workflow Example
// Step 1: Get encryption key
var keysClient = new KeysClient();
PublicKey panKey = await keysClient.GetPanKeyAsync();
// Step 2: Collect customer information
string customerAccountId = "CUST-12345"; // From your system
string cardNumber = GetCardNumberFromSecureForm();
string cvv = GetCvvFromSecureForm();
string expiry = GetExpiryFromSecureForm();
// Step 3: Validate inputs
if (!IsValidCardNumber(cardNumber)) { /* show error */ }
if (!IsValidExpiry(expiry)) { /* show error */ }
// Step 4: Encrypt sensitive data
string encryptedCard = EncryptWithRSA(cardNumber, panKey.Key);
// Step 5: Clear plain text immediately
cardNumber = null;
cvv = null;
// Step 6: Create payor
var payorClient = new CreatePayorClient(bearerToken);
PayorResponse result = await payorClient.CreatePayorWithCreditCardAsync(
merchantId,
customerAccountId,
encryptedCard,
expiry,
firstName,
lastName,
address,
city,
state,
zip,
email
);
// Step 7: Store IDs in your database
SaveToDatabase(customerAccountId, result.PayorId, result.SavedPaymentMethodId);
// Step 8: Confirm to user
ShowMessage($"Payment method ending in {result.LastFour} saved successfully!");
Troubleshooting
400 Bad Request - "Invalid encrypted account number"
- Account number was not encrypted or improperly encrypted
- Make sure you're using the PAN key (not SAD key)
- Verify RSA encryption with PKCS#1 v1.5 padding
- Check that encrypted data is base64 encoded
400 Bad Request - "Customer Account ID already exists"
- A payor profile already exists for this customerAccountId
- Use GET endpoint to retrieve existing payor
- If you want to add another payment method, use POST savedPaymentMethods endpoint
404 Not Found - "Merchant not found"
- Merchant ID is invalid or doesn't exist
- Verify merchant ID is exactly 11 digits
- Check that merchant account is active
408 Request Timeout
- The payment vault service took too long to respond
- This is usually temporary - retry after a brief delay
- Check the payor wasn't actually created before retrying
Related Endpoints
- GET /v1/rest/merchants/{merchantId}/payors - Search for existing payors
- POST /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods - Add another payment method to existing payor
- GET /v1/rest/keys/pan - Get encryption key for account numbers
Additional Notes
- Creating a payor automatically creates both the profile AND the first payment method in one request
- The
customerAccountIdmust be unique per merchant - you can't have duplicate customer IDs - For credit cards,
cardExpiryis required; for ACH,abaRoutingNumberis required - The first payment method is usually set as default, but you can control this with
setAsDefaultPaymentMethod - Use
idLimitedBitFlag: trueif your system needs 64-bit integer IDs instead of GUIDs - The
customerDatafield lets you store any custom key-value pairs you need - If
allowTransactionalEmailsis true, the customer will receive email receipts (requires valid email) - SponsorKey is optional but useful for tracking payments by sponsor, program, or campaign