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:

  1. Get access to the system
  2. Configure the file send
    1. Access SFTP Credentials
    2. 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:

  1. Environment Keys for each environment where you would like access
  2. IP addresses from which you (the merchant) will be accessing the SFTP system
  3. 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.pem

Using 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

HeaderValueDescription
X-Api-KeyBearer {jwt_token}Your authentication token
Content-Typeapplication/jsonRequest 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

FieldDescription
versionAPI version
algorithmEncryption algorithm (RSA-OAEP)
encrypted_credentialsBase64-encoded encrypted SFTP credentials
generated_atTimestamp when credentials were encrypted
merchant_idYour 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 credentials

Decrypted 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

FieldDescription
uUsername
pPassword
hHost
portPort (usually 22)
dirHome directory
uriFull SFTP URI

Error Handling

Common Errors

ErrorCauseSolution
Public key is requiredMissing public key in requestInclude your public key in the request body
Invalid public keyMalformed or unsupported key formatEnsure key is valid RSA in PEM or DER format
Merchant not foundInvalid merchant IDVerify your merchant ID
UnauthorizedInvalid or expired JWT tokenRefresh your authentication token
ForbiddenAccessing another merchant's credentialsUse your own merchant ID

Decryption Errors

ErrorCauseSolution
Padding errorWrong private key or corrupted dataVerify you're using the matching private key
Auth tag mismatchData integrity failureRequest new encrypted credentials
Invalid IV lengthCorrupted initialization vectorRequest new encrypted credentials

Technical Details

Encryption

ComponentDetails
AlgorithmRSA-OAEP (PKCS#1 OAEP padding)
Minimum Key Size4096 bits
Recommended Key Size4096 bits (sufficient for credential payload)

How It Works

  1. You send your RSA public key
  2. We encrypt your credentials directly with RSA-OAEP
  3. 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...)