skills/vendix-saas-billing/SKILL.md
SaaS subscription billing for Vendix stores: plan pricing, invoices, Wompi platform payments, manual payments, partner commissions, payouts, proration, and dunning. Trigger: When creating SaaS invoices, working with partner rev-share, margin/surcharge pricing, invoice sequence allocation, partner payout batches, subscription payments, manual payments, or dunning flows.
npx skillsauth add rzyfront/vendix vendix-saas-billingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
subscription_invoices.split_breakdown.apps/backend/src/domains/store/subscriptions/services/subscription-billing.service.tsapps/backend/src/domains/store/subscriptions/services/subscription-payment.service.tsapps/backend/src/domains/store/subscriptions/services/subscription-manual-payment.service.tsapps/backend/src/domains/store/subscriptions/services/apps/backend/src/domains/superadmin/subscriptions/apps/backend/src/domains/superadmin/subscriptions/gateway/apps/backend/src/jobs/commission-accrual.job.ts, partner-payout-batch.job.tsapps/frontend/src/app/private/modules/store/subscription/apps/frontend/src/app/private/modules/super-admin/subscriptions/store_subscription_state_enum: draft, trial, active, grace_soft, grace_hard, suspended, blocked, cancelled, expired, pending_payment, no_plan.subscription_invoice_state_enum: draft, issued, paid, overdue, void, refunded, refunded_chargeback.subscription_payment_state_enum: pending, succeeded, failed, refunded, partial_refund.partner_commission_state_enum: accrued, pending_payout, paid, reversed, reversed_pending_recovery.store_subscriptions.lock_reason exists.organizations.fraud_blocked fields exist.SubscriptionBillingService.issueInvoice() is the core invoice path.
Current behavior:
GlobalPrismaService.store_subscriptions row with FOR UPDATE.pg_advisory_xact_lock(0x53414153) and format SAAS-YYYYMMDD-00001.base_price + margin_amount + fixed_surcharge.tax_amount=0, currency=sub.currency, line_items, and split_breakdown.unitPrice <= 0 and margin_amount <= 0; advances period and writes a renewed event with skipped_reason='zero_price'.metadata.pending_credit to non-prorated invoices, capped to subtotal, and rolls over excess.Use Prisma.Decimal for money. Round at storage boundaries, not mid-calculation.
subscription_invoices.split_breakdown is the partner/Vendix share contract. Store decimal values as strings when serializing JSON to avoid JS number precision issues.
Typical fields:
vendix_sharepartner_sharemargin_pct_usedpartner_org_idWhen reading it back, convert with new Prisma.Decimal(value).
SaaS subscription payments use platform Wompi credentials from PlatformGatewayService.getActiveCredentials('wompi').
Important distinctions:
WompiProcessor directly for platform Wompi charges.SubscriptionPaymentService.refund() still uses PaymentGatewayService.refundPayment(); do not claim SaaS never touches PaymentGatewayService.Current payment flow:
prepareWidgetCharge() creates a pending subscription_payments row and returns widget config.vendix_saas_{store_subscription_id}_{invoiceId}_{timestamp}.charge() supports saved Wompi recurrent source via provider_payment_source_id and legacy token fallback under WOMPI_RECURRENT_ENFORCE.WOMPI_CHARGE_PATH path=recurrent|legacy|no_pm|recurrent_failover.succeeded, invoice becomes paid, subscription may become active, card may be auto-registered, and commission outbox is upserted.Manual/offline payments are implemented.
subscription-manual-payment.service.ts.superadmin/subscriptions/controllers/manual-payment.controller.ts.POST /superadmin/subscriptions/invoices/:id/manual-payment.superadmin:subscriptions.subscription_payments(state='succeeded', payment_method='manual') with bank reference.paid.store_subscriptions.metadata.pending_credit.active in the same transaction.Super-admin plan service facts:
is_free forces base_price=0 and setup_fee=null.plan_type supports base, partner_custom, and promotional.plan_type === 'promotional'.setDefault() clears existing default and sets one active plan as default in a serializable transaction.Partner facts:
organizations with is_partner=true.partner_plan_overrides keyed by (organization_id, base_plan_id).PartnersService.createOverride() enforces margin_pct <= plan.max_partner_margin_pct.PartnersService; billing skips commission for promotional plans. Treat this as a known implementation gap if product policy requires blocking overrides.Commission accrual paths:
PartnerCommissionsService.accrueCommission(invoiceId) upserts partner_commissions(state='accrued') from invoice.split_breakdown.partner_share.commission-accrual.job.ts processes commission_accrual_pending, upserts commission as pending_payout, and promotes existing accrued to pending_payout.partner-payout-batch.job.ts runs 0 4 5 * *, creates monthly draft batches for previous-month accrued commissions, and transitions them to pending_payout.Known current inconsistency: commission-accrual.job.ts promotes commissions to pending_payout, while partner-payout-batch.job.ts selects only accrued. Do not document the payout lifecycle as perfectly consistent until this is resolved.
Admin payout service:
approve() moves draft batch to approved.rejectBatch() releases commissions back to accrued.markPaid() requires approved batch and sets batch plus commissions to paid.POST /platform/webhooks/wompi.@SkipSubscriptionGate().SAAS_WEBHOOK_ENABLED defaults enabled.^vendix_saas_(\d+)_(\d+)_\d+$.400; this differs from store Wompi webhooks.Store subscription routes include overview, plans, picker, payment, history, timeline, invoice detail, checkout, and dunning.
Key frontend facts:
SubscriptionFacade exposes observables and signals with toSignal(..., { initialValue }).no_plan path and filters promotional plans unless show_in_picker === true.base_price, billing_cycle, and currency.Number for money math.vendix-subscription-gate - Store write gate, AI feature gate, paywall behaviorvendix-payment-processors - Wompi processor and ecommerce payment processorsvendix-prisma-migrations - Safe schema/enum changesvendix-currency-formatting - Currency display/inputvendix-date-timezone - UTC period and display rulesvendix-auto-entries - Accounting entries for subscription paymentsdevelopment
Mobile app development rules for Vendix Expo/React Native project. Trigger: When editing, creating, or modifying any file under apps/mobile, or when developing mobile-specific features.
development
Feature gating by store subscription state: global store write guard, AI feature gate, Redis feature resolution, quota consumption, frontend paywall interceptor, banner, and subscription UI states. Trigger: When adding feature gates, paywalls, subscription-based access control, protecting store write operations, AI feature gates, or rollout flags.
development
Periodic quota counters with Redis, UTC period keys, Lua-based idempotent AI quota consumption, request-id deduplication, and post-success consumption. Trigger: When building quota counters, enforcing monthly/daily feature caps, or reusing AI quota patterns for uploads, emails, exports, or rate-limited features.
data-ai
Product and service variant rules for Vendix: variants as sellable options, inventory-independent availability, service variant booking overrides, and ecommerce/POS/cart behavior. Trigger: When creating, editing, validating, or selling product variants, service variants, products without stock, or any flow where variant availability must not be confused with inventory.