@carllee1983/tappay-backend-payment-node
v1.7.0
Published
TapPay Backend Payment SDK for Node.js - A TypeScript SDK for TapPay Backend Payment APIs with full type support
Maintainers
Readme
TapPay Backend Payment SDK
A TypeScript SDK for TapPay Backend Payment APIs
Features
- 🔒 Full TypeScript Support - Complete type definitions for all APIs
- 🚀 Modern Architecture - Built with ES Modules and CommonJS support
- ⚡ Zero Dependencies - Uses native
fetchAPI (Node.js 18+) - 🛡️ Error Handling - Typed error classes for precise error handling
- 📦 Small Bundle Size - Minimal footprint for production use
Installation
npm install @carllee1983/tappay-backend-payment-node-node
# or
yarn add @carllee1983/tappay-backend-payment-node-node
# or
pnpm add @carllee1983/tappay-backend-payment-node-node
# or
bun add @carllee1983/tappay-backend-payment-node-nodeQuick Start
import { TapPayClient, Env, Currency } from '@carllee1983/tappay-backend-payment-node-node'
// Create client
const client = new TapPayClient({
partnerKey: 'your_partner_key',
merchantId: 'your_merchant_id',
env: Env.Sandbox, // Use Env.Production for production
})
// Pay by Prime
const payment = await client.payByPrime({
prime: 'prime_from_frontend',
amount: 100,
currency: Currency.TWD,
details: 'Product Description',
cardholder: {
phone_number: '+886912345678',
name: 'Test User',
email: '[email protected]',
},
})
console.log(`Transaction ID: ${payment.rec_trade_id}`)API Reference
TapPayClient
The main client class for interacting with TapPay APIs.
Constructor
new TapPayClient({
partnerKey: string, // Required: Partner Key from TapPay Portal
merchantId: string, // Required: Merchant ID
env?: Env, // Optional: Env.Sandbox (default) or Env.Production
timeout?: number, // Optional: Request timeout in ms (default: 30000)
})Payment Methods
Pay by Prime
Process a payment using a prime token from frontend SDK.
const response = await client.payByPrime({
prime: 'test_prime_123',
amount: 100,
currency: Currency.TWD,
details: 'Test Payment',
cardholder: {
phone_number: '+886912345678',
name: 'Test User',
email: '[email protected]',
},
// Optional: Enable 3D Secure
three_domain_secure: true,
result_url: {
frontend_redirect_url: 'https://example.com/payment/success',
backend_notify_url: 'https://example.com/api/notify',
},
// Optional: Remember card for future payments
remember: true,
})
// If remember=true, save these for future payments
if (response.card_secret) {
const { card_key, card_token } = response.card_secret
// Store securely for recurring payments
}Pay by Card Token
Process a payment using saved card credentials.
const response = await client.payByToken({
card_key: 'saved_card_key',
card_token: 'saved_card_token',
amount: 100,
currency: Currency.TWD,
details: 'Recurring Payment',
})Transaction Management
Refund
Process a full or partial refund.
// Full refund
await client.refund('D20231201123456789')
// Partial refund
await client.refund('D20231201123456789', { amount: 50 })Query Records
Retrieve transaction records with filtering and pagination.
const records = await client.getRecords({
records_per_page: 10,
page: 0,
filters: {
time: {
start_time: Date.now() - 7 * 24 * 60 * 60 * 1000, // Last 7 days
end_time: Date.now(),
},
},
order_by: {
attribute: 'time',
is_descending: true,
},
})Get Single Transaction
const record = await client.getTransaction('D20231201123456789')
if (record) {
console.log(`Status: ${record.status}`)
}Get Trade History
Get detailed transaction history including all events.
const history = await client.getTradeHistory('D20231201123456789')
history.trade_history?.forEach(event => {
console.log(`${event.event_type}: ${event.status}`)
})Advanced APIs
Capture Today
Capture a delayed-capture transaction immediately.
await client.capToday('D20231201123456789')Cancel Capture
Cancel a pending capture before bank batch processing.
await client.cancelCapture('D20231201123456789')Cancel Refund
Cancel a pending refund (currently only supported by Taishin Bank).
await client.cancelRefund('D20231201123456789', 'R20231201123456789')Card Management
Bind Card
Bind a card for future token-based payments without charging.
const response = await client.bindCard({
prime: 'prime_from_frontend',
currency: Currency.TWD,
cardholder: {
phone_number: '+886912345678',
name: 'Test User',
email: '[email protected]'
}
})
if (response.card_secret) {
const { card_key, card_token } = response.card_secret
// Store for future payments
}Remove Card
Remove a bound card from TapPay servers.
await client.removeCard('card_key_123', 'card_token_123')Error Handling
The SDK provides typed error classes for precise error handling. All errors extend the standard Error class and can be caught using try-catch blocks.
Error Types
TapPayError
Thrown when the TapPay API returns an error response (e.g., invalid prime, insufficient funds, card declined).
import { TapPayError } from '@carllee1983/tappay-backend-payment-node'
try {
const response = await client.payByPrime({ ... })
} catch (error) {
if (error instanceof TapPayError) {
console.error(`API Error: ${error.message}`)
console.error(`Status Code: ${error.status}`)
console.error(`Transaction ID: ${error.recTradeId}`)
// Check if transaction failed
if (error.isTransactionFailed) {
// Handle failed transaction
}
}
}Common TapPay Error Status Codes:
10001: Invalid prime10002: Invalid card number10003: Insufficient funds10004: Card declined- See TapPay Documentation for complete list
TapPayValidationError
Thrown when input validation fails before making the API request (e.g., missing required fields, invalid values).
import { TapPayValidationError } from '@carllee1983/tappay-backend-payment-node'
try {
await client.payByPrime({
prime: '', // Empty prime will trigger validation error
amount: 100,
})
} catch (error) {
if (error instanceof TapPayValidationError) {
console.error(`Validation Error: ${error.message}`)
console.error(`Field: ${error.field}`) // e.g., 'prime', 'amount'
}
}Common Validation Errors:
- Missing required fields (prime, amount, currency, etc.)
- Empty strings for required fields
- Invalid amount (zero or negative)
- Empty transaction IDs
TapPayTimeoutError
Thrown when the API request times out.
import { TapPayTimeoutError } from '@carllee1983/tappay-backend-payment-node'
try {
await client.payByPrime({ ... })
} catch (error) {
if (error instanceof TapPayTimeoutError) {
console.error(`Request timed out after ${error.timeout}ms`)
console.error(`Endpoint: ${error.endpoint}`)
// Consider retrying the request
}
}TapPayConfigError
Thrown when client configuration is invalid (e.g., missing partner key, invalid timeout).
import { TapPayConfigError } from '@carllee1983/tappay-backend-payment-node'
try {
const client = new TapPayClient({
partnerKey: '', // Empty key will trigger config error
merchantId: 'test_merchant_id',
})
} catch (error) {
if (error instanceof TapPayConfigError) {
console.error(`Config Error: ${error.message}`)
console.error(`Field: ${error.field}`) // e.g., 'partnerKey', 'merchantId'
}
}Complete Error Handling Example
import {
TapPayClient,
TapPayError,
TapPayConfigError,
TapPayTimeoutError,
TapPayValidationError,
} from '@carllee1983/tappay-backend-payment-node'
async function processPayment(prime: string, amount: number) {
try {
const response = await client.payByPrime({
prime,
amount,
currency: Currency.TWD,
details: 'Product Purchase',
})
return { success: true, transactionId: response.rec_trade_id }
} catch (error) {
if (error instanceof TapPayValidationError) {
// Input validation failed - fix the input and retry
console.error(`Invalid input: ${error.field} - ${error.message}`)
return { success: false, error: 'INVALID_INPUT', field: error.field }
} else if (error instanceof TapPayTimeoutError) {
// Request timed out - consider retrying
console.error(`Request timeout: ${error.endpoint}`)
return { success: false, error: 'TIMEOUT', retryable: true }
} else if (error instanceof TapPayError) {
// API returned an error
console.error(`Payment failed: ${error.message} (Status: ${error.status})`)
return {
success: false,
error: 'PAYMENT_FAILED',
status: error.status,
transactionId: error.recTradeId,
}
} else {
// Unexpected error
console.error('Unexpected error:', error)
return { success: false, error: 'UNKNOWN' }
}
}
}Error Handling Best Practices
- Always handle errors: Wrap API calls in try-catch blocks
- Check error types: Use
instanceofto handle different error types appropriately - Log errors: Include error details in logs for debugging
- Retry timeouts: Consider retrying on
TapPayTimeoutError - Validate input: Catch
TapPayValidationErrorearly to provide better user feedback
Backend Notify
For 3D Secure and e-payment transactions, TapPay will POST to your backend_notify_url:
import type { BackendNotifyPayload } from '@carllee1983/tappay-backend-payment-node'
// Express.js example
app.post('/api/notify', (req, res) => {
const payload: BackendNotifyPayload = req.body
if (payload.status === 0) {
// Payment successful
console.log(`Transaction ${payload.rec_trade_id} completed`)
} else {
// Payment failed
console.log(`Transaction failed: ${payload.msg}`)
}
res.status(200).send('OK')
})Currency Support
The SDK supports multiple currencies:
import { Currency, CurrencyMultiplier } from '@carllee1983/tappay-backend-payment-node'
Currency.TWD // Taiwan Dollar (multiplier: 1)
Currency.USD // US Dollar (multiplier: 100)
Currency.JPY // Japanese Yen (multiplier: 1)
Currency.EUR // Euro (multiplier: 100)
// ... and moreNote: For currencies with multiplier of 100, the amount should be multiplied by 100. For example, USD $1.00 should be sent as
amount: 100.
Test Cards
For sandbox testing, use TapPay's test cards:
- Success:
4242424242424242 - Failed:
4111111111111111
See TapPay Documentation for more test cards.
Requirements
- Node.js >= 18.0.0 (for native fetch support)
- TypeScript >= 5.0 (for development)
License
MIT © Carl Lee
