Easy Labs
BillingMigration

Migrate from Stripe to Easy Billing

Mechanical mapping from Stripe's Billing API to Easy Labs.

This page is a side-by-side reference. The two APIs share the Customer / Product / Price / Subscription / Invoice / Coupon / Promotion code model, so most code is a method-name swap and a small payload translation. Differences worth knowing are called out under Object-model differences.

Subscriptions

StripeEasy Labs (@easylabs/node)Notes
stripe.subscriptions.create({ customer, items, default_payment_method })easy.createSubscription({ identity_id, items, instrument_id })customeridentity_id; default_payment_methodinstrument_id. Items use price_id + quantity in both.
stripe.subscriptions.retrieve(id)easy.getSubscription(id)Returns { data: SubscriptionData }.
stripe.subscriptions.update(id, body)easy.updateSubscription(id, body)cancel_at_period_end, proration_behavior, items, metadata are 1:1.
stripe.subscriptions.cancel(id, { invoice_now })easy.cancelSubscription(id, { at_period_end })Easy treats at_period_end: false (default) as "cancel now".
stripe.subscriptions.update(id, { pause_collection })easy.pauseSubscription(id, { behavior }) / easy.resumeSubscription(id)Dedicated endpoints. behavior is void / keep_as_draft / mark_uncollectible.
stripe.subscriptionItems.create / update / deleasy.addSubscriptionItem / updateSubscriptionItem / removeSubscriptionItemSame shape.
stripe.invoices.retrieveUpcoming({ subscription, subscription_items })easy.getSubscriptionProrationPreview(id, { items, remove_items })Returns the proration delta only (not a full upcoming invoice).
stripe.subscriptionItems.createUsageRecordeasy.reportSubscriptionUsage(subscriptionId, body)Same quantity + action (increment / set) + timestamp semantics.

Invoices

StripeEasy LabsNotes
stripe.invoices.create(body)easy.createInvoice(body)customerbuyer_id; line items pass inline as items[] rather than separate invoiceItems.create calls.
stripe.invoices.finalizeInvoice(id) + sendInvoice(id)easy.sendInvoice(id, { attach_pdf, cc_recipients })One call: finalizes and sends.
stripe.invoices.pay(id, { payment_method })easy.payInvoice(id, { instrument_id })Use idempotency_key on retries.
stripe.invoices.sendInvoice(id) (reminder)easy.remindInvoice(id)Sends a follow-up email on an already-sent invoice.
stripe.invoices.voidInvoice(id)easy.voidInvoice(id)Same.
stripe.invoices.list({ status, collection_method })easy.listInvoices({ status, collection_method, due_date_from, due_date_to })status enum is uppercase (OPEN, PAID, ...) on Easy.

Coupons and promotion codes

StripeEasy LabsNotes
stripe.coupons.create({ percent_off, duration, duration_in_months })easy.createCoupon({ percent_off, duration, duration_in_months })Same. Or pass amount_off + currency for fixed discounts.
stripe.promotionCodes.create({ coupon, code })easy.createPromotionCode({ coupon_id, code })couponcoupon_id. Same first_time_only, max_redemptions, minimum_amount.
stripe.promotionCodes.list({ code }) then check activeeasy.validatePromotionCode({ code, identity_id, amount })Single call returns valid, coupon, discount_preview, and reason.
stripe.subscriptions.update(id, { discounts: [{ coupon }] })easy.applySubscriptionDiscount(id, { coupon_id }) or { promotion_code }Dedicated endpoint. subscription_item_id scopes to one item.

Customer portal

StripeEasy LabsNotes
stripe.billingPortal.configurations.update(...)PATCH /v1/api/customer-portal-config/Toggles for payment methods, subscriptions, cancellations.
stripe.billingPortal.sessions.create({ customer, return_url })POST /v1/api/customer-portal/access/request-link ({ company_id, email, destination })Easy emails the customer a magic link instead of returning a session URL directly. For an inline same-tab handoff, use the access/handoff/create + access/handoff/exchange pair.

Webhooks

Both platforms sign webhooks with HMAC-SHA256 over the raw body. Headers and helpers:

StripeEasy Labs
Stripe-Signature header + stripe.webhooks.constructEvent(body, sig, secret)x-easy-webhook-signature: sha256=<hex> header + EasyWebhooks.constructEvent(body, sig, secret) from @easylabs/node
customer.subscription.created / .updated / .deletedsubscription.created / .updated / .deleted
customer.subscription.trial_will_endsubscription.trial_will_end
customer.subscription.paused / .resumedsubscription.paused / .resumed
invoice.created / .finalized / .paid / .payment_failed / .voidedSame names.
invoice.marked_uncollectibleinvoice.marked_uncollectible
coupon.created / .updated / .deletedcoupon.created / .updated / .deleted
promotion_code.created / .updatedpromotion_code.created / .updated / .deleted
checkout.session.completedcheckout.session.completed (plus checkout.session.crypto_confirmed if you accept crypto)

See the webhooks reference for the full event list and signature format.

Object-model differences

  • Customer is Identity under the hood — most fields you read are nested under data.entity.*. Anywhere Stripe takes customer, Easy takes identity_id.
  • Payment method is "instrument"payment_methodinstrument_id. The shape returned from createPaymentInstrument carries instrument-level enable/disable flags rather than card-level metadata.
  • No detached invoice items. Stripe's two-step invoiceItems.create + invoices.create is collapsed: pass items inline on createInvoice.
  • Single dunning policy per merchant. Stripe's per-customer retry rules are replaced by dunning-config + revenue-recovery automations applied globally.
  • Money is in the smallest currency unit (cents) on both platforms. currency is uppercase ISO 4217 on Easy (e.g. "USD").
  • Status enums are uppercase on Easy invoices (OPEN, PAID, VOID) but lowercase on subscriptions (active, trialing, past_due) — matching the underlying engines.
  • No expand. Endpoints return the linked records they need (e.g. getProductWithPrices); there is no generic expansion mechanism.

On this page