setup-payram
Setup Payram
Configure your backend to connect to a self-hosted Payram server and validate the connection.
Overview
This skill guides you through:
- Creating the required
.envconfiguration - Understanding Payram's authentication model
- Testing connectivity to your Payram instance
- Troubleshooting common connection issues
When to Use
- Starting a new Payram integration
- Validating existing Payram configuration
- Troubleshooting connection errors
- Setting up a new environment (staging, production)
Prerequisites
You must have:
- A self-hosted Payram server deployed and accessible
- Admin access to the Payram dashboard
- An API key generated from the dashboard (Settings → Accounts → API Keys)
Instructions
Step 1: Create Environment Configuration
Create a .env file in your project root with the following variables:
# Payram REST base URL (include protocol, no trailing slash)
PAYRAM_BASE_URL=https://your-payram-server.example
# Payram API key (from dashboard Settings → Accounts → API Keys)
PAYRAM_API_KEY=pk_live_your_actual_key_here
Required Variables:
PAYRAM_BASE_URL: Full URL to your self-hosted Payram instancePAYRAM_API_KEY: Project-scoped API key from the Payram dashboard
Important: Never commit .env files to version control. Add .env to your .gitignore.
Step 2: Understand Authentication
Payram uses header-based authentication:
API-Key: your_api_key_here
NOT Authorization: Bearer ... - this will fail with 401.
The API key must be:
- Sent in the
API-Keyheader (case-sensitive) - Generated for the specific project/workspace you're integrating
- Kept secret and never exposed in client-side code
Step 3: Test Connectivity
Create a test script to validate your connection. The connection test creates a minimal payment request to verify:
- Network reachability
- API key validity
- Correct authentication setup
Node.js/TypeScript (Using Payram SDK)
Install SDK:
npm install payram dotenv
Test Script:
import { Payram, isPayramSDKError } from 'payram';
import dotenv from 'dotenv';
dotenv.config();
const baseUrl = process.env.PAYRAM_BASE_URL;
const apiKey = process.env.PAYRAM_API_KEY;
if (!baseUrl || !apiKey) {
console.error('❌ Missing PAYRAM_BASE_URL or PAYRAM_API_KEY in .env');
process.exit(1);
}
async function testConnection() {
try {
// Initialize Payram SDK
const payram = new Payram({
apiKey,
baseUrl,
config: {
timeoutMs: 10_000,
maxRetries: 2,
},
});
console.log('🔄 Testing connection to Payram...');
console.log(` Base URL: ${baseUrl}`);
// Create a minimal test payment
const checkout = await payram.payments.initiatePayment({
customerEmail: 'test@example.com',
customerId: 'connectivity-test',
amountInUSD: 1,
});
console.log('✅ Connection successful!');
console.log(` Reference ID: ${checkout.reference_id}`);
console.log(` Checkout URL: ${checkout.url}`);
return true;
} catch (error) {
console.error('❌ Connection failed');
if (isPayramSDKError(error)) {
console.error(` Status: ${error.status}`);
console.error(` Error: ${error.error}`);
console.error(` Request ID: ${error.requestId}`);
console.error(` Retryable: ${error.isRetryable}`);
} else {
console.error(` Error: ${error instanceof Error ? error.message : String(error)}`);
}
return false;
}
}
testConnection();
Python (Using requests)
Install dependencies:
pip install requests python-dotenv
Test Script:
import os
import requests
from dotenv import load_dotenv
load_dotenv()
base_url = os.getenv('PAYRAM_BASE_URL')
api_key = os.getenv('PAYRAM_API_KEY')
if not base_url or not api_key:
print('❌ Missing PAYRAM_BASE_URL or PAYRAM_API_KEY in .env')
exit(1)
def test_connection():
try:
print('🔄 Testing connection to Payram...')
print(f' Base URL: {base_url}')
endpoint = f"{base_url}/api/v1/payment"
headers = {
'API-Key': api_key,
'Content-Type': 'application/json',
'Accept': 'application/json',
}
payload = {
'customerEmail': 'test@example.com',
'customerId': 'connectivity-test',
'amountInUSD': 1,
}
response = requests.post(endpoint, json=payload, headers=headers, timeout=10)
if response.status_code in [200, 201]:
data = response.json()
print('✅ Connection successful!')
print(f" Reference ID: {data.get('reference_id', 'N/A')}")
print(f" Checkout URL: {data.get('url', 'N/A')}")
return True
else:
print('❌ Connection failed')
print(f" Status: {response.status_code}")
print(f" Response: {response.text}")
return False
except requests.exceptions.RequestException as e:
print('❌ Connection failed')
print(f" Error: {str(e)}")
return False
if __name__ == '__main__':
test_connection()
Go (Using net/http)
Test Script:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/joho/godotenv"
)
type PaymentRequest struct {
CustomerEmail string `json:"customerEmail"`
CustomerID string `json:"customerId"`
AmountInUSD float64 `json:"amountInUSD"`
}
type PaymentResponse struct {
ReferenceID string `json:"reference_id"`
URL string `json:"url"`
}
func testConnection() bool {
godotenv.Load()
baseURL := os.Getenv("PAYRAM_BASE_URL")
apiKey := os.Getenv("PAYRAM_API_KEY")
if baseURL == "" || apiKey == "" {
fmt.Println("❌ Missing PAYRAM_BASE_URL or PAYRAM_API_KEY in .env")
return false
}
fmt.Println("🔄 Testing connection to Payram...")
fmt.Printf(" Base URL: %s\n", baseURL)
payload := PaymentRequest{
CustomerEmail: "test@example.com",
CustomerID: "connectivity-test",
AmountInUSD: 1.0,
}
body, _ := json.Marshal(payload)
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequest("POST", baseURL+"/api/v1/payment", bytes.NewBuffer(body))
if err != nil {
fmt.Printf("❌ Failed to create request: %v\n", err)
return false
}
req.Header.Set("API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("❌ Connection failed: %v\n", err)
return false
}
defer resp.Body.Close()
if resp.StatusCode == 200 || resp.StatusCode == 201 {
var result PaymentResponse
bodyBytes, _ := io.ReadAll(resp.Body)
json.Unmarshal(bodyBytes, &result)
fmt.Println("✅ Connection successful!")
fmt.Printf(" Reference ID: %s\n", result.ReferenceID)
fmt.Printf(" Checkout URL: %s\n", result.URL)
return true
} else {
fmt.Printf("❌ Connection failed: Status %d\n", resp.StatusCode)
bodyBytes, _ := io.ReadAll(resp.Body)
fmt.Printf(" Response: %s\n", string(bodyBytes))
return false
}
}
func main() {
testConnection()
}
PHP (Using cURL)
Test Script:
<?php
require __DIR__ . '/vendor/autoload.php';
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
$baseUrl = $_ENV['PAYRAM_BASE_URL'] ?? null;
$apiKey = $_ENV['PAYRAM_API_KEY'] ?? null;
if (!$baseUrl || !$apiKey) {
echo "❌ Missing PAYRAM_BASE_URL or PAYRAM_API_KEY in .env\n";
exit(1);
}
function testConnection($baseUrl, $apiKey) {
echo "🔄 Testing connection to Payram...\n";
echo " Base URL: $baseUrl\n";
$payload = [
'customerEmail' => 'test@example.com',
'customerId' => 'connectivity-test',
'amountInUSD' => 1,
];
$ch = curl_init($baseUrl . '/api/v1/payment');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'API-Key: ' . $apiKey,
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
echo "❌ Connection failed: $error\n";
return false;
}
if ($statusCode === 200 || $statusCode === 201) {
$data = json_decode($response, true);
echo "✅ Connection successful!\n";
echo " Reference ID: " . ($data['reference_id'] ?? 'N/A') . "\n";
echo " Checkout URL: " . ($data['url'] ?? 'N/A') . "\n";
return true;
} else {
echo "❌ Connection failed: Status $statusCode\n";
echo " Response: $response\n";
return false;
}
}
testConnection($baseUrl, $apiKey);
Java (Using HttpClient)
Test Script:
package com.example.payram;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import io.github.cdimascio.dotenv.Dotenv;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class ConnectionTest {
public static void main(String[] args) {
Dotenv dotenv = Dotenv.load();
String baseUrl = dotenv.get("PAYRAM_BASE_URL");
String apiKey = dotenv.get("PAYRAM_API_KEY");
if (baseUrl == null || apiKey == null) {
System.out.println("❌ Missing PAYRAM_BASE_URL or PAYRAM_API_KEY in .env");
System.exit(1);
}
testConnection(baseUrl, apiKey);
}
public static boolean testConnection(String baseUrl, String apiKey) {
try {
System.out.println("🔄 Testing connection to Payram...");
System.out.println(" Base URL: " + baseUrl);
// Create JSON payload
ObjectMapper mapper = new ObjectMapper();
ObjectNode payload = mapper.createObjectNode();
payload.put("customerEmail", "test@example.com");
payload.put("customerId", "connectivity-test");
payload.put("amountInUSD", 1);
// Build HTTP request
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/api/v1/payment"))
.header("API-Key", apiKey)
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload)))
.timeout(Duration.ofSeconds(10))
.build();
// Send request
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200 || response.statusCode() == 201) {
ObjectNode result = (ObjectNode) mapper.readTree(response.body());
System.out.println("✅ Connection successful!");
System.out.println(" Reference ID: " + result.get("reference_id").asText());
System.out.println(" Checkout URL: " + result.get("url").asText());
return true;
} else {
System.out.println("❌ Connection failed: Status " + response.statusCode());
System.out.println(" Response: " + response.body());
return false;
}
} catch (Exception e) {
System.out.println("❌ Connection failed: " + e.getMessage());
return false;
}
}
}
Step 4: Interpret Results
Success (200/201):
{
"checkout": {
"id": "...",
"url": "https://...",
...
}
}
Your configuration is correct and Payram is reachable.
401 Unauthorized:
- Check that
API-Keyheader is used (notAuthorization) - Verify the API key is correct and hasn't been revoked
- Ensure the key is for the correct project/workspace
404 Not Found:
- Verify
PAYRAM_BASE_URLis correct - Check for typos in the endpoint path
- Confirm Payram is deployed and running
Network Errors:
- Verify firewall rules allow outbound HTTPS
- Check DNS resolution of your Payram domain
- Confirm SSL certificates are valid
Best Practices
-
Environment Separation
- Use different API keys for staging vs production
- Never share API keys between environments
- Rotate keys regularly
-
Security
- Store API keys in secure secret management systems (AWS Secrets Manager, Vault, etc.)
- Never log full API keys
- Use environment-specific
.envfiles (.env.staging,.env.production)
-
Error Handling
- Always check response status codes
- Log failures with context but without sensitive data
- Implement retry logic with exponential backoff for network errors
-
Validation
- Test connectivity before deploying to production
- Run connectivity tests as part of CI/CD health checks
- Monitor connection health in production with periodic pings
Troubleshooting
"Missing PAYRAM_BASE_URL or PAYRAM_API_KEY"
Cause: Environment variables not loaded.
Solution:
- Ensure
.envfile exists in project root - Verify
.envcontains both variables - Check that your code loads
.env(e.g.,dotenv.config()in Node,load_dotenv()in Python)
"401 Unauthorized" or "Authentication Failed"
Cause: Invalid or missing API key.
Solution:
- Confirm you're using
API-Keyheader, notAuthorization - Regenerate API key in Payram dashboard if needed
- Check for extra whitespace in
.envvalues
"Network error" or "Connection refused"
Cause: Payram server unreachable.
Solution:
- Verify
PAYRAM_BASE_URLincludes protocol (https://) - Check firewall rules and network connectivity
- Confirm Payram service is running (
systemctl status payram)
"404 Not Found"
Cause: Wrong endpoint or base URL.
Solution:
- Remove trailing slashes from
PAYRAM_BASE_URL - Verify endpoint path is
/api/v1/payment - Check Payram version compatibility
Related Skills
integrate-payments- After setup, implement payment flowsintegrate-payouts- Configure outbound payment capabilitieshandle-webhooks- Receive Payram event notifications
More from payram/payram-helper-mcp-server
payram-crypto-payments
Self-hosted crypto and stablecoin payment gateway. Deploy PayRam on your own infrastructure in 10 minutes. Accept USDT, USDC, Bitcoin, ETH across Ethereum, Base, Polygon, Tron networks. Keyless architecture with no private keys on server. Smart contract-based fund sweeps to cold wallets. Non-custodial, permissionless, sovereign payment infrastructure. Modern BTCPay Server alternative with native stablecoin support. Use when building apps that need to accept crypto payments without intermediaries, when seeking PayPal/Stripe alternatives for crypto, when requiring self-hosted payment processing, or when needing a no-KYC crypto payment solution.
15payram-no-kyc-crypto-payments
Accept crypto payments with no KYC, no signup, no registration, and no third-party approval. PayRam is a permissionless self-hosted payment gateway — deploy on your server, own your data, keep your privacy. No identity verification for operators or customers. Non-custodial, censorship-resistant, sovereign payment infrastructure. Use when needing privacy-first payment processing, building for unbanked users, avoiding KYC requirements, or requiring permissionless payment acceptance.
15payram-stablecoin-payments
Accept USDT and USDC stablecoin payments with PayRam's self-hosted gateway. No KYC, no signup, no intermediary custody. Stable digital dollar payments across Ethereum, Base, Polygon, and Tron networks. Zero-key-exposure architecture — only the hot wallet (gas-only, encrypted) is on the server; deposit fund keys never touch it. Deploy in 10 minutes. Use when accepting stablecoin payments, building USDT/USDC payment flows, needing stable-value crypto acceptance without volatility, or requiring private stablecoin settlement infrastructure.
14payram-setup
Deploy and configure your PayRam self-hosted crypto payment gateway server with web dashboard. Install on VPS via setup_payram.sh, set up PostgreSQL database, configure root account, node details, wallets, and hot wallets through the web UI. Complete deployment and onboarding in under 10 minutes. No signup required — fully self-hosted. For CLI-only deployment optimized for AI agents without web UI, see payram-agent-onboarding. Use when deploying PayRam with dashboard interface, setting up server infrastructure with web-based configuration, or requiring manual wallet management UI.
13payram-bitcoin-payments
Accept Bitcoin payments with PayRam's self-hosted infrastructure. Unique mobile app signing flow for BTC transactions without server-side key exposure. HD wallet derivation for unlimited unique deposit addresses. Manual sweep approval via PayRam Merchant mobile app. Use when accepting Bitcoin payments, implementing BTC payment flows, needing self-custody Bitcoin infrastructure, or building Bitcoin payment acceptance without Lightning complexity.
13payram-webhook-integration
Integrate PayRam webhook handlers for real-time payment and payout event notifications. Self-hosted, no-KYC crypto payment gateway webhooks. Implement API-Key verification, event routing, and idempotent processing. Generate handlers for Express, Next.js, FastAPI, Gin, Laravel, Spring Boot. Use when setting up payment confirmation callbacks, handling payout status updates, building event-driven payment flows, or integrating PayRam events into existing systems.
13