Integrating billing in a single PR

5 mins

Nov 13, 2025

Summary

At Lark, we're building a billing platform for modern pricing. Something that truly absorbs all of your billing complexity so that you don’t end up with custom billing logic all over your codebase.

It’s 2025 and we’re expected to have simple APIs for everything, but surprisingly billing integrations still suck. A few months back we tried doing a billing integration with a large billing provider and were annoyed by how frustrating that experience was. So we decided to build a better billing platform.

Over the last weekend we re-attempted doing a billing integration (for turkey.uselark.ai). Only this time we did it side by side with a large billing provider vs our new billing platform.


Today's pricing models 

Most consumer / prosumer AI companies today (think replit, cursor, lovable, etc) offer some form of fixed fee + credits based pricing. The fixed fee pricing is familiar for consumers and the usage based credits help businesses safeguard their margins against high cost of LLMs.

For our project we opted for a similar pricing model - 

  1. On sign up every customer is subscribed to a free plan that offers 5 free monthly credits

  2. Each action consumes a single credit. If a user runs out of credits, they can upgrade to paid plans to get more credits. 

  3. The premium plan allows them to be charged at the end of month for any additional usage after 105 credits.


Billing integration without Lark

I won’t walk through the entire integration but here’s a summary of the challenges we ran into with one of the larger self serve billing providers - 

  1. Defining pricing - It’s super easy to define a fixed fee price but you can’t specify included credits as part of the price. We’d have to model credits and drawdowns in house in our database - this is what most people do today.

  1. Upgrades to paid plans - In our case every customer needs to be subscribed to a free plan that entitles them to 5 credits per month. When a customer upgrades, there is no payment method on file so the customer needs to be shown a payment screen or checkout. Our options are -

    1. Redirect the customer to a provider billing portal to add payment method and then coordinate a subscription update after - this is just an ugly disjointed experience.

    2. Collect payment method after a subscription update call but it requires manually handling the checkout flow and wrestling with complicated payment + subscription apis.


  2. Syncing billing state with webhooks - At several places (both frontend and backend) we need to check if a customer has an active subscription and sufficient credits to let them take an action. If they don’t, we need to show them a paywall. We can either

    1. always compute this billing state on the fly by calling into our billing provider and checking credits, or

    2. we can store it as a flag in our database.

    With the former you run into scale issues quickly and the latter requires careful management of webhooks which can be a nightmare.


Billing integration with Lark

We were able to do the integration in 5 simple steps in a single PR as outlined in this quickstart -

  1. Create rate cards that model included credits out of the box

  1. Subscribe customers to free plans on signup and start reporting usage to draw down credits

    // on signup
    subject = lark.subjects.create_subject(
    external_id=subject_external_id,
    name=name,
    email=email,
    )
    
    lark.subscriptions.create_subscription(
    subject_id=subject.id,
    rate_card_id=FREE_PLAN_RATE_CARD_ID,
    )
    
    // on user action
    lark.usage_events.create_usage_event(
        idempotency_key=idempotency_key,
        subject_id=subject_external_id,
        event_name=PRICING_METRIC_EVENT_NAME,
        data={
            "value": usage,
        },
    )
    
    
  1. Checking billing state on frontend using with sdks

    export async function getBillingState({subjectId}) {
        const billingState = await lark.customerAccess.getBillingState(subjectId);
        const activeSubscription = billingState.active_subscriptions[0];
        const subscribedRateCardId = activeSubscription.rate_card_id;
        const subscriptionId = activeSubscription.subscription_id;
    
        const includedCredits = billingState.usage_data[0].included_units;
        const usedCredits = parseInt(billingState.usage_data[0].used_units);
        
        return {
          subscriptionId,
          subscribedRateCardId,
          creditsRemaining: includedCredits - usedCredits,
        };
    }
  1. Support plan upgrades with a single API call that handles payment method collection if needed

    response = lark.subscriptions.change_subscription_rate_card(
        subscription_id=subscription_id,
        rate_card_id=new_rate_card_id,
        upgrade_behavior="rate_difference",
        checkout_callback_urls=CheckoutCallback(
            success_url=checkout_success_callback_url,
            cancelled_url=checkout_cancel_callback_url,
        ),
    )
    
    if response.result.type == "requires_action":
        # In this case frontend simply redirects to checkout & payment method collection is handled for us
        return UpdateSubscriptionResponse(
            type="checkout_action_required",
            checkout_url=response.result.action.checkout_url,
        )
    elif response.result.type == "success":
        return UpdateSubscriptionResponse(
            type="success",
            checkout_url=None,
        )
  1. Setup customer portal to let customers view their usage and cancel subscription if needed

    # frontend simply redirtects to the customer_portal_session.url to let users manage their subscription
    customer_portal_session = (
        lark.customer_portal.create_customer_portal_session(
            subject_id=subject_external_id,
            return_url=return_url,
        )
    )
    
    


Here’s a PR showing the integration done in just a few simple commits. You can see it in action at turkey.uselark.ai :)


Takeaway

Most engineers shouldn’t have to deal with billing complexities. They deserve a billing platform that truly abstracts away complexity while offering the configurability required by modern pricing models. 

Fun fact - me and my cofounder worked at a large billing provider as engineers. This is the mission we wanted to achieve there but it was too hard to pull off at a large company. So now we’re building Lark to be that ideal billing platform.

Feel free to reach out if you’re looking for help with your billing setup!