npm.io
1.0.4 • Published 5d ago

@ghostpay/sdk

Licence
MIT
Version
1.0.4
Deps
8
Size
824 kB
Vulns
0
Weekly
0

@ghostpay/sdk

Decentralized P2P Cryptocurrency Payment SDK with Mesh Network & Privacy

MIT License TypeScript Tests


Table of Contents


Quick Start

import { Wallet, Checkout, WebhookClient } from '@ghostpay/sdk';

// 1. Create wallet
const wallet = new Wallet();
wallet.generateMnemonic();
console.log('BTC Address:', wallet.getAddress('bitcoin'));

// 2. Setup checkout (hosted mode - transactions via Ghost Pay hosted page)
const checkout = Checkout.fromJSON({
  receiver: { name: 'My Store', email: 'pay@mystore.com' },
  mode: 'fixed',
  fixedAmount: 25.00,
  fixedCurrency: 'USD',
  transactionMode: 'hosted', // Default - uses hosted payment page
  webhookUrl: 'https://mystore.com/api/ghostpay',
  webhookSecret: 'my-secret-key',
});

// 3. Generate payment link
const link = checkout.generatePaymentLink(
  'bc1q...',
  undefined,
  process.env.GHOSTPAY_SIGNING_KEY
);
// → https://ghostpay-systems.vercel.app/payment?receiver=...&amount=25&sig=...

Installation

npm install @ghostpay/sdk
CDN / Script Tag
<script src="https://unpkg.com/@ghostpay/sdk/dist/umd/ghostpay-sdk.js"></script>
<script>
  const wallet = new GhostPaySDK.Wallet();
</script>

Architecture

@ghostpay/sdk
├── core/
│   ├── crypto.ts       # SHA-256, RIPEMD-160, secp256k1, AES-256-GCM
│   ├── chains.ts       # Multi-chain configs (BTC, ETH, SOL, Polygon, BSC)
│   ├── wallet.ts       # BIP39/BIP32 HD wallet
│   ├── checkout.ts     # Payment pages: fixed, plans, custom, catalog
│   ├── webhook.ts      # Webhook notifications + verification
│   ├── privacy.ts      # Stealth addresses, Pedersen, Ring, CoinJoin, ZK
│   ├── network.ts      # WebSocket signaling + WebRTC mesh
│   ├── transaction.ts  # TX builder, signer, validator, SPV
│   ├── pow.ts          # Hashcash anti-spam, rate limiting
│   └── storage.ts      # Encrypted storage, sessions
└── types/
    └── index.ts        # TypeScript interfaces
Dependencies
Package Purpose
@noble/hashes SHA-256, RIPEMD-160, HMAC
@noble/secp256k1 ECDSA signatures
@scure/bip39 BIP39 mnemonic (2048 words)
@scure/bip32 HD key derivation
@scure/base Hex, Base58, Bech32 encoding

API Reference

Wallet
import { Wallet, createWallet, importWallet, validateMnemonic, generateNewMnemonic } from '@ghostpay/sdk';
new Wallet()

Creates a new empty wallet instance.

wallet.generateMnemonic(wordCount?: 12 | 24): string

Generates a new BIP39 mnemonic and derives all chain addresses.

const wallet = new Wallet();
const mnemonic = wallet.generateMnemonic(); // 12 words
wallet.importMnemonic(mnemonic: string): void

Imports wallet from an existing BIP39 mnemonic.

wallet.getAddress(chain: ChainId): string

Returns the address for a specific chain.

wallet.getAddress('bitcoin');   // bc1q...
wallet.getAddress('ethereum');  // 0x...
wallet.getAddress('solana');    // Base58...
wallet.exportEncrypted(password: string): Promise<string>

Exports wallet encrypted with a password (PBKDF2 600k iterations + AES-256-GCM).

const encrypted = await wallet.exportEncrypted('my-secure-password');
Wallet.importEncrypted(encryptedJson: string, password: string): Promise<Wallet>

Static method to import an encrypted wallet.

wallet.sign(data: Uint8Array, chain: ChainId): Promise<Uint8Array>

Sign data with real secp256k1 ECDSA signature.

const signature = await wallet.sign(messageHash, 'bitcoin');

Checkout

Configurable payment pages: fixed amount, plans, custom amount, or product catalog. Supports hosted mode (via Ghost Pay payment page) or local mode (custom URI scheme).

import { Checkout, createFixedCheckout, createPlanCheckout, createCustomCheckout, createCatalogCheckout } from '@ghostpay/sdk';
Transaction Modes
Mode Description
hosted (default) Transactions are processed via the hosted Ghost Pay payment page at https://ghostpay-systems.vercel.app/payment
local Transactions use the ghostpay:payment? URI scheme for local processing
const checkout = Checkout.fromJSON({
  receiver: { name: 'My Store' },
  mode: 'fixed',
  fixedAmount: 25.00,
  fixedCurrency: 'USD',
  transactionMode: 'hosted', // or 'local'
  hostedPaymentUrl: 'https://custom-domain.com/payment', // optional override
});
Fixed Amount
const checkout = Checkout.fromJSON({
  receiver: { name: 'My Store', email: 'pay@mystore.com' },
  mode: 'fixed',
  fixedAmount: 25.00,
  fixedCurrency: 'USD',
  supportedChains: ['bitcoin', 'ethereum'],
});
Plans
const checkout = Checkout.fromJSON({
  receiver: { name: 'My Store' },
  mode: 'plans',
  plans: [
    { id: 'monthly', name: 'Monthly', price: 12.00, currency: 'USD', period: '/mo' },
    { id: 'annual', name: 'Annual', price: 10.00, currency: '/mo', period: '/mo', selected: true },
  ],
});
Custom Amount
const checkout = createCustomCheckout({ name: 'My Store' }, 'USD');
const link = checkout.generatePaymentLink('bc1q...', 50.00);
Catalog Mode

Product catalog for small stores. Users select products and quantities via a modal.

import { createCatalogCheckout } from '@ghostpay/sdk';

const checkout = createCatalogCheckout(
  { name: 'My Store' },
  [
    { id: '1', name: 'T-Shirt', price: 25.00, image: 'https://...', description: 'Cotton, sizes S-XL' },
    { id: '2', name: 'Cap', price: 15.00, image: 'https://...', description: 'Adjustable' },
    { id: '3', name: 'Sticker Pack', price: 5.00, inStock: false },
  ]
);

// Add items to cart
checkout.addCatalogItem('1', 2);  // 2x T-Shirt = $50
checkout.addCatalogItem('2', 1);  // 1x Cap = $15

// Check total
console.log(checkout.catalogTotal);  // 65
console.log(checkout.selectedItems); // [{ product: ..., quantity: 2 }, ...]

// Update quantity
checkout.setCatalogItemQuantity('1', 3);  // 3x T-Shirt = $75

// Remove item
checkout.removeCatalogItem('2');

// Clear cart
checkout.clearCatalog();

// Generate payment link
const link = checkout.generatePaymentLink('bc1q...');
Catalog Types
interface CatalogProduct {
  id: string;
  name: string;
  description?: string;
  price: number;
  currency?: string;    // Default: 'USD'
  image?: string;       // URL to product image
  category?: string;
  inStock?: boolean;    // Default: true
}

interface CatalogItem {
  product: CatalogProduct;
  quantity: number;
}
CheckoutConfig (Catalog)
const checkout = Checkout.fromJSON({
  receiver: { name: 'My Store' },
  mode: 'catalog',
  catalogProducts: [
    { id: '1', name: 'T-Shirt', price: 25.00, image: 'https://...' },
    { id: '2', name: 'Cap', price: 15.00 },
  ],
  supportedChains: ['bitcoin', 'ethereum'],
});
CheckoutData (Catalog)
const data = checkout.buildCheckoutData('bc1q...');
// data.catalogItems = [
//   { product: { id: '1', name: 'T-Shirt', price: 25.00 }, quantity: 2 },
// ]
// data.amount = 50.00
checkout.generatePaymentLink(address, amount?, signingKey?): string

Generates a payment link with optional HMAC signature.

// Hosted mode (default)
const link = checkout.generatePaymentLink('bc1q...', undefined, 'my-signing-key');
// → https://ghostpay-systems.vercel.app/payment?receiver=...&amount=25&sig=hmac-sha256...

// Local mode
const localCheckout = new Checkout({
  receiver: { name: 'My Store' },
  mode: 'fixed',
  fixedAmount: 25.00,
  transactionMode: 'local',
});
const localLink = localCheckout.generatePaymentLink('bc1q...');
// → ghostpay:payment?receiver=...&amount=25
checkout.openPaymentPage(address, amount?, signingKey?): string

Opens the payment page in a new browser tab (hosted mode only).

checkout.openPaymentPage('bc1q...'); // Opens new tab with hosted payment page
checkout.notifyWebhook(event, data): Promise<{ success, statusCode }>

Send webhook notification when payment status changes.

await checkout.notifyWebhook('payment.confirmed', {
  txHash: 'abc123...',
  amount: 2500000000n,
  from: '1A1zP1...',
  to: '1BvBMSE...',
  confirmations: 3,
});

Webhook

Stateless webhook notifications for payment confirmations.

import { WebhookClient, WebhookVerifier } from '@ghostpay/sdk';
Sending Webhooks
const client = new WebhookClient({
  url: 'https://mystore.com/api/ghostpay-webhook',
  secret: 'my-webhook-secret',
});

const result = await client.notify('payment.confirmed', {
  txHash: 'abc123...',
  chain: 'bitcoin',
  amount: 2500000000n,
  currency: 'USD',
  from: '1A1zP1...',
  to: '1BvBMSE...',
  confirmations: 3,
  receiver: 'My Store',
  plan: 'monthly',
  nonce: '...',
});
// { success: true, statusCode: 200 }
Verifying Webhooks (Server-Side)
import { WebhookVerifier } from '@ghostpay/sdk';

const verifier = new WebhookVerifier('my-webhook-secret');

// In your webhook handler:
app.post('/ghostpay-webhook', (req, res) => {
  const signature = req.headers['x-ghostpay-signature'];
  const isValid = verifier.verify(req.body, signature);

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process payment confirmation
  const { event, transaction, checkout } = req.body;
  res.status(200).json({ received: true });
});
Webhook Payload
{
  "event": "payment.confirmed",
  "transaction": {
    "hash": "abc123...",
    "chain": "bitcoin",
    "amount": "2500000000",
    "currency": "USD",
    "from": "1A1zP1...",
    "to": "1BvBMSE...",
    "confirmations": 3
  },
  "checkout": {
    "receiver": "My Store",
    "plan": "monthly",
    "nonce": "..."
  },
  "timestamp": 1719123456789,
  "signature": "hmac-sha256-signature"
}

Privacy

5 privacy techniques to hide transaction traces on blockchain.

import {
  StealthAddressGenerator,
  PedersenCommitmentEngine,
  RingSignatureEngine,
  CoinJoinEngine,
  PrivacyManager,
} from '@ghostpay/sdk';
Stealth Addresses

One-time addresses per transaction — real address never exposed on-chain.

const stealth = StealthAddressGenerator.generate(recipientPubKey);
// { stealthPubKey, ephemeralPubKey, viewTag, address }

// Generate batch
const stealths = StealthAddressGenerator.generateBatch(recipientPubKey, 10);

// Check ownership
const belongs = StealthAddressGenerator.belongsTo(stealth, viewKey, spendKey);
Pedersen Commitments

Hide transaction amounts (C = vG + rH).

const commitment = PedersenCommitmentEngine.commit(2500000000n);
// { commitment, blindingFactor, value }

// Verify
const valid = PedersenCommitmentEngine.verify(commitment);

// Homomorphic addition
const sum = PedersenCommitmentEngine.add(commitment1, commitment2);

// Range proof (proves value is valid without revealing)
const rangeProof = PedersenCommitmentEngine.createRangeProof(commitment, 64);
Ring Signatures

Sign as part of a group — impossible to determine who signed.

const ringSig = RingSignatureEngine.sign(
  message,
  signerPrivateKey,
  publicKeys, // Ring members
  0           // Signer index
);

// Verify
const valid = RingSignatureEngine.verify(ringSig);

// With decoy keys
const ringSig = RingSignatureEngine.signWithDecoys(
  message, signerKey, allPublicKeys, signerIndex, 11
);
CoinJoin Mixing

Pool transactions to break traceability.

const round = CoinJoinEngine.createRound(inputs, fee);

// Sign
await CoinJoinEngine.signRound(round.id, inputIndex, privateKey);

// Verify
const valid = CoinJoinEngine.verifyRound(round.id);

// Finalize
const finalized = CoinJoinEngine.finalizeRound(round.id);
Privacy Manager

Orchestrates all privacy features.

const pm = new PrivacyManager({
  useStealthAddresses: true,
  usePedersenCommitments: true,
  useRingSignatures: true,
  useCoinJoin: true,
  ringSize: 11,
});

const result = await pm.applyPrivacy({
  senderPrivKey,
  recipientPubKey,
  amount: 2500000000n,
  inputs: [...],
  ringPublicKeys: [...],
  senderIndex: 0,
});
// { stealthAddress, commitment, rangeProof, ringSignature, coinJoinRound, privateOutputs }

Network
import { MeshNetwork } from '@ghostpay/sdk';
new MeshNetwork(config?)
const network = new MeshNetwork({
  signalingUrl: 'wss://signal.ghostpay.dev',
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
  maxPeers: 50,
});
network.start(): Promise<void>
await network.start();
network.broadcastTransaction(tx: Transaction): string
const txHash = network.broadcastTransaction(tx);
Events
network.on('peer:connected', (event) => console.log('Peer:', event.data));
network.on('transaction:received', (event) => console.log('TX:', event.data));
Mesh Intent Manager

Use this API when you want to create, list, receive, and sync payment intents as first-class mesh events without reaching into the internal network classes.

import { MeshIntentManager } from '@ghostpay/sdk';

const mesh = new MeshIntentManager();

mesh.on('mesh:intent-created', (event) => {
  console.log('Intent created:', event.data);
});

const intent = mesh.create({
  receiver: 'My Store',
  amount: 49.99,
  currency: 'USD',
  chain: 'bitcoin',
  address: 'bc1q...',
  nonce: 'abc123',
  nodeId: 'local-node-1',
});

const allIntents = mesh.list();
mesh.sync(intent.id);

Events:

  • mesh:intent-created
  • mesh:intent-received
  • mesh:intent-synced

Transaction
import { TransactionBuilder, TransactionSigner, TransactionValidator, TransactionSerializer, SPVVerifier } from '@ghostpay/sdk';
TransactionBuilder
const tx = new TransactionBuilder('bitcoin')
  .addInput('txid_abc', 0, 100000n)
  .addOutput('bc1q...', 99000n)
  .setFee(1000n)
  .build();

// Build with privacy
const { transaction, privacy } = await tx.buildWithPrivacy({
  senderPrivKey, recipientPubKey, ringPublicKeys, senderIndex: 0,
});
TransactionSigner
const signed = await TransactionSigner.signAll(tx, privateKey);
const valid = TransactionSigner.verify(signed, 0);
TransactionSerializer
const hex = TransactionSerializer.serialize(tx);
const deserialized = TransactionSerializer.deserialize(hex);

Storage
import { SecureStorage, SessionManager, WalletStorage } from '@ghostpay/sdk';
SecureStorage
const storage = createSecureStorage();
await storage.init('password'); // First time
await storage.unlock('password'); // Subsequent
await storage.set('key', { secret: 'value' });
const data = await storage.get('key');
SessionManager
const sessionManager = createSessionManager(storage);
const sessionId = await sessionManager.createSession('wallet-id', 3600000);
const session = await sessionManager.getSession();

Proof of Work
import { PoWEngine, Hashcash, SpamPrevention } from '@ghostpay/sdk';
const engine = new PoWEngine();
const proof = await engine.generateProof(transactionData, 16);
const result = await engine.verifyProof(transactionData, proof.token);

Crypto
import {
  sha256, hash256, hash160, hmacSha256,
  aesEncrypt, aesDecrypt, pbkdf2DeriveKey,
  secp256k1GetPublicKey, secp256k1Sign, secp256k1Verify,
  bytesToHex, hexToBytes, randomBytes, constantTimeCompare,
} from '@ghostpay/sdk';
Hash Functions
const hash = sha256('hello');
const hash = hash256(data);        // Double SHA-256
const hash = hash160(publicKey);   // RIPEMD160(SHA256(key))
AES-256-GCM
const key = await generateAESKey();
const encrypted = await aesEncrypt({ secret: 'data' }, key);
const decrypted = await aesDecrypt(encrypted, key);
PBKDF2 (600,000 iterations)
const { key, salt } = await pbkdf2DeriveKey('password');
secp256k1
const pubKey = secp256k1GetPublicKey(privKey, true);
const sig = await secp256k1Sign(messageHash, privKey);
const valid = secp256k1Verify(messageHash, sig, pubKey);

Chains
import { CHAINS, getChainConfig, generateAddress } from '@ghostpay/sdk';

const btcConfig = getChainConfig('bitcoin');
const address = generateAddress('bitcoin', publicKey);

Supported Chains

Chain Symbol Derivation Path Address Format Decimals
Bitcoin BTC m/44'/0'/0'/0/0 Bech32 (bc1q...) 8
Ethereum ETH m/44'/60'/0'/0/0 EIP-55 (0x...) 18
Solana SOL m/44'/501'/0' Base58 9
Polygon MATIC m/44'/60'/0'/0/0 EVM (0x...) 18
BNB Chain BNB m/44'/60'/0'/0/0 EVM (0x...) 18

Testing

npm test              # Run all 104 tests
npm run test:watch    # Watch mode
npm run test:coverage # Coverage report
Test Structure
tests/
├── crypto.test.ts      # 17 tests - Hash, AES, PBKDF2, secp256k1
├── wallet.test.ts      # 15 tests - BIP39, HD derivation, encrypt/decrypt
├── transaction.test.ts # 11 tests - Builder, serializer, multi-input/output
├── pow.test.ts         # 12 tests - Hashcash, spam prevention, difficulty
├── storage.test.ts     # 17 tests - SecureStorage, sessions, wallets
├── checkout.test.ts    # 16 tests - Checkout modes, payment links, catalog
└── integration.test.ts # 16 tests - Full integration tests

Building

npm run build          # Build all formats (ESM + CJS + UMD + Types)
npm run build:esm      # ES Modules only
npm run build:cjs      # CommonJS only
npm run build:umd      # UMD bundle only
npm run build:types    # TypeScript declarations only
npm run typecheck      # Type checking without emit
npm run lint           # ESLint
Output Structure
dist/
├── esm/index.js           # ES Modules (tree-shakeable)
├── cjs/index.cjs.js       # CommonJS
├── umd/ghostpay-sdk.js    # UMD bundle (minified)
└── types/index.d.ts       # TypeScript declarations

Security

Tamper Protection (Anti-Modification)

The SDK implements 3 layers of defense against payment amount tampering:

Layer Mechanism Protection
1. HMAC Signatures generatePaymentLink(address, amount, signingKey) Payment link is signed with HMAC-SHA256. Payment page verifies before display.
2. Link Expiration timestamp + ttl params in every link Links expire after 5 minutes (default). Prevents replay attacks.
3. Blockchain Verification isSecurelyConfirmed(txHash, chain) On-chain confirmation with minimum thresholds per chain.

Critical: Always pass signingKey to generatePaymentLink():

const checkout = new Checkout({
  receiver: { name: 'Loja' },
  plans: [{ id: 'basic', name: 'Plano Basico', price: 29.99, currency: 'USD' }],
});

// ❌ INSEGURO — sem signingKey, pode ser adulterado
const link = checkout.generatePaymentLink(address);

// ✅ SEGURO — com signingKey, verificacao HMAC
const link = checkout.generatePaymentLink(address, undefined, 'my-secret-key');
Confirmation Thresholds

Payments are only confirmed when the blockchain reaches minimum confirmations:

Chain Minimum Confirmations
Bitcoin 3
Ethereum 12
Solana 32
Polygon 128
BSC 15
const broadcaster = new BlockchainBroadcaster();
const result = await broadcaster.isSecurelyConfirmed(txHash, 'bitcoin');
if (result.secure) {
  // Payment is settled with 3+ BTC confirmations
}
On-Chain Amount Verification
const amount = await broadcaster.getTransactionAmount(txHash, 'ethereum');
if (amount && amount >= expectedAmount) {
  // Amount verified on-chain
}
Security Audit Fixes Applied
Issue Fix
Fake EC operations in privacy module Real secp256k1 via @noble/secp256k1
sign() returning hash instead of signature Real secp256k1 ECDSA signing
PBKDF2 with 2048 iterations Increased to 600,000
Timing leak in constantTimeCompare Constant-time comparison
PBKDF2 hash mismatch (SHA-256 vs SHA-512) Unified to SHA-256
innerHTML XSS vulnerabilities escapeHtml() + textContent
Password stored in sessionStorage Removed, kept in memory only
Unsigned payment links HMAC-SHA256 signature
Math.random() in nonce generation crypto.getRandomValues()
base58Decode leading zeros bug Fixed zero restoration
seenTransactions memory leak LRU pruning with timestamps
Session overwrite Multiple concurrent sessions
PBKDF2 minimum iterations not enforced Validation in pbkdf2DeriveKey()
Signaling messages not validated Schema + payload validation
P2P data channel no validation Gossip message schema validation
console.error leaks internal state Sanitized error logging
Wallet export exposes raw mnemonic Deprecation warning added
Payment page ignores HMAC HMAC verification added
No link expiration timestamp + TTL params
localStorage-only tx detection Blockchain RPC polling
No minimum confirmations Per-chain thresholds enforced
Best Practices
  • Passwords: Never stored in plaintext, always encrypted with PBKDF2 + AES-256-GCM
  • Signing: Real secp256k1 ECDSA, not hash placeholders
  • Privacy: Real elliptic curve math for Stealth, Pedersen, Ring, CoinJoin
  • Webhooks: HMAC-SHA256 signed, constant-time verification
  • Input Validation: Transaction deserialization validates all fields
  • Payment Links: Always use signingKey — unsigned links are vulnerable to tampering
  • Confirmation: Use isSecurelyConfirmed() — never trust unconfirmed transactions

License

MIT License - See LICENSE for details.

Keywords