npm zero dependencies MIT

tappay-web-module

One zero-dependency module, 18 TapPay payment rails. Drop-in dark/light checkout UI, or promise-based hexagonal adapters for your own UI. Built for Taiwan & APAC commerce.

Install

npm install tappay-web-module

Pure ESM, zero runtime dependencies. The TapPay SDK and Google Pay scripts are injected dynamically — no manual <script> tags.

Quick Start — Drop-In Checkout UI

Mount the whole payment surface — secure card fields, wallet buttons, and a grid of regional payment methods — with one widget:

<div id="payment-widget"></div>
<link rel="stylesheet" href="tappay-web-module/style.css" />

<script type="module">
  import { createTapPay, Checkout } from 'tappay-web-module';

  const client = createTapPay({
    appId: 159881,                                  // TapPay public sandbox App ID
    appKey: 'app_wh614Aq3T392m8Nu2xEP5Nc0Yq6oMfN7ky4y9GkkjZi3lFDkFC9Kb0nyDcWz',
    serverType: 'sandbox',                          // 'production' when live
  });

  const checkout = new Checkout(client, {
    theme: 'auto',                                  // 'light' | 'dark' | 'auto'
    mode: 'inline',                                 // 'inline' | 'modal'
    container: '#payment-widget',
    amount: 1280, currency: 'TWD', title: 'Order #1024',
    card: true,
    googlePay: { merchantName: 'Demo Store' },
    wallets: ['jkoPay', 'linePay', 'easyWallet', 'aftee', 'virtualAccount'],
    onPrime: async ({ method, prime, redirect }) => {
      const res = await fetch('/api/checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ prime, method, amount: 1280 }),
      }).then(r => r.json());
      if (redirect && res.payment_url) redirect(res.payment_url); // wallets
      else alert('Transaction complete! Order: ' + res.order_id);
    },
    onError: (err) => console.error(err.code, err.message),
  });

  checkout.mount();
</script>

All 18 Payment Rails

CategoryRails
Credit & Debit CardsDirect Pay (hosted fields / iframe / CCV prime)
Global Mobile WalletsApple Pay, Google Pay, Samsung Pay
Taiwan & Regional WalletsLINE Pay, JKO Pay, Easy Wallet, Pi Wallet, iPASS MONEY, PXPay Plus, Plus Pay (台灣Pay), GoGo Pay, OP Pay
BNPL / FinancingAFTEE 先享後付, Pay Later (Atome)
Bank & LogisticsATM Virtual Account, Cash on Delivery (超商取貨)

Low-Level Adapters (Bring Your Own UI)

Every rail exposes a uniform async contract: init(), renderButton(), getPrime(). Card example:

import { createTapPay } from 'tappay-web-module';

const client = createTapPay({ appId: 159881, appKey: 'app_...', serverType: 'sandbox' });
const card = client.card({
  fields: { number: '#card-num', expirationDate: '#card-exp', ccv: '#card-ccv' },
});
card.on('statuschange', (s) => { submitBtn.disabled = !s.canGetPrime; });
await card.mount();

submitBtn.onclick = async () => {
  const { prime } = await card.getPrime();
  // POST prime to your backend → TapPay Pay-by-Prime
};

Regional wallets share one universal adapter — 13 rails, one class:

const linePay = client.linePay('LINE Pay Express');
const { prime } = await linePay.getPrime();
await linePay.redirect(backendResponse.payment_url);

Server Side — Pay-by-Prime

Your backend exchanges the one-time prime for a real charge using the confidential Partner Key (never ship it to the browser):

POST https://sandbox.tappaysdk.com/tpc/payment/pay-by-prime
x-api-key: PARTNER_KEY

{ "prime": "…", "partner_key": "…", "merchant_id": "…",
  "amount": 1280, "currency": "TWD", "details": "Order #1024",
  "cardholder": { "phone_number": "+886…", "name": "…", "email": "…" } }

See examples/server.example.js in the repo for a full Express reference.

Architecture

Hexagonal ports & adapters: src/core/ owns SDK lifecycle & errors, src/payments/ owns bounded adapters per rail, src/ui/ owns the decoupled themeable checkout. Full decision history: Architecture Decision Records.