Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Paddle Node SDK
https://github.com/paddlehq/paddle-node-sdk
Admin
Node.js SDK for working with the Paddle API in server-side apps.
Tokens:
25,524
Snippets:
95
Trust Score:
9.3
Update:
5 months ago
Context
Skills
Chat
Benchmark
83.9
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Paddle Node.js SDK The Paddle Node.js SDK is a comprehensive server-side JavaScript library for integrating Paddle Billing into Node.js applications. Paddle Billing is a complete digital product sales and subscription management platform designed for modern software businesses, helping increase revenue, retain customers, and scale operations. This SDK provides a type-safe interface to all Paddle API endpoints, supporting both CommonJS and ES modules, with optimized builds for Node.js, browsers, and edge runtimes. The SDK offers complete feature parity with the Paddle API, supporting all operations including products, prices, subscriptions, transactions, customers, discounts, webhooks, and more. It uses a resource-based architecture where each Paddle entity has a dedicated resource class with standard CRUD operations. The SDK includes built-in pagination support, webhook signature verification, comprehensive error handling with typed error codes, and automatic rate limit handling with retry mechanisms for `too_many_requests` errors. ## Initialization and Configuration Initialize the Paddle client with API key and optional configuration ```typescript import { Environment, LogLevel, Paddle } from '@paddle/paddle-node-sdk' // Basic initialization const paddle = new Paddle('your_api_key_here') // Advanced initialization with all options const paddleAdvanced = new Paddle('your_api_key_here', { environment: Environment.sandbox, // or Environment.production (default) logLevel: LogLevel.verbose, // or LogLevel.error (default) customHeaders: { 'X-Custom-Header': 'custom-value', 'X-Request-ID': 'unique-request-id' } }) // Environment.production points to: https://api.paddle.com // Environment.sandbox points to: https://sandbox-api.paddle.com ``` ## Products - Create and Manage Products Create, update, list, get, and archive products in your catalog ```typescript import { Paddle, Product, ProductCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create a product const newProduct = await paddle.products.create({ name: 'ChatApp Pro', taxCategory: 'standard', description: 'Professional chat application with advanced features', customData: { internal_id: 'prod_123' } }) console.log('Created product:', newProduct.id, newProduct.name) // Update a product const updatedProduct = await paddle.products.update('pro_01gsz97mq9pa4fkyy0wqenepkz', { name: 'ChatApp Pro Plus', description: 'Updated description' }) console.log('Updated product:', updatedProduct.name) // Get a single product with related prices included const product = await paddle.products.get('pro_01gsz97mq9pa4fkyy0wqenepkz', { include: ['prices'] }) console.log('Product with prices:', product.name, product.prices) // List products with pagination const productCollection: ProductCollection = paddle.products.list({ status: ['active'], perPage: 20 }) const firstPage: Product[] = await productCollection.next() console.log(`Found ${firstPage.length} products`) if (productCollection.hasMore) { const secondPage: Product[] = await productCollection.next() console.log(`Second page has ${secondPage.length} products`) } // Iterate all products across all pages async function getAllProducts(): Promise<Product[]> { const collection = paddle.products.list() const allProducts: Product[] = [] for await (const product of collection) { allProducts.push(product) } return allProducts } // Archive a product const archivedProduct = await paddle.products.archive('pro_01gsz97mq9pa4fkyy0wqenepkz') console.log('Archived product:', archivedProduct.status) // status: 'archived' ``` ## Prices - Configure Product Pricing Create and manage pricing for products with support for one-time and recurring billing ```typescript import { Paddle, Price, PriceCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create a recurring monthly price const monthlyPrice = await paddle.prices.create({ description: 'Monthly Pro Plan', productId: 'pro_01gsz97mq9pa4fkyy0wqenepkz', unitPrice: { amount: '2999', // $29.99 in cents currencyCode: 'USD' }, billingCycle: { interval: 'month', frequency: 1 }, trialPeriod: { interval: 'day', frequency: 14 }, taxMode: 'account_setting' }) console.log('Created monthly price:', monthlyPrice.id) // Create a one-time price const oneTimePrice = await paddle.prices.create({ description: 'Lifetime Access', productId: 'pro_01gsz97mq9pa4fkyy0wqenepkz', unitPrice: { amount: '29900', // $299.00 currencyCode: 'USD' }, taxMode: 'account_setting' }) // Update a price const updatedPrice = await paddle.prices.update('pri_01gsz8z1q1n00f12qt82y31smh', { description: 'Monthly Pro Plan - Special Rate', unitPrice: { amount: '2499', // Reduced to $24.99 currencyCode: 'USD' } }) // Get a price with product details const price = await paddle.prices.get('pri_01gsz8z1q1n00f12qt82y31smh', { include: ['product'] }) console.log('Price:', price.unitPrice.amount, price.product?.name) // List all active prices const priceCollection = paddle.prices.list({ status: ['active'], includeProduct: true }) const prices = await priceCollection.next() prices.forEach(p => console.log(p.description, p.unitPrice.amount)) ``` ## Customers - Manage Customer Records Create, update, and retrieve customer information ```typescript import { Paddle, Customer, CustomerCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create a customer const newCustomer = await paddle.customers.create({ email: 'john.doe@example.com', name: 'John Doe', locale: 'en', customData: { company: 'Acme Corp', account_type: 'enterprise' } }) console.log('Created customer:', newCustomer.id, newCustomer.email) // Update customer details const updatedCustomer = await paddle.customers.update('ctm_01grnn4zta5a1mf02jjze7y2ys', { name: 'Johnathan Doe', customData: { company: 'Acme Corporation', account_type: 'enterprise', tier: 'premium' } }) // Get a customer const customer = await paddle.customers.get('ctm_01grnn4zta5a1mf02jjze7y2ys') console.log('Customer:', customer.name, customer.email, customer.status) // List customers with search const customerCollection = paddle.customers.list({ email: 'john.doe@example.com', status: ['active'], perPage: 50 }) const customers = await customerCollection.next() // Archive a customer const archivedCustomer = await paddle.customers.archive('ctm_01grnn4zta5a1mf02jjze7y2ys') console.log('Customer archived:', archivedCustomer.status) ``` ## Subscriptions - Manage Recurring Subscriptions Create, update, pause, resume, and cancel subscriptions ```typescript import { Paddle, Subscription, SubscriptionCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create a subscription via a transaction const transaction = await paddle.transactions.create({ items: [{ priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 1 }], customerId: 'ctm_01grnn4zta5a1mf02jjze7y2ys', collectionMode: 'automatic', billingDetails: { enableCheckout: true, purchaseOrderNumber: 'PO-12345' } }) // Get subscription with details const subscription = await paddle.subscriptions.get('sub_01h04vsc0qhwtsbsxh3422wjs4', { include: ['next_transaction', 'recurring_transaction_details'] }) console.log('Subscription:', subscription.status, subscription.currentBillingPeriod) // Update subscription (add items, change billing) const updated = await paddle.subscriptions.update('sub_01h04vsc0qhwtsbsxh3422wjs4', { items: [{ priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 2 }], prorationBillingMode: 'prorated_immediately', customData: { upgraded: true } }) // Preview subscription update before applying const preview = await paddle.subscriptions.previewUpdate('sub_01h04vsc0qhwtsbsxh3422wjs4', { items: [{ priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 3 }] }) console.log('Update preview:', preview.immediateTransaction?.total) // Pause a subscription const paused = await paddle.subscriptions.pause('sub_01h04vsc0qhwtsbsxh3422wjs4', { effectiveFrom: 'next_billing_period', resumeAt: '2025-12-31' }) console.log('Paused subscription:', paused.scheduledChange) // Resume a paused subscription const resumed = await paddle.subscriptions.resume('sub_01h04vsc0qhwtsbsxh3422wjs4', { effectiveFrom: 'immediately' }) // Cancel a subscription const canceled = await paddle.subscriptions.cancel('sub_01h04vsc0qhwtsbsxh3422wjs4', { effectiveFrom: 'next_billing_period' }) console.log('Canceled subscription:', canceled.scheduledChange) // Activate a trialing subscription const activated = await paddle.subscriptions.activate('sub_01h04vsc0qhwtsbsxh3422wjs4') // Create one-time charge for existing subscription const charge = await paddle.subscriptions.createCharge('sub_01h04vsc0qhwtsbsxh3422wjs4', { items: [{ priceId: 'pri_one_time_charge', quantity: 1 }], effectiveFrom: 'next_billing_period' }) // List subscriptions by customer const subscriptions = paddle.subscriptions.list({ customerId: 'ctm_01grnn4zta5a1mf02jjze7y2ys', status: ['active', 'trialing'] }) for await (const sub of subscriptions) { console.log('Subscription:', sub.id, sub.status, sub.nextBilledAt) } ``` ## Transactions - Handle Payments and Checkouts Create and manage transactions for one-time and recurring payments ```typescript import { Paddle, Transaction, TransactionCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create a transaction for checkout const transaction = await paddle.transactions.create({ items: [ { priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 1 }, { priceId: 'pri_addon_feature', quantity: 2 } ], customerId: 'ctm_01grnn4zta5a1mf02jjze7y2ys', addressId: 'add_01gm302t81w94gyjpjpqypkzkf', currencyCode: 'USD', collectionMode: 'automatic', discountId: 'dsc_01gv5kpg05xp104ek2fmgjwttf', customData: { order_id: 'ord_12345', referral_code: 'SAVE10' }, billingDetails: { enableCheckout: true, paymentTerms: { interval: 'day', frequency: 30 } } }) console.log('Transaction created:', transaction.id, transaction.checkoutUrl) // Update a draft transaction const updated = await paddle.transactions.update('txn_01h04vsbhqc62t8hmd4z3b578c', { customData: { order_id: 'ord_12345', updated: true }, items: [{ priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 2 }] }) // Get transaction with related data const txn = await paddle.transactions.get('txn_01h04vsbhqc62t8hmd4z3b578c', { include: ['customer', 'discount', 'business', 'address'] }) console.log('Transaction:', txn.status, txn.total, txn.customer?.email) // Preview transaction pricing before creation const preview = await paddle.transactions.preview({ items: [{ priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 1 }], customerId: 'ctm_01grnn4zta5a1mf02jjze7y2ys', addressId: 'add_01gm302t81w94gyjpjpqypkzkf', currencyCode: 'USD', discountId: 'dsc_01gv5kpg05xp104ek2fmgjwttf' }) console.log('Preview:', preview.details.totals.total) // List transactions with filtering const transactions = paddle.transactions.list({ customerId: 'ctm_01grnn4zta5a1mf02jjze7y2ys', status: ['completed', 'billed'], collectionMode: 'automatic', invoiceNumber: ['10001-10010'], after: 'txn_01h04vsbhqc62t8hmd4z3b578c', perPage: 50 }) const firstPage = await transactions.next() firstPage.forEach(t => console.log(t.id, t.status, t.billedAt)) // Invoice a ready transaction const invoiced = await paddle.transactions.invoice('txn_01h04vsbhqc62t8hmd4z3b578c') console.log('Invoiced:', invoiced.invoiceNumber) ``` ## Discounts - Apply Promotional Codes Create and manage discounts and promotional codes ```typescript import { Paddle, Discount, DiscountCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create a percentage discount const percentDiscount = await paddle.discounts.create({ description: '20% Off Summer Sale', type: 'percentage', amount: '20', code: 'SUMMER20', enabledForCheckout: true, recur: true, maximumRecurringIntervals: 3, restrictTo: ['pri_01gsz8z1q1n00f12qt82y31smh'], expiresAt: '2025-08-31T23:59:59Z', customData: { campaign: 'summer_2025' } }) console.log('Created discount:', percentDiscount.code, percentDiscount.id) // Create a flat amount discount const flatDiscount = await paddle.discounts.create({ description: '$10 Off First Purchase', type: 'flat', amount: '1000', // $10.00 in cents currencyCode: 'USD', code: 'WELCOME10', enabledForCheckout: true, recur: false, usageLimit: 1000, timesUsed: 0 }) // Create a flat per seat discount const perSeatDiscount = await paddle.discounts.create({ description: '$5 Off Per User', type: 'flat_per_seat', amount: '500', // $5.00 per seat currencyCode: 'USD', code: 'TEAM5', enabledForCheckout: true }) // Update discount const updated = await paddle.discounts.update('dsc_01gv5kpg05xp104ek2fmgjwttf', { description: '25% Off Extended Summer Sale', amount: '25', expiresAt: '2025-09-15T23:59:59Z' }) // Get discount details const discount = await paddle.discounts.get('dsc_01gv5kpg05xp104ek2fmgjwttf') console.log('Discount:', discount.code, discount.amount, discount.timesUsed) // List active discounts const discounts = paddle.discounts.list({ status: ['active'], code: 'SUMMER20' }) for await (const disc of discounts) { console.log(disc.code, disc.type, disc.amount, disc.timesUsed) } // Archive a discount const archived = await paddle.discounts.archive('dsc_01gv5kpg05xp104ek2fmgjwttf') console.log('Archived discount:', archived.status) ``` ## Addresses - Manage Customer Addresses Create and manage addresses for customers ```typescript import { Paddle, Address, AddressCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create an address for a customer const newAddress = await paddle.addresses.create('ctm_01grnn4zta5a1mf02jjze7y2ys', { description: 'Home Address', countryCode: 'US', postalCode: '10001', region: 'NY', city: 'New York', firstLine: '123 Main Street', secondLine: 'Apartment 4B', customData: { address_type: 'residential' } }) console.log('Created address:', newAddress.id, newAddress.firstLine) // Update an address const updated = await paddle.addresses.update( 'ctm_01grnn4zta5a1mf02jjze7y2ys', 'add_01gm302t81w94gyjpjpqypkzkf', { description: 'Primary Home Address', firstLine: '456 Oak Avenue', secondLine: 'Suite 200' } ) // Get an address const address = await paddle.addresses.get( 'ctm_01grnn4zta5a1mf02jjze7y2ys', 'add_01gm302t81w94gyjpjpqypkzkf' ) console.log('Address:', address.firstLine, address.city, address.countryCode) // List all addresses for a customer const addresses = paddle.addresses.list('ctm_01grnn4zta5a1mf02jjze7y2ys', { status: ['active'] }) const addressList = await addresses.next() addressList.forEach(addr => console.log(addr.description, addr.firstLine)) // Archive an address const archived = await paddle.addresses.archive( 'ctm_01grnn4zta5a1mf02jjze7y2ys', 'add_01gm302t81w94gyjpjpqypkzkf' ) console.log('Archived address:', archived.status) ``` ## Adjustments - Handle Refunds and Credits Create adjustments for refunds, credits, and chargebacks ```typescript import { Paddle, Adjustment, AdjustmentCollection } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Create a full refund const fullRefund = await paddle.adjustments.create({ action: 'refund', transactionId: 'txn_01h04vsbhqc62t8hmd4z3b578c', reason: 'Customer requested refund', items: [{ itemId: 'txnitm_01hvcc94b7qgz60qmrqmbm19zw', type: 'full' }] }) console.log('Full refund created:', fullRefund.id, fullRefund.status) // Create a partial refund const partialRefund = await paddle.adjustments.create({ action: 'refund', transactionId: 'txn_01h04vsbhqc62t8hmd4z3b578c', reason: 'Partial refund for service issues', items: [{ itemId: 'txnitm_01hvcc94b7qgz60qmrqmbm19zw', type: 'partial', amount: '1500' // $15.00 in cents }] }) // Create a credit adjustment const credit = await paddle.adjustments.create({ action: 'credit', transactionId: 'txn_01h04vsbhqc62t8hmd4z3b578c', reason: 'Account credit for service downtime', items: [{ itemId: 'txnitm_01hvcc94b7qgz60qmrqmbm19zw', type: 'partial', amount: '1000' // $10.00 credit }] }) // Create a proration adjustment const proration = await paddle.adjustments.create({ action: 'credit', transactionId: 'txn_01h04vsbhqc62t8hmd4z3b578c', reason: 'Prorated upgrade', items: [{ itemId: 'txnitm_01hvcc94b7qgz60qmrqmbm19zw', type: 'proration', proration: { rate: '0.50', billingPeriod: { startsAt: '2025-01-01T00:00:00Z', endsAt: '2025-02-01T00:00:00Z' } } }] }) // Get adjustment details const adjustment = await paddle.adjustments.get('adj_01h04vsc0qhwtsbsxh3422wjs4') console.log('Adjustment:', adjustment.action, adjustment.total, adjustment.status) // List adjustments by transaction const adjustments = paddle.adjustments.list({ transactionId: 'txn_01h04vsbhqc62t8hmd4z3b578c', action: ['refund'], status: ['approved', 'pending_approval'] }) for await (const adj of adjustments) { console.log(adj.id, adj.action, adj.total, adj.reason) } ``` ## Pricing Preview - Calculate Pricing Before Purchase Preview pricing with discounts, taxes, and billing details before creating a transaction ```typescript import { Paddle, PricingPreview } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') // Preview pricing with basic configuration const basicPreview = await paddle.pricingPreview.preview({ items: [ { priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 1 }, { priceId: 'pri_addon_feature', quantity: 3 } ], currencyCode: 'USD', address: { countryCode: 'US', postalCode: '10001' } }) console.log('Subtotal:', basicPreview.details.lineItems[0].totals.subtotal) console.log('Tax:', basicPreview.details.lineItems[0].totals.tax) console.log('Total:', basicPreview.details.lineItems[0].totals.total) // Preview with customer, discount, and address const fullPreview = await paddle.pricingPreview.preview({ items: [ { priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 5 } ], customerId: 'ctm_01grnn4zta5a1mf02jjze7y2ys', addressId: 'add_01gm302t81w94gyjpjpqypkzkf', discountId: 'dsc_01gv5kpg05xp104ek2fmgjwttf', currencyCode: 'USD', includeTax: true }) console.log('Line items:', fullPreview.details.lineItems.length) console.log('Total after discount:', fullPreview.details.totals.total) console.log('Tax total:', fullPreview.details.totals.tax) // Preview with custom business and address for tax calculation const taxPreview = await paddle.pricingPreview.preview({ items: [{ priceId: 'pri_01gsz8z1q1n00f12qt82y31smh', quantity: 1 }], currencyCode: 'EUR', address: { countryCode: 'DE', postalCode: '10115', city: 'Berlin' }, business: { name: 'Example GmbH', taxIdentifier: 'DE123456789' } }) console.log('Tax breakdown:', taxPreview.details.totals) ``` ## Webhooks - Verify and Process Webhook Events Verify webhook signatures and process event notifications from Paddle ```typescript import { Paddle, EventName } from '@paddle/paddle-node-sdk' import express, { Request, Response } from 'express' const paddle = new Paddle('your_api_key_here') const app = express() const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET_KEY || '' // Webhook endpoint - use raw body parser app.post('/webhooks/paddle', express.raw({ type: 'application/json' }), async (req: Request, res: Response) => { const signature = (req.headers['paddle-signature'] as string) || '' const rawBody = req.body.toString('utf8') // Convert Buffer to string try { if (!signature || !rawBody) { console.log('Missing signature or body') return res.status(400).send('Bad Request') } // Verify signature and unmarshal event const eventData = await paddle.webhooks.unmarshal(rawBody, WEBHOOK_SECRET, signature) // Process different event types switch (eventData.eventType) { case EventName.TransactionCompleted: console.log(`Transaction ${eventData.data.id} completed`) console.log('Customer:', eventData.data.customerId) console.log('Total:', eventData.data.details.totals.total) // Update your database, send confirmation email, etc. break case EventName.SubscriptionCreated: console.log(`New subscription ${eventData.data.id} created`) console.log('Status:', eventData.data.status) console.log('Next billed at:', eventData.data.nextBilledAt) // Provision access, activate features, etc. break case EventName.SubscriptionUpdated: console.log(`Subscription ${eventData.data.id} updated`) console.log('Scheduled change:', eventData.data.scheduledChange) // Update user access, adjust features, etc. break case EventName.SubscriptionCanceled: console.log(`Subscription ${eventData.data.id} canceled`) console.log('Canceled at:', eventData.data.canceledAt) // Revoke access, send cancellation email, etc. break case EventName.SubscriptionPaused: console.log(`Subscription ${eventData.data.id} paused`) // Temporarily disable features break case EventName.SubscriptionResumed: console.log(`Subscription ${eventData.data.id} resumed`) // Re-enable features break case EventName.TransactionPaymentFailed: console.log(`Payment failed for transaction ${eventData.data.id}`) // Send payment failure notification, retry logic break case EventName.CustomerCreated: console.log(`New customer ${eventData.data.id}: ${eventData.data.email}`) // Add to CRM, send welcome email break case EventName.ProductUpdated: console.log(`Product ${eventData.data.id} updated: ${eventData.data.name}`) // Update catalog, refresh cache break case EventName.DiscountCreated: console.log(`Discount ${eventData.data.code} created`) // Update promotional campaigns break default: console.log('Received event:', eventData.eventType) console.log('Event ID:', eventData.eventId) console.log('Occurred at:', eventData.occurredAt) } // Always return 200 to acknowledge receipt res.status(200).send('Webhook processed') } catch (error) { console.error('Webhook validation failed:', error) // Log for investigation but still return 200 to prevent retries res.status(200).send('Error logged') } }) app.listen(3000, () => console.log('Webhook server listening on port 3000')) ``` ## Error Handling - Handle API Errors Handle API errors with typed error codes and validation details ```typescript import { ApiError, Paddle } from '@paddle/paddle-node-sdk' const paddle = new Paddle('your_api_key_here') async function createProductWithErrorHandling() { try { const product = await paddle.products.create({ name: 'New Product', taxCategory: 'standard' }) console.log('Product created:', product.id) return product } catch (error: any) { const apiError = error as ApiError // Handle specific error codes switch (apiError.code) { case 'conflict': console.error('Conflict: Resource already exists') console.error('Details:', apiError.detail) // Try to update instead of create break case 'unauthorized': console.error('Authentication failed: Invalid API key') // Refresh credentials break case 'forbidden': console.error('Permission denied: Check API key permissions') break case 'not_found': console.error('Resource not found') break case 'validation_error': console.error('Validation failed:') apiError.errors?.forEach(err => { console.error(` - Field: ${err.field}`) console.error(` Message: ${err.message}`) }) // Show validation errors to user break case 'too_many_requests': console.error('Rate limited') if (apiError.retryAfter) { console.error(`Retry after ${apiError.retryAfter} seconds`) // Wait and retry await new Promise(resolve => setTimeout(resolve, apiError.retryAfter! * 1000)) // The SDK handles this automatically for you } break case 'internal_error': console.error('Paddle API internal error - retry may succeed') break default: console.error('API Error:', apiError.code, apiError.detail) console.error('Request ID:', apiError.requestId) console.error('Documentation:', apiError.documentationUrl) } throw error // Re-throw if needed } } // Batch operation with comprehensive error handling async function batchCreateCustomers(customerData: any[]) { const results = { succeeded: [], failed: [] } for (const data of customerData) { try { const customer = await paddle.customers.create(data) results.succeeded.push({ data, customer }) } catch (error: any) { const apiError = error as ApiError results.failed.push({ data, error: { code: apiError.code, message: apiError.detail, errors: apiError.errors } }) } } console.log(`Created ${results.succeeded.length} customers`) console.log(`Failed ${results.failed.length} customers`) return results } createProductWithErrorHandling() ``` ## Summary The Paddle Node.js SDK provides a complete, type-safe interface to the Paddle Billing API for managing digital product sales and subscriptions. Core functionality includes managing products and pricing, processing transactions and payments, handling subscriptions with pause/resume/cancel operations, managing customers and their addresses, applying discounts and promotional codes, processing refunds and credits through adjustments, previewing pricing with taxes and discounts, and receiving/validating webhook notifications for real-time event processing. The SDK supports all major use cases including checkout flows, recurring billing, subscription management, and financial operations. Integration patterns are straightforward: initialize the Paddle client with your API key, use resource methods for CRUD operations (create, list, get, update, archive), handle paginated responses with iterators using `next()` or `for await...of` loops, process webhook events in your server with automatic signature verification, and handle errors with typed error codes for specific scenarios. The SDK automatically handles rate limiting with the `Retry-After` header, provides comprehensive TypeScript types for all entities and operations, supports both Node.js and edge runtimes (Cloudflare Workers, Deno, Bun), and follows JavaScript naming conventions with camelCase (while the API uses snake_case). All operations are promise-based for modern async/await patterns.