Payment Processing
Overview
Invoice Generation and Payment
import { Transaction, PrivateKey, P2PKH, Script } from '@bsv/sdk'
/**
* Invoice Payment Processor
*
* Generate and process invoice payments
*/
class InvoiceProcessor {
/**
* Generate a payment invoice
*/
generateInvoice(params: {
merchantAddress: string
amount: number
invoiceId: string
description: string
expiresAt?: number
}): Invoice {
const invoice: Invoice = {
id: params.invoiceId,
merchantAddress: params.merchantAddress,
amount: params.amount,
description: params.description,
createdAt: Date.now(),
expiresAt: params.expiresAt || Date.now() + 24 * 60 * 60 * 1000, // 24 hours default
status: 'pending',
paymentTxid: null
}
console.log('Invoice generated')
console.log('Invoice ID:', invoice.id)
console.log('Amount:', invoice.amount, 'satoshis')
console.log('Expires:', new Date(invoice.expiresAt).toISOString())
return invoice
}
/**
* Process invoice payment
*/
async processInvoicePayment(
invoice: Invoice,
payerKey: PrivateKey,
utxo: {
txid: string
vout: number
satoshis: number
script: Script
}
): Promise<{
tx: Transaction
invoice: Invoice
}> {
try {
// Validate invoice
if (invoice.status !== 'pending') {
throw new Error('Invoice already paid or expired')
}
if (Date.now() > invoice.expiresAt) {
throw new Error('Invoice expired')
}
if (utxo.satoshis < invoice.amount + 500) {
throw new Error('Insufficient funds')
}
const tx = new Transaction()
// Add input
tx.addInput({
sourceTXID: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(payerKey),
sequence: 0xffffffff
})
// Add payment output to merchant
tx.addOutput({
satoshis: invoice.amount,
lockingScript: Script.fromAddress(invoice.merchantAddress)
})
// Add change output
const fee = 500
const change = utxo.satoshis - invoice.amount - fee
if (change >= 546) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(payerKey.toPublicKey().toHash())
})
}
await tx.sign()
// Update invoice
invoice.status = 'paid'
invoice.paymentTxid = tx.id('hex')
invoice.paidAt = Date.now()
console.log('Invoice payment processed')
console.log('Invoice ID:', invoice.id)
console.log('Transaction ID:', tx.id('hex'))
return { tx, invoice }
} catch (error) {
throw new Error(`Invoice payment failed: ${error.message}`)
}
}
/**
* Verify invoice payment on-chain
*/
async verifyInvoicePayment(
invoice: Invoice,
tx: Transaction
): Promise<{
verified: boolean
message: string
}> {
try {
if (!invoice.paymentTxid) {
return {
verified: false,
message: 'Invoice has no payment transaction'
}
}
// Check transaction ID matches
if (tx.id('hex') !== invoice.paymentTxid) {
return {
verified: false,
message: 'Transaction ID mismatch'
}
}
// Check payment output
let paymentFound = false
for (const output of tx.outputs) {
try {
const outputAddress = output.lockingScript.toAddress()
if (outputAddress === invoice.merchantAddress && output.satoshis === invoice.amount) {
paymentFound = true
break
}
} catch {
// Skip non-address outputs
continue
}
}
if (!paymentFound) {
return {
verified: false,
message: 'Payment output not found or amount incorrect'
}
}
return {
verified: true,
message: 'Invoice payment verified'
}
} catch (error) {
return {
verified: false,
message: `Verification failed: ${error.message}`
}
}
}
/**
* Generate payment receipt
*/
generateReceipt(invoice: Invoice): PaymentReceipt {
if (invoice.status !== 'paid') {
throw new Error('Invoice not paid')
}
return {
invoiceId: invoice.id,
amount: invoice.amount,
merchantAddress: invoice.merchantAddress,
paymentTxid: invoice.paymentTxid!,
paidAt: invoice.paidAt!,
description: invoice.description
}
}
}
interface Invoice {
id: string
merchantAddress: string
amount: number
description: string
createdAt: number
expiresAt: number
status: 'pending' | 'paid' | 'expired'
paymentTxid: string | null
paidAt?: number
}
interface PaymentReceipt {
invoiceId: string
amount: number
merchantAddress: string
paymentTxid: string
paidAt: number
description: string
}
/**
* Usage Example
*/
async function invoiceExample() {
const processor = new InvoiceProcessor()
const customerKey = PrivateKey.fromRandom()
// Merchant generates invoice
const invoice = processor.generateInvoice({
merchantAddress: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
amount: 50000,
invoiceId: 'INV-001',
description: 'Website hosting - Monthly'
})
// Customer pays invoice
const utxo = {
txid: 'customer-utxo...',
vout: 0,
satoshis: 100000,
script: new P2PKH().lock(customerKey.toPublicKey().toHash())
}
const { tx, invoice: paidInvoice } = await processor.processInvoicePayment(
invoice,
customerKey,
utxo
)
// Verify payment
const verification = await processor.verifyInvoicePayment(paidInvoice, tx)
console.log('Payment verified:', verification.verified)
// Generate receipt
const receipt = processor.generateReceipt(paidInvoice)
console.log('Receipt:', receipt)
}Recurring Payments
Subscription Management
Related Examples
See Also
Last updated
