@biorbank/moon-sdk
v0.1.6
Published
TypeScript SDK for Moon's Card Issuing API
Maintainers
Readme
Moon Card Issuing SDK for TypeScript
A comprehensive TypeScript SDK for Moon's Card Issuing API, providing type-safe access to card management, transaction processing, and crypto funding capabilities.
Features
- 🔒 Type Safe - Full TypeScript support with auto-generated types
- 🏗️ Modular - Clean resource-based architecture
- ⚡ Modern - Built with async/await and ES modules
- 🛡️ Robust - Comprehensive error handling and retries
- 🔄 Auto Retry - Smart retry logic for transient failures
- 📖 Well Documented - Complete JSDoc documentation
- 🛡️ PCI DSS Compliant - Automatic card data masking and secure handling
- 🔐 Webhook Security - HMAC signature validation and replay protection
- ⚡ JIT Authorization - Sub-second response with rate limiting
- 🔑 API Key Management - Rotation tracking and security monitoring
- 📋 Secure Logging - Environment-aware sensitive data filtering
- 🔍 Audit Framework - Comprehensive security event tracking
- 🔓 Sensitive Data Access - Controlled access to unredacted card data for legitimate business use
⚠️ Important: Sensitive Data Access
Version 0.1.4+ includes fixed sensitive data access methods that properly return unredacted card data:
getUnredactedPAN()- Returns actual PAN (not masked)getUnredactedCVV()- Returns actual CVV (not redacted)getUnredactedPIN()- Returns actual PIN (not masked)getAllSensitiveData()- Returns all unredacted data
These methods now bypass automatic sanitization and return the raw data from the Moon API as intended. Use with extreme caution and proper authorization controls.
Installation
npm install moon-card-issuing-sdk
# or
yarn add moon-card-issuing-sdkSetup
Quick Setup (Recommended)
Use our automated setup script to get started quickly:
# Create .env file and generate secure webhook secret
npm run setup
# This will:
# 1. Copy .env.example to .env
# 2. Generate a cryptographically secure webhook secret
# 3. Show you what values to editManual Setup
1. Environment Configuration
Copy the example environment file and configure your settings:
cp .env.example .envEdit .env with your actual values:
# Required: Your Moon API key
MOON_API_KEY=your_actual_moon_api_key_here
# Required for webhooks: Secure webhook secret (16+ characters)
MOON_WEBHOOK_SECRET=your_cryptographically_secure_webhook_secret
# Environment (staging or production)
NODE_ENV=staging2. Generate Secure Webhook Secret
For webhook security, generate a cryptographically secure secret:
# Use our built-in generator
npm run setup:webhook-secret
# Or generate manually with OpenSSL
openssl rand -hex 32
# Or use Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"3. API Key Security
- Staging: Use test API keys (may start with
pk_test_) - Production: Use live API keys (may start with
pk_live_) - Never: Commit API keys to version control
- Rotate: Change keys regularly (monthly recommended)
4. Run Security Checks
Validate your setup with security checks:
# Run type checking and security validation
npm run security:check
# View security documentation
npm run security:docsQuick Start
import { MoonClient } from 'moon-card-issuing-sdk';
const moon = new MoonClient({
apiKey: 'your-api-key',
environment: 'staging' // or 'production'
});
// Create a card
const card = await moon.cards.create('card-product-id', {
amount: 100,
card_type: 'VIRTUAL'
});
// Add balance
await moon.cards.addBalance(card.id, 50);
// Get transactions
const transactions = await moon.cards.getTransactions(card.id);Configuration
Client Options
const moon = new MoonClient({
apiKey: 'your-api-key', // Required: Your Moon API key
environment: 'staging', // 'staging' | 'production'
baseURL: 'custom-url', // Optional: Override base URL
timeout: 30000, // Optional: Request timeout (ms)
retryOptions: { // Optional: Retry configuration
retries: 3,
retryDelay: 1000,
retryCondition: (error) => true
}
});Environment URLs
- Staging:
https://stagingapi.paywithmoon.com - Production:
https://api.paywithmoon.com
API Reference
Cards Resource
Create Card
const card = await moon.cards.create(cardProductId, {
amount?: number,
card_type?: 'VIRTUAL' | 'PHYSICAL',
card_currency?: 'USD' | 'MXN',
end_customer_id?: string
});Get Card
const card = await moon.cards.get(cardId);List Cards
const cards = await moon.cards.list({
currentPage: 1,
perPage: 10,
end_customer_id?: string,
include_inactive_cards?: boolean
});Add Balance
const updatedCard = await moon.cards.addBalance(cardId, amount);Card Management
// Freeze/unfreeze
await moon.cards.freeze(cardId, true); // freeze
await moon.cards.freeze(cardId, false); // unfreeze
// Activate
await moon.cards.activate(cardId);
// PIN management
const pin = await moon.cards.getPin(cardId);
await moon.cards.updatePin(cardId, '1234');
// CVV access (always redacted for PCI DSS compliance)
const cvv = await moon.cards.getCvv(cardId); // Returns { cvv: "***" }
// ⚠️ SENSITIVE DATA ACCESS - Use with extreme caution!
// These methods return UNREDACTED sensitive data for legitimate business use
// All access is heavily logged and monitored
// Get actual PAN (unredacted)
const { pan } = await moon.cards.getUnredactedPAN(cardId); // Returns actual PAN
// Get actual CVV (unredacted)
const { cvv } = await moon.cards.getUnredactedCVV(cardId); // Returns actual CVV
// Get actual PIN (unredacted)
const { pin } = await moon.cards.getUnredactedPIN(cardId); // Returns actual PIN
// Get all sensitive data at once (maximum security risk)
const sensitiveData = await moon.cards.getAllSensitiveData(cardId);
// Returns: { pan, cvv, pin, expiration, display_expiration, support_token }
// Assign cardholder
await moon.cards.assignCardholder(cardId, externalId);Transactions
// Get transactions
const transactions = await moon.cards.getTransactions(cardId, {
currentPage: 1,
perPage: 20
});
// Simulate transaction (sandbox only)
await moon.cards.simulateTransaction(cardId, {
transactionAmount: 25.00,
transactionCurrency: 'USD',
transactionType: 'AUTHORIZATION',
merchantName: 'Test Store',
merchantCountryCode: 'US'
});Card Products
const products = await moon.cards.getCardProducts({
currentPage: 1,
perPage: 10
});Webhooks Resource
Register Webhook
// Register a webhook endpoint
await moon.webhooks.register({
url: 'https://your-app.com/webhooks/moon'
});
// Validate webhook URL before registration
const validation = moon.webhooks.validateWebhookUrl('https://your-app.com/webhooks/moon');
if (!validation.isValid) {
console.error('Invalid webhook URL:', validation.error);
}Delete Webhook
// Delete registered webhook
await moon.webhooks.delete();Complete Webhook Integration
import { MoonClient, createWebhookMiddleware, createWebhookConfig } from 'moon-card-issuing-sdk';
import express from 'express';
const moon = new MoonClient({
apiKey: 'your-api-key',
environment: 'staging'
});
const app = express();
// 1. Register webhook endpoint
await moon.webhooks.register({
url: 'https://your-app.com/webhooks/moon'
});
// 2. Set up webhook handler with security validation
const config = createWebhookConfig(process.env.MOON_WEBHOOK_SECRET!);
app.post('/webhooks/moon',
createWebhookMiddleware(config, async (payload) => {
console.log('Secure webhook received:', payload.type);
// Handle different event types
switch (payload.type) {
case 'CARD_TRANSACTION':
// Handle card transaction
break;
case 'CARD_DECLINE':
// Handle declined transaction
break;
case 'MOON_CREDIT_FUNDS_CREDITED':
// Handle funds credited
break;
}
})
);Gift Cards Resource
Purchase Gift Card
// Purchase a gift card
const giftCard = await moon.giftCards.purchase({
card_product_id: 'product-id',
amount: '50.00'
});
// Validate purchase amount before buying
const product = await moon.giftCards.getProducts();
const validation = moon.giftCards.validatePurchaseAmount(product.data[0], 50);
if (!validation.isValid) {
console.error('Invalid amount:', validation.error);
}Get Gift Card Details
// Get gift card by ID (returns masked sensitive data)
const giftCard = await moon.giftCards.get('gift-card-id');
console.log('Gift card value:', giftCard.value);
console.log('Barcode:', giftCard.barcode);
// PIN and security code are masked for security: "***"Manage Gift Card Usage
// Mark as used
await moon.giftCards.markAsUsed('gift-card-id');
// Mark as unused
await moon.giftCards.markAsUnused('gift-card-id');
// Custom usage status
await moon.giftCards.updateUsageStatus('gift-card-id', {
marked_used: true
});Browse Gift Card Products
// Get all available products
const products = await moon.giftCards.getProducts({
currentPage: 1,
perPage: 10
});
// Filter by category
const retailProducts = await moon.giftCards.getProductsByCategory('retail');
// Filter by merchant
const amazonCards = await moon.giftCards.getProductsByMerchant('Amazon');Calculate Costs
// Calculate total cost including fees and discounts
const product = products.data[0];
const calculation = moon.giftCards.calculateTotalCost(product, 100);
console.log('Gift card amount:', calculation.giftCardAmount);
console.log('Fee amount:', calculation.feeAmount);
console.log('Total cost:', calculation.totalCost);
console.log('Final amount (after discount):', calculation.finalAmount);Cardholders Resource
Create and Manage Cardholders
// Create a new cardholder
const cardholder = await moon.cardholders.create({
email: '[email protected]',
external_id: 'EMP-12345',
organization_id: 'org-id'
});
// Get cardholder by ID
const cardholder = await moon.cardholders.get('cardholder-id');
// Update cardholder information
const updatedCardholder = await moon.cardholders.update('cardholder-id', {
external_id: 'NEW-EMP-67890'
});List and Search Cardholders
// List all cardholders with pagination
const cardholders = await moon.cardholders.list({
currentPage: 1,
perPage: 10,
organization_id: 'org-id'
});
// Find cardholders by external ID
const found = await moon.cardholders.findByExternalId('EMP-12345');
// Find cardholders by email
const byEmail = await moon.cardholders.findByEmail('[email protected]');Two-Factor Authentication
// Step 1: Request login code
await moon.cardholders.requestLoginCode({
email: '[email protected]',
organization_id: 'org-id'
});
// Step 2: Redeem code for token
const session = await moon.cardholders.redeemLoginCode({
email: '[email protected]',
code: '123456',
organization_id: 'org-id'
});
// Use the JWT token for authenticated requests
console.log('Token:', session.token);
console.log('Expires:', session.expiresAt);
// Complete authentication flow with convenience method
const auth = await moon.cardholders.authenticate(
'[email protected]',
'org-id',
async () => {
// This function should prompt user for the 6-digit code
// and return it (e.g., from a form input)
return prompt('Enter the 6-digit code from your email:') || '';
}
);Cardholder-Card Associations
// Get all cards for a cardholder
const cards = await moon.cardholders.getCards('cardholder-id', {
currentPage: 1,
perPage: 10,
include_inactive_cards: false
});
// Check if cardholder has active cards
const hasCards = await moon.cardholders.hasActiveCards('cardholder-id');
// Get total card count
const cardCount = await moon.cardholders.getCardCount('cardholder-id', true);Utility Methods
// Update external ID
await moon.cardholders.updateExternalId('cardholder-id', 'NEW-ID-123');
// Move to different organization
await moon.cardholders.moveToOrganization('cardholder-id', 'new-org-id');
// Update KYC status
await moon.cardholders.updateKYCStatus('cardholder-id', true);Error Handling
The SDK provides specific error types for different scenarios:
import {
MoonError,
MoonAPIError,
MoonNetworkError,
MoonValidationError,
MoonRateLimitError,
MoonAuthenticationError,
MoonNotFoundError
} from 'moon-card-issuing-sdk';
try {
const card = await moon.cards.get('invalid-id');
} catch (error) {
if (error instanceof MoonNotFoundError) {
console.log('Card not found');
} else if (error instanceof MoonAuthenticationError) {
console.log('Invalid API key');
} else if (error instanceof MoonRateLimitError) {
console.log('Rate limited, retry after:', error.retryAfter);
} else if (error instanceof MoonAPIError) {
console.log('API error:', error.status, error.message);
}
}TypeScript Support
The SDK is fully typed with auto-generated interfaces:
import { Card, Transaction, CardProduct } from 'moon-card-issuing-sdk';
// All responses are properly typed
const card: Card = await moon.cards.get('card-id');
const transactions: PaginatedResponse<Transaction> = await moon.cards.getTransactions('card-id');Pagination
List methods return paginated responses:
interface PaginatedResponse<T> {
data: T[];
pagination: {
currentPage: number;
from: number;
lastPage: number;
perPage: number;
total: number;
};
}Development
Building
npm run build # Build for production
npm run dev # Build in watch modeTesting
npm test # Run tests
npm run test:watch # Run tests in watch modeType Generation
npm run generate-types # Regenerate types from API schemasExamples
Check the examples directory for complete usage examples:
- Basic Usage - Card creation and management with PCI DSS compliance
- Secure Integration - Complete security implementation
- Webhook Management - Secure webhook integration
- Gift Card Management - Complete gift card lifecycle
- CVV Access - Secure CVV retrieval with audit logging
- Sensitive Data Access - Unredacted sensitive data access with security controls
Running Examples
Use the built-in npm scripts to run examples easily:
# Run basic card management example
npm run example:basic
# Run complete security integration example (includes webhook server)
npm run example:secure
# Run webhook management example
npm run example:webhooks
# Run gift card management example
npm run example:gift-cards
# Run CVV access example with security compliance
npm run example:cvv
# Run sensitive data access example (⚠️ UNREDACTED data)
npm run example:sensitiveMake sure to configure your .env file first with:
npm run setup🛡️ Security Features
This SDK implements comprehensive security measures for production use:
PCI DSS Compliance
- Default Security: Automatic card data masking (PAN shows only last 4 digits)
- CVV Protection: CVV always redacted in standard responses
- PIN Protection: PIN never shown in production environment
- Secure Logging: No sensitive data in standard logs
⚠️ Sensitive Data Access
For legitimate business operations, the SDK provides secure access to unredacted sensitive data:
Standard (Secure) Methods
// These methods return REDACTED/MASKED data for safety
const card = await moon.cards.get(cardId); // PAN: ****1234
const cvv = await moon.cards.getCvv(cardId); // CVV: ***
const pin = await moon.cards.getPin(cardId); // PIN: [REDACTED]Sensitive Data Access Methods
// ⚠️ These methods return ACTUAL unredacted data - use with extreme caution!
const { pan } = await moon.cards.getUnredactedPAN(cardId); // Actual PAN
const { cvv } = await moon.cards.getUnredactedCVV(cardId); // Actual CVV
const { pin } = await moon.cards.getUnredactedPIN(cardId); // Actual PIN
// Maximum risk: Get all sensitive data at once
const all = await moon.cards.getAllSensitiveData(cardId); // All unredactedSecurity Measures for Sensitive Access
- Heavy Audit Logging: All access logged with
[CRITICAL-AUDIT]tags - Environment Tracking: Logs include environment information
- Timestamp Recording: Precise access time tracking
- Card ID Masking: Even audit logs mask card identifiers
- Access Warnings: Clear warnings about data sensitivity
Implementation Details
The sensitive data access methods use existing Moon API endpoints:
- PAN & CVV: Retrieved from the regular card endpoint (
/v1/api-gateway/card/{id}) - PIN: Retrieved from the dedicated PIN endpoint (
/v1/api-gateway/card/{id}/pin) - All Data: Combines multiple endpoint calls for comprehensive access
This approach ensures compatibility with the actual Moon API while maintaining security logging and audit trails.
⚠️ CRITICAL SECURITY WARNING
The sensitive data access methods (getUnredactedPAN, getUnredactedCVV, getUnredactedPIN, getAllSensitiveData) return ACTUAL UNREDACTED sensitive data. This represents a maximum security risk and should only be used for legitimate business purposes.
Before using these methods:
- Ensure you have proper authorization/permissions
- Implement additional authentication (2FA, biometric, etc.)
- Use environment-based access controls
- Never log the returned sensitive values
- Clear sensitive data from memory after use
- Monitor and audit all access attempts
- Consider if you truly need unredacted data vs. masked data
Webhook Security
- HMAC-SHA256 signature validation
- Replay attack protection with timestamp validation
- Automatic idempotency handling
- Secure error handling
JIT Authorization
- Sub-second response time enforcement
- Rate limiting per card
- Request validation and sanitization
- Comprehensive audit logging
API Key Management
- Automatic rotation tracking
- Environment-specific security recommendations
- Usage monitoring and analytics
- Secure key masking for logs
See SECURITY.md for complete security documentation.
Webhook Support
Full webhook security implementation with HMAC validation:
import { createWebhookMiddleware, createWebhookConfig } from 'moon-card-issuing-sdk';
// CRITICAL: Never use hardcoded secrets or fallbacks
if (!process.env.MOON_WEBHOOK_SECRET) {
throw new Error('MOON_WEBHOOK_SECRET environment variable is required');
}
const config = createWebhookConfig(process.env.MOON_WEBHOOK_SECRET);
app.post('/webhooks/moon',
createWebhookMiddleware(config, async (payload) => {
console.log('Secure webhook received:', payload.type);
})
);API Coverage
Implemented Resources
- ✅ Cards - Full CRUD, balance management, transactions, PIN management
- ✅ Webhooks - Register and delete webhook endpoints with security validation
- ✅ Gift Cards - Purchase, manage, and track branded gift cards
- ✅ Cardholders - Identity management, two-factor authentication, card associations
- ⏳ Invoices - Coming soon
- ⏳ Velocity Controls - Coming soon
- ⏳ Organizations - Coming soon
- ⏳ Accounts - Coming soon
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Make your changes and add tests
- Run tests:
npm test - Commit changes:
git commit -m 'Add feature' - Push to the branch:
git push origin feature-name - Submit a pull request
License
MIT License - see LICENSE file for details.
