> ## Documentation Index
> Fetch the complete documentation index at: https://dev.commerce.moonpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless Payments

> Run complete payment flows via API - no UI required

Using MoonPay Commerce, you can make end-to-end headless payments with no UI required. This is ideal for seamless integrations with third-party services, allowing merchants to accept payments through MoonPay Commerce without a UI.

> [!NOTE]
>
> This feature is currently supported **only on the Solana network**.

> [!NOTE]
>
> There's also an OpenClaw skill available for headless functions - [https://clawhub.ai/mavagio/mpc-accept-crypto-payments](https://clawhub.ai/mavagio/mpc-accept-crypto-payments)

# How It Works

* Merchants generate Pay Links that define the payment request, including supported currencies.
* Payers prepare a transaction via the API using the Pay Link and their public key.
* MoonPay Commerce provides the transaction payload (serialized transaction, message, and token) needed for signing.
* Payers sign and submit the transaction, completing the payment fully through the API without a UI.

## How to Get Started

1. **Create a Pay Link**

The merchant creates and shares a **Pay Link ID**. Optionally, they may also specify a **currency ID**.

* If no currency is provided, it will default to **USDC**.
* If USDC is not available, the first enabled currency on the Pay Link will be used.

2. **Prepare the Transaction**

The payer calls the [**prepare endpoint**](https://docs.hel.io/reference/transaction/headless/prepare), passing the following parameters:

* `paymentRequestId` - The Pay Link ID
* `senderPublicKey `- the payer's public key
* `currencyId` (optional)
* any other required fields (see API reference for full list)

3. **Sign the Transaction**

The `/prepare` endpoint responds with:

* `transactionToken` - Identifier required to submit the signed transaction.
* `transactionMessage` - Metadata or message tied to the transaction.
* `serializedTransaction` - Unsigned transaction data to be signed by the payer.
* `addressLookupTableAccounts` - Accounts used to resolve addresses in the transaction.

To sign the transaction, you can use the `WalletService` helper shown below:

```bash TypeScript theme={null}
import { Keypair, VersionedTransaction } from '@solana/web3.js'
import bs58 from 'bs58'
import { PrepareTransactionExtended } from '@heliofi/common'

class WalletService {
  private getPrivateKey(): string {
    // The base58 encoded private key.
    const privateKey = import.meta.env.VITE_WALLET_PRIVATE_KEY
    if (!privateKey) {
      throw new Error('Wallet private key not configured.')
    }
    return privateKey
  }

  createWallet() {
    try {
      const privateKey = this.getPrivateKey()
      // The Keypair object built from the private key.
      return Keypair.fromSecretKey(bs58.decode(privateKey))
    } catch (error) {
      console.error('Error creating wallet:', error)
      throw error
    }
  }

  async signTransaction(
    preparedTransaction: PrepareTransactionExtended,
  ): Promise<string> {
    try {
      const wallet = this.createWallet()
      // The serialized transaction from the prepared transaction (The response from the prepare endpoint).
      const { serializedTransaction } = preparedTransaction

      // Create a VersionedTransaction object from the serialized transaction.
      const vtx = VersionedTransaction.deserialize(
        Buffer.from(serializedTransaction!, 'base64'),
      )

      // Sign the transaction with the wallet.
      vtx.sign([wallet])

      // Serialize the signed transaction and return it as a base64 string.
      const signedAsBufferJson = Buffer.from(vtx.serialize()).toString('base64')

      return signedAsBufferJson
    } catch (error) {
      console.error('Error signing transaction:', error)
      throw new Error(
        `Failed to sign transaction: ${error instanceof Error ? error.message : 'Unknown error'}`,
      )
    }
  }

  getWalletAddress(): string {
    try {
      const wallet = this.createWallet()
      return wallet.publicKey.toBase58()
    } catch (error) {
      console.error('Error getting wallet address:', error)
      throw error
    }
  }
}

export const walletService = new WalletService()
```

4. **Submit the Transaction**

The payer calls the [**submit endpoint**](https://docs.hel.io/reference/transaction/headless/submit), providing:

* the `signedTransaction` value
* the `transactionToken` from the `/prepare` response.

This submits the fully signed transaction for processing.
