NavenDocs
NavenDocs
Introduction
MPP RelayIntegration GuideAPI Reference
Back to Naven Network
MPP Relay

Integration Guide

Connect an mppx server to Naven MPP Relay.

Naven MPP Relay is called by your backend, not by a browser or the paying client. Your backend owns challenge issuance and uses the relay only for validation and settlement. The public relay does not require a Naven account or API key.

Prerequisites

  • A receiving wallet on Robinhood Chain
  • USDG payment terms
  • A server using mppx with a private challenge secret

Install the SDK in your server project:

bun add mppx@0.9.2

Configure customer-owned server variables. Never expose your challenge secret to client code:

NAVEN_MPP_RELAY_URL=https://mpp.naven.network
MPP_SECRET_KEY=at-least-32-random-bytes
MERCHANT_RECIPIENT=0xYourReceivingWallet

MPP_SECRET_KEY belongs only to your application and is not shared with Naven.

Configure the payment method

Create an evm/charge server method whose lifecycle hooks call the relay:

import { Method } from "mppx";
import { Methods } from "mppx/evm";
import { Mppx } from "mppx/server";

const relayUrl = process.env.NAVEN_MPP_RELAY_URL!;

async function callRelay(
  operation: "validate" | "broadcast",
  credential: any,
  request: any,
) {
  const response = await fetch(`${relayUrl}/v1/mpp/${operation}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      ...(operation === "broadcast"
        ? {
            "Idempotency-Key":
              `mpp_${credential.payload.from}_${credential.payload.nonce}`,
          }
        : {}),
    },
    body: JSON.stringify({ credential, request }),
  });

  const result = await response.json();
  if (!response.ok || result.success !== true) {
    throw new Error(result.error?.message ?? "MPP relay rejected the payment");
  }
  return result;
}

const defaults = {
  chainId: 4663,
  currency: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
  credentialTypes: ["authorization"] as ["authorization"],
  decimals: 6,
  recipient: process.env.MERCHANT_RECIPIENT as `0x${string}`,
};

const evmCharge = Method.toServer<
  typeof Methods.charge,
  typeof defaults
>(Methods.charge, {
  defaults,
  async validate({ credential }) {
    const result = await callRelay(
      "validate",
      credential,
      credential.challenge.request,
    );
    return {
      challenge: credential.challenge,
      credential,
      details: result.details,
      intent: "charge",
      method: "evm",
      request: credential.challenge.request,
      ...(credential.source ? { source: credential.source } : {}),
    };
  },
  async broadcast({ credential }) {
    return (
      await callRelay("broadcast", credential, credential.challenge.request)
    ).receipt;
  },
});

export const mppx = Mppx.create({
  methods: [evmCharge],
  secretKey: process.env.MPP_SECRET_KEY!,
});

The deterministic idempotency key makes a network retry refer to the same payment. Never reuse one key for a different credential.

Protect a route

Use the configured method in your HTTP handler:

export async function handle(request: Request) {
  const payment = await mppx.evm.charge({
    amount: "0.01",
    description: "Generate one report",
  })(request);

  if (payment.status === 402) return payment.challenge;

  const report = await generateReport();
  return payment.withReceipt(Response.json({ report }));
}

The route price uses display units, so 0.01 USDG becomes the atomic amount 10000 in the challenge. mppx verifies your challenge HMAC and route binding before it calls the relay. The paid result is returned only after validation and broadcast succeed.

Make the purchased operation idempotent where possible. Settlement occurs before your application produces the response, so your application remains responsible for retry and fulfillment behavior if its business operation fails afterward.

Production checklist

  • Keep MPP_SECRET_KEY on your server.
  • Generate a unique challenge for the protected route and exact payment terms.
  • Use a stable, credential-specific Idempotency-Key for broadcast retries.
  • Call broadcast before releasing paid content or recording the order as paid.
  • Attach the returned receipt with payment.withReceipt(...).
  • Continue using the Naven x402 facilitator unchanged for any existing x402 routes.

See the API Reference for request and response fields.

MPP Relay

Validate and settle native MPP EVM charges through Naven.

API Reference

Naven MPP Relay endpoints, public access, and response formats.

On this page

PrerequisitesConfigure the payment methodProtect a routeProduction checklist