// ledger/service.go
func (l *Service) ExecutePayment(ctx context.Context, req *PaymentRequest) (*Transaction, error) {
// Check idempotency
existing, err := l.txRepo.GetByRequestID(ctx, req.RequestID)
if err == nil && existing != nil {
return existing, nil // Return existing for duplicate
}
// Start transaction
tx, err := l.db.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
defer tx.Rollback()
// Get or create accounts
senderAcc, err := l.getOrCreateAccount(ctx, tx, req.UserID, req.Currency)
if err != nil {
return nil, err
}
recipientAcc, err := l.getOrCreateAccount(ctx, tx, req.RecipientID, req.Currency)
if err != nil {
return nil, err
}
// Validate balance
if senderAcc.Balance < req.Amount {
return nil, ErrInsufficientBalance
}
// Debit sender
if err := l.debit(ctx, tx, senderAcc.ID, req.Amount); err != nil {
return nil, err
}
// Credit recipient
if err := l.credit(ctx, tx, recipientAcc.ID, req.Amount); err != nil {
return nil, err
}
// Create transaction record
txn := &Transaction{
ID: generateID(),
RequestID: req.RequestID,
UserID: req.UserID,
RecipientID: req.RecipientID,
Amount: req.Amount,
Currency: req.Currency,
Status: "COMPLETED",
CreatedAt: time.Now(),
CompletedAt: time.Now(),
}
if err := l.txRepo.Create(ctx, tx, txn); err != nil {
return nil, err
}
// Audit log
if err := l.auditLog(ctx, tx, txn); err != nil {
return nil, err
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err
}
return txn, nil
}