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

@palomma/payment-widget

v0.1.0

Published

React payment button for Palomma. Creates an invoice, opens the hosted payment page in a modal, and reports the transaction result.

Downloads

78

Readme

@palomma/payment-widget

React payment button for Palomma. One click:

  1. Opens a centered popup window (synchronously, so popup blockers allow it) with a loading screen.
  2. Creates an invoice (POST /invoices) and navigates the popup to the hosted payment page (paymentUrl). A real browser window — not an iframe — so bank flows like PSE that block framing work normally.
  3. Polls GET /invoices/{id} while the customer pays.
  4. When the invoice reaches a terminal status (paid, cancelled, chargeback), closes the popup and optionally shows the result below the button.

Pending payments are never lost or duplicated:

  • If the customer closes the popup mid-payment, polling continues in the background (onDismiss fires, the button switches to "Continuar pago"). A payment that resolves at the bank after the window closed still settles.
  • Clicking again reopens the same invoice instead of creating a new one.
  • The pending invoice is persisted in localStorage keyed by invoice.reference, so a page reload resumes it too. Use a stable reference per logical payment (your order id) — if you generate a fresh reference on every render, resume cannot match it after a reload.
  • When the invoice expires (its expirationDate, or 30 minutes without one), the button locks as "Verificando el pago…" and polling continues: only a terminal status from the API (Palomma cancels expired invoices server-side) releases the button and allows a new invoice.

Install

npm install @palomma/payment-widget

React 17+ is a peer dependency.

Quick start (sandbox)

import { PalommaPaymentButton } from '@palomma/payment-widget';

export function Checkout() {
  return (
    <PalommaPaymentButton
      apiKey={import.meta.env.VITE_PALOMMA_SANDBOX_KEY}
      environment="sandbox"
      invoice={{
        reference: `INV-${Date.now()}`,
        amount: 50000, // COP
        description: 'Suscripción mensual',
        customerDocumentNumber: '900123456',
        documentType: 'nit',
        customerName: 'Empresa Ejemplo SAS',
      }}
      onPaymentEnd={(invoice) => console.log('final status:', invoice.status)}
    >
      Pagar con Palomma
    </PalommaPaymentButton>
  );
}

Production: keep the API key off the browser

An apiKey passed to the component is visible to anyone who opens your page's source. For production, proxy the two calls through your backend and pass callbacks instead — the component behaves identically:

<PalommaPaymentButton
  invoice={{ ... }}
  createInvoice={async (params) => {
    const res = await fetch('/api/palomma/invoices', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(params),
    });
    return res.json();
  }}
  getInvoice={async (id) => {
    const res = await fetch(`/api/palomma/invoices/${id}`);
    return res.json();
  }}
/>

Your backend endpoints simply forward to the Palomma API with the secret key in the Authorization: Bearer header.

Props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | invoice | CreateInvoiceParams | required | Invoice created on click. redirectUrl defaults to the current page URL. | | apiKey | string | — | Palomma API key (sandbox/internal use). | | environment | 'production' \| 'sandbox' | 'production' | Which API base URL to use with apiKey. | | createInvoice | (params) => Promise<Invoice> | — | Backend-proxied creation; takes precedence over apiKey. | | getInvoice | (id) => Promise<Invoice> | — | Backend-proxied lookup used for polling. | | pollIntervalMs | number | 3000 | Status polling interval while the popup is open. | | showResult | boolean | true | Render the transaction result below the button when the payment ends. | | resume | boolean | true | Persist the pending invoice (localStorage, keyed by reference) and resume it across clicks and reloads instead of creating duplicates. | | popupWidth / popupHeight | number | 480 / 760 | Payment popup window size. | | onPaymentStart | (invoice) => void | — | Invoice created, popup navigated to the payment page. | | onPaymentEnd | (invoice) => void | — | Invoice reached paid, cancelled, or chargeback. With resume, may fire shortly after mount if the payment finished before a reload. | | onError | (error) => void | — | Invoice creation failed, or the popup was blocked. | | onDismiss | () => void | — | Customer closed the popup while the payment was still pending (background polling continues). | | children | ReactNode | 'Pagar' | Button label. | | className / style / disabled | — | — | Standard button customization. When className is set, the default inline styles are dropped entirely so your CSS is in full control. |

The API helpers are also exported for standalone use: createInvoice(options, params), getInvoiceById(options, id), and PalommaApiError.

Notes

  • Amounts are in COP, integer values between 3,000 and 550,000,000.
  • Payment completion is detected by polling the invoice status, not by the redirectUrl — so the redirect target does not need any special handling.
  • The popup is opened synchronously inside the click handler, which is what keeps popup blockers happy. If a blocker still intervenes (rare), the button shows an error asking the customer to allow popups. Note that some mobile browsers open the popup as a new tab; the flow works the same there.

Development

npm install
npm run build     # bundles ESM + CJS + .d.ts into dist/
npm run typecheck

Publishing

npm version patch
npm publish --access public