Zahlungs-Gateway
Zahlungs-Gateway
Whitepaper: Einleitung – Kurze Zusammenfassung
Vision
Wir entwickeln das bestmögliche erlaubnisfreie, dezentrale Open-Source-Zahlungssystem - auf Basis von Stablecoins wie eUSD, USDT, USDC, für alle Menschen weltweit - um finanzielle Inklusion und Zensurresistenz zu gewährleisten.
-
Unser Fokus liegt auf einem herausragenden Benutzererlebnis für beide Nutzergruppen in der osCASH.me APP (Smartphone) und im
-
osCASH.me GATE (Online-Shop, Offline-Kasse (POS). Der Zahlende (Freund, Kunde) und der Zahlungsempfänger (Freund, Händler, Dienstleister) muss es als so einfach empfinden, wie das zahlen mit Google-Pay.
-
Einfach, schnell, anonym, erlaubnisfrei und so gut wie kostenlos sind unsere Basics.
-
Kunden können flexibel direkt mit ihrer verbundenen Kreditkarte über ihrer Banken-APP (FinTech Curve/Garmin Pay + DeFi Sentz) auf ihrem Smartphone bezahlen.
-
Der Empfänger hat die Freiheit, sein Guthaben in seiner Self Custody Wallet (osCASH.me, Sentz, Signal, usw.) zu verwalten, indem er es in andere Coins wie MOB, Monero, USDT oder USDC umtauscht oder in Fiatwährung auszahlt, was maximale Flexibilität und Liquidität bietet.
Folge gerne unserer Entwicklung: Wiki auf Github
Der Zahlungsempfänger generiert in seiner osCASH.me APP am Phone oder über das osCASH.me GATE (über API-Aufruf in seinem Webshop) einen Zahlungs QR-Code oder sendet einen Quick-Link als Zahlungsaufforderung per Messenger. Der Kunde kann diese Aufforderung einfach und schnell auf seinem Gerät (Smartphone oder Laptop) in seiner FinTech Banking-App oder DeFi Phone APP bestätigen, wodurch eine nahtlose und effiziente Transaktion möglicht wird.
Unser Zahlungs-Gateway aus der Sichtweise von fünf Zielgruppen
Zielgruppe 1: Erkläre es mir, als wäre ich fünf Jahre alt
Hallo! Stell dir vor, wir machen eine Art Zauber-Geld, das jeder auf der Welt benutzen kann, ohne dass jemand sagt: „Das darfst du nicht!“ Es heißt eUSD und lebt in einem besonderen Computer-Spiel namens MobileCoin, wo alles sicher und schnell ist. Wenn du etwas kaufen willst, wie ein Eis, kannst du mit deiner Spielkarte (wie eine Kreditkarte) oder einer lustigen App auf deinem Handy bezahlen. Der Eisverkäufer macht einen magischen Code mit seinem Handy, den du mit deinem Handy anschaust und sagst: „Ja, das ist okay!“ Dann hat er das Geld, und du hast dein Eis – ganz einfach! Er kann das Geld behalten oder es in andere Zauber-Münzen tauschen, die er mag.
Zielgruppe 2: Eltern & Großeltern
Unser Zahlungs-Gateway ist wie eine moderne Art, Geld zu schicken oder zu empfangen, aber ohne die komplizierten Banken, die du kennst. Stelle dir vor, du gehst in ein Café und bezahlst mit deiner Kreditkarte oder Apple Pay – das kannst du hier auch, nur dass das Geld in eine digitale Währung namens eUSD umgewandelt wird, die stabil ist wie der Dollar. Oder du nutzt eine App namens Sentz auf deinem Smartphone, ähnlich wie Online-Banking, aber schneller. Der Verkäufer erstellt einen QR-Code (wie die Codes, die du manchmal scannst) oder schickt dir einen Link über WhatsApp. Du klickst darauf, bestätigst die Zahlung mit deiner Bank-App oder Sentz, und fertig! Der Verkäufer kann das Geld behalten, es in andere digitale Münzen wie USDT tauschen oder auf sein Bankkonto überweisen – alles ohne Warteschlangen am Bankautomaten.
Zielgruppe 3: PowerUser & Studierende
Ihr seid mit Signal, WhatsApp und Krypto wie Bitcoin bei Revolut oder Trust Wallet vertraut – unser Zahlungs-Gateway passt perfekt zu euch! Es nutzt eUSD, einen Stablecoin auf der MobileCoin-Blockchain, der stabil bleibt und super schnell ist (Transaktionen in 5 Sekunden). Ihr könnt mit eurer Kreditkarte über Revolut oder direkt in der Sentz-App bezahlen – keine Umwege. Der Händler schickt euch einen QR-Code oder einen Quick-Link über eure Messenger-App, den ihr scannt oder anklickt, und bestätigt die Zahlung mit einem Klick in Sentz oder eurer Banking-App. Der Händler kann sein eUSD in MOB, USDT oder USDC umtauschen oder es in Fiat auszahlen lassen, alles in der Sentz-App. Es ist dezentral, Open-Source und zensurresistent – genau das, was ihr für euren digitalen Lifestyle braucht.
Zielgruppe 4: Softwareentwickler
Unser Open-Source-Zahlungs-Gateway ist ein erlaubnisfreies, dezentrales System, das auf Stablecoins wie eUSD basiert, einem asset-backed Token auf der MobileCoin-Blockchain mit Ende-zu-Ende-Verschlüsselung und 5-Sekunden-Settlement-Zeiten. Es integriert die Sentz-App (Apache-2.0-lizenziert) für Wallet- und Kreditkartenzahlungen, mit APIs wie Full Service und JSON RPC für Backend-Integration, verfügbar auf MobileCoin GitHub. Der Zahlungsempfänger generiert QR-Codes oder Quick-Links über eine REST-API oder die App, die Kunden in ihrer Banking-App oder Sentz bestätigen. Transaktionen werden über MobileCoin abgewickelt, mit einer Gebühr von 0,0026 USD, und der Empfänger kann sein Guthaben in der Sentz-App in MOB, USDT oder USDC konvertieren oder via Fiat-Offramp auszahlen. Langfristig planen wir die Integration mit Mixin’s MixPay für breitere Krypto-Unterstützung, was zusätzliche Skalierbarkeit bietet. Entwickler können das System anpassen, um finanzielle Inklusion und Zensurresistenz zu fördern.
Zielgruppe 5: INSIDE – Freelancer & Business Guys
Ihr finanziert euer freies Leben mit Open-Source-Strategien – unser Zahlungs-Gateway ist euer Werkzeug! Es ist ein dezentrales, erlaubnisfreies System auf Basis von eUSD, einem Stablecoin, der stabil bleibt und euch volle Kontrolle gibt. Als Freelancer oder Unternehmer generiert ihr mit unserer App oder einem API-Aufruf in eurem Webshop einen QR-Code oder Quick-Link, den ihr per Messenger an Kunden schickt. Eure Kunden bezahlen mit Kreditkarte (über ihre Bank) oder der Sentz-App, und ihr seht die eUSD sofort in eurer Wallet. In der Sentz-App könnt ihr das Geld flexibel verwalten: behalten, in MOB für MobileCoin-Zahlungen, USDT/USDC für Krypto-Handel oder in Fiat auf euer Konto auszahlen – alles ohne Mittelsmänner. Es ist einfach, effizient und Open-Source, sodass ihr eure Unabhängigkeit maximieren könnt.
Schlussfolgerung
Dieses Whitepaper legt den Grundstein für ein Open-Source-Zahlungs-Gateway, das finanzielle Inklusion und Zensurresistenz durch ein benutzerfreundliches, dezentrales System fördert. Die Vision und der Zahlungsvorgang sind auf die Bedürfnisse aller Zielgruppen zugeschnitten, von Kindern bis zu Entwicklern und Freelancern, und nutzen die Stärken des Sentz-Ökosystems mit Blick auf eine zukünftige Mixin-Integration.

Privatsphäre ist DEIN Grundrecht

Key Citations
- Electronic Dollar (eUSD)
- Sentz: The Wallet Built for Payments
- Invoicing in Sentz: Freelancer Payments Simplified
- MobileCoin GitHub SDKs
- CoinMarketCap Electronic USD Market Data
Technische Dokumentation: Erlaubnisfreies Open Source Zahlungsgateway basierend auf MobileCoin
Überblick
Diese technische Dokumentation zeigt die Implementierung eines erlaubnisfreien Open Source Zahlungsgateways basierend auf einem Molly Fork mit MobileCoin-Integration und modularem Add-On System. Das System kombiniert die bewährte Signal-Protokoll-Basis von Molly mit MobileCoin's datenschutzfreundlichen Zahlungsfunktionen und einer erweiterbaren Plugin-Architektur.
Die Lösung bietet:
- F-Droid kompatible MobileCoin-Wallet basierend auf Molly-FOSS
- Modulares Add-On System für erweiterte Zahlungsfunktionen
- Offline-fähige Transaktionen mit Mesh-Network-Unterstützung
- Cross-Chain Swaps über SENTZ und Mixin Network APIs
- Vollständige EU-Compliance für erlaubnisfreie Zahlungssysteme
1. MOLLY FORK als technische Basis
Codebase-Architektur
Molly bietet die ideale Grundlage für ein MobileCoin-Zahlungsgateway durch zwei Varianten:
Molly-FOSS (F-Droid kompatibel):
- Repository: https://github.com/mollyim/mollyim-android
- Lizenz: GNU AGPLv3
- Build-System: Gradle mit Docker-Containerisierung
- Sicherheitsfeatures: Passphrase-Verschlüsselung, RAM-Shredding, Tor-Support
Technische Unterschiede zu Signal
Vorteile für Payment-Integration:
// Enhanced Security Model in Molly
public class MollySecurityManager {
private PassphraseDatabase passphraseDB;
private RAMShredder ramShredder;
private ProxyManager proxyManager;
public void initializePaymentSecurity() {
// Zusätzliche Verschlüsselung für Wallet-Keys
passphraseDB.enableWalletEncryption();
// Sichere Speicher-Bereinigung
ramShredder.schedulePaymentDataCleanup();
// Tor-Integration für MobileCoin-Netzwerk
proxyManager.configureMobileCoinProxy();
}
}
F-Droid Kompatibilität:
- Keine Google Dependencies: Entfernt Google Play Services, Firebase
- UnifiedPush: Dezentrale Benachrichtigungen via MollySocket
- OpenStreetMap: Ersetzt Google Maps
- Reproducible Builds: Vollständig reproduzierbare Builds
MobileCoin Integration in Molly
Gradle Konfiguration:
repositories {
maven {
url "https://dl.cloudsmith.io/qxAgwaeEE1vN8aLU/mobilecoin/mobilecoin/maven/"
}
}
dependencies {
implementation 'com.mobilecoin:android-sdk:4.0.0'
implementation 'com.mobilecoin:libmobilecoin-foss:1.0.0' // FOSS-Version
}
Core Payment Integration:
class MollyMobileCoinManager {
private lateinit var mobileCoinClient: MobileCoinClient
private lateinit var accountKey: AccountKey
fun initializeWallet(entropy: String, context: Context) {
// FOSS Fog Services für F-Droid Kompatibilität
val fogUri = Uri.parse("fog://foss-fog-service.example.com")
val consensusUri = Uri.parse("mc://consensus.mobilecoin.network")
accountKey = AccountKey.fromBip39Entropy(
entropy,
fogUri,
getFogReportId(),
getFogAuthorityKey()
)
mobileCoinClient = MobileCoinClient(
accountKey,
fogUri,
consensusUri,
FOSSAttestationVerifier() // SGX-Alternative
)
}
suspend fun sendPayment(recipient: PublicAddress, amount: BigInteger): TransactionResult {
val fee = mobileCoinClient.estimateTotalFee(amount)
val pending = mobileCoinClient.prepareTransaction(recipient, amount, fee)
return mobileCoinClient.submitTransaction(pending.transaction)
}
}
2. MobileCoin SDK & Protokoll Implementation
Android/Kotlin SDK Integration
Account Management:
class MobileCoinAccountManager {
companion object {
private const val FOG_URI = "fog://fog.prod.mobilecoinww.com"
private const val CONSENSUS_URI = "mc://node1.prod.mobilecoinww.com"
}
fun createAccount(bip39Entropy: ByteArray): AccountKey {
return AccountKey.fromBip39Entropy(
bip39Entropy,
Uri.parse(FOG_URI),
fogReportId,
fogAuthorityKey
)
}
suspend fun getBalance(): Balance {
return mobileCoinClient.getBalance()
}
suspend fun getTransactionHistory(): List<TxOut> {
return mobileCoinClient.getOwnedTxOuts()
}
}
eUSD Stablecoin Implementation
eUSD Charakteristika:
- 1:1 Asset-backed durch diversifizierte Stablecoin-Basket
- Transaktionsgebühr: Fixe $0,0025 USD
- Settlement-Zeit: Unter 5 Sekunden
- Privacy: End-to-End Zero-Knowledge Verschlüsselung
class EUSDManager {
fun calculateEUSDFee(amount: BigInteger): BigInteger {
// Feste Gebühr unabhängig von Transaktionsgröße
return BigInteger.valueOf(2500) // $0.0025 in picoUSD
}
suspend fun swapMOBToEUSD(mobAmount: BigInteger): SwapResult {
val swapRate = getCurrentSwapRate()
val eUSDAmount = mobAmount.multiply(swapRate)
return executeSwap(mobAmount, eUSDAmount, TokenType.EUSD)
}
}
Offline Transaction Capabilities
Offline Transaction Builder:
class OfflineTransactionBuilder {
fun buildOfflineTransaction(
accountKey: AccountKey,
recipient: PublicAddress,
amount: BigInteger
): OfflineTransaction {
// Lokale UTXO Auswahl ohne Netzwerk
val utxos = localUTXOManager.selectUTXOs(amount)
// Ring Signature Vorbereitung
val ringMembers = localRingManager.getCachedRingMembers()
return OfflineTransaction.builder()
.setInputs(utxos)
.setOutputs(createOutputs(recipient, amount))
.setRingMembers(ringMembers)
.build()
}
fun queueForBroadcast(tx: OfflineTransaction) {
offlineTransactionQueue.add(tx)
// Broadcast wenn Netzwerk verfügbar
networkManager.onConnected {
broadcastQueuedTransactions()
}
}
}
Payment Request Protocol
QR Code Generation (BIP21-Style):
class PaymentRequestGenerator {
fun generatePaymentQR(
publicAddress: PublicAddress,
amount: BigInteger?,
label: String?,
message: String?
): String {
val uri = StringBuilder("mobilecoin:")
uri.append(publicAddress.toB58String())
val params = mutableListOf<String>()
amount?.let { params.add("amount=$it") }
label?.let { params.add("label=${URLEncoder.encode(it)}") }
message?.let { params.add("message=${URLEncoder.encode(it)}") }
if (params.isNotEmpty()) {
uri.append("?").append(params.joinToString("&"))
}
return uri.toString()
}
fun generateQRCode(paymentURI: String): Bitmap {
return QRCode.from(paymentURI)
.withSize(300, 300)
.withErrorCorrection(ErrorCorrectionLevel.M)
.bitmap()
}
}
3. SENTZ und MIXIN Network API Integration
SENTZ Full Service API
JSON RPC 2.0 Integration:
class SENTZApiClient {
private val httpClient = HttpClient {
install(ContentNegotiation) {
gson()
}
install(Auth) {
header {
headers.append("X-API-KEY", apiKey)
}
}
}
data class RPCRequest(
val jsonrpc: String = "2.0",
val method: String,
val params: Map<String, Any>,
val id: Int
)
suspend fun createAccount(name: String): Account {
val request = RPCRequest(
method = "create_account",
params = mapOf("name" to name),
id = generateId()
)
return httpClient.post("http://localhost:9090") {
setBody(request)
}.body()
}
suspend fun buildTransaction(
accountId: String,
recipient: String,
amount: String
): PendingTransaction {
return httpClient.post("http://localhost:9090") {
setBody(RPCRequest(
method = "build_transaction",
params = mapOf(
"account_id" to accountId,
"recipient_public_address" to recipient,
"amount" to amount
),
id = generateId()
))
}.body()
}
}
MIXIN Network Bot API
JWT Authentication:
class MixinBotClient {
fun generateJWT(
uid: String,
sid: String,
privateKey: String,
method: String,
uri: String,
body: String
): String {
val expire = System.currentTimeMillis() / 1000 + 30 * 60
val sum = MessageDigest.getInstance("SHA-256")
.digest("$method$uri$body".toByteArray())
.toHex()
val claims = mapOf(
"uid" to uid,
"sid" to sid,
"iat" to System.currentTimeMillis() / 1000,
"exp" to expire,
"jti" to UUID.randomUUID().toString(),
"sig" to sum,
"scp" to "FULL"
)
return Jwts.builder()
.setClaims(claims)
.signWith(parsePrivateKey(privateKey), SignatureAlgorithm.EdDSA)
.compact()
}
suspend fun createTransfer(
assetId: String,
recipientId: String,
amount: String,
traceId: String
): Transfer {
val body = mapOf(
"asset_id" to assetId,
"opponent_id" to recipientId,
"amount" to amount,
"trace_id" to traceId,
"memo" to "Payment via Gateway"
)
return httpClient.post("https://api.mixin.one/transfers") {
header("Authorization", "Bearer ${generateJWT(...)}")
setBody(body)
}.body()
}
}
SWAP Funktionalität
Multi-Chain Swap Implementation:
class SwapManager {
suspend fun executeSwap(
fromAsset: String,
toAsset: String,
amount: BigDecimal,
slippage: Double = 0.01
): SwapResult {
// SENTZ für eUSD/MOB Swaps
if (isInternalSwap(fromAsset, toAsset)) {
return executeSENTZSwap(fromAsset, toAsset, amount)
}
// Mixin Network für Cross-Chain Swaps
return executeMixinSwap(fromAsset, toAsset, amount, slippage)
}
private suspend fun executeMixinSwap(
fromAsset: String,
toAsset: String,
amount: BigDecimal,
slippage: Double
): SwapResult {
// MixPay Integration für DEX-Routing
val quote = mixPayClient.getSwapQuote(fromAsset, toAsset, amount)
val minReceived = quote.outputAmount * (1 - slippage).toBigDecimal()
return mixPayClient.executeSwap(
SwapRequest(
inputAsset = fromAsset,
outputAsset = toAsset,
inputAmount = amount,
minOutputAmount = minReceived,
traceId = UUID.randomUUID().toString()
)
)
}
}
4. Modulare Add-On Architektur
Plugin Management System
Plugin Registry:
interface PaymentPlugin {
val id: String
val name: String
val version: String
val requiredPermissions: List<Permission>
fun initialize(context: PluginContext)
fun onPaymentRequest(request: PaymentRequest): PaymentResponse?
fun onTransactionComplete(transaction: Transaction)
fun cleanup()
}
class PluginManager {
private val plugins = mutableMapOf<String, PaymentPlugin>()
private val pluginContexts = mutableMapOf<String, PluginContext>()
fun registerPlugin(plugin: PaymentPlugin) {
// Verify plugin signature
if (!verifyPluginSignature(plugin)) {
throw SecurityException("Invalid plugin signature")
}
// Create isolated context
val context = createPluginContext(plugin)
plugin.initialize(context)
plugins[plugin.id] = plugin
pluginContexts[plugin.id] = context
}
fun processPaymentRequest(request: PaymentRequest): PaymentResponse {
var response = PaymentResponse(request)
// Process through all applicable plugins
plugins.values.forEach { plugin ->
plugin.onPaymentRequest(request)?.let { pluginResponse ->
response = response.merge(pluginResponse)
}
}
return response
}
}
REST API für Add-Ons
API Endpoints:
@RestController
@RequestMapping("/api/v2")
class PluginAPIController {
@GetMapping("/plugins")
fun listPlugins(): List<PluginInfo> {
return pluginManager.getRegisteredPlugins()
}
@PostMapping("/plugins/{pluginId}/execute")
fun executePlugin(
@PathVariable pluginId: String,
@RequestBody request: PluginExecutionRequest
): PluginExecutionResponse {
return pluginManager.executePlugin(pluginId, request)
}
@PostMapping("/payments/process")
fun processPayment(
@RequestBody paymentRequest: PaymentRequest
): PaymentResponse {
// Durch Plugin-Pipeline verarbeiten
return pluginManager.processPaymentRequest(paymentRequest)
}
@GetMapping("/offline/queue")
fun getOfflineQueue(): List<QueuedTransaction> {
return offlineTransactionManager.getQueuedTransactions()
}
@PostMapping("/conflicts/resolve")
fun resolveConflict(
@RequestBody resolution: ConflictResolution
): ConflictResult {
return conflictResolver.resolve(resolution)
}
}
Authentication zwischen Wallet und Add-On Server
OAuth 2.0 + HMAC Signing:
class PluginAuthenticator {
fun authenticatePluginRequest(
pluginId: String,
request: HttpServletRequest
): AuthenticationResult {
// JWT Token Validation
val token = extractBearerToken(request)
val claims = validateJWT(token)
// HMAC Signature Verification
val signature = request.getHeader("X-Plugin-Signature")
val body = request.inputStream.readBytes()
val expectedSignature = calculateHMAC(pluginId, body)
if (!verifySignature(signature, expectedSignature)) {
throw AuthenticationException("Invalid signature")
}
return AuthenticationResult.success(claims)
}
private fun calculateHMAC(pluginId: String, data: ByteArray): String {
val secret = getPluginSecret(pluginId)
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(secret, "HmacSHA256"))
return Base64.getEncoder().encodeToString(mac.doFinal(data))
}
}
5. Offline Payment Implementation
Mesh Network Payment Protocol
Bluetooth LE Discovery:
class MeshPaymentNetwork {
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private val connectedPeers = mutableSetOf<BluetoothDevice>()
fun startDiscovery() {
val advertiser = bluetoothAdapter.bluetoothLeAdvertiser
val settings = AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setConnectable(true)
.build()
val data = AdvertiseData.Builder()
.addServiceUuid(ParcelUuid(MESH_PAYMENT_UUID))
.addServiceData(ParcelUuid(MESH_PAYMENT_UUID), getNodeInfo())
.build()
advertiser.startAdvertising(settings, data, advertisingCallback)
}
fun propagateTransaction(transaction: OfflineTransaction) {
val payload = TransactionPayload(
transaction = transaction,
ttl = 10, // Maximum 10 hops
sender = getNodeId(),
timestamp = System.currentTimeMillis()
)
connectedPeers.forEach { peer ->
sendToPeer(peer, payload)
}
}
private fun sendToPeer(peer: BluetoothDevice, payload: TransactionPayload) {
val gatt = peer.connectGatt(context, false, object : BluetoothGattCallback() {
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val characteristic = gatt.getService(MESH_PAYMENT_UUID)
?.getCharacteristic(TRANSACTION_CHARACTERISTIC_UUID)
characteristic?.value = payload.serialize()
gatt.writeCharacteristic(characteristic)
}
})
}
}
Conflict Resolution System
Vector Clock Implementation:
class ConflictResolver {
data class VectorClock(val clocks: MutableMap<String, Long> = mutableMapOf()) {
fun increment(nodeId: String) {
clocks[nodeId] = (clocks[nodeId] ?: 0) + 1
}
fun update(other: VectorClock) {
other.clocks.forEach { (nodeId, clock) ->
clocks[nodeId] = maxOf(clocks[nodeId] ?: 0, clock)
}
}
fun happensBefore(other: VectorClock): Boolean {
return clocks.all { (nodeId, clock) ->
clock <= (other.clocks[nodeId] ?: 0)
} && this != other
}
}
fun resolveTransactionConflict(
tx1: OfflineTransaction,
tx2: OfflineTransaction
): ConflictResolution {
// Vector Clock Vergleich
when {
tx1.vectorClock.happensBefore(tx2.vectorClock) -> {
return ConflictResolution.ACCEPT_TX2
}
tx2.vectorClock.happensBefore(tx1.vectorClock) -> {
return ConflictResolution.ACCEPT_TX1
}
else -> {
// Concurrent transactions - use reputation
return resolveByReputation(tx1, tx2)
}
}
}
private fun resolveByReputation(
tx1: OfflineTransaction,
tx2: OfflineTransaction
): ConflictResolution {
val reputation1 = reputationManager.getReputation(tx1.sender)
val reputation2 = reputationManager.getReputation(tx2.sender)
return if (reputation1 > reputation2) {
ConflictResolution.ACCEPT_TX1
} else {
ConflictResolution.ACCEPT_TX2
}
}
}
Double-Spend Prevention
UTXO Reservation System:
class UTXOReservationManager {
private val reservedUTXOs = mutableMapOf<String, UTXOReservation>()
private val reputationThreshold = 7.0 // Minimum reputation für Auto-Accept
data class UTXOReservation(
val utxoId: String,
val reservedBy: String,
val timestamp: Long,
val transactionId: String,
val reputation: Double
)
fun reserveUTXO(
utxoId: String,
nodeId: String,
transactionId: String
): ReservationResult {
val existingReservation = reservedUTXOs[utxoId]
val nodeReputation = reputationManager.getReputation(nodeId)
if (existingReservation != null) {
// Conflict detected
return handleReservationConflict(
existingReservation,
nodeId,
nodeReputation,
transactionId
)
}
// Create new reservation
val reservation = UTXOReservation(
utxoId = utxoId,
reservedBy = nodeId,
timestamp = System.currentTimeMillis(),
transactionId = transactionId,
reputation = nodeReputation
)
reservedUTXOs[utxoId] = reservation
return ReservationResult.Success(reservation)
}
private fun handleReservationConflict(
existing: UTXOReservation,
newNodeId: String,
newReputation: Double,
newTransactionId: String
): ReservationResult {
// High reputation nodes can override low reputation
if (newReputation >= reputationThreshold &&
existing.reputation < reputationThreshold) {
reservedUTXOs[existing.utxoId] = existing.copy(
reservedBy = newNodeId,
transactionId = newTransactionId,
reputation = newReputation,
timestamp = System.currentTimeMillis()
)
return ReservationResult.Override(existing, newNodeId)
}
return ReservationResult.Conflict(existing)
}
}
6. Technische Architektur
Empfohlener Technology Stack
Backend Services (Go):
// Payment Gateway Server
type PaymentGateway struct {
mobileCoinClient *mobilecoin.Client
pluginManager *plugin.Manager
offlineQueue *queue.TransactionQueue
conflictResolver *conflict.Resolver
}
func (pg *PaymentGateway) ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) {
// Plugin preprocessing
pluginResp := pg.pluginManager.PreProcess(req)
if pluginResp.Abort {
return pluginResp.Response, nil
}
// Check if online or offline
if pg.isOnline() {
return pg.processOnlinePayment(ctx, req)
} else {
return pg.processOfflinePayment(ctx, req)
}
}
func (pg *PaymentGateway) processOfflinePayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) {
// Build offline transaction
tx, err := pg.buildOfflineTransaction(req)
if err != nil {
return nil, err
}
// Queue for later broadcast
if err := pg.offlineQueue.Enqueue(tx); err != nil {
return nil, err
}
// Propagate through mesh network
pg.meshNetwork.Propagate(tx)
return &PaymentResponse{
Status: "queued",
TransactionID: tx.ID,
}, nil
}
Plugin Runtime (Node.js):
class PluginRuntime {
constructor() {
this.plugins = new Map();
this.vm = new VM({
timeout: 5000,
sandbox: this.createSecureSandbox()
});
}
async loadPlugin(pluginCode, manifest) {
// Verify plugin signature
if (!this.verifySignature(pluginCode, manifest.signature)) {
throw new Error('Invalid plugin signature');
}
// Create isolated execution context
const context = this.createPluginContext(manifest);
// Execute plugin in sandbox
const plugin = this.vm.run(pluginCode, context);
this.plugins.set(manifest.id, plugin);
return plugin;
}
createSecureSandbox() {
return {
console: console,
setTimeout: setTimeout,
clearTimeout: clearTimeout,
// Restricted API access
fetch: this.createRestrictedFetch(),
localStorage: this.createIsolatedStorage(),
};
}
async executePlugin(pluginId, method, params) {
const plugin = this.plugins.get(pluginId);
if (!plugin) {
throw new Error(`Plugin ${pluginId} not found`);
}
return await plugin[method](params);
}
}
Database Schema
PostgreSQL Schema:
-- Haupttabellen
CREATE TABLE accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
public_address TEXT UNIQUE NOT NULL,
encrypted_private_key TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE transactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
account_id UUID REFERENCES accounts(id),
recipient_address TEXT NOT NULL,
amount BIGINT NOT NULL,
fee BIGINT NOT NULL,
status transaction_status NOT NULL DEFAULT 'pending',
transaction_hash TEXT,
block_height BIGINT,
created_at TIMESTAMP DEFAULT NOW(),
confirmed_at TIMESTAMP
) PARTITION BY RANGE (created_at);
-- Offline Transaction Queue
CREATE TABLE offline_transaction_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
transaction_data JSONB NOT NULL,
retry_count INTEGER DEFAULT 0,
max_retries INTEGER DEFAULT 5,
next_retry TIMESTAMP DEFAULT NOW(),
created_at TIMESTAMP DEFAULT NOW()
);
-- Plugin Management
CREATE TABLE plugins (
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
version VARCHAR(50) NOT NULL,
code TEXT NOT NULL,
signature TEXT NOT NULL,
enabled BOOLEAN DEFAULT false,
installed_at TIMESTAMP DEFAULT NOW()
);
-- Conflict Resolution
CREATE TABLE transaction_conflicts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
transaction_id_1 UUID NOT NULL,
transaction_id_2 UUID NOT NULL,
resolution_strategy VARCHAR(50) NOT NULL,
resolved_at TIMESTAMP,
chosen_transaction_id UUID
);
-- Reputation System
CREATE TABLE node_reputation (
node_id VARCHAR(255) PRIMARY KEY,
reputation_score DECIMAL(3,1) DEFAULT 5.0,
total_transactions INTEGER DEFAULT 0,
successful_transactions INTEGER DEFAULT 0,
last_updated TIMESTAMP DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_transactions_account_created
ON transactions (account_id, created_at DESC);
CREATE INDEX idx_transactions_status
ON transactions (status, created_at);
CREATE INDEX idx_offline_queue_retry
ON offline_transaction_queue (next_retry, retry_count);
Message Queue (Apache Kafka)
Kafka Topics:
# kafka-topics.yml
topics:
- name: payment.requests
partitions: 12
replication_factor: 3
config:
retention.ms: 604800000 # 7 days
- name: payment.responses
partitions: 12
replication_factor: 3
- name: offline.transactions
partitions: 6
replication_factor: 3
- name: conflict.resolutions
partitions: 3
replication_factor: 3
- name: plugin.events
partitions: 6
replication_factor: 3
Kafka Producer (Go):
type PaymentEventProducer struct {
producer sarama.AsyncProducer
}
func (p *PaymentEventProducer) PublishPaymentEvent(event PaymentEvent) error {
msg := &sarama.ProducerMessage{
Topic: "payment.requests",
Key: sarama.StringEncoder(event.TransactionID),
Value: sarama.StringEncoder(event.ToJSON()),
Headers: []sarama.RecordHeader{
{
Key: []byte("event_type"),
Value: []byte(event.Type),
},
{
Key: []byte("source"),
Value: []byte("payment-gateway"),
},
},
}
p.producer.Input() <- msg
return nil
}
7. QR Code Standards und UI Patterns
EPC QR Code Implementation
class EPCQRGenerator {
fun generateEPCQR(
iban: String,
beneficiaryName: String,
amount: Double,
reference: String
): String {
return buildString {
appendLine("BCD")
appendLine("002")
appendLine("2")
appendLine("SCT")
appendLine("") // BIC optional
appendLine(beneficiaryName)
appendLine(iban)
appendLine("EUR${amount}")
appendLine("") // Purpose code optional
appendLine(reference)
appendLine("") // Unstructured remittance
}
}
fun createQRBitmap(epcData: String): Bitmap {
val writer = QRCodeWriter()
val bitMatrix = writer.encode(
epcData,
BarcodeFormat.QR_CODE,
300,
300,
mapOf(
EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.M,
EncodeHintType.CHARACTER_SET to "UTF-8"
)
)
return bitMatrix.toBitmap()
}
}
Progressive Disclosure UI
@Composable
fun PaymentForm() {
var showAdvanced by remember { mutableStateOf(false) }
var selectedMethod by remember { mutableStateOf(PaymentMethod.MOBILECOIN) }
Column(modifier = Modifier.padding(16.dp)) {
// Basic Payment Section
PaymentMethodSelector(
selectedMethod = selectedMethod,
onMethodSelected = { selectedMethod = it }
)
AmountInput()
RecipientInput()
// Progressive Disclosure für Advanced Features
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { showAdvanced = !showAdvanced }
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Advanced Options")
Icon(
imageVector = if (showAdvanced) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
contentDescription = null
)
}
AnimatedVisibility(visible = showAdvanced) {
Column(modifier = Modifier.padding(16.dp)) {
// Advanced Features nur wenn benötigt
when (selectedMethod) {
PaymentMethod.MOBILECOIN -> {
OfflinePaymentToggle()
FeeSelector()
PrivacyLevelSelector()
}
PaymentMethod.EUSD -> {
SwapOptionsSelector()
ScheduledPaymentOption()
}
}
PluginOptionsSection()
}
}
}
}
// Submit Button
Button(
onClick = { /* Process payment */ },
modifier = Modifier.fillMaxWidth()
) {
Text("Send Payment")
}
}
}
@Composable
fun PluginOptionsSection() {
val availablePlugins = remember { mutableStateOf(emptyList<Plugin>()) }
LaunchedEffect(Unit) {
availablePlugins.value = pluginManager.getAvailablePlugins()
}
availablePlugins.value.forEach { plugin ->
if (plugin.hasPaymentOptions()) {
plugin.RenderPaymentOptions()
}
}
}
Offline-First Design Patterns
class OfflineFirstRepository(
private val localDB: PaymentDatabase,
private val apiClient: PaymentApiClient,
private val syncManager: SyncManager
) {
suspend fun getTransactions(): Flow<List<Transaction>> =
localDB.transactionDao().getAllTransactions()
suspend fun createPayment(payment: Payment): Result<Payment> {
// Lokale Transaktion sofort erstellen
val localPayment = payment.copy(
status = TransactionStatus.PENDING,
createdAt = System.currentTimeMillis()
)
localDB.transactionDao().insert(localPayment)
// Sync Queue hinzufügen
syncManager.enqueueSync(SyncOperation.CREATE_PAYMENT, localPayment)
return Result.success(localPayment)
}
suspend fun syncWhenOnline() {
if (!networkManager.isOnline()) return
val pendingOperations = syncManager.getPendingOperations()
pendingOperations.forEach { operation ->
try {
when (operation.type) {
SyncOperation.CREATE_PAYMENT -> {
val result = apiClient.createPayment(operation.data)
updateLocalTransaction(operation.data.id, result)
syncManager.markCompleted(operation)
}
SyncOperation.UPDATE_STATUS -> {
val result = apiClient.getTransactionStatus(operation.data.id)
updateLocalTransaction(operation.data.id, result)
syncManager.markCompleted(operation)
}
}
} catch (e: Exception) {
// Exponential backoff für failed syncs
syncManager.scheduleRetry(operation, calculateBackoff(operation.retryCount))
}
}
}
private fun calculateBackoff(retryCount: Int): Long {
return minOf(30000 * (2.0.pow(retryCount)).toLong(), 300000) // Max 5 minutes
}
}
8. Compliance und rechtliche Aspekte
Erlaubnisfreie Implementierung
Self-Custody Wallet Kriterien:
class ComplianceManager {
fun ensureSelfCustodyCompliance(): ComplianceResult {
val checks = listOf(
"Private keys generated and stored only on user device",
"No central server access to user funds",
"User has full control over transaction signing",
"Backup/restore handled by user only",
"No custodial services provided"
)
return ComplianceResult(
isCompliant = checks.all { checkCompliance(it) },
checks = checks,
recommendations = generateRecommendations()
)
}
fun checkMiCACompliance(): MiCAComplianceResult {
// MiCA Artikel 3(2) - CASP Definition prüfen
val isCASP = checksCASPCriteria()
return MiCAComplianceResult(
requiresAuthorization = isCASP,
applicableObligations = if (isCASP) getCASPObligations() else emptyList(),
exemptions = getApplicableExemptions()
)
}
private fun checksCASPCriteria(): Boolean {
return listOf(
hasControlOverCryptoAssets(),
providesCustodyServices(),
operatesExchangeServices(),
providesMarketMaking()
).any { it }
}
}
F-Droid Compliance Checklist
# fdroid-compliance.yml
compliance_checks:
source_code:
- "All code available under FOSS license"
- "No proprietary dependencies"
- "Reproducible builds enabled"
google_services:
- "No Google Play Services"
- "No Firebase dependencies"
- "No Google Analytics"
- "Alternative push notifications (UnifiedPush)"
mobilecoin_specific:
- "libmobilecoin built from source"
- "No Intel SGX proprietary components"
- "FOSS fog service implementation"
- "Open source attestation verification"
build_system:
- "gradle.build configured for F-Droid"
- "No binary dependencies in repository"
- "All dependencies from trusted Maven repositories"
anti_features:
tracking: false
ads: false
non_free_net: false # FOSS fog services required
non_free_add: false
non_free_dep: false
9. Deployment und Monitoring
Docker Compose Setup
# docker-compose.yml
version: '3.8'
services:
payment-gateway:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/payment_gateway
- KAFKA_BROKERS=kafka:9092
- MOBILECOIN_CONSENSUS_URI=mc://consensus.mobilecoin.network
- MOBILECOIN_FOG_URI=fog://foss-fog-service.example.com
depends_on:
- db
- kafka
- redis
plugin-runtime:
build: ./plugin-runtime
ports:
- "3000:3000"
environment:
- GATEWAY_URL=http://payment-gateway:8080
- NODE_ENV=production
volumes:
- ./plugins:/app/plugins
db:
image: postgres:15
environment:
POSTGRES_DB: payment_gateway
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- postgres_data:/var/lib/postgresql/data
kafka:
image: confluentinc/cp-kafka:latest
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
depends_on:
- zookeeper
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
volumes:
postgres_data:
redis_data:
grafana_data:
Monitoring Setup
// Prometheus Metrics
var (
paymentRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "payment_requests_total",
Help: "Total number of payment requests",
},
[]string{"method", "status"},
)
paymentDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "payment_duration_seconds",
Help: "Payment processing duration",
Buckets: []float64{0.1, 0.5, 1.0, 2.0, 5.0},
},
[]string{"method"},
)
offlineQueueSize = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "offline_queue_size",
Help: "Number of transactions in offline queue",
},
)
)
func (pg *PaymentGateway) recordMetrics(method string, duration time.Duration, status string) {
paymentRequests.WithLabelValues(method, status).Inc()
paymentDuration.WithLabelValues(method).Observe(duration.Seconds())
queueSize := pg.offlineQueue.Size()
offlineQueueSize.Set(float64(queueSize))
}
Fazit und nächste Schritte
Diese technische Dokumentation zeigt einen vollständigen Implementierungsleitfaden für ein erlaubnisfreies Open Source Zahlungsgateway basierend auf MobileCoin mit modularer Add-On-Architektur.
Die Lösung bietet:
- Vollständige F-Droid Kompatibilität durch Molly-FOSS Basis
- Enterprise-taugliche Plugin-Architektur für modulare Erweiterungen
- Robuste Offline-Funktionalität mit Mesh-Network-Unterstützung
- EU-konforme Implementierung ohne Lizenzierungsanforderungen
- Produktionsreife Architektur mit Monitoring und Skalierbarkeit
Empfohlene Umsetzungsphasen:
- Phase 1 (Monate 1-3): Molly-FOSS Fork mit grundlegender MobileCoin Integration
- Phase 2 (Monate 4-6): Plugin-System und REST API Implementierung
- Phase 3 (Monate 7-9): Offline-Funktionalität und Mesh-Network
- Phase 4 (Monate 10-12): Produktionsdeployment und Compliance-Auditierung
Die Architektur ermöglicht den Aufbau eines sicheren, skalierbaren und regulierungskonformen Zahlungsgateways, das sowohl für individuelle Nutzer als auch für Unternehmen geeignet ist.

