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
| Category | Rails |
|---|---|
| Credit & Debit Cards | Direct Pay (hosted fields / iframe / CCV prime) |
| Global Mobile Wallets | Apple Pay, Google Pay, Samsung Pay |
| Taiwan & Regional Wallets | LINE Pay, JKO Pay, Easy Wallet, Pi Wallet, iPASS MONEY, PXPay Plus, Plus Pay (台灣Pay), GoGo Pay, OP Pay |
| BNPL / Financing | AFTEE 先享後付, Pay Later (Atome) |
| Bank & Logistics | ATM 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.