@spacepay/client-sdk
v1.0.26
Published
Lightweight SDK for SpacePay-like API - works in Node 18+ and modern browsers
Maintainers
Readme
SpacePay Client SDK
A lightweight, TypeScript-first SDK for integrating with SpacePay-like crypto payment gateways. Works in Node.js 18+ and modern browsers.
Features
- 🚀 Lightweight - No heavy dependencies, minimal bundle size
- 🔒 Type-safe - Full TypeScript support with comprehensive type definitions
- 🌐 Universal - Works in Node.js 18+ and modern browsers
- ⚡ Modern - Uses native fetch API and modern JavaScript features
- 🛡️ Robust - Built-in error handling, timeout management, and retry logic
This package focuses on external merchant APIs: secret-key payments (server-side) and payment-secret flows (browser-safe checkout client).
Installation
yarn add @spacepay/client-sdkBackend (Node.js)
import { createBackendClient } from '@spacepay/client-sdk/backend'
const client = createBackendClient({
apiBaseUrl: 'https://api.app.spacepay.solutions', // optional
publicKey: 'your_public_key_here',
secretKey: 'your_secret_key_here', // make sure to never expose this key on frontend
})
// Create a payment
const payment = await client.createPayment({
amount: 5000, // 5000 cents = $50.00
currency: 'USD',
orderId: 'order_123',
})
console.log('Payment:', payment)
console.log('Payment URL:', payment.paymentUrl)
console.log('Payment Secret:', payment.secret)
console.log('Payment ID:', payment.paymentId)Frontend (TypeScript / bundlers)
Hosted Checkout
For the most simple integration, redirect the user to paymentUrl returned from the createPayment request. After the payment completes, the user is redirected back to the success URL you set when creating the payment. For this flow, you do not need to load the frontend SDK at all.
Embedded Checkout
You can embed the SpacePay checkout UI into your own checkout page. We support 2 options, simple payment button, or full payment modal experience.
You can render a create order button and lazily create the payment on first click using fetchPaymentContext:
import { initPayment } from '@spacepay/client-sdk/frontend'
const checkoutButton = await initPayment({
mode: 'embedded-button', // embedded-button, embedded-checkout, redirect
fetchPaymentContext: async () => {
// Called the first time the user clicks the \"Pay with crypto\" button
const res = await fetch('/api/create-spacepay-order', { method: 'POST' })
const { paymentId, paymentSecretKey } = await res.json()
return { paymentId, paymentSecretKey }
},
onSuccess: (payload) => {
// Called only in embedded-button and embedded-checkout modes
console.log('Payment success', payload)
},
})
checkoutButton.mount('#spacepay-checkout')Alternatively, you can manage order lifecycle yourself and pass the payment details to the SDK:
import {
initEmbeddedCheckoutButton,
initEmbeddedCheckoutModal,
} from '@spacepay/client-sdk/frontend'
// Option 1 - Embedded button (inline iframe)
const checkoutButton = await initEmbeddedCheckoutButton({
paymentId: 'payment_id_from_backend',
paymentSecretKey: 'payment_secret_from_backend',
onClose: (payload) => {
console.log('Payment closed', payload)
},
onSuccess: (payload) => {
console.log('Payment success', payload)
},
onError: (payload) => {
console.log('Payment error', payload)
},
})
checkoutButton.mount('#spacepay-checkout')// Option 2 - Embedded full checkout in a modal
import {
initEmbeddedCheckoutButton,
initEmbeddedCheckoutModal,
} from '@spacepay/client-sdk/frontend'
const checkoutModal = await initEmbeddedCheckoutModal({
paymentId: 'payment_id_from_backend',
paymentSecretKey: 'payment_secret_from_backend',
onClose: (payload) => {
console.log('Payment closed', payload)
},
onSuccess: (payload) => {
console.log('Payment success', payload)
},
onError: (payload) => {
console.log('Payment error', payload)
},
})
checkoutModal.mount()Show payment details (optional)
Use the frontend checkout client for browser-safe usage (no secret key), and build your own UI. For they payment itself, redirect the user to paymentUrl (Hosted Checkout) or use Embedded Checkout option (below).
import {
createCheckoutClient,
initEmbeddedCheckoutButton,
initEmbeddedCheckoutModal,
} from '@spacepay/client-sdk/frontend'
const paymentClient = createCheckoutClient({
// Optional, defaults to https://api.app.spacepay.solutions
apiBaseUrl: 'https://api.app.spacepay.solutions',
publicKey: 'pk_...',
paymentSecret: 'ps_...',
})
// Check payment status using payment client
const paymentStatus = await paymentClient.getPaymentStatus(paymentId)
console.log('Payment status:', paymentStatus.status)
// Get payment details using payment client
const paymentDetails = await paymentClient.getPaymentDetails(paymentId)
console.log('Payment details:', paymentDetails)
console.log('Deposit address:', paymentDetails.depositAddress?.address)
// Get active quotes for the payment using payment client
const quotes = await paymentClient.getActiveQuotes(paymentId)
console.log('Active quotes:', quotes)CDN / Browser (global)
If you prefer a <script> tag, you can use the prebuilt browser bundle:
<script src="https://pub-e21c1fba794f48a6b4ec1facf5d68801.r2.dev/latest/spacepay-full.bundle.min.js"></script>
<script>
;(async function () {
const checkout = await window.SpacePaySDK.initEmbeddedCheckoutButton({
paymentId: 'payment_id_from_backend',
paymentSecretKey: 'payment_secret_from_backend',
onClose: (payload) => {
console.log('Payment closed', payload)
},
onSuccess: (payload) => {
console.log('Payment success', payload)
},
onError: (payload) => {
console.log('Payment error', payload)
},
})
checkout.mount('#spacepay-checkout')
})()
</script>The same bundle also exposes SpacePaySDK.createBackendClient and SpacePaySDK.createCheckoutClient for backend and frontend integration.
API Reference
Backend client options
type BackendClientOptions = {
apiBaseUrl?: string // defaults to https://api.app.spacepay.solutions
publicKey: string // Merchant public (access) key; sent as X-SpacePay-Access-Key
secretKey: string // Sent as X-SpacePay-Secret-Key; never expose on the frontend
timeoutMs?: number // default 30000
}Checkout client options
type CheckoutClientOptions = {
apiBaseUrl?: string
publicKey: string
paymentSecret: string // Sent as X-SpacePay-Payment-Secret
timeoutMs?: number
}Creating payments
interface CreatePaymentRequest {
orderId: string
currency: Currency // e.g. Currency.USD — only USD is supported by the API
amount?: number // For default type `payment`: required by the API, minimum 250 cents. Ignored for type `deposit`.
type?: 'payment' | 'deposit' // default `payment`
redirectUrl?: string
customMetadata?: string
}
const payment = await backendClient.createPayment({
amount: 10000, // 10000 cents = $100.00 (must be ≥ 250 for type payment)
currency: Currency.USD,
orderId: 'order_456',
})Optionally pass { idempotencyKey: '...' } as the second argument so retries of the same logical create reuse the same key and the server can deduplicate.
Listing payments
const page = await backendClient.listPayments({
status: 'pending',
limit: 50,
offset: 0,
})
// page.data — PaymentDto[]; page.pagination — totals / hasMore, etc.Getting payment details
Use getPaymentDetails to load the full payment record, including status.
const details = await backendClient.getPaymentDetails('payment_id_here')Creating withdrawals
Send funds from the merchant balance to a recipient address. Minimum amountInCents is 100.
interface CreateWithdrawalRequest {
amountInCents: number // minimum 100
chainId: number
recipientAddress: string
orderId?: string
customMetadata?: string
currency?: Currency // API defaults to USD
}
const withdrawal = await backendClient.createWithdrawal(
{
amountInCents: 10_000,
chainId: 1,
recipientAddress: '0x...',
orderId: 'internal_ref_optional',
currency: Currency.USD,
},
{ idempotencyKey: 'optional-stable-key' }
)For withdrawals, use the same optional { idempotencyKey: '...' } second argument when you need retry-safe creates.
Listing withdrawals
const withdrawalsPage = await backendClient.listWithdrawals({
status: 'pending',
limit: 50,
offset: 0,
})
// withdrawalsPage.data — WithdrawalDto[]; withdrawalsPage.pagination — same shape as paymentsGetting withdrawal details
const one = await backendClient.getWithdrawalDetails(withdrawal.id)Payment status and refresh (checkout client)
Use the checkout client with the payment secret for getPaymentStatus and refreshPaymentStatus when polling or refreshing payment state from the browser.
const checkout = createCheckoutClient({
publicKey: 'pk_...',
paymentSecret: 'from_create_payment_response',
})
const status = await checkout.getPaymentStatus('payment_id_here')
// { id, status }
await checkout.refreshPaymentStatus('payment_id_here', {
transactionHash: '0x...', // all optional
})Quotes (checkout client)
// Default quote (no contract / chain — per API)
await checkout.createOrUpdateQuote(paymentId, {})
// Specific token
await checkout.createOrUpdateQuote(paymentId, {
contractAddress: '0x...',
chainId: 1,
})Payment (incl. deposits) and withdrawal states:
Payment status (and PaymentStatusDto.status):
pending— created, awaiting fundsprocessing— transfer observed / confirmingcompleted— settled successfullyfailed— failed or rejectedexpired— expired before completioncancelled— cancelled
Withdrawal status (on WithdrawalDto):
pending— created, awaiting processingprocessing— in progresscompleted— settled successfullyfailed— failed or rejectedcancelled— cancelled
SDK Development
Prerequisites
- Node.js 18+
- npm or yarn
Setup
# Install dependencies
yarn install
# Build the project (includes format checking)
yarn build
# Development mode with watch
yarn dev
# Clean build artifacts
yarn clean
# Format code with Prettier
yarn format
# Check code formatting
yarn format:check
# Lint code with ESLint
yarn lint
# Fix linting issues automatically
yarn lint:fixProject Structure
src/
├── index.ts # Re-exports frontend + backend factories
├── frontend.ts # Browser-safe entry (checkout client, embeds, utils)
├── backend.ts # Node / server entry (secret key client)
├── client/ # HTTP clients
│ ├── base-client.ts
│ ├── backend-client.ts
│ ├── checkout-client.ts
│ ├── spacepay.ts
│ └── index.ts
├── embedded/ # Embedded checkout / wallet UI helpers
├── types/
│ ├── index.ts
│ ├── payment.ts
│ ├── config.ts
│ └── errors.ts
└── utils/
├── index.ts
├── formatting.ts
├── url.ts
└── validation.tsCode Quality
This project uses both Prettier and ESLint for code quality:
Prettier - Code formatting (.prettierrc):
ESLint - Code linting (eslint.config.js)
The build process automatically checks both formatting and linting, ensuring code quality standards are met.
Browser Support
This SDK works in modern browsers that support:
- Fetch API
- AbortController
For older browsers, you may need to include polyfills for these features.
Node.js Support
- Node.js 18+: Full support with native fetch API
- Node.js 16-17: Requires
node-fetchpolyfill - Node.js <16: Not supported
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
Support
For support and questions:
- Create an issue on GitHub
- Check the examples directory
- Review the API documentation
