Wen Open Lab · open integration

Stage 4: Payments Bridgelink verified

Wen payment method design
A new payment method, open to all

Card acquiring does three things for a shop: it takes the payment, confirms it, and tells the shop's system. This model does the same on Solana with the open Solana Pay standard: a payment link or QR per order, a watcher that finds the payment by a unique reference, a strict validation, and a webhook to the shop.

The task

Let an online shop accept payments on Solana and mark orders as paid automatically, with the same reliability the shop expects from a card acquirer.

The model

  1. Create. For each order the shop makes a Solana Pay link with recipient, amount, token, a unique random reference address, a label and a memo order:ID. The link becomes a button or a QR.
  2. Pay. The customer opens it in any Solana Pay wallet and confirms.
  3. Find. The shop's server asks the chain for the transaction that includes the reference address.
  4. Validate. It checks recipient, amount, token, reference and memo. Anything wrong throws an error.
  5. Notify. It posts {orderId, status: "paid", signature} to the shop's webhook. The order system ships the goods.
Card acquiringThis bridge
ConfirmationAuthorization in seconds, money settled in daysConfirmed in seconds, final in about 13 seconds
FeePercentage plus fixed fee per paymentNetwork fee, a fraction of a cent, plus any conversion costs
ChargebacksYes: protects the buyer, risk for the shopNo: refunds are a new payment from the shop
RegulationLicensed acquirer handles itDepends on country; may require a licensed partner

The code

File: lab/code/src/04-solana-pay-bridge.ts · Apache-2.0

// Model 4: a payment bridge for e-commerce. The shop creates a Solana Pay link/QR for an order,
// watches the chain for the payment by its unique reference, validates amount and token,
// then notifies its own order system (the same role a card acquirer's callback plays).
// License: Apache-2.0. Example code, not audited. Devnet first. Built on @solana/pay + @solana/kit.
import { encodeURL, findReference, validateTransfer, FindReferenceError } from '@solana/pay';
import { address, createSolanaRpc, generateKeyPairSigner, type Address } from '@solana/kit';

/** Tokens a shop may accept. USDC for stable prices; $WEN as an optional community payment. */
export const ACCEPTED = {
  USDC_MAINNET: address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
  WEN_MAINNET: address('WENWENvqqNya429ubCdR81ZmD69brwQaaBYY6p3LCpk'),
} as const;

export interface PaymentRequest {
  orderId: string;
  recipient: Address;      // the shop's wallet
  amount: number;          // in token units, e.g. 24.99
  splToken?: Address;      // omit for SOL
  reference: Address;      // unique per order: how the shop finds the payment
  url: string;             // solana: URL to show as a link or QR
}

export async function createPaymentRequest(orderId: string, recipient: Address, amount: number, splToken?: Address): Promise<PaymentRequest> {
  const reference = (await generateKeyPairSigner()).address;   // random, never reused
  const url = encodeURL({
    recipient, amount, splToken, reference,
    label: 'Wen Open Lab demo shop',
    message: `Order ${orderId}`,
    memo: `order:${orderId}`,
  });
  return { orderId, recipient, amount, splToken, reference, url: url.toString() };
}

/** Poll until the payment appears, then validate it and call the shop's webhook. */
export async function waitForPayment(req: PaymentRequest, opts: { rpcUrl: string; webhook: string; timeoutMs?: number }) {
  const rpc = createSolanaRpc(opts.rpcUrl);
  const deadline = Date.now() + (opts.timeoutMs ?? 5 * 60_000);
  while (Date.now() < deadline) {
    try {
      const found = await findReference(rpc, req.reference, { commitment: 'confirmed' });
      // Throws if recipient, amount, token, reference or memo do not match the request.
      await validateTransfer(rpc, found.signature, {
        recipient: req.recipient, amount: req.amount, splToken: req.splToken, reference: req.reference, memo: `order:${req.orderId}`,
      }, { commitment: 'confirmed' });
      await fetch(opts.webhook, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ orderId: req.orderId, status: 'paid', signature: found.signature }),
      });
      return found.signature;
    } catch (e) {
      if (!(e instanceof FindReferenceError)) throw e;   // not found yet: keep waiting
      await new Promise(r => setTimeout(r, 1500));
    }
  }
  throw new Error(`Order ${req.orderId}: payment not found before timeout`);
}

What was verified

The payment link round-trips through the official Solana Pay parser with the same recipient, amount, token, reference and memo, and every order gets a unique reference. The watcher and validation use the official findReference and validateTransfer and are type-checked; running them against a live chain is part of the devnet stage.

Where it is useful

  • Online shops selling to crypto-native customers worldwide.
  • Digital goods, tickets and subscriptions where instant settlement matters.
  • Payment providers and banks exploring stablecoin acceptance next to cards.

Limits and risks

  • Price volatility: quote in a stablecoin or convert at checkout.
  • Converting to fiat (off-ramp) has its own fees and rules.
  • No chargebacks means the shop must offer a clear refund policy.
  • Run your own RPC endpoint for production; public endpoints are rate-limited.

Want the numbers for your shop? Try the simulator.

Wen in pictures

Wen on Jupiter Mobile
Pay from the phone
Solana Seeker with Wen coin
Wallet in the pocket
Wen crypto wallet checkout
Checkout in seconds
Jupiter wallet payment
Any token, one standard
Wen ecosystem mobile payments
From QR to confirmed order

Where Wen fits in this model

The shop chooses which tokens it accepts. USDC keeps prices stable; WEN can be offered as a community payment option, with its contract written into the code as ACCEPTED.WEN_MAINNET. Before accepting any token, check which token program owns its mint (the helper detectTokenProgram in common.ts does this) and test the full flow on devnet.

The Wen thread: one contract, everywhere

Every model in the Lab is free and open, and every one carries the same signature: the $WEN token contract. Copy it, repeat it in your own docs, and always verify it against an explorer before use.

WENWENvqqNya429ubCdR81ZmD69brwQaaBYY6p3LCpk

Check: Solana Explorer · how to find the contract ID

Open use policy

  • Free and open. All Lab code is released under the Apache License 2.0, the same license as the Wen Program Library. Use it, change it, ship it, commercially or not; keep the license notice.
  • Open tools only. Built on public, documented tools: Solana SPL Token-2022 (Token Extensions), the token layout of the Wen New Standard (WNS), Solana Pay, and LiteSVM for local testing.
  • The rule: always verify your own model. Run the tests, start on devnet, read the official documentation, review every account and amount, and get an independent audit before any mainnet use. A model that is not checked is not a model, it is a guess.
  • Not audited, not advice. These are educational examples. They are not financial, legal or tax advice, and not a licensed payment or banking product. Payments, stablecoins and acquiring are regulated activities in most countries.
  • Security. Never paste a private key into a website. Do not install the npm package @wen-community/wen-new-standard: npm removed it as malicious. Build from official sources.

Spoken by Wen, for the community. More in our editorial and data policy.

Wen Open Lab · roadmap · all code · Wen Program Library on GitHub