Payments

Payment Intents

A payment intent is the single object for collecting money: it describes what the customer owes, where it settles, and — once verified on chain — how it was paid.

One resource

A payment intent is the payment. /v1/payment-intents lists every checkout you created; /v1/payments lists the same objects filtered to those with an on-chain attempt (submitted, confirmed, finalized, failed, refunded). There is no separate "payment" record to reconcile.

Create parameters#

FieldTypeDescription
amountdecimal stringWhole MUNY, e.g. "249" or "12.5". Required unless line_items is given. Never a JSON number.
line_itemsarrayAlternative to amount: name, quantity, unit_amount. The total is computed exactly server-side.
token"MUNY"V1 accepts MUNY. The model is ready for USDC, SOL and other approved SPL tokens.
referencestringYour order reference. Shown on checkout, searchable, included in webhooks.
external_order_idstringOptional second identifier from your system.
descriptionstringShown to the customer on checkout.
customerobject{ email, name } — prefills the payment record.
collect_email / collect_namebooleanAsk the customer on checkout before they pay.
metadataobjectUp to 20 string pairs for your own use. Never shown to the customer.
success_url / cancel_urlhttps URLWhere checkout sends the customer. Defaults come from Payments settings.
display_amount / display_currencystringA price you show elsewhere (e.g. 29.99 USD). Informational only.
expires_in_minutesinteger5–10,080. Defaults to your merchant setting (30).
wallet_iduuidSettle into a specific wallet instead of your default receiving wallet.
idempotency_keystringRequired (or the Idempotency-Key header). Same key + same body returns the same payment.
Line items, customer and metadata
const payment = await muny.paymentIntents.create({
  line_items: [
    { name: "Pro Plan", quantity: 1, unit_amount: "249" },
    { name: "Extra seat", quantity: 2, unit_amount: "40" },
  ],
  token: "MUNY",
  reference: "ORDER-48291",
  customer: { email: "customer@example.com" },
  metadata: { plan: "pro", user_id: "12345" },
  display_amount: "39.48",
  display_currency: "USD",
  expires_in_minutes: 60,
});
// payment.amount === "329"

The object#

FieldDescription
statusSee the lifecycle below.
amount, amount_refunded, token, token_mintWhat is owed, what has been refunded, and the exact SPL mint.
recipient_address, wallet_idThe settlement wallet, fixed at creation. Changing settings later never redirects an open payment.
checkout_urlHosted checkout: /pay/{id}.
solana_pay_url, reference_keySolana Pay transfer request and the unique reference key Muny uses to find the payment.
payer_address, transaction_signature, explorer_url, transactionFilled from on-chain verification, never from the client.
fiat_value_at_payment{ amount, currency, price, source } snapshot taken when the payment confirmed. Historic reports never use today's price.
late_confirmationTrue when a valid payment arrived after expires_at. The funds are real, so the payment is recorded.
risk_decisionallow | review | deny from compliance/risk providers at settlement.
refundsRefunds of this payment (on retrieve).
livemode, networkTest (devnet) and live (mainnet) records never mix.

Lifecycle#

status
requires_payment ──► pending ──► submitted ──► confirmed ──► finalized
        │               │             │              │             │
        │               │             │              └──► partially_refunded ──► refunded
        ├──► cancelled  ├──► cancelled│
        └──► expired ◄──┴─────────────┘   (a late, valid payment still moves expired ──► confirmed)
                                       failed  (underpaid / overpaid transfer carrying this reference)
StatusMeaning
requires_paymentCreated; waiting for the customer.
pendingThe customer connected a wallet and Muny built the transaction.
submittedA signature was reported or seen. Not proof of payment.
confirmedVerified on chain at confirmed commitment. Safe to fulfil for most goods.
finalizedReached finalized commitment — irreversible.
expiredNo valid payment before expires_at. Muny keeps watching the reference for 24 h for late payments.
cancelledCancelled by you before any payment was in flight.
failedA transfer carrying this payment's reference was underpaid or overpaid. Review and refund.
partially_refunded / refundedAfter refunds confirm on chain.

List, search and cancel#

ts
// Search by payment id, transaction signature, reference or customer email
const { data } = await muny.paymentIntents.list({ q: "ORDER-48291" });

// Only payments that saw an on-chain attempt
const paid = await muny.payments.list({ status: "confirmed", created_gte: "2026-09-01T00:00:00Z" });

// Iterate everything
for await (const p of muny.paymentIntents.listAll({ status: "expired" })) console.log(p.id);

await muny.paymentIntents.cancel(payment.id); // only while requires_payment / pending

Checkout sessions

POST /v1/checkout/sessions is a thin wrapper for cart-style integrations: pass line_items and it returns a payment intent whose description lists the items.

ts
// Equivalent convenience endpoint: POST /v1/checkout/sessions
const payment = await muny.checkout.sessions.create({
  line_items: [{ name: "Pro Plan", quantity: 1, unit_amount: "249" }],
  token: "MUNY",
  success_url: "https://shop.example/thanks",
  cancel_url: "https://shop.example/cart",
});
redirect(payment.checkout_url);

Endpoints#

  • POST /v1/payment-intents · GET /v1/payment-intents · GET /v1/payment-intents/:id · POST /v1/payment-intents/:id/cancel
  • GET /v1/payments · GET /v1/payments/:id · GET /v1/payments/:id/events (webhook deliveries) · GET /v1/payments/analytics
  • POST /v1/checkout/sessions

Required scopes: create_payments to create/cancel, view_payments to read. See the API keys guide.