Dartvel, home
Docs
Features
Studio
Cloud
Compared

Search the site

GitHub
pub.dev

GETTING STARTED

Getting started
Existing Flutter apps
Existing Native apps
Run on your phone

APP

UI and styling
Routing
State
Accessibility
Keyboard shortcuts
Localization
Devices and desktop
Native device access
Media, 3D and XR

DATA

Data models
Forms
Search
Sync and offline
Import and export
Change capture
Database
Cache
File storage
Images
Privacy and erasure

BACKEND

Backend functions
Auth and sessions
Authorization
Queues and jobs
Workers and memory
Notifications and mail
Outbound HTTP
AI
Webhooks
GraphQL and OpenAPI
API keys and OAuth
Multi-tenancy
Billing and commerce
Modules

OPERATIONS

Edge security
Secrets and environments
Monitoring
Releases

SHIPPING

Build targets
Telegram Mini Apps
Static web hosting
Servers and deploying

REFERENCE

Testing
CLI reference
Coding agents

BACKEND

Billing and commerce

Sell subscriptions, charge with tax and promotions, and cap usage per customer, with every grant checked on the server.

Stripe, Paddle and the store paths are tested against fakes. None has been run against the live service yet.

ON THIS PAGE

Sell a plan through Stripe or Paddle

Check app store purchases on your server

Charge with tax and promotions, and refund cleanly

Meter usage and cap it per tenant

Sell a plan through Stripe or Paddle

Checkout reads the plan's price from the provider first and refuses when it differs from your plan, so a page cannot show one price and charge another.

Webhooks grant and revoke entitlements from subscription status, and each is believed only when its signature matches within five minutes.

Trials and usage meters are passed through to the provider.

// What you sell, and what each plan turns on. The price is the number the
// application is held to: checkout reads the provider's own price first and
// refuses when the two disagree, so a page cannot show one price and charge
// another.
const BillingPlan team = BillingPlan(
  id: 'team',
  displayName: 'Team',
  priceMinorUnits: 4900,
  currency: 'USD',
  trialDays: 14,
);

const Entitlement reports = Entitlement('reports');

Copy code to clipboard

void useStripe() {
  DV.Billing.useProvider(DVStripeBillingProvider(
    secretKey: DV.Secrets.get('STRIPE_SECRET_KEY'),
    webhookSecret: DV.Secrets.get('STRIPE_WEBHOOK_SECRET'),
    // Your plan id on the left, the provider's price on the right.
    prices: const <String, String>{'team': 'price_1234'},
    // What each plan grants once the subscription is live.
    entitlements: const <String, Set<Entitlement>>{
      'team': <Entitlement>{reports},
    },
    successUrl: Uri.parse('https://example.com/billing/done'),
    cancelUrl: Uri.parse('https://example.com/pricing'),
  ));
}

Copy code to clipboard

Future<Uri?> subscribe(Workspace workspace) async {
  final DVBillingCheckoutSession session =
      await DV.Billing.checkout(plan: team, customer: workspace);
  // Null when the provider gave back no hosted page. Send the customer
  // nowhere; a blank tab is worse.
  return session.checkoutUrl;
}

Copy code to clipboard

// Asked on the server, never read off the client: the page that shows a
// feature and the code that allows it must not be two different answers.
Future<bool> mayExport(Workspace workspace) =>
    DV.Billing.hasEntitlement(workspace, reports);

Copy code to clipboard

Partial

Spec section: Billing

Planned work and implementation limits

Neither provider has been exercised against the live service.

Stripe and Paddle keep webhook grants in memory, so a refund revokes a grant only with the local provider.

Check app store purchases on your server

DVPurchases writes a grant only from the store's own answer about a receipt, and refuses one bought under another account.

Store notifications are signature-checked, applied once and in order, and access ends when the paid period and grace end.

A refund or chargeback is told apart from a lapse, so DVCommerce can reverse the sale.

Partial

Spec section: Purchases and Entitlements

Planned work and implementation limits

No real App Store or Play adapters yet, and no StoreKit or Play Billing binding, so DV.Purchases.buy does not exist.

Products are not generated from models.

Charge with tax and promotions, and refund cleanly

DVTax asks your tax provider and refuses the sale when the answer does not add up. Nothing is ever taxed at zero by accident.

Promotions stack by group, respect global and per-customer limits under load, and are checked on the server.

DVCommerce.charge captures last, so a failure at any step leaves nothing behind. A refund reverses tax and revokes what the sale granted.

Partial

Spec section: Commerce: Tax, Promotions, Disputes and Payouts

Planned work and implementation limits

Tested against fakes only. No live tax, payment or payout provider has been run.

DVTaxTable ships no rates. It is the offline fallback and the reference for rounding.

Meter usage and cap it per tenant

Count or gauge anything per tenant, in memory or in your database. Each record needs an idempotency key, so a retry is not counted twice.

Limits can block, throttle or allow and bill, with thresholds announced once.

Each period's usage is reported to your billing provider, with a retry queue and a reconcile that is safe to run again.

Partial

Spec section: Usage Metering and Quotas

Planned work and implementation limits

No @DVMeter annotations, so meters are not generated or counted for you.

No dartvel meters commands, no Studio view, and no scheduled period close.

PREVIOUS Multi-tenancy One deployment, many customers
NEXT Modules Mount apps, and wrap Dart, npm, C, Rust, JVM or Swift code
GitHub
pub.dev
npm
Acknowledgements
Privacy
Terms

FSL-1.1-MIT licensed. Built with Dartvel.

Dartvel is made by

SigmaDev Digital

To the bottom