Why Teams Retire In-House Payment Stacks

Building your own agent payment stack used to be the only option. Self-custody key splits, ethers.js signing, hand-written daily-cap policy enforcement, a webhook bus that listens for confirmations on three chains — if you needed it in 2024, you built it. The work was real and the resulting code is often genuinely good.

What changes the calculus is total cost of ownership over time:

The migration is not "we wasted time." It is "the work we did is no longer the work we should be doing." See MoltPe vs building your own AI payment stack for the longer-form comparison.

What to Keep Custom vs Move to MoltPe

The honest answer to "should we migrate?" depends on which features are load-bearing for your business. Run this matrix.

Capability Best Choice Why
Standard USDC transfer with daily/per-tx caps MoltPe Hosted, audit-logged, no on-call
Recipient allowlist policies MoltPe Built-in policy engine matches the common patterns
Multi-network signing (Polygon, Base, Tempo) MoltPe Already integrated, gas sponsored
x402 server and client implementation MoltPe SDK provided; protocol moves fast on its own
Highly bespoke on-chain logic in policy Custom Custom DSLs cannot always be expressed in a generic policy engine
Regulated self-custody (legally required) Custom If law requires you to hold keys, hosted is not an option
Extreme-volume sub-cent transactions Hybrid Per-tx fees may favor in-house signing at the very top of the volume curve
Non-USDC stablecoins or native tokens Mixed Check MoltPe's current asset list; some require staying custom

Most teams find the matrix points to "migrate the standard 80% to MoltPe and keep the custom edge cases until they earn or lose their keep on their own merits."

Migration Prep Checklist (Feature Parity Audit)

This is the longest part of the migration, and skipping it is how teams discover halfway through that one obscure feature blocks the cutover. Spend a day on it.

Code Migration: Before and After

The before/after is the most cathartic part of this kind of migration — the diff is usually a net deletion.

Before — in-house signer with ethers.js and a homegrown policy check:

// Old custom flow: load split key, reassemble, instantiate signer, run policy, sign, broadcast.
import { ethers } from "ethers";
import { reassembleKey } from "./shamir-split.js";
import { checkPolicy } from "./policy-engine.js";
import { recordAudit } from "./audit-log.js";

async function payFromAgent(agentId, amount, recipient) {
  const shares = await fetchKeyShares(agentId);
  const privateKey = reassembleKey(shares);
  const provider = new ethers.JsonRpcProvider(process.env.POLYGON_RPC);
  const wallet = new ethers.Wallet(privateKey, provider);

  const policy = await loadPolicy(agentId);
  const decision = checkPolicy(policy, { amount, recipient });
  if (!decision.allowed) throw new Error(decision.reason);

  const usdc = new ethers.Contract(USDC_ADDR, USDC_ABI, wallet);
  const tx = await usdc.transfer(
    recipient,
    ethers.parseUnits(amount, 6),
  );
  await tx.wait();

  await recordAudit({ agentId, amount, recipient, txHash: tx.hash });
  return tx.hash;
}

After — one MoltPe call. Policy, signing, gas, audit log, and confirmation are server-side:

// New flow: one HTTP call. The deleted code is the migration's payoff.
async function payFromAgent(agentId, amount, recipient) {
  const res = await fetch("https://api.moltpe.com/v1/payments/send", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.MOLTPE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      agent_id: agentId,
      amount: amount,
      recipient: recipient,
      reference: `internal-${Date.now()}`,
    }),
  });

  const payment = await res.json();
  if (payment.status !== "confirmed") throw new Error(payment.error || "payment failed");
  return payment.tx_hash;
}

The deleted modules — shamir-split.js, policy-engine.js, audit-log.js, the RPC retry wrapper, the gas estimator — are no longer your team's problem. Keep their tests around as a regression suite for the migration period; delete them once production is stable.

Cutover Plan: Three Phases

Phase 1 — Shadow (week 1 to 2). Pick one low-volume agent. Create the matching MoltPe agent and policy. For each transaction the custom stack signs, ALSO send a "shadow" call to MoltPe and compare results: same amount, same recipient, same final state. Assert parity. No real switchover yet.

Phase 2 — Primary (week 3 to 4). Promote shadowed agents to MoltPe-primary. The custom stack still runs alongside as a fallback for the same agents. Migrate new agents straight onto MoltPe. Watch error rates and policy denials.

Phase 3 — Decommission (week 5 to 6). Sweep remaining balances out of custom wallets to MoltPe. Remove the in-house signer from production. Delete the deprecated modules. Update runbooks. Retire on-call alerts that monitored the old stack. The team gets back significant maintenance bandwidth.

Risks and Rollback

Sweep timing. Moving balances from old wallets to new requires a quiet window where no agent is mid-transaction. Schedule a maintenance window per agent, not the whole fleet at once. Policy semantic drift. A custom policy might enforce something subtly different from the equivalent MoltPe policy; the shadow phase exists specifically to surface that. External integrations — any external system that referenced the old wallet addresses needs to be updated; keep a redirect layer for two weeks after the cut.

Rollback during phases 1 and 2 is straightforward: the custom stack is still running. After phase 3, rollback means re-deploying the deprecated modules from git, which is uglier but possible. The shadow phase is the insurance that makes phase 3 safe.