// ── Manual card tokenization ──────────────────────────────────── // // Wraps `clover.createToken()` with a rate limiter. The limiter prevents // abusive call patterns from spamming Clover's API (the legacy WP plugin // observed double-submits from misconfigured themes/plugins). // // Default: 2 calls per 5-second window. import type { CloverIframeTokenizationResponse, CloverInstance } from './types'; export interface RateLimiter { /** Returns true if a call is allowed (and consumes a slot); false if rate-limited. */ check(): boolean; } export interface CreateRateLimiterOptions { readonly windowMs?: number; readonly maxCalls?: number; } export function createRateLimiter(options: CreateRateLimiterOptions = {}): RateLimiter { const windowMs = options.windowMs ?? 5000; const maxCalls = options.maxCalls ?? 2; const timestamps: number[] = []; return { check(): boolean { const now = Date.now(); const cutoff = now - windowMs; while (timestamps.length > 0 && timestamps[0] < cutoff) { timestamps.shift(); } if (timestamps.length >= maxCalls) { return false; } timestamps.push(now); return true; }, }; } export interface TokenizeOptions { readonly rateLimiter?: RateLimiter; } export async function tokenize( clover: CloverInstance, options: TokenizeOptions = {}, ): Promise { if (options.rateLimiter && !options.rateLimiter.check()) { throw new Error('Rate limit exceeded for tokenization. Wait a moment and try again.'); } return clover.createToken(); }