NoddPay Integration

Sign In/Sign Up with Wallet Address delivery via Webhook

Simplified Objective

NoddPay does NOT process payments. It only manages:

The partner is responsible for executing the pay-in directly to the received wallet address.

Integration Architecture

graph TB subgraph Partner["Your Application (Partner)"] MobileApp[Mobile App
iOS/Android] WebApp[Web App
Desktop/Mobile] Backend[Your Backend
Webhook Endpoint] end subgraph NoddPay["NoddPay (Auth Service)"] Service[NoddPay Auth Service

✓ Login/Signup with OTP
✓ USDC Wallet Management
✓ Webhook Sending] end MobileApp -->|"① Open WebView
URL: noddpay.com/auth"| Service WebApp -->|"① Redirect
URL: noddpay.com/auth"| Service Service -.->|"② Callback
Deep Link"| MobileApp Service -.->|"② Redirect
Return URL"| WebApp Service ==>|"③ Webhook POST
{walletAddress, email, userId}"| Backend Backend -->|"④ Process Pay-in
Transfer USDC"| Blockchain[⛓️ Blockchain
USDC Network] style Partner fill:#f7fafc,stroke:#4a5568,stroke-width:3px style NoddPay fill:#e6fffa,stroke:#319795,stroke-width:3px style MobileApp fill:#ed8936,color:#fff,stroke:#c05621,stroke-width:2px style WebApp fill:#667eea,color:#fff,stroke:#5a67d8,stroke-width:2px style Backend fill:#48bb78,color:#fff,stroke:#38a169,stroke-width:3px style Service fill:#34FBCF,color:#114D46,stroke:#319795,stroke-width:2px style Blockchain fill:#a0aec0,color:#fff,stroke:#718096,stroke-width:2px

Required Integration Points

Component Action Description
Mobile Frontend Open WebView Load NoddPay URL in native WebView (iOS/Android)
Handle Deep Link Listen for callback when user completes the process
Web Frontend Redirect Redirect to NoddPay URL with parameters
Handle Return Receive user back at your returnUrl
Backend Webhook Endpoint Create POST endpoint to receive wallet address
Verify Signature Validate HMAC signature of webhook
Blockchain Implement Pay-in Transfer USDC a la wallet address recibida

What you DON'T need to do

  • ❌ You DON'T need to implement login/signup UI
  • ❌ You DON'T need to handle OTP authentication
  • ❌ You DON'T need to manage wallets
  • ❌ You DON'T need to process KYC
  • ✓ You only need to: open NoddPay, receive the webhook, and execute the pay-in

Integration Flow (Partner's View)

This diagram shows only the interactions the partner needs to handle. NoddPay's internal processes (OTP, validations, wallet management) are abstracted.

sequenceDiagram participant User as User participant App as Your App
(Partner) participant NoddPay as NoddPay
(Black Box) participant Backend as Your Backend
(Partner) participant Blockchain as Blockchain User->>App: 1. Click "Connect Wallet" App->>App: 2. Build NoddPay URL Note over App: URL with: partnerId, webhookUrl,
returnUrl, metadata App->>NoddPay: 3. Open WebView/Redirect rect rgb(230, 255, 250) Note over NoddPay: NoddPay handles internally:
• Login/Signup
• OTP sending via email
• Code verification
• Wallet address management end NoddPay-->>User: 4. User completa auth Note over User,NoddPay: NoddPay UI:
Login or Signup with OTP NoddPay->>App: 5. Callback/Redirect Note over NoddPay,App: returnUrl?status=success&
userId=xyz&sessionId=abc App->>User: 6. Show "Connecting..." par Asynchronous Webhook NoddPay->>Backend: 7. POST Webhook Note over NoddPay,Backend: Payload:
{userId, email, walletAddress,
blockchain, network, kycStatus} Backend->>Backend: 8. Verify Signature HMAC Backend->>Backend: 9. Save Wallet Address Backend-->>NoddPay: 200 OK end Backend->>App: 10. Notify (WebSocket/Polling) Note over Backend,App: Wallet address received App->>User: 11. Show "Wallet Connected" opt Start Automatic Pay-in Backend->>Blockchain: 12. Transfer USDC Note over Backend,Blockchain: Send to received walletAddress Blockchain-->>Backend: 13. Tx Hash Backend->>App: 14. Update status App->>User: 15. "Payment Processed" end

Partner's Responsibilities

Step Component Action Requerida
2-3 Frontend Build URL with parameters and open NoddPay
5 Frontend Handle return callback/redirect
7-9 Backend Receive webhook, verify signature, save wallet address
10 Backend → Frontend Notify frontend that wallet was received
12-15 Backend + Blockchain Execute USDC transfer to wallet address

Timing Consideration

The callback (step 5) is immediate when the user finishes, but the webhook (step 7) may take a few seconds.

Partner Integration

1 Initiate Authentication

Partner opens NoddPay in WebView/Redirect for user authentication.

NoddPay URL:

https://noddpay.com/auth

Query String Parameters:

Parameter Type Required Description
partnerId string ✓ Sí Unique partner ID
webhookUrl string ✓ Sí URL where NoddPay will send wallet data
returnUrl string ✓ Sí Callback URL/deep link to return to app
email string ✓ Sí Email para pre-llenar

iOS Example:

let urlString = "https://noddpay.com/auth?" + "partnerId=ABC123&" + "webhookUrl=https://api.partner.com/noddpay-webhook&" + "returnUrl=myapp://auth-callback&" + "email=test@noddpay.com" let url = URL(string: urlString)! let webView = WKWebView() webView.load(URLRequest(url: url))

Web Example:

const params = new URLSearchParams({ partnerId: 'ABC123', webhookUrl: 'https://api.partner.com/noddpay-webhook', returnUrl: 'https://partner.com/auth-callback', email: 'test@noddpay.com' }); window.location.href = `https://noddpay.com/auth?${params.toString()}`;

2 Immediate Callback (Frontend)

Optional but Recommended

After the user authenticates successfully, NoddPay redirects to the returnUrl.

Parameters de Callback:

Parameter Description
status success, cancelled, error
userId Unique user ID in NoddPay
sessionId Authentication session ID
timestamp ISO 8601 timestamp

Callback URL Example:

myapp://auth-callback?status=success&userId=usr_abc123&sessionId=sess_xyz789×tamp=2025-11-16T10:30:00Z
ℹNote: This callback is only to inform the frontend that the process finished. The wallet address is NOT sent here for security. The wallet address is sent to the partner's backend via webhook.

3 Webhook with Wallet Address (Backend)

Critical

NoddPay sends a POST request to the webhookUrl with the user data and wallet address.

Webhook Payload:

{ "eventType": "user.authenticated", "eventId": "evt_abc123xyz", "timestamp": "2025-11-16T10:30:00Z", "partnerId": "ABC123", "data": { "userId": "usr_abc123", "email": "user@example.com", "name": "Juan Pérez", "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "blockchain": "Ethereum", "network": "Mainnet", "currency": "USDC", "kycStatus": "approved", "kycLevel": "LEVEL_2", "createdAt": "2025-11-16T10:30:00Z", "metadata": { "orderId": "ORDER-123", "amount": 50 } }, "signature": "a1b2c3d4e5f6..." }

Webhook Headers:

POST /noddpay-webhook HTTP/1.1 Host: api.partner.com Content-Type: application/json X-NoddPay-Signature: sha256=a1b2c3d4e5f6... X-NoddPay-Event-Id: evt_abc123xyz X-NoddPay-Timestamp: 2025-11-16T10:30:00Z User-Agent: NoddPay-Webhook/1.0

Implementación del Webhook Endpoint:

// Node.js/Express example const express = require('express'); const crypto = require('crypto'); const router = express.Router(); const NODDPAY_WEBHOOK_SECRET = process.env.NODDPAY_WEBHOOK_SECRET; function verifyWebhookSignature(payload, signature, secret) { const expectedSignature = crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex'); return `sha256=${expectedSignature}` === signature; } router.post('/noddpay-webhook', async (req, res) => { const signature = req.headers['x-noddpay-signature']; const eventId = req.headers['x-noddpay-event-id']; // 1. Verify signature if (!verifyWebhookSignature(req.body, signature, NODDPAY_WEBHOOK_SECRET)) { console.error('Invalid webhook signature'); return res.status(401).json({ error: 'Invalid signature' }); } // 2. Verify it's not a duplicate event const eventExists = await db.webhookEvents.findOne({ eventId }); if (eventExists) { console.log('Duplicate event, already processed'); return res.status(200).json({ message: 'Already processed' }); } // 3. Save event await db.webhookEvents.create({ eventId, payload: req.body, processedAt: new Date() }); // 4. Process the event const { eventType, data } = req.body; if (eventType === 'user.authenticated') { const { userId, email, walletAddress, metadata } = data; // Save user's wallet address await db.users.update( { email }, { noddpayUserId: userId, walletAddress, blockchain: data.blockchain, network: data.network, kycStatus: data.kycStatus } ); // If there's metadata with orderId, update the order if (metadata && metadata.orderId) { await db.orders.update( { orderId: metadata.orderId }, { walletAddress, status: 'wallet_connected', readyForPayment: true } ); // Optional: Start pay-in process automatically await initiatePayIn({ orderId: metadata.orderId, toAddress: walletAddress, amount: metadata.amount }); } console.log(`Wallet address received for user ${email}: ${walletAddress}`); } // 5. Respond 200 OK quickly res.status(200).json({ received: true }); }); module.exports = router;

4 Procesar Pay-In

Once the partner receives the wallet address via webhook, they can process the pay-in.

async function initiatePayIn({ orderId, toAddress, amount }) { try { // 1. Get order const order = await db.orders.findOne({ orderId }); // 2. Initiate USDC transfer on-chain const transaction = await blockchainService.transferUSDC({ from: PARTNER_WALLET_ADDRESS, to: toAddress, amount: amount, metadata: { orderId, type: 'pay-in' } }); // 3. Save transaction await db.transactions.create({ orderId, txHash: transaction.hash, from: PARTNER_WALLET_ADDRESS, to: toAddress, amount, currency: 'USDC', blockchain: 'Ethereum', status: 'pending', createdAt: new Date() }); // 4. Monitor confirmation await monitorTransaction(transaction.hash, orderId); console.log(`Pay-in initiated: ${transaction.hash}`); return transaction; } catch (error) { console.error('Error initiating pay-in:', error); throw error; } } async function monitorTransaction(txHash, orderId) { // Polling or websocket to monitor confirmations const confirmations = await blockchainService.waitForConfirmations( txHash, 3 // Wait for 3 confirmations ); if (confirmations >= 3) { // Update order as paid await db.orders.update( { orderId }, { status: 'paid', paidAt: new Date(), txHash } ); await db.transactions.update( { txHash }, { status: 'confirmed' } ); console.log(`Transaction confirmed: ${txHash}`); } }

Webhook Security

CRITICAL: Always Verify the Signature

Never trust a webhook without verifying its HMAC signature. Anyone could send fake requests to your endpoint.

Signature Calculation

// NoddPay generates the signature like this: const payload = JSON.stringify(webhookPayload); const signature = crypto .createHmac('sha256', PARTNER_WEBHOOK_SECRET) .update(payload) .digest('hex'); // Sends in header: X-NoddPay-Signature: sha256={signature}

Signature Verification (Partner)

function verifyWebhookSignature(payload, signature, secret) { // Payload must be the raw body string const expectedSignature = crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); const receivedSignature = signature.replace('sha256=', ''); // Use timing-safe comparison return crypto.timingSafeEqual( Buffer.from(expectedSignature), Buffer.from(receivedSignature) ); } // In Express, you need the raw body app.use('/noddpay-webhook', express.raw({ type: 'application/json' })); router.post('/noddpay-webhook', (req, res) => { const signature = req.headers['x-noddpay-signature']; const rawBody = req.body.toString('utf8'); if (!verifyWebhookSignature(rawBody, signature, SECRET)) { return res.status(401).json({ error: 'Invalid signature' }); } const payload = JSON.parse(rawBody); // Process webhook... });

Other Security Aspects

Types de Events del Webhook

Event When It's Sent Included Data
user.authenticated When a user successfully completes login/signup userId, email, walletAddress

Webhook Retry Handling

If your endpoint fails or doesn't respond, NoddPay will retry sending:

Attempt Wait Time
1° attempt Immediate
2° attempt +5 minutes
3° attempt +15 minutes
4° attempt +1 hour

Important

If after 4 attempts the webhook still fails, NoddPay will send an alert to the partner and mark the event as "failed". The partner can query failed events in the NoddPay dashboard.

Get Events Fallidos (API Opcional)

GET https://api.noddpay.com/v1/webhook-events/failed Authorization: Bearer {partner_api_key} // Response: { "events": [ { "eventId": "evt_abc123", "eventType": "user.authenticated", "attempts": 4, "lastAttempt": "2025-11-16T12:00:00Z", "lastError": "Connection timeout", "payload": { ... } } ] }

Implementation Checklist

Partner Backend

  • Create POST endpoint for webhook (e.g.: /noddpay-webhook)
  • Implement HMAC signature verification
  • Implement duplicate event detection (eventId)
  • Save user's wallet address
  • Respond 200 OK quickly (< 10 segundos)
  • Lógica de pay-in

Partner Frontend

  • Implement "Connect Wallet" button or similar
  • Generate NoddPay URL with the correct parameters
  • Open WebView (mobile) or redirect (web)
  • Implement callback/deep link
  • Show loading state while waiting for webhook
  • Polling or websocket to know when webhook arrived

NoddPay Configuration

  • Register partnerId
  • Configure webhookUrl
  • Get webhook secret
  • Configure allowed returnUrls
  • Verify webhooks arrive correctly

Advantages of this Approach

Flow Summary

  1. User hace click en "Connect Wallet" en app del partner
  2. App opens NoddPay in WebView/Redirect
  3. User hace login/signup con OTP en NoddPay
  4. NoddPay sends webhook to partner backend with wallet address
  5. NoddPay redirects user back to partner app
  6. Partner uses the wallet address to execute pay-in directly