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

ouisys-widget-cc-pay

v1.0.7

Published

DynamicCCPay — embeddable credit-card / Apple Pay / Google Pay subscription widget.

Readme

ouisys-widget-cc-pay — DynamicCCPay

Full API reference: src/embed/README.md

An embeddable credit-card / Apple Pay / Google Pay subscription widget. A host page adds one <script> (or one npm import) and a <div>; the widget loads its config, renders the payment methods (wallets + card), and processes payment inline — packaged as a single self-contained file.

<script src="https://<cdn>/dynamic-cc-pay.js"></script>
<div id="pay"></div>
<script>
  // rockmanId is optional — the widget resolves/generates one itself.
  DynamicCCPay.mount('#pay', { xcid: 'xph20' });
</script>

Table of contents


Install

npm install ouisys-widget-cc-pay
# or
yarn add ouisys-widget-cc-pay

Peer dependencies: react >= 18 and react-dom >= 18 must be installed in the host project.

import { DynamicCCPay } from 'ouisys-widget-cc-pay';

DynamicCCPay.mount('#pay', {
  xcid: 'xph20',
  mode: 'card-applepay-google-pay',
  onSuccess: (result) => console.log('paid', result),
  onError:   (err)    => console.warn('payment failed', err)
});

For pure script-tag usage (no bundler), load dynamic-cc-pay.js from the CDN and call the global DynamicCCPay.mount(...) — see Loading the script.


Quick start

mount(target, config) returns a promise resolving to a MountResult:

const widget = await DynamicCCPay.mount('#pay', { xcid: 'xph20' });

widget.unmount();        // tear down + clean up
widget.tracker;          // push host product events (see Host tracking)
  • target may be a CSS selector or an HTMLElement.
  • Only a config object is required; rockmanId is optional (resolved/generated — see below).
  • All markup is scoped under .dynamic-cc-pay and never leaks into the host page.

Loading the script (ccWidgetHash)

The bundle is content-hashed (dynamic-cc-pay.<hash>.js) so it can be cached forever and busted atomically. The host needs to know which hash is current. Preferred: read it inline from the server-injected page config — no extra round-trip.

<script>
(function () {
  var base = '/path/to/widget/';                 // your CDN base path
  var hash = window.configJson
          && window.configJson.pageConfigs
          && window.configJson.pageConfigs.ccWidgetHash;

  function load(src) {
    var s = document.createElement('script');
    s.src = src; s.defer = true;
    s.onload  = boot;   // your DynamicCCPay.mount(...) call
    s.onerror = function () { console.error('[cc] widget failed to load: ' + src); };
    document.head.appendChild(s);
  }

  if (hash) {
    load(base + 'dynamic-cc-pay.' + hash + '.js');   // fast path — no round-trip
  } else {
    fetch(base + 'embed-manifest.json')              // fallback: discover the hash
      .then(function (r) { return r.json(); })
      .then(function (m) { load(base + m.main); })
      .catch(function (e) { console.error('[cc] manifest fallback failed', e); });
  }
})();
</script>

| State | Behavior | | --- | --- | | ccWidgetHash present | Loads dynamic-cc-pay.<hash>.js directly. No lookup. | | ccWidgetHash absent | Falls back to embed-manifest.json to discover the hash (one extra round-trip). | | Hash absent and manifest fetch fails | onerror / .catch fires; widget does not mount. |

Plain stable URL (dynamic-cc-pay.js, no hash) also works and always tracks the latest deploy, but it's fronted by a CDN with a short TTL and query strings are not in the cache key (?cb= won't bust it). Use the hashed URL when you need a fresh build immediately (e.g. verifying a deploy). This is what the demo pages use.


Config: two modes

A) By xcid (recommended)

The widget fetches the published page and scrapes the server-injected window.configJson from its HTML — the same config object the landing page runs on.

DynamicCCPay.mount('#pay', {
  xcid: 'xph20',                         // published page id (`xid` alias accepted)
  apiBaseUrl: 'https://<api-host>'       // API host; also the page host by default
});
  • Fetches ${pageUrl}/<xcid>; pageUrl defaults to apiBaseUrl, then '' (same-origin).
  • ?xcid= (alias ?xid=) query param overrides the host xcid — it always wins and is not persisted (each load re-reads it). Handy for pointing the same embed at a different published page for testing without touching the integration.
  • Same-page fast path: mounted on its own published page, the widget reuses the already-inlined window.configJson and skips the fetch when its page matches the mount's xcid. Pass forceFetchConfig: true to always re-fetch (debugging).
  • Optional configUrl: if a JSON config endpoint exists, the widget GETs ${configUrl}?id=<xcid> and parses { pageConfigs } instead of scraping HTML.

B) Inline (no xcid)

Pass the full config directly — no network fetch. Useful when the host already holds the merchant data, or for local testing.

DynamicCCPay.mount('#pay', {
  apiBaseUrl: 'https://<api-host>',
  slug: 'cc_celerispay-example50_001-',
  gateway: 'celeris',
  service: { id: 'example-service', displayName: 'Example Service' },
  trialPrice: '0.01',
  isLocalCurrency: true,
  countryCode: 'SE',
  payments: {
    applePay: {
      bankId: 0,
      merchantIdentifier: 'merchant.com.example.service',
      label: 'Example Service',
      supportedNetworks: ['visa', 'masterCard']
    },
    googlePay: { bankId: 0 /* + gateway params Google Pay isReadyToPay requires */ }
  }
});

Visitor id (rockmanId)

rockmanId is the per-visitor id every payment and analytics call carries. It is optional — the widget resolves it in this order:

  1. ?rockmanId= URL query param (alias ?rockman_id=) — always wins.
  2. config.rockmanId passed to mount().
  3. A persisted id (sessionStorage) — reused across page→page navigation so the whole funnel keeps one visitor id.
  4. A freshly generated id — minted on the first load of a tab and on every hard refresh (a reload deliberately starts a new visitor session).

When no host/query id is supplied, the generated id behaves like this:

| Action | Result | | --- | --- | | Navigate page → page (same tab) | Same id (from sessionStorage) | | Back / forward | Same id | | Hard refresh (F5 / reload) | New id | | New tab / close + reopen tab | New id (sessionStorage is per-tab) | | ?rockmanId= present in URL | That id is used regardless |

Reload vs. navigation is distinguished via the Navigation Timing API. If sessionStorage is unavailable (private mode), it degrades to a fresh id per load.

Production note: the analytics backend only accepts events whose rockmanId is a real server-issued 32-hex id. A generated fallback id still drives the payment UI and API calls, but its tracking events are rejected (HTTP 400). Pass a real id in production; the fallback exists for embeds that can't.

The resolved id is mirrored to window.pac_analytics.userId and window.pac_analytics.visitor.rockmanId, so the wallet hooks, card flow, and analytics all share one consistent value.


Mount config reference

| Key | Type | Default | Notes | | --- | --- | --- | --- | | xcid / xid | string | — | Published page id (xcid mode). Overridable via ?xcid=. | | rockmanId | string | resolved/generated | Per-visitor id. See Visitor id. | | apiBaseUrl | string | '' (same-origin) | Host for /api/v1/frontend/*; also default page host. | | pageUrl | string | apiBaseUrl | Override the published-page host for the config scrape. | | configUrl | string | — | Dedicated JSON config endpoint instead of HTML scrape. | | forceFetchConfig | boolean | false | Skip the same-page fast path and always re-fetch. | | analyticsUrl | string | derived | Only if analytics lives on a different host. | | countryCode | string | — | Sets d_country / d_currency; required for the card-flow slug. | | paymentMethods | string[] | ['googlePay','applePay','card'] | Which methods, in order. Aliases accepted. | | mode | string | 'tab' | Layout — see modes. | | requireConsent | boolean | true | Render legal consent checkboxes; block payment until ticked. | | walletRequireConsent | boolean | mirrors requireConsent | Consent toggle for wallet tabs independently. | | slug, gateway, service, trialPrice, isLocalCurrency, payments | — | — | Inline-mode config fields. | | onSuccess(result) | fn | — | Payment API success (before gateway redirect). | | onError(error) | fn | — | Merchant-validation / payment failure. |


Payment methods, modes & consent

Payment methods (paymentMethods)

  • Canonical ids: 'card', 'applePay', 'googlePay'. Aliases are case/space-insensitive ('apple', 'apple pay', 'gpay', 'credit-card', …).
  • Default: ['googlePay', 'applePay', 'card'].
  • A listed method only renders if the device supports it (Apple Pay needs an Apple device; Google Pay needs isReadyToPay; the card form is always available).

Display mode (mode)

| Value | Layout | | --- | --- | | 'tab' (default) | Two grouped tabs — Pay (Apple Pay + Google Pay) and Card. Tab bar shows only when both groups are available. | | 'inline' | Wallet group and card form stacked with an "or" divider. No tab bar. | | 'card-applepay-google-pay' | One tab per method — Card → Apple Pay → Google Pay. | | 'applepay-google-pay-card' | One tab per method — Apple Pay → Google Pay → Card. |

Consent gating (requireConsent / walletRequireConsent)

| requireConsent | walletRequireConsent | Card | Apple Pay / Google Pay | | --- | --- | --- | --- | | true (default) | not set | consent required | consent required | | false | not set | no consent | no consent | | true | false | consent required | no consent | | false | true | no consent | consent required |

Use requireConsent: false when the host page collects consent before mounting.


Callbacks & host tracking

onSuccess(result) fires when the payment API returns success, before the gateway redirect. onError(error) fires on merchant-validation or payment failure. Both are advisory — a throwing callback never breaks the flow, and the redirect still happens.

The host can push its own product events through the widget's already-configured analytics pipeline (same endpoint + rockmanId):

const widget = await DynamicCCPay.mount('#pay', { xcid: 'xph20' });

widget.tracker.customEvent(category, action, label, data); // generic event
widget.tracker.advancedInFlow(flow, step, data);           // funnel forward
widget.tracker.recedeInFlow(flow, step, data);             // funnel back / drop
widget.tracker.advancedInPreFlow(trigger, data);           // pre-payment CTA

The same object is exposed as DynamicCCPay.tracker. It is wired once mount() resolves; calling earlier is a safe no-op that logs a warning.


Styling

All visual properties read --dccp-* CSS custom properties. Set them on .dynamic-cc-pay (or any ancestor) to re-theme without a rebuild:

.dynamic-cc-pay {
  --dccp-accent:       #e11d48;   /* primary CTA / focus / active tab */
  --dccp-accent-2:     #9f1239;   /* gradient end */
  --dccp-radius:       16px;      /* field + button corners */
  --dccp-font:         'Poppins', sans-serif;
  --dccp-text:         #1e293b;
  --dccp-muted:        #64748b;
  --dccp-border:       #cbd5e1;
  --dccp-field-bg:     #ffffff;
  --dccp-field-height: 48px;
  --dccp-error:        #dc2626;
}

Development

yarn install
yarn dev           # run the widget as a React app w/ live reload → http://localhost:8093
yarn build:embed   # → dist/dynamic-cc-pay.js
yarn demo:embed    # build + serve demos on http://localhost:8000

yarn dev mounts the widget (inline config, src/embed/dev.tsx) in a dev page and hot-reloads on edits to src/embed/*. No backend needed to render the UI.

Demos (after yarn demo:embed):

  • embed-xcid-demo-8.html — full interactive demo: mode / methods / consent toggles, host-tracker button.
  • embed-inline-demo.html — inline config (no backend needed to render), with a tracking-verification panel.
  • embed-xcid-demo.html — xcid (page-scrape) config example.

The demo pages load the stable dynamic-cc-pay.js URL so they always track the latest deploy. After a deploy, allow a short interval for the CDN to refresh.


Build & deploy

yarn build:embed   # single self-contained file, assets inlined
yarn build:lib     # library build (dist-lib) for npm consumers — runs on `npm publish`
yarn deploy:embed  # build:embed + upload to S3/CDN (needs AWS credentials)

deploy:embed uploads a stable key, a content-hashed sibling (dynamic-cc-pay.<md5>.js, served fresh by the CDN immediately), an embed-manifest.json, and the demo HTML files.

  • Configure the target S3 bucket and key prefix in upload-embed.js.
  • Credentials: supply standard AWS credentials via the env vars the deploy script reads.

Publishing to npm

This package publishes as a public unscoped package (publishConfig.access). prepublishOnly runs build:lib automatically.

npm login          # an account with publish rights to this package
npm publish

Prerequisites (backend)

The UI renders anywhere, but payments will not complete until these are in place:

  1. apiBaseUrl — required off the SamMedia origin, or all /api/v1/frontend/* calls 404.
  2. CORS — the SamMedia backend must allow the host origin (preflight + POST) on /api/v1/frontend/ap-validate, /ap-payment, /gp-payment, /initiate-payment-generic, and the analytics store endpoint.
  3. Apple Pay domain registration — the host domain must be registered with Apple and served over HTTPS, or ApplePaySession won't validate the merchant. (Google Pay needs none.)
  4. rockmanId — optional, but supply a real server-issued id in production so tracking is accepted.
  5. Google Pay configpayments.googlePay must include the gateway's allowedCardNetworks / merchant params, or isReadyToPay fails and the button is hidden.
  6. countryCode — the card flow appends d_country / d_currency to build the final slug. Supported local-currency countries: se no gb dk us sa ca pl ae qa kw om bh jo is au nz.

Quick triage: buttons missing → config didn't load (check the GET to ${pageUrl||apiBaseUrl}/<xcid>: 404 = wrong xcid/host, CORS error = pages host not allowing the origin) or wallet unavailable on device. Buttons present but tap fails → CORS / 404 on /api/v1/frontend/* or Apple domain not registered.


Project layout

src/embed/            widget entry, mount, config resolution, tracker, styles, README
src/components/       payment UI reused by the widget (card form, wallets, …)
src/providers/        RootContext (shared runtime state)
src/services/         Apple Pay / Google Pay API calls
src/utils/            wallet handlers, config maps, rockmanId helpers
src/localization/     react-intl messages + translations
src/__doNotModify/    ouisys-engine store / flows wiring
webpack.embed.js      single-file build (assets inlined)
webpack.lib.js        library build (dist-lib) for npm consumers
upload-embed.js       S3/CDN deploy (stable + content-hashed + manifest)

The complete, exhaustive API reference lives in src/embed/README.md.