@misterhomer1992/payment-manager
v1.0.2
Published
Payment management library with WayForPay and Firebase integration
Readme
payment-manager
A TypeScript library for managing subscriptions, one-time token purchases, and payment webhooks with WayForPay and Cloud Firestore.
Framework-agnostic — wire it into Express, Fastify, Cloud Functions, or anything else.
Features
- Subscription lifecycle: create, upgrade/downgrade (with proration), cancel, expire
- One-time token pack purchases
- WayForPay webhook handling (payment confirmation + recurrent billing)
- Cron-compatible expiration checks with grace periods
- Payment URL caching (5-minute deduplication window)
- Typed error classes for every failure scenario
- Optional structured logging
Installation
npm install payment-manager firebase-adminfirebase-admin (>=12.0.0) is a peer dependency.
Quick Start
import * as admin from 'firebase-admin';
import { init } from 'payment-manager';
admin.initializeApp();
const pm = init({
firestore: admin.firestore(),
wayforpay: {
merchantAccount: 'your_merchant_account',
merchantSecretKey: 'your_merchant_secret_key',
merchantDomainName: 'example.com',
},
platform: 'my-app',
webhookSecret: 'your-webhook-secret',
logger: console,
});
await pm.plans.seedDefaultPlans([
{ id: 'basic-monthly', name: 'Basic', amount: 100, regularMode: 'monthly', isActive: true },
{ id: 'pro-monthly', name: 'Pro', amount: 300, regularMode: 'monthly', isActive: true },
]);
const { paymentUrl } = await pm.subscriptions.create({
userId: 'user-123',
planId: 'basic-monthly',
currency: 'UAH',
productName: 'My App Subscription',
});Configuration
interface PaymentManagerConfig {
firestore: Firestore;
wayforpay: {
merchantAccount: string;
merchantSecretKey: string;
merchantDomainName: string;
};
platform: string; // included in order references
webhookSecret?: string; // validates x-secret-key header on webhooks
logger?: { // any object with info/warn/error (console, pino, winston)
info: (message: string, ...args: unknown[]) => void;
warn: (message: string, ...args: unknown[]) => void;
error: (message: string, ...args: unknown[]) => void;
};
}API
init() returns a PaymentManager object with five namespaces.
pm.plans
| Method | Signature | Description |
|---|---|---|
| seedDefaultPlans | (plans: PlanInput[]) => Promise<void> | Upsert an array of plans by ID |
| create | (plan: PlanInput) => Promise<void> | Create a single plan |
| getById | (planId: string) => Promise<SubscriptionPlanEntity \| null> | Get a plan by ID |
| getAll | () => Promise<SubscriptionPlanEntity[]> | List all plans |
| update | (planId: string, data: Partial<PlanInput>) => Promise<void> | Update plan fields |
| deactivate | (planId: string) => Promise<void> | Mark a plan as inactive |
PlanInput=Omit<SubscriptionPlanEntity, 'createdAt' | 'updatedAt'>
pm.subscriptions
| Method | Signature | Description |
|---|---|---|
| create | (params: { userId, planId, currency, productName }) => Promise<{ paymentUrl }> | Create a subscription and return a WayForPay payment URL |
| getActiveByUserId | (userId: string) => Promise<SubscriptionEntity \| null> | Get the active subscription for a user |
| changePlan | (userId: string, newPlanId: string) => Promise<void> | Change plan (upgrade = immediate proration, downgrade = next billing cycle) |
| deactivate | (userId: string) => Promise<void> | Cancel subscription via WayForPay |
| expire | (userId: string) => Promise<void> | Manually expire a subscription |
pm.tokenPacks
| Method | Signature | Description |
|---|---|---|
| create | (pack: TokenPackInput) => Promise<string> | Create a token pack, returns the generated ID |
| getAll | () => Promise<TokenPackEntity[]> | List all token packs |
| activate | (packId: string) => Promise<void> | Activate a token pack |
| deactivate | (packId: string) => Promise<void> | Deactivate a token pack |
| remove | (packId: string) => Promise<void> | Delete a token pack |
| buyExtraTokens | (params: { userId, packId, currency, productName }) => Promise<{ paymentUrl }> | Purchase tokens, returns a one-time payment URL |
TokenPackInput=Omit<TokenPackEntity, 'id' | 'createdAt' | 'updatedAt'>
pm.userTokens
| Method | Signature | Description |
|---|---|---|
| getBalance | (userId: string) => Promise<UserTokenBalance> | Get user's token balance (returns zero-balance default if no record exists) |
pm.webhooks
| Method | Signature | Description |
|---|---|---|
| handlePaymentCheck | (reqBody, headers) => Promise<WebhookResponse> | Handle WayForPay PAYMENT_CHECK callback |
| handleSubscriptionDeactivationCheck | (headers) => Promise<DeactivationResult> | Expire overdue subscriptions and apply pending downgrades (call from a cron job) |
Errors
All errors extend PaymentManagerError with a code property:
import { PaymentManagerError } from 'payment-manager';
try {
await pm.subscriptions.create({ userId, planId, currency, productName });
} catch (error) {
if (error instanceof PaymentManagerError) {
console.error(error.code, error.message);
}
}| Error | Code |
|---|---|
| PlanNotFoundError | PLAN_NOT_FOUND |
| PlanInactiveError | PLAN_INACTIVE |
| SubscriptionNotFoundError | SUBSCRIPTION_NOT_FOUND |
| SubscriptionAlreadyActiveError | SUBSCRIPTION_ALREADY_ACTIVE |
| TokenPackNotFoundError | TOKEN_PACK_NOT_FOUND |
| TokenPackInactiveError | TOKEN_PACK_INACTIVE |
| InvalidWebhookSecretError | INVALID_WEBHOOK_SECRET |
| PaymentProviderError | PAYMENT_PROVIDER_ERROR |
| InvalidOrderReferenceError | INVALID_ORDER_REFERENCE |
Grace Periods
The deactivation check uses grace periods before expiring subscriptions:
| Billing Cycle | Grace Period | |---|---| | daily | 6 hours | | monthly | 1 day | | yearly | 1 day |
Firestore Collections
| Collection | Description |
|---|---|
| subscriptionPlans | Plan definitions |
| subscriptions | User subscription records |
| payments | Payment transactions |
| tokenPacks | Token pack definitions |
| userTokenBalances | Per-user token balances |
License
ISC
