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:
- Get access to the system
- Configure the file send
- Access SFTP Credentials
- Configure per PSP (outside of Spreedly)
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
Once you have supplied all of this information, access can be granted to the Consolidated Settlement Reporting system
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/{merchant_id}/encrypted_credentials
Headers
| Header | Value | Description |
|---|---|---|
X-Api-Key | Bearer {jwt_token} | Your authentication token |
Content-Type | application/json | Request content type |
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 contain the date in the file name in the format
YYYY-MM-DD.
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...)
Updated about 15 hours ago
