Onboarding to Consolidated Settlement Reporting
Set up the file sharing and access to the system
In order to start using the Consolidated Settlement Reporting System, you will need to do the following:
Access to Consolidated Settlement Reporting
In order to access the system, a Spreedly team member will need to create your access. Provide the following information in your request:
- Environment Keys for each environment where you would like access
- IP addresses from which you (the merchant) will be accessing the SFTP system
- IP addresses from where the PSPs will be sending the files
- Email address for the user account(s) who will access the system
Once you have supplied all of this information, access can be granted to the Consolidated Settlement Reporting system. Spreedly will supply you a merchant identifier and log in information for each user.
Configure the File Send
You will need to configure access with each PSP per their guidelines to send reconciliation files to Spreedly. You will need to request the SFTP information from Spreedly using an API call, and then pass that information to each PSP per their requirements.
Retrieve your SFTP Credentials
This guide explains how to securely retrieve your SFTP credentials. You provide your public key, we encrypt your credentials, and only you can decrypt them.
+----------------+ +-----------------------+
| Merchant | | Settlement System |
+----------------+ +-----------------------+
| |
| 1. Generate RSA key pair |
| |
| 2. Send PUBLIC KEY via API |
|-------------------------------------> |
| |
| 3. Encrypt credentials with your key|
| |
| 4. Receive encrypted credentials |
|<------------------------------------- |
| |
| 5. Decrypt with PRIVATE KEY (one step) |
| |
Step 1: Generate Your RSA Key Pair
Generate a 4096-bit (or higher) RSA key pair. Keep your private key secure and never share it.
Using OpenSSL (Recommended)
# Generate private key
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:4096
# Extract public key
openssl rsa -pubout -in private_key.pem -out public_key.pemUsing Ruby
require 'openssl'
# Generate key pair
key = OpenSSL::PKey::RSA.new(4096)
# Save private key (KEEP THIS SECURE!)
File.write('private_key.pem', key.to_pem)
# Save public key (this is what you'll send to us)
File.write('public_key.pem', key.public_key.to_pem)Using Python
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
# Generate key pair
private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
# Save private key (KEEP THIS SECURE!)
with open('private_key.pem', 'wb') as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
# Save public key
public_key = private_key.public_key()
with open('public_key.pem', 'wb') as f:
f.write(public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
))Using Node.js
const crypto = require('crypto');
const fs = require('fs');
// Generate key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 4096,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
// Save keys
fs.writeFileSync('private_key.pem', privateKey); // KEEP THIS SECURE!
fs.writeFileSync('public_key.pem', publicKey);Step 2: Request Encrypted Credentials
Make a POST request to retrieve your encrypted SFTP credentials.
API Endpoint
POST /v1/merchants/encrypted_credentials
Headers
| Header | Value | Description |
|---|---|---|
X-Api-Key | Bearer {jwt_token} | Your authentication token |
Content-Type | application/json | Request content type |
Merchant must login to retrieve authentication token
Request Body
{
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----"
}Note: You can send the public key in either:
- PEM format (with
-----BEGIN PUBLIC KEY-----headers) - Base64-encoded DER format (raw key data)
Example Request (cURL)
# Read your public key
PUBLIC_KEY=$(cat public_key.pem)
# Make the API request
curl -X POST "https://api.example.com/v1/merchants/{merchant_id}/encrypted_credentials" \
-H "X-Api-Key: Bearer your_jwt_token_here" \
-H "Content-Type: application/json" \
-d "{\"public_key\": \"$PUBLIC_KEY\"}"Example Response
{
"status": true,
"message": "Credentials encrypted successfully",
"data": {
"version": "2.0",
"algorithm": "RSA-OAEP",
"encrypted_credentials": "base64_encoded_encrypted_credentials...",
"generated_at": "2025-12-15T10:00:00Z",
"merchant_id": "550e8400-e29b-41d4-a716-446655440000"
},
"decryption_instructions": { ... }
}Response Fields
| Field | Description |
|---|---|
version | API version |
algorithm | Encryption algorithm (RSA-OAEP) |
encrypted_credentials | Base64-encoded encrypted SFTP credentials |
generated_at | Timestamp when credentials were encrypted |
merchant_id | Your merchant ID |
Step 3: Decrypt Your Credentials
Decrypt with your private key in one step:
Ruby
require 'openssl'
require 'base64'
require 'json'
private_key = OpenSSL::PKey::RSA.new(File.read('private_key.pem'))
encrypted = Base64.decode64(response[:data][:encrypted_credentials])
json = private_key.private_decrypt(encrypted, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
creds = JSON.parse(json)
puts "Username: #{creds['u']}"
puts "Password: #{creds['p']}"
puts "Host: #{creds['h']}"
puts "Port: #{creds['port']}"
puts "Directory: #{creds['dir']}"Python
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import base64
import json
with open('private_key.pem', 'rb') as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
encrypted = base64.b64decode(response['data']['encrypted_credentials'])
json_str = private_key.decrypt(
encrypted,
padding.OAEP(mgf=padding.MGF1(hashes.SHA1()), algorithm=hashes.SHA1(), label=None)
)
creds = json.loads(json_str)
print(f"Username: {creds['u']}")
print(f"Password: {creds['p']}")
print(f"Host: {creds['h']}")Node.js
const crypto = require('crypto');
const fs = require('fs');
const privateKey = fs.readFileSync('private_key.pem', 'utf8');
const encrypted = Buffer.from(response.data.encrypted_credentials, 'base64');
const json = crypto.privateDecrypt(
{ key: privateKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
encrypted
);
const creds = JSON.parse(json.toString());
console.log('Username:', creds.u);
console.log('Password:', creds.p);
console.log('Host:', creds.h);Java
import javax.crypto.Cipher;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
byte[] keyBytes = Files.readAllBytes(Paths.get("private_key.der"));
PrivateKey privateKey = KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
byte[] encrypted = Base64.getDecoder().decode(encryptedCredentials);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
String json = new String(cipher.doFinal(encrypted));
// Parse JSON to get credentialsDecrypted Credentials Format
After decryption, you'll get a JSON object with your SFTP credentials:
{
"u": "your-sftp-username",
"p": "your-sftp-password",
"h": "sftp.example.com",
"port": 22,
"dir": "/data/your-merchant",
"uri": "sftp://[email protected]"
}Field Mapping
| Field | Description |
|---|---|
u | Username |
p | Password |
h | Host |
port | Port (usually 22) |
dir | Home directory |
uri | Full SFTP URI |
Error Handling
Common Errors
| Error | Cause | Solution |
|---|---|---|
Public key is required | Missing public key in request | Include your public key in the request body |
Invalid public key | Malformed or unsupported key format | Ensure key is valid RSA in PEM or DER format |
Merchant not found | Invalid merchant ID | Verify your merchant ID |
Unauthorized | Invalid or expired JWT token | Refresh your authentication token |
Forbidden | Accessing another merchant's credentials | Use your own merchant ID |
Decryption Errors
| Error | Cause | Solution |
|---|---|---|
| Padding error | Wrong private key or corrupted data | Verify you're using the matching private key |
| Auth tag mismatch | Data integrity failure | Request new encrypted credentials |
| Invalid IV length | Corrupted initialization vector | Request new encrypted credentials |
Technical Details
Encryption
| Component | Details |
|---|---|
| Algorithm | RSA-OAEP (PKCS#1 OAEP padding) |
| Minimum Key Size | 4096 bits |
| Recommended Key Size | 4096 bits (sufficient for credential payload) |
How It Works
- You send your RSA public key
- We encrypt your credentials directly with RSA-OAEP
- You decrypt with your private key (single operation)
Support
If you encounter issues with this process, please contact our support team with:
- Your merchant ID
- The error message received
- The public key fingerprint (from the response, if available)
Never share your private key with anyone, including support staff.
Share the SFTP Information
You will need to work with each PSP to give them the credentials from the previous step as well configure the file send with the following requirements:
- The files should be sent once per day.
- The file should be in csv format.
- The file name should contain the date in the format
YYYY-MM-DD. - The file name should not include any spaces.
- Only files should be pushed to the SFTP location. Additional folders should not be created or an error may occur.
Spreedly requires that you request that each PSP remove the following information from your reconciliation files:
- Card information (PAN, First 6, Last 4...)
- Cardholder information (Name, address...)
SFTP credentials sharing
Encrypts and returns SFTP credentials for a merchant and associated PSPs using the merchant's provided public key. The public key is used on-the-fly and NOT stored in the database.
API endpoint
POST {{baseURL}}/v1/merchants/encrypted_credentials
Authentication
See the merchant user login section for information on how to obtain the authentication token.
Header
X-Api-Key: Bearer {{merchantAuthToken}}
Request details
Request
{
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----"
}Request fields
| Field | Type | Required | Description |
|---|---|---|---|
public_key | string | yes | RSA public key in PEM or Base64-encoded DER format |
Response details
Response
{
"message": "Credentials encrypted successfully",
"data": {
"version": "3.0",
"algorithm": "AES-256-GCM + RSA-OAEP",
"encrypted_key": "base64_encoded_rsa_encrypted_aes_key...",
"encrypted_credentials": "base64_encoded_aes_encrypted_credentials...",
"iv": "base64_encoded_initialization_vector",
"auth_tag": "base64_encoded_authentication_tag",
"generated_at": "2025-12-15T10:00:00Z",
"merchant_id": "550e8400-e29b-41d4-a716-446655440000"
},
"decryption_instructions": {
"overview": "Decrypt your SFTP credentials using hybrid decryption (AES-256-GCM + RSA-OAEP):",
"steps": [
"1. Decode 'encrypted_key' from Base64 and decrypt with your RSA private key (OAEP padding)",
"2. Decode 'encrypted_credentials', 'iv', and 'auth_tag' from Base64",
"3. Use the decrypted AES key to decrypt credentials with AES-256-GCM",
"4. Parse the JSON to get your credentials"
],
"credential_structure": {
"merchant_sftp": "Your merchant SFTP credentials (u, p, h, port, dir, uri)",
"psp_sftp": "Array of PSP SFTP credentials with psp_name and credentials",
"web_portal": "SFTP web portal URL for browser access"
},
"credential_fields": {
"u": "username",
"p": "password",
"h": "host",
"port": "port",
"dir": "home_directory",
"uri": "sftp_uri"
},
"code_examples": {
"ruby": "...",
"python": "...",
"nodejs": "...",
"bash": "..."
}
}
}
Decrypted payload structure
{
"merchant_sftp": {
"u": "username",
"p": "password",
"h": "host",
"port": 22,
"dir": "/path",
"uri": "sftp://..."
},
"psp_sftp": [
{
"psp_name": "Adyen",
"u": "username",
"p": "password",
"h": "host",
"port": 22,
"dir": "/path",
"uri": "sftp://..."
}
],
"web_portal": "https://..."
}Error responses
- 422 Unprocessable Entity - Missing Public Key
{
"error": "Public key is required",
"details": "Please provide your RSA public key in PEM or Base64-encoded DER format"
}- 422 Unprocessable Entity - Invalid Public Key
{
"error": "Invalid public key",
"details": "Unable to parse public key",
"supported_formats": [
"PEM format (-----BEGIN PUBLIC KEY-----)",
"Base64-encoded DER format"
]
}- 422 Unprocessable Entity - No Merchant
{
"error": "No merchant associated with your account"
}Credentials are encrypted using hybrid encryption (AES-256-GCM + RSA-OAEP)
Updated 13 days ago
