Payment Processing Examples
Real-world payment processing implementations using Omne blockchain. From simple transfers to complex e-commerce integrations with automatic handling.
E-commerce Integration
Online Store Checkout
Complete checkout flow with payment confirmation and order processing.
// Frontend - React Checkout Component
import { useState } from 'react'
import { OmneClient, Account } from '@omne/sdk'
interface CheckoutProps {
orderTotal: string
merchantAddress: string
orderId: string
}
export function Checkout({ orderTotal, merchantAddress, orderId }: CheckoutProps) {
const [paymentStatus, setPaymentStatus] = useState<'pending' | 'processing' | 'completed' | 'failed'>('pending')
const [txHash, setTxHash] = useState<string>('')
const processPayment = async () => {
try {
setPaymentStatus('processing')
// Connect to user's wallet
const client = new OmneClient({ network: 'mainnet' })
const account = await client.connectWallet()
// Create payment transaction
const payment = await client.transfer({
from: account.address,
to: merchantAddress,
amount: orderTotal,
memo: `Order #${orderId}`,
confirmations: 3 // Wait for 3 confirmations
})
setTxHash(payment.hash)
// Notify backend
await fetch('/api/payments/confirm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orderId,
txHash: payment.hash,
amount: orderTotal,
customerAddress: account.address
})
})
setPaymentStatus('completed')
} catch (error) {
console.error('Payment failed:', error)
setPaymentStatus('failed')
}
}
return (
<div className="checkout-container">
<div className="order-summary">
<h3>Order Total: {orderTotal} OMNE</h3>
<p>Order ID: {orderId}</p>
</div>
<button
onClick={processPayment}
disabled={paymentStatus === 'processing'}
className="pay-button"
>
{paymentStatus === 'processing' ? 'Processing...' : 'Pay with OMNE'}
</button>
{txHash && (
<div className="payment-confirmation">
<p>Payment Transaction: {txHash}</p>
<a href={`https://explorer.omne.foundation/tx/${txHash}`}>
View on Explorer
</a>
</div>
)}
</div>
)
}
Backend Payment Verification
Server-side payment verification and order fulfillment.
// Backend - Node.js Payment Verification
import express from 'express'
import { OmneClient } from '@omne/sdk'
const app = express()
const client = new OmneClient({ network: 'mainnet' })
// Payment confirmation endpoint
app.post('/api/payments/confirm', async (req, res) => {
try {
const { orderId, txHash, amount, customerAddress } = req.body
// Verify transaction on blockchain
const transaction = await client.getTransaction(txHash)
// Validate transaction details
if (!transaction || transaction.status !== 'success') {
return res.status(400).json({ error: 'Invalid transaction' })
}
if (transaction.amount !== amount) {
return res.status(400).json({ error: 'Amount mismatch' })
}
if (transaction.to !== process.env.MERCHANT_ADDRESS) {
return res.status(400).json({ error: 'Invalid recipient' })
}
// Wait for sufficient confirmations
if (transaction.confirmations < 3) {
return res.status(202).json({
status: 'pending',
confirmations: transaction.confirmations
})
}
// Update order in database
await updateOrderStatus(orderId, {
status: 'paid',
txHash,
paidAmount: amount,
paidAt: new Date(),
customerAddress
})
// Trigger order fulfillment
await fulfillOrder(orderId)
// Send confirmation email
await sendPaymentConfirmation(orderId, customerAddress, txHash)
res.json({
status: 'confirmed',
orderId,
txHash
})
} catch (error) {
console.error('Payment verification failed:', error)
res.status(500).json({ error: 'Payment verification failed' })
}
})
// Webhook for real-time transaction updates
app.post('/api/webhooks/transaction', async (req, res) => {
const { txHash, status, confirmations } = req.body
// Find order by transaction hash
const order = await findOrderByTxHash(txHash)
if (!order) {
return res.status(404).json({ error: 'Order not found' })
}
// Update order status based on confirmations
if (confirmations >= 3 && status === 'success') {
await updateOrderStatus(order.id, { status: 'confirmed' })
await fulfillOrder(order.id)
}
res.json({ status: 'processed' })
})
async function fulfillOrder(orderId: string) {
// Implement order fulfillment logic
// - Update inventory
// - Generate shipping labels
// - Send confirmation emails
// - Trigger delivery process
}
app.listen(3001, () => {
console.log('Payment server running on port 3001')
})
Subscription Payments
Monthly Subscription Handler
Automated recurring payments with smart contract escrow.
// Subscription Management Smart Contract
import { SmartContract, Account, OmneClient } from '@omne/sdk'
class SubscriptionManager {
private contract: SmartContract
private client: OmneClient
constructor(contractAddress: string) {
this.client = new OmneClient({ network: 'mainnet' })
this.contract = new SmartContract(contractAddress, subscriptionABI)
}
// Create new subscription
async createSubscription(
account: Account,
serviceProvider: string,
amount: string,
intervalDays: number
) {
const tx = await this.contract.execute(account, 'createSubscription', {
provider: serviceProvider,
amount: amount,
interval: intervalDays * 24 * 60 * 60, // Convert to seconds
startTime: Math.floor(Date.now() / 1000)
})
return await this.client.waitForTransaction(tx.hash)
}
// Process subscription payment
async processPayment(subscriptionId: string, account: Account) {
try {
// Check if payment is due
const subscription = await this.contract.call('getSubscription', [subscriptionId])
const now = Math.floor(Date.now() / 1000)
if (now < subscription.nextPaymentDue) {
throw new Error('Payment not yet due')
}
// Execute payment
const tx = await this.contract.execute(account, 'processPayment', {
subscriptionId,
amount: subscription.amount
})
// Update next payment date
await this.contract.execute(account, 'updateNextPayment', {
subscriptionId,
nextPayment: now + subscription.interval
})
return tx
} catch (error) {
console.error('Subscription payment failed:', error)
throw error
}
}
// Cancel subscription
async cancelSubscription(subscriptionId: string, account: Account) {
return await this.contract.execute(account, 'cancelSubscription', {
subscriptionId
})
}
// Get subscription status
async getSubscriptionStatus(subscriptionId: string) {
const subscription = await this.contract.call('getSubscription', [subscriptionId])
const now = Math.floor(Date.now() / 1000)
return {
id: subscriptionId,
isActive: subscription.isActive,
amount: subscription.amount,
nextPaymentDue: subscription.nextPaymentDue,
isPastDue: now > subscription.nextPaymentDue,
provider: subscription.provider,
subscriber: subscription.subscriber
}
}
}
// Usage example
const subscriptionManager = new SubscriptionManager('omne1contract...')
// Create monthly subscription for $99
await subscriptionManager.createSubscription(
userAccount,
'omne1service-provider...',
'99000000', // 99 OMNE
30 // 30 days
)
Automated Payment Processing
Background service for processing recurring payments automatically.
import asyncio
import schedule
import time
from datetime import datetime, timedelta
from omne_sdk import OmneClient, Account
class SubscriptionProcessor:
def __init__(self, contract_address: str, processor_account: Account):
self.client = OmneClient(network='mainnet')
self.contract_address = contract_address
self.processor_account = processor_account
async def process_due_subscriptions(self):
"""Process all subscriptions that are due for payment"""
try:
# Get all active subscriptions
subscriptions = await self.get_active_subscriptions()
due_subscriptions = [
sub for sub in subscriptions
if self.is_payment_due(sub)
]
print(f"Processing {len(due_subscriptions)} due subscriptions")
# Process payments concurrently
tasks = [
self.process_subscription_payment(sub)
for sub in due_subscriptions
]
results = await asyncio.gather(*tasks, return_exceptions=True)
# Log results
successful = sum(1 for r in results if not isinstance(r, Exception))
failed = len(results) - successful
print(f"Processed: {successful} successful, {failed} failed")
except Exception as e:
print(f"Error processing subscriptions: {e}")
async def process_subscription_payment(self, subscription):
"""Process individual subscription payment"""
try:
subscription_id = subscription['id']
amount = subscription['amount']
subscriber = subscription['subscriber']
# Check subscriber balance
balance = await self.client.get_balance(subscriber)
if int(balance.amount) < int(amount):
await self.handle_insufficient_funds(subscription)
return
# Execute payment
tx = await self.client.contract_execute(
self.contract_address,
'processSubscriptionPayment',
{
'subscription_id': subscription_id,
'processor': self.processor_account.address
},
account=self.processor_account
)
print(f"Processed subscription {subscription_id}: {tx.hash}")
# Send confirmation
await self.send_payment_confirmation(subscription, tx.hash)
except Exception as e:
print(f"Failed to process subscription {subscription['id']}: {e}")
await self.handle_payment_failure(subscription, str(e))
async def handle_insufficient_funds(self, subscription):
"""Handle cases where subscriber has insufficient funds"""
subscription_id = subscription['id']
# Mark subscription as past due
await self.client.contract_execute(
self.contract_address,
'markPastDue',
{'subscription_id': subscription_id},
account=self.processor_account
)
# Send notification to subscriber
await self.send_insufficient_funds_notification(subscription)
# Schedule retry in 24 hours
retry_time = datetime.now() + timedelta(hours=24)
await self.schedule_retry(subscription_id, retry_time)
def is_payment_due(self, subscription) -> bool:
"""Check if payment is due for subscription"""
now = int(time.time())
next_payment = subscription['next_payment_due']
return now >= next_payment
async def get_active_subscriptions(self):
"""Retrieve all active subscriptions from contract"""
return await self.client.contract_call(
self.contract_address,
'getActiveSubscriptions',
[]
)
def start_processing(self):
"""Start the subscription processing scheduler"""
# Process subscriptions every hour
schedule.every().hour.do(
lambda: asyncio.run(self.process_due_subscriptions())
)
print("Subscription processor started")
while True:
schedule.run_pending()
time.sleep(60) # Check every minute
# Start the processor
processor_account = Account.from_private_key(os.getenv('PROCESSOR_PRIVATE_KEY'))
processor = SubscriptionProcessor('omne1subscription-contract...', processor_account)
processor.start_processing()
Escrow Payments
Marketplace Escrow
Secure escrow system for marketplace transactions with dispute resolution.
// Escrow Smart Contract Integration
import { SmartContract, Account } from '@omne/sdk'
class EscrowService {
private contract: SmartContract
constructor(contractAddress: string) {
this.contract = new SmartContract(contractAddress, escrowABI)
}
// Create escrow for marketplace transaction
async createEscrow(
buyer: Account,
sellerAddress: string,
arbitratorAddress: string,
amount: string,
description: string,
timeoutDays: number = 30
) {
const escrowId = `escrow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
const tx = await this.contract.execute(buyer, 'createEscrow', {
escrowId,
buyer: buyer.address,
seller: sellerAddress,
arbitrator: arbitratorAddress,
amount,
description,
timeout: timeoutDays * 24 * 60 * 60, // Convert to seconds
createdAt: Math.floor(Date.now() / 1000)
}, {
// Send funds to escrow contract
amount: amount,
denom: 'uomne'
})
return { escrowId, txHash: tx.hash }
}
// Seller confirms delivery/completion
async confirmDelivery(seller: Account, escrowId: string) {
return await this.contract.execute(seller, 'confirmDelivery', {
escrowId,
confirmedBy: seller.address,
confirmedAt: Math.floor(Date.now() / 1000)
})
}
// Buyer releases funds to seller
async releaseFunds(buyer: Account, escrowId: string, rating?: number) {
const params: any = {
escrowId,
releasedBy: buyer.address,
releasedAt: Math.floor(Date.now() / 1000)
}
if (rating !== undefined) {
params.buyerRating = rating
}
return await this.contract.execute(buyer, 'releaseFunds', params)
}
// Initiate dispute (by buyer or seller)
async initiateDispute(
account: Account,
escrowId: string,
reason: string,
evidence: string[]
) {
return await this.contract.execute(account, 'initiateDispute', {
escrowId,
disputeReason: reason,
evidence: evidence,
disputedBy: account.address,
disputedAt: Math.floor(Date.now() / 1000)
})
}
// Arbitrator resolves dispute
async resolveDispute(
arbitrator: Account,
escrowId: string,
resolution: 'release_to_seller' | 'refund_to_buyer' | 'partial_split',
buyerAmount?: string,
sellerAmount?: string,
reasoning?: string
) {
const params: any = {
escrowId,
resolution,
resolvedBy: arbitrator.address,
resolvedAt: Math.floor(Date.now() / 1000)
}
if (resolution === 'partial_split') {
params.buyerAmount = buyerAmount
params.sellerAmount = sellerAmount
}
if (reasoning) {
params.reasoning = reasoning
}
return await this.contract.execute(arbitrator, 'resolveDispute', params)
}
// Get escrow details
async getEscrowDetails(escrowId: string) {
const escrow = await this.contract.call('getEscrow', [escrowId])
return {
id: escrowId,
buyer: escrow.buyer,
seller: escrow.seller,
arbitrator: escrow.arbitrator,
amount: escrow.amount,
description: escrow.description,
status: escrow.status, // 'created', 'delivered', 'completed', 'disputed', 'resolved'
createdAt: new Date(escrow.createdAt * 1000),
deliveredAt: escrow.deliveredAt ? new Date(escrow.deliveredAt * 1000) : null,
completedAt: escrow.completedAt ? new Date(escrow.completedAt * 1000) : null,
disputeReason: escrow.disputeReason,
resolution: escrow.resolution
}
}
// Auto-release after timeout (if no disputes)
async checkTimeouts() {
const activeEscrows = await this.contract.call('getActiveEscrows', [])
const now = Math.floor(Date.now() / 1000)
for (const escrow of activeEscrows) {
if (escrow.status === 'delivered' &&
now > escrow.deliveredAt + escrow.timeout &&
!escrow.hasDispute) {
// Auto-release to seller after timeout
await this.contract.execute(null, 'autoRelease', {
escrowId: escrow.id
})
}
}
}
}
// Usage example - Marketplace transaction
const escrowService = new EscrowService('omne1escrow-contract...')
// Buyer creates escrow for purchase
const { escrowId } = await escrowService.createEscrow(
buyerAccount,
'omne1seller-address...',
'omne1arbitrator-address...',
'50000000', // 50 OMNE
'Digital artwork purchase',
7 // 7 days timeout
)
// Seller confirms delivery
await escrowService.confirmDelivery(sellerAccount, escrowId)
// Buyer releases funds after receiving item
await escrowService.releaseFunds(buyerAccount, escrowId, 5) // 5-star rating
Payment Streaming
Salary Streaming
Real-time salary streaming for continuous payment distribution.
package main
import (
"context"
"time"
"math/big"
"github.com/omne-foundation/omne-go-sdk/client"
"github.com/omne-foundation/omne-go-sdk/contracts"
)
type SalaryStream struct {
ID string
Employer string
Employee string
TotalAmount *big.Int
StartTime time.Time
EndTime time.Time
WithdrawnAmount *big.Int
IsActive bool
}
type StreamingPayroll struct {
client *client.OmneClient
contract *contracts.Contract
}
func NewStreamingPayroll(contractAddr string) (*StreamingPayroll, error) {
client, err := client.NewOmneClient(&client.Config{Network: "mainnet"})
if err != nil {
return nil, err
}
contract, err := contracts.NewContract(contractAddr, streamingABI)
if err != nil {
return nil, err
}
return &StreamingPayroll{
client: client,
contract: contract,
}, nil
}
// Create salary stream
func (sp *StreamingPayroll) CreateSalaryStream(
ctx context.Context,
employer *account.Account,
employeeAddr string,
monthlySalary *big.Int,
startDate time.Time,
durationMonths int,
) (*SalaryStream, error) {
streamID := fmt.Sprintf("salary_%d_%s", time.Now().Unix(), employeeAddr[0:8])
endDate := startDate.AddDate(0, durationMonths, 0)
totalAmount := new(big.Int).Mul(monthlySalary, big.NewInt(int64(durationMonths)))
// Create stream on contract
result, err := sp.contract.Execute(ctx, employer, "createStream", []interface{}{
streamID,
employeeAddr,
totalAmount.String(),
startDate.Unix(),
endDate.Unix(),
}, &contracts.ExecuteOptions{
Amount: totalAmount.String(),
Denom: "uomne",
})
if err != nil {
return nil, fmt.Errorf("failed to create stream: %w", err)
}
return &SalaryStream{
ID: streamID,
Employer: employer.Address(),
Employee: employeeAddr,
TotalAmount: totalAmount,
StartTime: startDate,
EndTime: endDate,
WithdrawnAmount: big.NewInt(0),
IsActive: true,
}, nil
}
// Calculate available amount for withdrawal
func (sp *StreamingPayroll) GetAvailableAmount(
ctx context.Context,
streamID string,
) (*big.Int, error) {
stream, err := sp.getStreamDetails(ctx, streamID)
if err != nil {
return nil, err
}
now := time.Now()
if now.Before(stream.StartTime) {
return big.NewInt(0), nil
}
var streamedAmount *big.Int
if now.After(stream.EndTime) {
streamedAmount = stream.TotalAmount
} else {
// Calculate pro-rata amount based on time elapsed
totalDuration := stream.EndTime.Sub(stream.StartTime)
elapsedDuration := now.Sub(stream.StartTime)
elapsedRatio := float64(elapsedDuration) / float64(totalDuration)
streamedFloat := new(big.Float).Mul(
new(big.Float).SetInt(stream.TotalAmount),
big.NewFloat(elapsedRatio),
)
streamedAmount, _ = streamedFloat.Int(nil)
}
// Available = Streamed - Already Withdrawn
available := new(big.Int).Sub(streamedAmount, stream.WithdrawnAmount)
return available, nil
}
// Employee withdraws available salary
func (sp *StreamingPayroll) WithdrawSalary(
ctx context.Context,
employee *account.Account,
streamID string,
amount *big.Int,
) error {
// Check available amount
available, err := sp.GetAvailableAmount(ctx, streamID)
if err != nil {
return err
}
if amount.Cmp(available) > 0 {
return fmt.Errorf("insufficient available amount: have %s, want %s",
available.String(), amount.String())
}
// Execute withdrawal
_, err = sp.contract.Execute(ctx, employee, "withdraw", []interface{}{
streamID,
amount.String(),
})
if err != nil {
return fmt.Errorf("withdrawal failed: %w", err)
}
return nil
}
// Get stream details
func (sp *StreamingPayroll) getStreamDetails(
ctx context.Context,
streamID string,
) (*SalaryStream, error) {
result, err := sp.contract.Call(ctx, "getStream", []interface{}{streamID})
if err != nil {
return nil, err
}
streamData := result.(map[string]interface{})
totalAmount, _ := new(big.Int).SetString(streamData["totalAmount"].(string), 10)
withdrawnAmount, _ := new(big.Int).SetString(streamData["withdrawnAmount"].(string), 10)
return &SalaryStream{
ID: streamID,
Employer: streamData["employer"].(string),
Employee: streamData["employee"].(string),
TotalAmount: totalAmount,
StartTime: time.Unix(streamData["startTime"].(int64), 0),
EndTime: time.Unix(streamData["endTime"].(int64), 0),
WithdrawnAmount: withdrawnAmount,
IsActive: streamData["isActive"].(bool),
}, nil
}
// Monitor stream and auto-notify employee
func (sp *StreamingPayroll) MonitorStream(ctx context.Context, streamID string) {
ticker := time.NewTicker(24 * time.Hour) // Check daily
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
available, err := sp.GetAvailableAmount(ctx, streamID)
if err != nil {
continue
}
// Notify employee if significant amount available
minWithdrawal := big.NewInt(1000000) // 1 OMNE
if available.Cmp(minWithdrawal) >= 0 {
sp.notifyEmployee(streamID, available)
}
}
}
}
func (sp *StreamingPayroll) notifyEmployee(streamID string, amount *big.Int) {
// Implementation: Send email/SMS/push notification
fmt.Printf("Employee has %s OMNE available for withdrawal from stream %s
",
amount.String(), streamID)
}
// Usage example
func main() {
payroll, _ := NewStreamingPayroll("omne1streaming-contract...")
// Create 6-month salary stream for $5000/month
monthlySalary := big.NewInt(5000 * 1000000) // 5000 OMNE
stream, _ := payroll.CreateSalaryStream(
context.Background(),
employerAccount,
"omne1employee...",
monthlySalary,
time.Now(),
6, // 6 months
)
// Employee can withdraw available amount anytime
available, _ := payroll.GetAvailableAmount(context.Background(), stream.ID)
payroll.WithdrawSalary(context.Background(), employeeAccount, stream.ID, available)
}
Next Steps
Production Deployment
Deploy your payment system to mainnet with monitoring and security.
Deployment Guide →