MooChedda Website Payments API

This document explains how a website can accept MooChedda payments using the same invoice flow as the merchant POS: create an invoice, show or redirect to wallet payment, and confirm the result by checking invoice status.

Recommended model For websites, create the invoice from your server, send the customer into the hosted MooChedda wallet flow, then verify payment status from your backend before fulfilling the order.

The MooChedda merchant portal already uses invoice-based charging. A website integration should follow the same pattern: your storefront builds an order, the server creates an invoice, and the customer pays that invoice from the MooChedda wallet after scanning a QR or opening a hosted wallet link.

Integration Models

1. Hosted wallet redirect

This is the simplest website integration. After creating an invoice, redirect the customer to /wallet.html?invoice=.... The wallet reads the invoice, lets the customer approve payment, and can send the user back to your site with a returnUrl.

2. QR payment on your checkout page

If you want a scan-to-pay checkout similar to the merchant portal, render a QR with the invoice payload moochedda:invoice/<invoiceId>. The customer scans it from the MooChedda wallet and pays there. Your site should still poll the invoice status endpoint to confirm settlement.

Use hosted wallet if you want fewer moving parts The hosted wallet route is better for browser checkout because it reuses the existing payment UI, token selection, and post-payment return handling already supported by MooChedda.

Payment Flow

Step 1
Build the website order
Collect line items, quantities, optional tax rate, accepted tokens, and any order note from your storefront.
Step 2
Create a MooChedda invoice
Call POST /invoice/create with your merchant wallet address as the payment destination.
Step 3
Send the customer to payment
Either redirect to wallet.html?invoice=... or show a QR containing moochedda:invoice/<invoiceId>.
Step 4
Verify the result
Poll GET /invoice/:id/status until the invoice becomes paid or expired.
Step 5
Fulfil only after confirmation
Treat the backend invoice status as the source of truth. Do not ship goods or mark orders complete based only on client redirect state.

Create Invoice

POST /invoice/create Create a website payment request

Create a new invoice for a website order. The merchant wallet receives payment when the customer pays the invoice.

FieldTypeRequiredDescription
merchantAddressstringYesMooChedda merchant wallet address that receives funds.
merchantNamestringNoDisplay name shown in wallet and invoice views.
itemsarrayYesInvoice line items. Each entry needs name, price, and quantity.
taxRatenumberNoDecimal rate between 0 and 1. Example: 0.08 for 8%.
acceptedTokensarrayNoAllowed payment tokens. Defaults to ["CHEDDA"].
notestringNoOptional order note or internal checkout label.

Example request

{
  "merchantAddress": "04abc123...merchant_wallet_public_key...def456",
  "merchantName": "North Star Supply",
  "items": [
    { "name": "Canvas Tote", "price": 18, "quantity": 2 },
    { "name": "Sticker Pack", "price": 5, "quantity": 1 }
  ],
  "taxRate": 0.08,
  "acceptedTokens": ["CHEDDA", "USDT"],
  "note": "Order #WS-1042"
}

Example response

{
  "success": true,
  "invoiceId": "inv_m8r4j8m0xa1b2c",
  "invoice": {
    "invoiceId": "inv_m8r4j8m0xa1b2c",
    "merchantAddress": "04abc123...merchant_wallet_public_key...def456",
    "merchantName": "North Star Supply",
    "items": [
      { "name": "Canvas Tote", "price": 18, "quantity": 2 },
      { "name": "Sticker Pack", "price": 5, "quantity": 1 }
    ],
    "taxRate": 0.08,
    "subtotal": 41,
    "tax": 3.28,
    "total": 44.28,
    "acceptedTokens": ["CHEDDA", "USDT"],
    "note": "Order #WS-1042",
    "status": "pending",
    "createdAt": "2026-03-25T17:00:00.000Z",
    "expiresAt": "2026-03-25T17:30:00.000Z"
  }
}

Get Invoice

GET /invoice/:id Fetch full invoice details

Use this endpoint if you need to rehydrate an existing invoice in your own checkout or troubleshoot a pending payment.

The response includes the full invoice object. Pending invoices are automatically marked expired once their expiry time has passed.

Check Status

GET /invoice/:id/status Confirm whether payment completed

Poll this endpoint after redirect or QR presentation. This is the endpoint your website should trust when deciding whether an order is paid.

Example response

{
  "status": "paid",
  "paidAt": "2026-03-25T17:08:43.000Z",
  "paidBy": "04payer_public_key...",
  "paymentToken": "USDT",
  "paymentAmount": 44.28,
  "conversionRate": 1,
  "invoice": {
    "invoiceId": "inv_m8r4j8m0xa1b2c",
    "merchantName": "North Star Supply",
    "total": 44.28,
    "status": "paid"
  }
}
StatusMeaning
pendingThe invoice exists and is still waiting for a wallet payment.
paidThe payment transaction has been accepted and the invoice is settled.
expiredThe invoice timed out before payment. Create a new invoice instead of reusing it.

Hosted Wallet Redirect

The MooChedda wallet page already understands invoice deep links. After you create an invoice, redirect the customer to the wallet using query parameters.

https://YOUR_MOOCCHEDDA_HOST:3002/wallet.html?invoice=INV_ID&token=CHEDDA&source=website&returnUrl=https%3A%2F%2Fyourshop.com%2Fcheckout%2Fcomplete
ParamRequiredDescription
invoiceYesThe invoice ID returned by POST /invoice/create.
tokenNoPreferred initial payment token shown in the wallet.
sourceNoSet source=website for website checkout flows so wallet completion messaging and return behavior are tailored for web checkout.
returnUrlNoWhere the wallet sends the customer after payment. Use this for post-payment UX only, not as payment proof.
Do not trust the redirect alone The user returning to your site only means the wallet flow finished in the browser. Your backend should still confirm /invoice/:id/status before shipping, provisioning, or marking the order paid.

Browser Checkout Example

The following pattern matches the sample storefront in payment-example.html. It creates an invoice, redirects the customer to the hosted wallet, and verifies the payment on return.

const API_URL = 'https://v1.moochedda.com:3002';

async function startMooCheddaCheckout(cartItems) {
  const response = await fetch(`${API_URL}/invoice/create`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      merchantAddress: 'YOUR_MERCHANT_WALLET_ADDRESS',
      merchantName: 'Your Store',
      items: cartItems.map(item => ({
        name: item.name,
        price: item.price,
        quantity: item.quantity
      })),
      taxRate: 0.08,
      acceptedTokens: ['CHEDDA', 'USDT'],
      note: `Order ${Date.now()}`
    })
  });

  const data = await response.json();
  if (!response.ok) throw new Error(data.error || 'Failed to create invoice');

  const invoiceId = data.invoiceId;
  const returnUrl = encodeURIComponent(`${window.location.origin}/checkout/complete?invoice=${invoiceId}`);
  window.location.href = `${API_URL}/wallet.html?invoice=${invoiceId}&token=CHEDDA&source=website&returnUrl=${returnUrl}`;
}

async function confirmMooCheddaPayment(invoiceId) {
  const response = await fetch(`${API_URL}/invoice/${invoiceId}/status`);
  const data = await response.json();

  if (data.status !== 'paid') {
    throw new Error(`Invoice ${invoiceId} is ${data.status}`);
  }

  return data;
}

QR alternative

If you want the merchant-style scan flow directly on your site, generate a QR containing:

moochedda:invoice/inv_m8r4j8m0xa1b2c

Then keep polling GET /invoice/:id/status until the invoice becomes paid or expired.

Iframe Embed and Events

You can embed wallet checkout in an iframe and listen for payment lifecycle updates from the wallet. The wallet now emits postMessage events for invoice flows opened from websites.

Embed example

<iframe
  id="moocheddaPayFrame"
  title="MooChedda Payment"
  src="https://v1.moochedda.com:3002/wallet.html?invoice=INV_ID&token=USDC&source=website&returnUrl=https%3A%2F%2Fyourshop.com%2Fcheckout%2Fcomplete"
  style="border:0;width:100%;min-height:820px;background:#fff;"
  allow="payment *"
></iframe>

Parent-page listener

const MOOCHEDDA_ORIGIN = 'https://v1.moochedda.com:3002';

window.addEventListener('message', async (event) => {
  if (event.origin !== MOOCHEDDA_ORIGIN) return;

  const payload = event.data || {};
  if (payload.type !== 'moochedda:invoice') return;

  // payload.event is one of: opened | paid | expired
  if (payload.event === 'opened') {
    console.log('Wallet opened invoice', payload.invoiceId);
    return;
  }

  if (payload.event === 'paid') {
    console.log('Invoice paid in wallet', payload.invoiceId, payload.token);

    // Always confirm on server before fulfilment
    const statusRes = await fetch(`/api/orders/${payload.invoiceId}/confirm`, { method: 'POST' });
    if (!statusRes.ok) {
      console.error('Server confirmation failed');
      return;
    }

    // Update checkout UI
    showPaidState(payload.invoiceId);
    return;
  }

  if (payload.event === 'expired') {
    showInvoiceExpired(payload.invoiceId);
  }
});
FieldDescription
typeAlways moochedda:invoice for wallet invoice events.
eventopened, paid, or expired.
invoiceIdInvoice identifier associated with the event.
statusCurrent invoice status from wallet context.
tokenSelected payment token when available.
paidAtPayment timestamp when the invoice is paid.
returnUrlReturn URL passed to wallet, if present.
Allow the wallet origin in your site CSP If your website uses CSP, include https://v1.moochedda.com:3002 in frame-src (or child-src) so the iframe is not blocked.

Server Confirmation Pattern

The best production pattern is to let your server own the order state and invoice mapping. A common sequence looks like this:

  • Create your local order record first, with status like awaiting_payment.
  • Call POST /invoice/create from your backend and store the returned invoiceId alongside the order.
  • When the customer returns, ask your server to check GET /invoice/:id/status.
  • Only transition the order to paid after the status endpoint returns paid.
  • If the invoice is expired, issue a new invoice instead of attempting to reuse the old one.
Why this matters It keeps payment confirmation on the server side, which prevents client-side tampering and gives you a clean record of which invoice settled which order.

Security Notes

Do not collect a customer private key on your website A merchant website should never ask the customer to paste a MooChedda private key into your checkout. The wallet page handles signing and payment approval.
  • Create invoices on your server when possible so your merchant wallet address and order logic stay authoritative.
  • Use the invoice status endpoint as your confirmation source. The browser redirect is not enough.
  • Treat each invoice as single-use. If it expires, generate a new one.
  • Store the invoice ID in your own order records so support and reconciliation are straightforward.

Related Files

  • payment-example.html for a storefront example that creates an invoice and redirects to the wallet.
  • DOCS.html for merchant portal operations, POS behavior, and merchant management endpoints.
  • wallet.html for the hosted payment interface that accepts invoice, token, source, and returnUrl query parameters and emits iframe invoice events.

MooChedda Website Payments API ยท Invoice creation, wallet redirect, QR payment, and settlement confirmation