Skip to main content

Model 4 — Subscription

What the shopper experiences

The shopper subscribes to a try-on membership, such as $4.99/month for a monthly quota or unlimited-feeling access with a fair-use cap.

What the store configures in Genvoris

Create a plan representing the subscriber quota. Your billing system activates or cancels the shopper's Genvoris customer record based on subscription lifecycle events.

How billing flows

  • Shopper → store: recurring subscription payment through your billing system.
  • Store → Genvoris: try-on credits are consumed as subscribers use the widget.
  • Genvoris enforces quota and returns quota errors when limits are reached.

Complete code example

import express from 'express'
import Stripe from 'stripe'

const app = express()
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
const GEN_VORIS_API_KEY = process.env.GENVORIS_API_KEY!
const GEN_VORIS_BASE = 'https://genvoris.org'

async function genvoris(path: string, init: RequestInit = {}) {
const res = await fetch(`${GEN_VORIS_BASE}${path}`, {
...init,
headers: {
'content-type': 'application/json',
authorization: `Bearer ${GEN_VORIS_API_KEY}`,
...(init.headers ?? {}),
},
})
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`)
return res.json()
}

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
const event = stripe.webhooks.constructEvent(req.body, req.headers['stripe-signature']!, process.env.STRIPE_WEBHOOK_SECRET!)

if (event.type === 'customer.subscription.created' || event.type === 'customer.subscription.updated') {
const sub = event.data.object as Stripe.Subscription
const shopperId = sub.metadata.shopperId
const active = ['active', 'trialing'].includes(sub.status)

const plan = await genvoris('/api/v1/plans', {
method: 'POST',
body: JSON.stringify({ externalId: 'tryon-subscription', name: 'Try-on subscriber', monthlyTryOns: 250 }),
})

await genvoris('/api/v1/customers', {
method: 'POST',
body: JSON.stringify({
externalId: shopperId,
planId: active ? plan.id : null,
metadata: { model: 'SUBSCRIPTION', subscriptionId: sub.id, active },
}),
})
}

res.json({ received: true })
})

app.post('/api/tryon/session', express.json(), async (req, res) => {
const customer = await genvoris('/api/v1/customers', {
method: 'POST',
body: JSON.stringify({ externalId: req.body.shopperId, metadata: { model: 'SUBSCRIPTION' } }),
})

const session = await genvoris(`/api/v1/customers/${customer.id}/sessions`, {
method: 'POST',
body: JSON.stringify({ productId: req.body.productId }),
})

res.json({ token: session.token })
})

Growth for early subscriptions. Pro or Business if the subscription is bundled into a high-traffic loyalty program.

Native platform support

  • Shopify app: native when using Shopify subscription products; custom subscription systems should use the API.
  • WordPress plugin: requires WooCommerce Subscriptions; custom subscription systems should use the API.
  • API: supported with Stripe, Paddle, Shopify, WooCommerce, or custom billing.