npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coinsph/ramp-sdk

v1.0.0

Published

Coins Ramp SDK - On-Ramp and Off-Ramp widget for merchants

Readme

@coinsph/ramp-sdk

Coins Ramp — On-Ramp (fiat → crypto) and Off-Ramp (crypto → fiat) for merchant integration.

Two integration methods:

| Method | Description | Recommended | |--------|-------------|:-----------:| | Link | Construct the buy/sell URL based on the parameters required by the document. | Yes | | SDK | JavaScript SDK opens the widget | — |

We recommend the Link method — it works everywhere (email, SMS, QR code, any web page), requires no frontend dependencies, and is the simplest integration path.


Table of Contents


Environments

| Environment | Base URL | Usage | |-------------|----------|-------| | production | https://coins.ph | Live transactions | | sandbox | https://9001.pl-qa.coinsxyz.me | Testing & development |


Prerequisites

Before integrating, obtain the following from Coins:

| Item | Description | |------|-------------| | apiKey | Public API key, identifies your merchant account in requests | | merchantId | Your unique merchant ID | | secretKey | Secret key, server-side only — used by your backend to compute signature. Never put it in frontend code. |

You also need a signing endpoint on your own backend that the client can call to obtain signature. See Authentication for the contract.

Contact Coins support to obtain credentials.


Authentication

Every Buy / Sell call needs three credentials: signature, timestamp, nonce.

Generation is split between client and server:

| Field | Generated by | How | |-------|--------------|-----| | timestamp | Client (frontend / link generator) | Milliseconds since epoch as a string, e.g. "1714200000000" | | nonce | Client (frontend / link generator) | UUID v4, must be unique per request | | signature | Your backend | HMAC-SHA256 of the business params + the client-provided timestamp and nonce, signed with secretKey, output as lowercase hex |

Flow

  1. Client generates a fresh timestamp and nonce.
  2. Client calls your backend signing endpoint, sending timestamp and nonce as HTTP headers (X-Timestamp, X-Nonce) along with the business parameters.
  3. Backend computes signature using secretKey + the headers + the business params, and returns just the signature.
  4. Client attaches all three values (signature, timestamp, nonce) to the URL (Link method) or to ramp.buy() / ramp.sell() (SDK method).

secretKey lives only on your server. The browser never sees it and never computes a signature.

Signing Parameters

The fields sent to the signing endpoint differ between Buy and Sell. Use the exact field names below — these must match what Coins uses for signature verification.

Buy (ON_RAMP) signing fields:

| Signing Field | Source | Description | |---------------|--------|-------------| | merchantId | Your merchant ID | — | | rampType | Fixed: ON_RAMP | — | | country | Country code | e.g. PH | | targetCurrency | Token symbol | e.g. USDT (maps to token in URL/SDK) | | targetNetwork | Network | e.g. SOL (maps to network in URL/SDK) | | targetAmount | Crypto amount | e.g. 100 (maps to amount in URL/SDK) | | targetAddress | Wallet address | (maps to address in URL/SDK) | | tag | Memo / Tag | Optional, only for XRP / XLM / EOS | | timestamp | Sent as HTTP header | Milliseconds since epoch | | nonce | Sent as HTTP header | UUID v4 |

Sell (OFF_RAMP) signing fields:

| Signing Field | Source | Description | |---------------|--------|-------------| | merchantId | Your merchant ID | — | | rampType | Fixed: OFF_RAMP | — | | country | Country code | e.g. PH | | sourceCurrency | Token symbol | e.g. USDT (maps to token in URL/SDK) | | sourceAmount | Crypto amount | e.g. 50 (maps to amount in URL/SDK) | | tag | Memo / Tag | Optional, only for XRP / XLM / EOS | | timestamp | Sent as HTTP header | Milliseconds since epoch | | nonce | Sent as HTTP header | UUID v4 |

Note: Sell does not include network or address in the signing fields.


Parameters

Both Buy and Sell use the same parameter set. The same values feed either the URL (Link method) or the SDK call (SDK method).

| Param | Required | In Link URL | In SDK | Description | |-------|:--------:|:-----------:|:------:|-------------| | type | Yes | Yes | — (use ramp.buy / ramp.sell) | buy or sell | | apiKey | Yes | Yes | In new CoinsRamp({ apiKey }) | Public API key | | merchantId | Yes | Yes | In new CoinsRamp({ merchantId }) | Merchant ID | | country | Yes | Yes | In new CoinsRamp({ country }) | Country code, e.g. PH | | token | Yes | Yes | Yes | Token symbol, e.g. USDT, BTC | | network | Yes | Yes | Yes | Network, e.g. SOL, TRC20, ERC20 | | address | Yes | Yes | Yes | Recipient (Buy) / Sender (Sell) wallet address | | amount | Yes | Yes | Yes | Crypto amount | | tag | No | Yes (if used) | Yes (if used) | Memo / Tag for XRP, XLM, EOS, etc. | | signature | Yes | Yes | Yes | Returned by your backend signing endpoint (see Authentication) | | timestamp | Yes | Yes | Yes | Milliseconds since epoch as string, e.g. "1714200000000" — generated client-side | | nonce | Yes | Yes | Yes | UUID v4, unique per request — generated client-side |


Method 1: Link Integration (Recommended)

The simplest way to integrate — your backend generates a signed URL, and the user opens it in a browser. No JavaScript SDK needed.

Use cases: email, SMS, QR code, <a> tags on any page, mobile WebView, anywhere a URL works.

URL Format

{BASE_URL}/ramp/widget
  ?type={buy|sell}
  &apiKey=...
  &merchantId=...
  &country=...
  &token=...
  &network=...
  &address=...
  &amount=...
  &tag=...           (optional)
  &signature=...
  &timestamp=...
  &nonce=...

{BASE_URL} is https://coins.ph (production) or https://9001.pl-qa.coinsxyz.me (sandbox). See Parameters for the full list.

Buy Link Example

https://coins.ph/ramp/widget?type=buy&apiKey=your-api-key&merchantId=2058690015133241344&country=PH&token=USDT&network=SOL&address=EKi9Z...wallet&amount=100&signature=abc123...&timestamp=1714200000000&nonce=f47ac10b-58cc-4372-a567-0e02b2c3d479

Sell Link Example

https://coins.ph/ramp/widget?type=sell&apiKey=your-api-key&merchantId=2058690015133241344&country=PH&token=USDT&network=SOL&address=EKi9Z...wallet&amount=50&signature=def456...&timestamp=1714200000000&nonce=a1b2c3d4-e5f6-7890-abcd-ef1234567890

Generating the Link

The link generator (your backend, or any service that builds the URL):

  1. Generates a fresh timestamp (ms) and nonce (UUID v4).
  2. Calls your signing endpoint with the business params, passing timestamp and nonce as HTTP headers.
  3. Receives signature from your backend.
  4. Builds the URL using URLSearchParams (which handles encoding for you).

Buy Link Generator:

import { v4 as uuidv4 } from 'uuid'

async function buildBuyLink({
  apiKey,
  merchantId,
  country,
  token,
  network,
  address,
  amount,
  tag,
}) {
  const timestamp = String(Date.now())
  const nonce = uuidv4()

  // Sign using the exact field names Coins expects for Buy (ON_RAMP)
  const res = await fetch('https://your-backend.example.com/ramp/sign', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Timestamp': timestamp,
      'X-Nonce': nonce,
    },
    body: JSON.stringify({
      merchantId,
      rampType: 'ON_RAMP',
      country,
      targetCurrency: token,
      targetNetwork: network,
      targetAmount: String(amount),
      targetAddress: address,
      ...(tag ? { tag } : {}),
    }),
  })
  const { signature } = await res.json()

  const params = new URLSearchParams({
    type: 'buy',
    apiKey,
    merchantId,
    country,
    token,
    network,
    address,
    amount: String(amount),
    ...(tag ? { tag } : {}),
    signature,
    timestamp,
    nonce,
  })

  return `https://coins.ph/ramp/widget?${params.toString()}`
}

Sell Link Generator:

import { v4 as uuidv4 } from 'uuid'

async function buildSellLink({
  apiKey,
  merchantId,
  country,
  token,
  network,
  address,
  amount,
  tag,
}) {
  const timestamp = String(Date.now())
  const nonce = uuidv4()

  // Sign using the exact field names Coins expects for Sell (OFF_RAMP)
  // Note: Sell does NOT include network or address in signing fields
  const res = await fetch('https://your-backend.example.com/ramp/sign', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Timestamp': timestamp,
      'X-Nonce': nonce,
    },
    body: JSON.stringify({
      merchantId,
      rampType: 'OFF_RAMP',
      country,
      sourceCurrency: token,
      sourceAmount: String(amount),
      ...(tag ? { tag } : {}),
    }),
  })
  const { signature } = await res.json()

  const params = new URLSearchParams({
    type: 'sell',
    apiKey,
    merchantId,
    country,
    token,
    network,
    address,
    amount: String(amount),
    ...(tag ? { tag } : {}),
    signature,
    timestamp,
    nonce,
  })

  return `https://coins.ph/ramp/widget?${params.toString()}`
}

Using the Link

HTML link:

<a href="https://coins.ph/ramp/widget?type=buy&..." target="_blank">
  Buy USDT with PHP
</a>

JavaScript:

const buyUrl = await fetch('/api/ramp/buy-link').then(r => r.text())
window.open(buyUrl, '_blank')

Email / SMS / QR code: drop the URL in directly — it works anywhere a hyperlink works.


Method 2: SDK Integration

Use the JavaScript SDK if you need programmatic control inside a web app.

Step 1: Install

npm install @coinsph/ramp-sdk
# or
yarn add @coinsph/ramp-sdk
# or
pnpm add @coinsph/ramp-sdk

Step 2: Import

import { CoinsRamp } from '@coinsph/ramp-sdk'

Step 3: Initialize

const ramp = new CoinsRamp({
  apiKey: 'your-api-key',
  merchantId: 'your-merchant-id',
  country: 'PH',                  // optional, default 'PH'
  environment: 'sandbox',         // 'production' | 'sandbox', default 'production'
})

| Config Field | Type | Required | Default | Description | |--------------|------|:--------:|---------|-------------| | apiKey | string | Yes | — | Your public API key | | merchantId | string | Yes | — | Your merchant ID | | country | string | No | 'PH' | Country code | | environment | 'production' | 'sandbox' | No | 'production' | Target environment |

Step 4: Open Widget

The frontend generates timestamp and nonce, sends them as headers to your backend signing endpoint, and receives signature back. See Authentication for the full flow.

Buy (On-Ramp):

import { v4 as uuidv4 } from 'uuid'

const timestamp = String(Date.now())
const nonce = uuidv4()

const params = {
  token: 'USDT',
  network: 'SOL',
  address: 'EKi9Z...your-wallet-address',
  amount: 100,
  // tag: '...',          // optional, only for XRP / XLM / EOS
}

// Sign using the exact field names Coins expects for Buy
const { signature } = await fetch('/api/ramp/sign', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Timestamp': timestamp,
    'X-Nonce': nonce,
  },
  body: JSON.stringify({
    merchantId: 'your-merchant-id',
    rampType: 'ON_RAMP',
    country: 'PH',
    targetCurrency: params.token,
    targetNetwork: params.network,
    targetAmount: String(params.amount),
    targetAddress: params.address,
    // ...(params.tag ? { tag: params.tag } : {}),
  }),
}).then(r => r.json())

ramp.buy({ ...params, signature, timestamp, nonce })

Sell (Off-Ramp):

import { v4 as uuidv4 } from 'uuid'

const timestamp = String(Date.now())
const nonce = uuidv4()

const params = {
  token: 'USDT',
  network: 'SOL',
  address: 'EKi9Z...sender-wallet-address',
  amount: 50,
}

// Sign using the exact field names Coins expects for Sell
// Note: Sell does NOT include network or address in signing fields
const { signature } = await fetch('/api/ramp/sign', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Timestamp': timestamp,
    'X-Nonce': nonce,
  },
  body: JSON.stringify({
    merchantId: 'your-merchant-id',
    rampType: 'OFF_RAMP',
    country: 'PH',
    sourceCurrency: params.token,
    sourceAmount: String(params.amount),
  }),
}).then(r => r.json())

ramp.sell({ ...params, signature, timestamp, nonce })

Both methods open the Coins widget in a new browser tab where the user completes the flow.

Step 5: Close Widget

ramp.close()

Programmatically closes the popup tab if it is still open.

Full React Example

import { useState } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { CoinsRamp } from '@coinsph/ramp-sdk'

function BuyCryptoButton() {
  const [ramp] = useState(
    () =>
      new CoinsRamp({
        apiKey: process.env.NEXT_PUBLIC_COINS_API_KEY!,
        merchantId: process.env.NEXT_PUBLIC_COINS_MERCHANT_ID!,
        environment: 'sandbox',
      }),
  )

  const handleBuy = async () => {
    const timestamp = String(Date.now())
    const nonce = uuidv4()

    const params = {
      token: 'USDT',
      network: 'SOL',
      address: 'EKi9Z...your-wallet-address',
      amount: 100,
    }

    // Sign using the exact field names Coins expects for Buy
    const { signature } = await fetch('/api/ramp/sign', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Timestamp': timestamp,
        'X-Nonce': nonce,
      },
      body: JSON.stringify({
        merchantId: process.env.NEXT_PUBLIC_COINS_MERCHANT_ID!,
        rampType: 'ON_RAMP',
        country: 'PH',
        targetCurrency: params.token,
        targetNetwork: params.network,
        targetAmount: String(params.amount),
        targetAddress: params.address,
      }),
    }).then(r => r.json())

    ramp.buy({ ...params, signature, timestamp, nonce })
  }

  return <button onClick={handleBuy}>Buy USDT</button>
}

CDN / UMD Usage

For pages without a bundler:

<script src="https://unpkg.com/uuid@latest/dist/umd/uuid.min.js"></script>
<script src="https://unpkg.com/@coinsph/ramp-sdk@latest/dist/umd/coins-ramp.js"></script>
<script>
  var ramp = new CoinsRampSDK.CoinsRamp({
    apiKey: 'your-api-key',
    merchantId: 'your-merchant-id',
    environment: 'sandbox',
  })

  document.getElementById('buyBtn').addEventListener('click', async function () {
    var timestamp = String(Date.now())
    var nonce = uuid.v4()
    var params = { token: 'USDT', network: 'SOL', address: 'EKi9Z...your-wallet-address', amount: 100 }

    // Sign using the exact field names Coins expects for Buy
    var res = await fetch('/api/ramp/sign', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Timestamp': timestamp,
        'X-Nonce': nonce,
      },
      body: JSON.stringify({
        merchantId: 'your-merchant-id',
        rampType: 'ON_RAMP',
        country: 'PH',
        targetCurrency: params.token,
        targetNetwork: params.network,
        targetAmount: String(params.amount),
        targetAddress: params.address,
      }),
    })
    var data = await res.json()

    ramp.buy(Object.assign({}, params, { signature: data.signature, timestamp: timestamp, nonce: nonce }))
  })
</script>

API Reference

CoinsRampConfig

| Field | Type | Required | Default | Description | |-------|------|:--------:|---------|-------------| | apiKey | string | Yes | — | Your public API key | | merchantId | string | Yes | — | Your merchant ID | | country | string | No | 'PH' | Country code | | environment | 'production' | 'sandbox' | No | 'production' | Target environment |

BuyParams / SellParams

| Field | Type | Required | Description | |-------|------|:--------:|-------------| | token | string | Yes | Token symbol, e.g. USDT | | network | string | Yes | Network, e.g. SOL, TRC20 | | address | string | Yes | Recipient (Buy) / Sender (Sell) wallet address | | amount | number | Yes | Crypto amount | | tag | string | No | Memo / Tag for XRP, XLM, EOS, etc. | | signature | string | Yes | From your backend signing endpoint (see Authentication) | | timestamp | string | Yes | Generated client-side, milliseconds since epoch | | nonce | string | Yes | Generated client-side, UUID v4 |

Methods

| Method | Description | |--------|-------------| | ramp.buy(params) | Open the Buy (On-Ramp) widget | | ramp.sell(params) | Open the Sell (Off-Ramp) widget | | ramp.close() | Close the widget tab if still open |


FAQ

Which integration method should I choose?

Link is recommended for most cases. It needs no frontend dependency and works anywhere a URL works (email, SMS, QR, web page, WebView).

Use SDK only if you need programmatic control from a single-page app (e.g. opening the widget from a React click handler with dynamic params).

Can I generate the signature in the frontend?

No. signature requires your secretKey, which must stay on your server. The frontend only generates timestamp and nonce, sends them as headers to your backend, and receives signature back. secretKey must never reach the browser.

Why does the popup not open?

Browsers block popups not triggered by a user click. With the SDK, always call ramp.buy() / ramp.sell() directly inside a user-initiated event handler (e.g. onClick). With the Link method, use <a target="_blank"> to avoid popup-blocker issues entirely.

What does the user see if the signature is invalid?

The widget shows an error and the user cannot proceed. Common causes: business param mismatch between sign request and final URL, expired timestamp, reused nonce. Regenerate timestamp / nonce on the client, request a fresh signature, and retry.


Support

For issues, questions, or feature requests, please contact Coins support