Page cover

Integration

CoindPay Payments Link provides a simple, universal way to initiate and complete payments. It works across web, mobile, and serE-commerce checkout pages, SaaS dashboards, or web-based payment portals.

Environment

CoindPay provides two environments for integration and testing: Testnet and Mainnet

Testnet

https://test.coindpay.xyz

The Testnet environment uses Ethereum Sepolia with ETH assets by default (and exclusively). It operates in Sandbox mode, which is restricted by an IP whitelist for security reasons. For more interface details, refer to Developers

To enable your test access:

Mainnet

https://coindpay.xyz

  • Payments support USDC on Solana and Base networks by default. Please confirm that your connected wallet matches the correct network in your account settings.

  • On/Off-Ramps support 450+ assets across 30+ networks. When creating Ramp orders, ensure that your selected asset type matches the corresponding wallet address.

1. Create Account

Before using CoindPay, you need to create an account. This account serves as a unified identity for both your developer role and merchant enterprise.

Steps:

  1. Visit the CoindPay website and choose one of the account creation methods:

    • EVM Wallet

    • SVM Wallet

  2. Bind your wallet address to CoindPay.

    ⚠️ Each account can bind one EVM wallet address and one SVM wallet address, but at least one wallet must be added to create an account.

  3. Email registration and team collaboration features are coming soon to offer greater flexibility for merchants and developers.

💡 Tip: Once your account is created, you will have a unified merchant identity to manage Payments Links, track orders, and manage team members.

A Payments Link is a convenient way to collect payments. You can generate a link and share it with customers or embed it into your applications.

Option 1: Few Products

If you have a small number of products:

  1. Create a Payments Link and provide the necessary form information at once.

  2. Share the link with your customers or embed it in your website/app.

If you have many products or need dynamic payment links:

  1. Create a Payments Link template.

  2. Dynamically append parameters to generate unlimited dynamic Payments Links.

  3. Share or embed these links into your applications or product flows.

All Payments Links support dynamic parameter appending only for the parameters listed in the table below. All other settings, such as payment methods, subscription type, and additional options, must be selected and configured directly in the payments form.

3. Multiple integrations

URL Parameters

You can extend the embedded checkout link by appending query parameters to the URL. This is especially useful when you want to dynamically pass metadata such as product title, description, price, images or others.

Parameter
Type
Description
Example

merchant_transaction_id

string*

Required. Unique identifier for the transaction generated by the merchant. Can be generated with randomUUID() to ensure uniqueness.

merchant_transaction_id=550e8400-e29b-41d4-a716-446655440000

title

string?

The product or payment title displayed on the checkout page.

title=Hello

desc

string?

A short description of the product or service.

desc=Text

price

string?

Payment amount in the supported fiat or crypto currency format. Each currency has its own min and max limits.

price=29.99

images

array?

A list of image objects to display in the checkout interface.

images=[{"url":"https://cdn.example.com/item.jpg"}]

email

string?

Pre-fill the customer's email in the checkout form.

embed_widget

boolean?

When true, renders the payment page in compact “widget” mode.

embed_widget=true

more

Please contact our Devs team for dynamic updates.

Example with Parameters

import { randomUUID } from 'crypto'

const payLink = 'https://test.coindpay.xyz/pay/link/4sA2-wKhjxkCkY0W42DnQ'

const merchant_transaction_id = randomUUID()

// Render to your dynamic product params
const paramsData = {
  merchant_transaction_id,  // '550e8400-e29b-41d4-a716-446655440000',
  title: 'Pro Subscription',
  desc: 'Monthly access to all features',
  price: 19.99,
  email: '[email protected]',
  // Support one or multiple cover images
  images: [ 
    { url: 'https://cdn.coindpay.xyz/stream/static/home/content/partners_bg_01.jpg' }, 
    { url:'https://picsum.photos/600/400' } 
  ],
  embed_widget: true,
})

export function getEncodePayLink(
  link: string,
  queryData?: Record<string, any>
): string {
  const url = new URL(link)

  Object.entries(queryData || {}).forEach(([key, value]) => {
    if (value === undefined || value === null) return
    url.searchParams.set(
      key,
      typeof value === 'object' ? JSON.stringify(value) : String(value)
    )
  })

  return url.toString()
}


const url = getEncodePayLink(payLink, paramsData)

// you can preview this url
// https://test.coindpay.xyz/pay/link/4sA2-wKhjxkCkY0W42DnQ?title=Pro+Subscription&desc=Monthly+access+to+all+features&price=19.99&email=test%40yourdomain.com&images=%5B%7B%22url%22%3A%22https%3A%2F%2Fcdn.coindpay.xyz%2Fstream%2Fstatic%2Fhome%2Fcontent%2Fpartners_bg_01.jpg%22%7D%2C%7B%22url%22%3A%22https%3A%2F%2Fpicsum.photos%2F600%2F400%22%7D%5D&embed_widget=true

When embed_widget mode is enabled and all input parameters are valid, the payment mini card will automatically launch for a seamless checkout experience.

💡 Tips:

  1. ? The value is optional, * the value is required.

  2. The merchant_transaction_id is required. Use randomUUID() to generate a unique ID for each transaction to avoid duplicates. If not provided, it won’t cause an error, but you may lose the ability to reliably track the order or query its status through this ID.

  3. The email value should be a user’s email address obtained in advance. If it’s in a valid format, it will be automatically pre-filled in the checkout form..

  4. The embed_widget flag enables a fast checkout mode. It only activates if all provided parameters are valid.

① Web Integration

E-commerce checkouts, SaaS dashboards, or any web-based payment page.

1. Embed via iFrame

Native HTML styles can be customized as needed.

You can freely customize the surrounding HTML, container styles, and transitions using TailwindCSS or your preferred styling approach.

// React component + TailwindCSS

const someCustomName = '', someCustomStyle = {}

const onIframeLoad = ()=> {} // If some scenes for iframe loading success

<iframe
  src={url}
  allowFullScreen
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
  className={someCustomName}
  style={someCustomStyle}
  onLoad={onIframeLoad}
/>

2. Redirect to Payment Page

Customize your content area (Button, image, etc.) style

<a href={url} target="_blank">
  Pay with CoindPay
</a>

Highlights:

  • ✅ No SDK required

  • ✅ Instant integration

  • ✅ Dynamic data and ui adaptation

② Mobile App Integration

iOS / Android / React Native / Flutter apps that need to embed or redirect to a payment flow.

1. WebView Embed

// React Native example

<WebView source={{ uri: url }} />

③ Server Integration

Automated payment creation, recurring billing, or backend-driven checkout systems.

API

Payments Link get payment product detail

GET /api/payment/payee

Payments Order get payment order detail

GET /api/payment/order

Handle the remaining workflow, using order API status and records to process callbacks automatically.

4. Account & Hooks Settings

Manage your account-related settings, including wallet bindings, event callback URLs, notifications, and developer or merchant information.

Go to More → Account/Wallet or Integration/Hooks to view and manage your developer information, webhook endpoints, and related configurations.

Developer settings

Wallet manage

Callback testing

Once your Webhooks are configured under Developer Settings → Callback URLs, CoindPay will automatically send webhook notifications to your specified endpoint whenever a payment event occurs (e.g., completed, failed, or pending).

Your server should handle these events, verify the request signature using your API Secret and update your own order database accordingly.

Payments/On-Ramps Status Reference

Below is a list of possible rampStatus values returned

when rampType=buy paymentMethod=card/mobile_pay

Status
Description

new

A new transaction has been created.

pending

Waiting for 3-D Secure verification from the user.

cancelled

The transaction was canceled, usually due to timeout or user action.

paid

The payment has been successfully deposited from the payment card.

order_failed

The transaction was rejected or declined by the bank.

order_scheduled

Funds are held on the card pending KYC verification; if verification fails, the funds will be released.

order_verified_not_complete

KYC verification succeeded, and the order has been queued on the exchange but not yet completed.

failed_exchange

Mercuryo failed to execute the exchange for various reasons (e.g., liquidity, rate, or technical issues).

Off-Ramp Status Reference

Below is a list of possible rampStatus values returned

when rampType=sell paymentMethod=card

Status
Description

new

A new transaction has been created.

pending

The transaction is in progress; payout processing has started.

succeeded

The payout has been successfully transferred to the user’s card.

failed

The transaction failed; the crypto amount will be returned to the user.

Below is an example based on Node (Next.js)

// The unique ID from your front-end with the customers
const merchant_transaction_id = 'xxx' // See => Example with Parameters

// https://api.yourdomain.com/webhooks/payments/landing
import type { NextApiRequest, NextApiResponse } from 'next'
import getRawBody from 'raw-body'
import { createHmac } from 'crypto'

function createSignature(secret: string, bodyString: string): string {
  return createHmac('sha256', secret).update(bodyString).digest('hex')
}

export const config = {
  api: {
    bodyParser: false, // ✅ Disable Next.js automatic body parsing
  },
}

export default async function landing(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ ok: false, message: 'Method not allowed ˙◠˙' })
  }
  
  try {
    const secretKey = process.env.COINDPAY_API_SECRET_KEY as string
    
    if (!secretKey) {
      throw new Error('Missing CoindPay API SECRET KEY in env ˙◠˙')
    }

    const signatureHeader = req.headers['x-signature'] as string

    if (!signatureHeader) {
      return res.status(400).json({
        ok: false,
        message: 'Invalid signature ˙◠˙',
      })
    }
    
    // 1. Retrieve the raw request body
    const rawBody = (await getRawBody(req)).toString('utf8')
    console.log('📦 raw body:', rawBody)
    
    // 2. Verify signature (must use rawBody for verification)
    const expectedSig = createSignature(secretKey, rawBody)

    if (signatureHeader !== expectedSig) {
      console.error(`🚫 x-signature mismatch:`, { expectedSig, got: signatureHeader })
      return res.status(401).json({ ok: false, message: 'Invalid signature ˙◠˙' })
    }
    
    // 3. Parse JSON payload after successful verification
    const data = JSON.parse(rawBody)
    console.log('💎 Parsed data:', data)
  
    if (!data || !data?.id) {
      return res.status(502).json({ ok: false, message: 'Invalid payment order ˙◠˙' })
    }
    
    console.log(`✅ CoindPay payment ${data?.id}-${data?.rampStatus} callback data:`, data)
    
    // post your order data to your db, create or poll for updates
    // const payment = await prisma.payment.upsert({
    //  where: { rampId: data.id },
    //  update: {
    //    ...data,
    //  },
    //  create: {
    //    ...data,
    //  },
    // })
    ....
  } catch (error) {
    console.error(`🚫 CoindPay order data storage failed:`, error?.message)
  }
}

💡 Tips:

  1. Keeping your secret key safe, don't forget add the .env file to .gitignore

  2. The rampId is required, from your front-end merchant_transaction_id

More tips

Sandbox payment card

1

Card #1

  • Card Number 4444 4444 4444 3333

  • Expiration Date any future date

  • CVV 123

  • Cardholder Name name + surname

2

Card #2

  • Card Number 5555 4444 3333 1111

  • Expiration Date any future date

  • CVV 123 for success, 555 for fail

  • Cardholder Name name + surname

Fiat payments demo

Last updated